SHARE
TWEET

Untitled

a guest Jan 26th, 2019 424 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. """OS Modules environ method to get the setup vars from the Environment"""
  2. # import built-in & third-party modules
  3. import time
  4. import datetime
  5. from math import ceil
  6. import random
  7. from sys import platform
  8. from platform import python_version
  9. import os
  10. import csv
  11. import json
  12. import requests
  13. from selenium import webdriver
  14. from selenium.webdriver import DesiredCapabilities
  15. from pyvirtualdisplay import Display
  16. import logging
  17. from contextlib import contextmanager
  18. from copy import deepcopy
  19. import unicodedata
  20.  
  21. # import InstaPy modules
  22. from .clarifai_util import check_image
  23. from .comment_util import comment_image
  24. from .comment_util import verify_commenting
  25. from .comment_util import get_comments_on_post
  26. from .like_util import check_link
  27. from .like_util import verify_liking
  28. from .like_util import get_links_for_tag
  29. from .like_util import get_links_from_feed
  30. from .like_util import get_tags
  31. from .like_util import get_links_for_location
  32. from .like_util import like_image
  33. from .like_util import get_links_for_username
  34. from .like_util import like_comment
  35. from .login_util import login_user
  36. from .settings import Settings
  37. from .print_log_writer import log_follower_num
  38. from .print_log_writer import log_following_num
  39.  
  40. from .time_util import sleep
  41. from .time_util import set_sleep_percentage
  42. from .util import get_active_users
  43. from .util import validate_username
  44. from .util import web_address_navigator
  45. from .util import interruption_handler
  46. from .util import highlight_print
  47. from .util import dump_record_activity
  48. from .util import truncate_float
  49. from .util import save_account_progress
  50. from .unfollow_util import get_given_user_followers
  51. from .unfollow_util import get_given_user_following
  52. from .unfollow_util import unfollow
  53. from .unfollow_util import unfollow_user
  54. from .unfollow_util import follow_user
  55. from .unfollow_util import follow_restriction
  56. from .unfollow_util import dump_follow_restriction
  57. from .unfollow_util import set_automated_followed_pool
  58. from .unfollow_util import get_follow_requests
  59. from .commenters_util import extract_information
  60. from .commenters_util import users_liked
  61. from .commenters_util import get_photo_urls_from_profile
  62. from .relationship_tools import get_following
  63. from .relationship_tools import get_followers
  64. from .relationship_tools import get_unfollowers
  65. from .relationship_tools import get_nonfollowers
  66. from .relationship_tools import get_fans
  67. from .relationship_tools import get_mutual_following
  68. from .database_engine import get_database
  69. from .text_analytics import text_analysis
  70. from .text_analytics import yandex_supported_languages
  71. from .browser import set_selenium_local_session
  72. from .browser import close_browser
  73.  
  74. # import exceptions
  75. from selenium.common.exceptions import NoSuchElementException
  76.  
  77.  
  78. class InstaPyError(Exception):
  79.     """General error for InstaPy exceptions"""
  80.     pass
  81.  
  82.  
  83. class InstaPy:
  84.     """Class to be instantiated to use the script"""
  85.  
  86.     def __init__(self,
  87.                  username=None,
  88.                  password=None,
  89.                  nogui=False,
  90.                  selenium_local_session=True,
  91.                  use_firefox=False,
  92.                  browser_profile_path=None,
  93.                  page_delay=25,
  94.                  show_logs=True,
  95.                  headless_browser=False,
  96.                  proxy_address=None,
  97.                  proxy_chrome_extension=None,
  98.                  proxy_port=None,
  99.                  disable_image_load=False,
  100.                  bypass_suspicious_attempt=False,
  101.                  bypass_with_mobile=False,
  102.                  multi_logs=True):
  103.  
  104.         if nogui:
  105.             self.display = Display(visible=0, size=(800, 600))
  106.             self.display.start()
  107.  
  108.         self.browser = None
  109.         self.headless_browser = headless_browser
  110.         self.proxy_address = proxy_address
  111.         self.proxy_port = proxy_port
  112.         self.proxy_chrome_extension = proxy_chrome_extension
  113.         self.multi_logs = multi_logs
  114.         self.selenium_local_session = selenium_local_session
  115.         self.bypass_suspicious_attempt = bypass_suspicious_attempt
  116.         self.bypass_with_mobile = bypass_with_mobile
  117.         self.disable_image_load = disable_image_load
  118.  
  119.         self.show_logs = show_logs
  120.         Settings.show_logs = show_logs or None
  121.  
  122.         self.username = username or os.environ.get('INSTA_USER')
  123.         self.password = password or os.environ.get('INSTA_PW')
  124.         Settings.profile["name"] = self.username
  125.         self.nogui = nogui
  126.         self.logfolder = Settings.log_location + os.path.sep
  127.         if self.multi_logs is True:
  128.             self.logfolder = '{0}{1}{2}{1}'.format(
  129.                 Settings.log_location, os.path.sep, self.username)
  130.         if not os.path.exists(self.logfolder):
  131.             os.makedirs(self.logfolder)
  132.  
  133.         self.page_delay = page_delay
  134.         self.switch_language = True
  135.         self.use_firefox = use_firefox
  136.         Settings.use_firefox = self.use_firefox
  137.         self.browser_profile_path = browser_profile_path
  138.  
  139.         self.do_comment = False
  140.         self.comment_percentage = 0
  141.         self.comments = ['Cool!', 'Nice!', 'Looks good!']
  142.         self.photo_comments = []
  143.         self.video_comments = []
  144.  
  145.         self.do_reply_to_comments = False
  146.         self.reply_to_comments_percent = 0
  147.         self.comment_replies = []
  148.         self.photo_comment_replies = []
  149.         self.video_comment_replies = []
  150.  
  151.         self.liked_img = 0
  152.         self.already_liked = 0
  153.         self.liked_comments = 0
  154.         self.commented = 0
  155.         self.replied_to_comments = 0
  156.         self.followed = 0
  157.         self.already_followed = 0
  158.         self.unfollowed = 0
  159.         self.followed_by = 0
  160.         self.following_num = 0
  161.         self.inap_img = 0
  162.         self.not_valid_users = 0
  163.         self.video_played = 0
  164.         self.already_Visited = 0
  165.  
  166.         self.follow_times = 1
  167.         self.do_follow = False
  168.         self.follow_percentage = 0
  169.         self.dont_include = set()
  170.         self.white_list = set()
  171.         self.blacklist = {'enabled': 'True', 'campaign': ''}
  172.         self.automatedFollowedPool = {"all": [], "eligible": []}
  173.         self.do_like = False
  174.         self.like_percentage = 0
  175.         self.smart_hashtags = []
  176.  
  177.         self.dont_like = ['sex', 'nsfw']
  178.         self.mandatory_words = []
  179.         self.ignore_if_contains = []
  180.         self.ignore_users = []
  181.  
  182.         self.user_interact_amount = 0
  183.         self.user_interact_media = None
  184.         self.user_interact_percentage = 0
  185.         self.user_interact_random = False
  186.         self.dont_follow_inap_post = True
  187.  
  188.         self.use_clarifai = False
  189.         self.clarifai_api_key = None
  190.         self.clarifai_models = []
  191.         self.clarifai_workflow = []
  192.         self.clarifai_probability = 0.50
  193.         self.clarifai_img_tags = []
  194.         self.clarifai_img_tags_skip = []
  195.         self.clarifai_full_match = False
  196.         self.clarifai_check_video = False
  197.         self.clarifai_proxy = None
  198.  
  199.         self.potency_ratio = 1.3466
  200.         self.delimit_by_numbers = True
  201.  
  202.         self.max_followers = 90000
  203.         self.max_following = 66834
  204.         self.min_followers = 35
  205.         self.min_following = 27
  206.  
  207.         self.delimit_liking = False
  208.         self.liking_approved = True
  209.         self.max_likes = 1000
  210.         self.min_likes = 0
  211.  
  212.         self.delimit_commenting = False
  213.         self.commenting_approved = True
  214.         self.max_comments = 35
  215.         self.min_comments = 0
  216.         self.comments_mandatory_words = []
  217.         self.max_posts = None
  218.         self.min_posts = None
  219.         self.skip_business_categories = []
  220.         self.dont_skip_business_categories = []
  221.         self.skip_business = False
  222.         self.skip_no_profile_pic = False
  223.         self.skip_private = True
  224.         self.skip_business_percentage = 100
  225.         self.skip_no_profile_pic_percentage = 100
  226.         self.skip_private_percentage = 100
  227.  
  228.         self.relationship_data = {
  229.             username: {"all_following": [], "all_followers": []}}
  230.  
  231.         self.simulation = {"enabled": True, "percentage": 100}
  232.  
  233.         self.mandatory_language = False
  234.         self.mandatory_character = []
  235.         self.check_letters = {}
  236.  
  237.         # use this variable to terminate the nested loops after quotient
  238.         # reaches
  239.         self.quotient_breach = False
  240.         # hold the consecutive jumps and set max of it used with QS to break
  241.         # loops
  242.         self.jumps = {"consequent": {"likes": 0, "comments": 0, "follows": 0,
  243.                                      "unfollows": 0},
  244.                       "limit": {"likes": 7, "comments": 3, "follows": 5,
  245.                                 "unfollows": 4}}
  246.  
  247.         # stores the features' name which are being used by other features
  248.         self.internal_usage = {}
  249.  
  250.         if (
  251.                 self.proxy_address and self.proxy_port > 0) or \
  252.                 self.proxy_chrome_extension:
  253.             Settings.connection_type = "proxy"
  254.  
  255.         self.aborting = False
  256.         self.start_time = time.time()
  257.  
  258.         # assign logger
  259.         self.logger = self.get_instapy_logger(self.show_logs)
  260.  
  261.         get_database(make=True)  # IMPORTANT: think twice before relocating
  262.  
  263.         webhookurl = "https://api.telegram.org/botXXXXXXX/sendMessage"
  264.  
  265.         payload = {
  266.             "chat_id": "-378468384",
  267.             "text": "-------------------------------\n\nSession *started* " + datetime.datetime.now().strftime(
  268.                 "%H:%M %d/%m/%Y") + " for _" + format(self.username) + "_",
  269.             "parse_mode": "Markdown"
  270.         }
  271.  
  272.         requests.post(webhookurl, json=payload,
  273.                       headers={"Content-type": "application/json;charset=UTF-8"})
  274.  
  275.         if self.selenium_local_session is True:
  276.             self.set_selenium_local_session()
  277.  
  278.     def get_instapy_logger(self, show_logs):
  279.         """
  280.        Handles the creation and retrieval of loggers to avoid
  281.        re-instantiation.
  282.        """
  283.  
  284.         existing_logger = Settings.loggers.get(self.username)
  285.         if existing_logger is not None:
  286.             return existing_logger
  287.         else:
  288.             # initialize and setup logging system for the InstaPy object
  289.             logger = logging.getLogger(self.username)
  290.             logger.setLevel(logging.DEBUG)
  291.             file_handler = logging.FileHandler(
  292.                 '{}general.log'.format(self.logfolder))
  293.             file_handler.setLevel(logging.DEBUG)
  294.             extra = {"username": self.username}
  295.             logger_formatter = logging.Formatter(
  296.                 '%(levelname)s [%(asctime)s] [%(username)s]  %(message)s',
  297.                 datefmt='%Y-%m-%d %H:%M:%S')
  298.             file_handler.setFormatter(logger_formatter)
  299.             logger.addHandler(file_handler)
  300.  
  301.             if show_logs is True:
  302.                 console_handler = logging.StreamHandler()
  303.                 console_handler.setLevel(logging.DEBUG)
  304.                 console_handler.setFormatter(logger_formatter)
  305.                 logger.addHandler(console_handler)
  306.  
  307.             logger = logging.LoggerAdapter(logger, extra)
  308.  
  309.             Settings.loggers[self.username] = logger
  310.             Settings.logger = logger
  311.             return logger
  312.  
  313.     def set_selenium_local_session(self):
  314.         self.browser, err_msg = set_selenium_local_session(self.proxy_address,
  315.                                                            self.proxy_port,
  316.                                                            self.proxy_chrome_extension,
  317.                                                            self.headless_browser,
  318.                                                            self.use_firefox,
  319.                                                            self.browser_profile_path,
  320.                                                            # Replaces
  321.                                                            # browser User
  322.                                                            # Agent from
  323.                                                            # "HeadlessChrome".
  324.                                                            self.disable_image_load,
  325.                                                            self.page_delay,
  326.                                                            self.logger)
  327.         if len(err_msg) > 0:
  328.             raise InstaPyError(err_msg)
  329.  
  330.     def set_selenium_remote_session(self, selenium_url='',
  331.                                     selenium_driver=None):
  332.         """
  333.        Starts remote session for a selenium server.
  334.        Creates a new selenium driver instance for remote session or uses
  335.        provided
  336.        one. Useful for docker setup.
  337.  
  338.        :param selenium_url: string
  339.        :param selenium_driver: selenium WebDriver
  340.        :return: self
  341.        """
  342.         if self.aborting:
  343.             return self
  344.  
  345.         if selenium_driver:
  346.             self.browser = selenium_driver
  347.         else:
  348.             if self.use_firefox:
  349.                 self.browser = webdriver.Remote(
  350.                     command_executor=selenium_url,
  351.                     desired_capabilities=DesiredCapabilities.FIREFOX)
  352.             else:
  353.                 self.browser = webdriver.Remote(
  354.                     command_executor=selenium_url,
  355.                     desired_capabilities=DesiredCapabilities.CHROME)
  356.  
  357.         message = "Session started!"
  358.         highlight_print(self.username, message, "initialization", "info",
  359.                         self.logger)
  360.         print('')
  361.  
  362.         return self
  363.  
  364.     def login(self):
  365.         """Used to login the user either with the username and password"""
  366.         if not login_user(self.browser,
  367.                           self.username,
  368.                           self.password,
  369.                           self.logger,
  370.                           self.logfolder,
  371.                           self.switch_language,
  372.                           self.bypass_suspicious_attempt,
  373.                           self.bypass_with_mobile):
  374.             message = "Wrong login data!"
  375.             highlight_print(self.username,
  376.                             message,
  377.                             "login",
  378.                             "critical",
  379.                             self.logger)
  380.  
  381.             self.aborting = True
  382.  
  383.         else:
  384.             message = "Logged in successfully!"
  385.             highlight_print(self.username,
  386.                             message,
  387.                             "login",
  388.                             "info",
  389.                             self.logger)
  390.             # try to save account progress
  391.             try:
  392.                 save_account_progress(self.browser,
  393.                                       self.username,
  394.                                       self.logger)
  395.             except Exception:
  396.                 self.logger.warning(
  397.                     'Unable to save account progress, skipping data update')
  398.  
  399.         self.followed_by = log_follower_num(self.browser,
  400.                                             self.username,
  401.                                             self.logfolder)
  402.         self.following_num = log_following_num(self.browser,
  403.                                                self.username,
  404.                                                self.logfolder)
  405.  
  406.         return self
  407.  
  408.     def set_sleep_reduce(self, percentage):
  409.         set_sleep_percentage(percentage)
  410.         return self
  411.  
  412.     def set_action_delays(self,
  413.                           enabled=False,
  414.                           like=None,
  415.                           comment=None,
  416.                           follow=None,
  417.                           unfollow=None,
  418.                           randomize=False,
  419.                           random_range=(None, None),
  420.                           safety_match=True):
  421.         """ Set custom sleep delay after actions """
  422.         Settings.action_delays.update({"enabled": enabled,
  423.                                        "like": like,
  424.                                        "comment": comment,
  425.                                        "follow": follow,
  426.                                        "unfollow": unfollow,
  427.                                        "randomize": randomize,
  428.                                        "random_range": random_range,
  429.                                        "safety_match": safety_match})
  430.  
  431.     def set_do_comment(self, enabled=False, percentage=0):
  432.         """
  433.         Defines if images should be commented or not.
  434.        E.g. percentage=25 means every ~4th picture will be commented.
  435.        """
  436.  
  437.         if self.aborting:
  438.             return self
  439.  
  440.         self.do_comment = enabled
  441.         self.comment_percentage = percentage
  442.  
  443.         return self
  444.  
  445.     def set_comments(self, comments=None, media=None):
  446.         """Changes the possible comments"""
  447.         if self.aborting:
  448.             return self
  449.  
  450.         if (media not in [None, 'Photo', 'Video']):
  451.             self.logger.warning('Unkown media type! Treating as "any".')
  452.             media = None
  453.  
  454.         self.comments = comments or []
  455.  
  456.         if media is None:
  457.             self.comments = comments
  458.         else:
  459.             attr = '{}_comments'.format(media.lower())
  460.             setattr(self, attr, comments)
  461.  
  462.         return self
  463.  
  464.     def set_do_follow(self, enabled=False, percentage=0, times=1):
  465.         """Defines if the user of the liked image should be followed"""
  466.         if self.aborting:
  467.             return self
  468.  
  469.         self.follow_times = times
  470.         self.do_follow = enabled
  471.         self.follow_percentage = percentage
  472.  
  473.         return self
  474.  
  475.     def set_do_like(self, enabled=False, percentage=0):
  476.         if self.aborting:
  477.             return self
  478.  
  479.         self.do_like = enabled
  480.         self.like_percentage = percentage
  481.  
  482.         return self
  483.  
  484.     def set_dont_like(self, tags=None):
  485.         """Changes the possible restriction tags, if one of this
  486.         words is in the description, the image won't be liked but user
  487.         still might be unfollowed"""
  488.         if self.aborting:
  489.             return self
  490.  
  491.         if not isinstance(tags, list):
  492.             self.logger.warning('Unable to use your set_dont_like '
  493.                                 'configuration!')
  494.             self.aborting = True
  495.  
  496.         self.dont_like = tags or []
  497.  
  498.         return self
  499.  
  500.     def set_mandatory_words(self, tags=None):
  501.         """Changes the possible restriction tags, if all of this
  502.         hashtags is in the description, the image will be liked"""
  503.         if self.aborting:
  504.             return self
  505.  
  506.         if not isinstance(tags, list):
  507.             self.logger.warning('Unable to use your set_mandatory_words '
  508.                                 'configuration!')
  509.             self.aborting = True
  510.  
  511.         self.mandatory_words = tags or []
  512.  
  513.         return self
  514.  
  515.     def set_user_interact(self,
  516.                           amount=10,
  517.                           percentage=100,
  518.                           randomize=False,
  519.                           media=None):
  520.         """Define if posts of given user should be interacted"""
  521.         if self.aborting:
  522.             return self
  523.  
  524.         self.user_interact_amount = amount
  525.         self.user_interact_random = randomize
  526.         self.user_interact_percentage = percentage
  527.         self.user_interact_media = media
  528.  
  529.         return self
  530.  
  531.     def set_ignore_users(self, users=None):
  532.         """Changes the possible restriction to users, if a user who posts
  533.        is one of these, the image won't be liked"""
  534.         if self.aborting:
  535.             return self
  536.  
  537.         self.ignore_users = users or []
  538.  
  539.         return self
  540.  
  541.     def set_ignore_if_contains(self, words=None):
  542.         """Ignores the don't likes if the description contains
  543.        one of the given words"""
  544.         if self.aborting:
  545.             return self
  546.  
  547.         self.ignore_if_contains = words or []
  548.  
  549.         return self
  550.  
  551.     def set_dont_include(self, friends=None):
  552.         """Defines which accounts should not be unfollowed"""
  553.         if self.aborting:
  554.             return self
  555.  
  556.         self.dont_include = set(friends) or set()
  557.         self.white_list = set(friends) or set()
  558.  
  559.         return self
  560.  
  561.     def set_switch_language(self, option=True):
  562.         self.switch_language = option
  563.         return self
  564.  
  565.     def set_use_clarifai(self,
  566.                          enabled=False,
  567.                          api_key=None,
  568.                          models=None,
  569.                          workflow=None,
  570.                          probability=0.50,
  571.                          full_match=False,
  572.                          check_video=False,
  573.                          proxy=None):
  574.         """
  575.        Defines if the clarifai img api should be used
  576.        Which 'project' will be used (only 5000 calls per month)
  577.  
  578.        Raises:
  579.            InstaPyError if os is windows
  580.        """
  581.         if self.aborting:
  582.             return self
  583.  
  584.         # if os.name == 'nt':
  585.         #    raise InstaPyError('Clarifai is not supported on Windows')
  586.  
  587.         self.use_clarifai = enabled
  588.  
  589.         if api_key is None and self.clarifai_api_key is None:
  590.             self.clarifai_api_key = os.environ.get('CLARIFAI_API_KEY')
  591.         elif api_key is not None:
  592.             self.clarifai_api_key = api_key
  593.  
  594.         self.clarifai_models = models or ['general']
  595.         self.clarifai_workflow = workflow or []
  596.         self.clarifai_probability = probability
  597.         self.clarifai_full_match = full_match
  598.         self.clarifai_check_video = check_video
  599.  
  600.         if proxy is not None:
  601.             self.clarifai_proxy = 'https://' + proxy
  602.  
  603.         return self
  604.  
  605.     def set_smart_hashtags(self,
  606.                            tags=None,
  607.                            limit=3,
  608.                            sort='top',
  609.                            log_tags=True):
  610.         """Generate smart hashtags based on https://displaypurposes.com/"""
  611.         """ranking, banned and spammy tags are filtered out."""
  612.  
  613.         if tags is None:
  614.             print('set_smart_hashtags is misconfigured')
  615.             return
  616.  
  617.         for tag in tags:
  618.             req = requests.get(
  619.                 u'https://d212rkvo8t62el.cloudfront.net/tag/{}'.format(tag))
  620.             data = json.loads(req.text)
  621.  
  622.             if data['tagExists'] is True:
  623.                 if sort == 'top':
  624.                     # sort by ranking
  625.                     ordered_tags_by_rank = sorted(
  626.                         data['results'], key=lambda d: d['rank'], reverse=True)
  627.                     ranked_tags = (ordered_tags_by_rank[:limit])
  628.                     for item in ranked_tags:
  629.                         # add smart hashtag to like list
  630.                         self.smart_hashtags.append(item['tag'])
  631.  
  632.                 elif sort == 'random':
  633.                     random_tags = random.sample(data['results'], limit)
  634.                     for item in random_tags:
  635.                         self.smart_hashtags.append(item['tag'])
  636.  
  637.                 if log_tags is True:
  638.                     for item in self.smart_hashtags:
  639.                         print(u'[smart hashtag generated: {}]'.format(item))
  640.             else:
  641.                 print(u'Too few results for #{} tag'.format(tag))
  642.  
  643.         # delete duplicated tags
  644.         self.smart_hashtags = list(set(self.smart_hashtags))
  645.         return self
  646.  
  647.     def set_mandatory_language(self, enabled=False, character_set='LATIN'):
  648.         """Restrict the description of the image to a character set"""
  649.         if self.aborting:
  650.             return self
  651.  
  652.         if (character_set not in ['LATIN', 'GREEK', 'CYRILLIC', 'ARABIC',
  653.                                   'HEBREW', 'CJK', 'HANGUL', 'HIRAGANA',
  654.                                   'KATAKANA', 'THAI']):
  655.             self.logger.warning('Unkown character set! Treating as "LATIN".')
  656.             character_set = 'LATIN'
  657.  
  658.         self.mandatory_language = enabled
  659.         self.mandatory_character = character_set
  660.  
  661.         return self
  662.  
  663.     def clarifai_check_img_for(self, tags=None, tags_skip=None, comment=False,
  664.                                comments=None):
  665.         """Defines the tags the images should be checked for"""
  666.         if self.aborting:
  667.             return self
  668.  
  669.         if tags is None and not self.clarifai_img_tags:
  670.             self.use_clarifai = False
  671.         elif tags:
  672.             self.clarifai_img_tags.append((tags, comment, comments))
  673.             self.clarifai_img_tags_skip = tags_skip or []
  674.  
  675.         return self
  676.  
  677.     def query_clarifai(self):
  678.         """Method for querying Clarifai using parameters set in
  679.        clarifai_check_img_for"""
  680.         return check_image(self.browser, self.clarifai_api_key,
  681.                            self.clarifai_img_tags,
  682.                            self.clarifai_img_tags_skip, self.logger,
  683.                            self.clarifai_models,
  684.                            self.clarifai_workflow, self.clarifai_probability,
  685.                            self.clarifai_full_match, self.clarifai_check_video,
  686.                            proxy=self.clarifai_proxy)
  687.  
  688.     def follow_commenters(self, usernames, amount=10, daysold=365, max_pic=50,
  689.                           sleep_delay=600, interact=False):
  690.         """ Follows users' commenters """
  691.  
  692.         if self.aborting:
  693.             return self
  694.  
  695.         message = "Starting to follow commenters.."
  696.         highlight_print(self.username, message, "feature", "info", self.logger)
  697.  
  698.         if not isinstance(usernames, list):
  699.             usernames = [usernames]
  700.  
  701.         followed_all = 0
  702.         followed_new = 0
  703.  
  704.         # hold the current global values for differentiating at the end
  705.         already_followed_init = self.already_followed
  706.         not_valid_users_init = self.not_valid_users
  707.         liked_init = self.liked_img
  708.         already_liked_init = self.already_liked
  709.         commented_init = self.commented
  710.         inap_img_init = self.inap_img
  711.  
  712.         relax_point = random.randint(7,
  713.                                      14)  # you can use some plain value
  714.         # `10` instead of this quitely randomized score
  715.         self.quotient_breach = False
  716.  
  717.         for username in usernames:
  718.             if self.quotient_breach:
  719.                 break
  720.  
  721.             self.logger.info(
  722.                 "Following commenters of '{}' from {} pictures in last {} "
  723.                 "days...\nScrapping wall..".format(
  724.                     username,
  725.                     max_pic,
  726.                     daysold))
  727.             commenters = extract_information(self.browser, username, daysold,
  728.                                              max_pic)
  729.  
  730.             if len(commenters) > 0:
  731.                 self.logger.info(
  732.                     "Going to follow top {} users.\n".format(amount))
  733.                 sleep(1)
  734.                 # This way of iterating will prevent sleep interference
  735.                 # between functions
  736.                 random.shuffle(commenters)
  737.                 for commenter in commenters[:amount]:
  738.                     if self.quotient_breach:
  739.                         self.logger.warning(
  740.                             "--> Follow quotient reached its peak!"
  741.                             "\t~leaving Follow-Commenters activity\n")
  742.                         break
  743.  
  744.                     with self.feature_in_feature("follow_by_list", True):
  745.                         followed = self.follow_by_list(commenter,
  746.                                                        self.follow_times,
  747.                                                        sleep_delay,
  748.                                                        interact)
  749.                     if followed > 0:
  750.                         followed_all += 1
  751.                         followed_new += 1
  752.                         self.logger.info(
  753.                             "Total Follow: {}\n".format(str(followed_all)))
  754.                         # Take a break after a good following
  755.                         if followed_new >= relax_point:
  756.                             delay_random = random.randint(
  757.                                 ceil(sleep_delay * 0.85),
  758.                                 ceil(sleep_delay * 1.14))
  759.                             sleep_time = ("{} seconds".format(delay_random) if
  760.                                           delay_random < 60 else
  761.                                           "{} minutes".format(truncate_float(
  762.                                               delay_random / 60, 2)))
  763.                             self.logger.info(
  764.                                 "------=>  Followed {} new users ~sleeping "
  765.                                 "about {}"
  766.                                 .format(followed_new, sleep_time))
  767.                             sleep(delay_random)
  768.                             relax_point = random.randint(7, 14)
  769.                             followed_new = 0
  770.                             pass
  771.  
  772.             else:
  773.                 self.logger.info("Noone commented, noone to follow.\n")
  774.  
  775.             sleep(1)
  776.  
  777.         self.logger.info("Finished following Commenters!\n")
  778.  
  779.         # find the feature-wide action sizes by taking a difference
  780.         already_followed = (self.already_followed - already_followed_init)
  781.         not_valid_users = (self.not_valid_users - not_valid_users_init)
  782.         liked = (self.liked_img - liked_init)
  783.         already_liked = (self.already_liked - already_liked_init)
  784.         commented = (self.commented - commented_init)
  785.         inap_img = (self.inap_img - inap_img_init)
  786.  
  787.         # print results
  788.         self.logger.info("Followed: {}".format(followed_all))
  789.         self.logger.info("Already followed: {}".format(already_followed))
  790.         self.logger.info("Not valid users: {}".format(not_valid_users))
  791.  
  792.         if interact is True:
  793.             print('')
  794.             # print results out of interactions
  795.             self.logger.info("Liked: {}".format(liked))
  796.             self.logger.info("Already Liked: {}".format(already_liked))
  797.             self.logger.info("Commented: {}".format(commented))
  798.             self.logger.info("Inappropriate: {}".format(inap_img))
  799.  
  800.         return self
  801.  
  802.     def follow_likers(self, usernames, photos_grab_amount=3,
  803.                       follow_likers_per_photo=3, randomize=True,
  804.                       sleep_delay=600,
  805.                       interact=False):
  806.         """ Follows users' likers """
  807.         if self.aborting:
  808.             return self
  809.  
  810.         message = "Starting to follow likers.."
  811.         highlight_print(self.username, message, "feature", "info", self.logger)
  812.  
  813.         if not isinstance(usernames, list):
  814.             usernames = [usernames]
  815.  
  816.         if photos_grab_amount > 12:
  817.             self.logger.info(
  818.                 "Sorry, you can only grab likers from first 12 photos for "
  819.                 "given username now.\n")
  820.             photos_grab_amount = 12
  821.  
  822.         followed_all = 0
  823.         followed_new = 0
  824.  
  825.         # hold the current global values for differentiating at the end
  826.         already_followed_init = self.already_followed
  827.         not_valid_users_init = self.not_valid_users
  828.         liked_init = self.liked_img
  829.         already_liked_init = self.already_liked
  830.         commented_init = self.commented
  831.         inap_img_init = self.inap_img
  832.  
  833.         relax_point = random.randint(7,
  834.                                      14)  # you can use some plain value
  835.         # `10` instead of this quitely randomized score
  836.         self.quotient_breach = False
  837.  
  838.         for username in usernames:
  839.             if self.quotient_breach:
  840.                 break
  841.  
  842.             photo_urls = get_photo_urls_from_profile(self.browser, username,
  843.                                                      photos_grab_amount,
  844.                                                      randomize)
  845.             sleep(1)
  846.             if not isinstance(photo_urls, list):
  847.                 photo_urls = [photo_urls]
  848.  
  849.             for photo_url in photo_urls:
  850.                 if self.quotient_breach:
  851.                     break
  852.  
  853.                 likers = users_liked(self.browser, photo_url,
  854.                                      follow_likers_per_photo)
  855.                 # This way of iterating will prevent sleep interference
  856.                 # between functions
  857.                 random.shuffle(likers)
  858.  
  859.                 for liker in likers[:follow_likers_per_photo]:
  860.                     if self.quotient_breach:
  861.                         self.logger.warning(
  862.                             "--> Follow quotient reached its peak!"
  863.                             "\t~leaving Follow-Likers activity\n")
  864.                         break
  865.  
  866.                     with self.feature_in_feature("follow_by_list", True):
  867.                         followed = self.follow_by_list(liker,
  868.                                                        self.follow_times,
  869.                                                        sleep_delay,
  870.                                                        interact)
  871.                     if followed > 0:
  872.                         followed_all += 1
  873.                         followed_new += 1
  874.                         self.logger.info(
  875.                             "Total Follow: {}\n".format(str(followed_all)))
  876.                         # Take a break after a good following
  877.                         if followed_new >= relax_point:
  878.                             delay_random = random.randint(
  879.                                 ceil(sleep_delay * 0.85),
  880.                                 ceil(sleep_delay * 1.14))
  881.                             sleep_time = ("{} seconds".format(delay_random) if
  882.                                           delay_random < 60 else
  883.                                           "{} minutes".format(truncate_float(
  884.                                               delay_random / 60, 2)))
  885.                             self.logger.info(
  886.                                 "------=>  Followed {} new users ~sleeping "
  887.                                 "about {}"
  888.                                 .format(followed_new, sleep_time))
  889.                             sleep(delay_random)
  890.                             relax_point = random.randint(7, 14)
  891.                             followed_new = 0
  892.                             pass
  893.  
  894.         self.logger.info("Finished following Likers!\n")
  895.  
  896.         # find the feature-wide action sizes by taking a difference
  897.         already_followed = (self.already_followed - already_followed_init)
  898.         not_valid_users = (self.not_valid_users - not_valid_users_init)
  899.         liked = (self.liked_img - liked_init)
  900.         already_liked = (self.already_liked - already_liked_init)
  901.         commented = (self.commented - commented_init)
  902.         inap_img = (self.inap_img - inap_img_init)
  903.  
  904.         # print results
  905.         self.logger.info("Followed: {}".format(followed_all))
  906.         self.logger.info("Already followed: {}".format(already_followed))
  907.         self.logger.info("Not valid users: {}".format(not_valid_users))
  908.  
  909.         if interact is True:
  910.             print('')
  911.             # print results out of interactions
  912.             self.logger.info("Liked: {}".format(liked))
  913.             self.logger.info("Already Liked: {}".format(already_liked))
  914.             self.logger.info("Commented: {}".format(commented))
  915.             self.logger.info("Inappropriate: {}".format(inap_img))
  916.  
  917.         return self
  918.  
  919.     def follow_by_list(self, followlist, times=1, sleep_delay=600,
  920.                        interact=False):
  921.         """Allows to follow by any scrapped list"""
  922.         if not isinstance(followlist, list):
  923.             followlist = [followlist]
  924.  
  925.         if self.aborting:
  926.             self.logger.info(">>> self aborting prevented")
  927.             # return self
  928.  
  929.         # standalone means this feature is started by the user
  930.         standalone = True if "follow_by_list" not in \
  931.                              self.internal_usage.keys() else False
  932.         # skip validation in case of it is already accomplished
  933.         users_validated = True if not standalone and not \
  934.         self.internal_usage["follow_by_list"]["validate"] else False
  935.  
  936.         self.follow_times = times or 0
  937.  
  938.         followed_all = 0
  939.         followed_new = 0
  940.         already_followed = 0
  941.         not_valid_users = 0
  942.  
  943.         # hold the current global values for differentiating at the end
  944.         liked_init = self.liked_img
  945.         already_liked_init = self.already_liked
  946.         commented_init = self.commented
  947.         inap_img_init = self.inap_img
  948.  
  949.         relax_point = random.randint(7,
  950.                                      14)  # you can use some plain value
  951.         # `10` instead of this quitely randomized score
  952.         self.quotient_breach = False
  953.  
  954.         for acc_to_follow in followlist:
  955.             if self.jumps["consequent"]["follows"] >= self.jumps["limit"][
  956.                 "follows"]:
  957.                 self.logger.warning(
  958.                     "--> Follow quotient reached its peak!\t~leaving "
  959.                     "Follow-By-Tags activity\n")
  960.                 # reset jump counter before breaking the loop
  961.                 self.jumps["consequent"]["follows"] = 0
  962.                 # turn on `quotient_breach` to break the internal iterators
  963.                 # of the caller
  964.                 self.quotient_breach = True if not standalone else False
  965.                 break
  966.  
  967.             if follow_restriction("read", acc_to_follow, self.follow_times,
  968.                                   self.logger):
  969.                 print('')
  970.                 continue
  971.  
  972.             if not users_validated:
  973.                 # Verify if the user should be followed
  974.                 validation, details = self.validate_user_call(acc_to_follow)
  975.                 if validation is not True or acc_to_follow == self.username:
  976.                     self.logger.info(
  977.                         "--> Not a valid user: {}".format(details))
  978.                     not_valid_users += 1
  979.                     continue
  980.  
  981.             # Take a break after a good following
  982.             if followed_new >= relax_point:
  983.                 delay_random = random.randint(
  984.                     ceil(sleep_delay * 0.85),
  985.                     ceil(sleep_delay * 1.14))
  986.                 sleep_time = ("{} seconds".format(delay_random) if
  987.                               delay_random < 60 else
  988.                               "{} minutes".format(truncate_float(
  989.                                   delay_random / 60, 2)))
  990.                 self.logger.info("Followed {} new users  ~sleeping about {}\n"
  991.                                  .format(followed_new, sleep_time))
  992.                 sleep(delay_random)
  993.                 followed_new = 0
  994.                 relax_point = random.randint(7, 14)
  995.                 pass
  996.  
  997.             if not follow_restriction("read", acc_to_follow, self.follow_times,
  998.                                       self.logger):
  999.                 follow_state, msg = follow_user(self.browser,
  1000.                                                 "profile",
  1001.                                                 self.username,
  1002.                                                 acc_to_follow,
  1003.                                                 None,
  1004.                                                 self.blacklist,
  1005.                                                 self.logger,
  1006.                                                 self.logfolder)
  1007.                 sleep(random.randint(1, 3))
  1008.  
  1009.                 if follow_state is True:
  1010.                     followed_all += 1
  1011.                     followed_new += 1
  1012.                     # reset jump counter after a successful follow
  1013.                     self.jumps["consequent"]["follows"] = 0
  1014.  
  1015.                     if standalone:  # print only for external usage (
  1016.                         # internal callers have their printers)
  1017.                         self.logger.info(
  1018.                             "Total Follow: {}\n".format(str(followed_all)))
  1019.  
  1020.                     # Check if interaction is expected
  1021.                     if interact and self.do_like:
  1022.                         do_interact = random.randint(0,
  1023.                                                      100) <= \
  1024.                                       self.user_interact_percentage
  1025.                         # Do interactions if any
  1026.                         if do_interact and self.user_interact_amount > 0:
  1027.                             original_do_follow = self.do_follow  # store the
  1028.                             # original value of `self.do_follow`
  1029.                             self.do_follow = False  # disable following
  1030.                             # temporarily cos the user is already followed
  1031.                             # above
  1032.                             self.interact_by_users(acc_to_follow,
  1033.                                                    self.user_interact_amount,
  1034.                                                    self.user_interact_random,
  1035.                                                    self.user_interact_media)
  1036.                             self.do_follow = original_do_follow  # revert
  1037.                             # back original `self.do_follow` value (either
  1038.                             # it was `False` or `True`)
  1039.  
  1040.                 elif msg == "already followed":
  1041.                     already_followed += 1
  1042.  
  1043.                 elif msg == "jumped":
  1044.                     # will break the loop after certain consecutive jumps
  1045.                     self.jumps["consequent"]["follows"] += 1
  1046.  
  1047.                 sleep(1)
  1048.  
  1049.         if standalone:  # print only for external usage (internal callers
  1050.             # have their printers)
  1051.             self.logger.info("Finished following by List!\n")
  1052.             # print summary
  1053.             self.logger.info("Followed: {}".format(followed_all))
  1054.             self.logger.info("Already followed: {}".format(already_followed))
  1055.             self.logger.info("Not valid users: {}".format(not_valid_users))
  1056.  
  1057.             if interact is True:
  1058.                 print('')
  1059.                 # find the feature-wide action sizes by taking a difference
  1060.                 liked = (self.liked_img - liked_init)
  1061.                 already_liked = (self.already_liked - already_liked_init)
  1062.                 commented = (self.commented - commented_init)
  1063.                 inap_img = (self.inap_img - inap_img_init)
  1064.  
  1065.                 # print the summary out of interactions
  1066.                 self.logger.info("Liked: {}".format(liked))
  1067.                 self.logger.info("Already Liked: {}".format(already_liked))
  1068.                 self.logger.info("Commented: {}".format(commented))
  1069.                 self.logger.info("Inappropriate: {}".format(inap_img))
  1070.  
  1071.         # always sum up general objects regardless of the request size
  1072.         self.followed += followed_all
  1073.         self.already_followed += already_followed
  1074.         self.not_valid_users += not_valid_users
  1075.  
  1076.         return followed_all
  1077.  
  1078.     def set_relationship_bounds(self,
  1079.                                 enabled=None,
  1080.                                 potency_ratio=None,
  1081.                                 delimit_by_numbers=None,
  1082.                                 min_posts=None,
  1083.                                 max_posts=None,
  1084.                                 max_followers=None,
  1085.                                 max_following=None,
  1086.                                 min_followers=None,
  1087.                                 min_following=None):
  1088.         """Sets the potency ratio and limits to the provide an efficient
  1089.        activity between the targeted masses"""
  1090.         self.potency_ratio = potency_ratio if enabled is True else None
  1091.         self.delimit_by_numbers = delimit_by_numbers if enabled is True else \
  1092.             None
  1093.  
  1094.         self.max_followers = max_followers
  1095.         self.min_followers = min_followers
  1096.  
  1097.         self.max_following = max_following
  1098.         self.min_following = min_following
  1099.  
  1100.         self.min_posts = min_posts if enabled is True else None
  1101.         self.max_posts = max_posts if enabled is True else None
  1102.  
  1103.     def validate_user_call(self, user_name):
  1104.         """Call the validate_username() function"""
  1105.         validation, details = validate_username(self.browser,
  1106.                                                 user_name,
  1107.                                                 self.username,
  1108.                                                 self.ignore_users,
  1109.                                                 self.blacklist,
  1110.                                                 self.potency_ratio,
  1111.                                                 self.delimit_by_numbers,
  1112.                                                 self.max_followers,
  1113.                                                 self.max_following,
  1114.                                                 self.min_followers,
  1115.                                                 self.min_following,
  1116.                                                 self.min_posts,
  1117.                                                 self.max_posts,
  1118.                                                 self.skip_private,
  1119.                                                 self.skip_private_percentage,
  1120.                                                 self.skip_no_profile_pic,
  1121.                                                 self.skip_no_profile_pic_percentage,
  1122.                                                 self.skip_business,
  1123.                                                 self.skip_business_percentage,
  1124.                                                 self.skip_business_categories,
  1125.                                                 self.dont_skip_business_categories,
  1126.                                                 self.logger)
  1127.         return validation, details
  1128.  
  1129.     def fetch_smart_comments(self, is_video, temp_comments):
  1130.         if temp_comments:
  1131.             # Use clarifai related comments only!
  1132.             comments = temp_comments
  1133.         elif is_video:
  1134.             comments = (self.comments +
  1135.                         self.video_comments)
  1136.         else:
  1137.             comments = (self.comments +
  1138.                         self.photo_comments)
  1139.  
  1140.         return comments
  1141.  
  1142.     def set_skip_users(self,
  1143.                        skip_private=True,
  1144.                        private_percentage=100,
  1145.                        skip_no_profile_pic=False,
  1146.                        no_profile_pic_percentage=100,
  1147.                        skip_business=False,
  1148.                        business_percentage=100,
  1149.                        skip_business_categories=[],
  1150.                        dont_skip_business_categories=[]):
  1151.  
  1152.         self.skip_business = skip_business
  1153.         self.skip_private = skip_private
  1154.         self.skip_no_profile_pic = skip_no_profile_pic
  1155.         self.skip_business_percentage = business_percentage
  1156.         self.skip_no_profile_pic_percentage = no_profile_pic_percentage
  1157.         self.skip_private_percentage = private_percentage
  1158.         if skip_business:
  1159.             self.skip_business_categories = skip_business_categories
  1160.             if len(skip_business_categories) == 0:
  1161.                 self.dont_skip_business_categories = \
  1162.                     dont_skip_business_categories
  1163.             else:
  1164.                 if len(dont_skip_business_categories) != 0:
  1165.                     self.logger.warning(
  1166.                         "Both skip_business_categories and "
  1167.                         "dont_skip_business categories provided in "
  1168.                         "skip_business feature," +
  1169.                         "will skip only the categories listed in "
  1170.                         "skip_business_categories parameter")
  1171.                     # dont_skip_business_categories = [] Setted by default
  1172.                     # in init
  1173.  
  1174.     def set_delimit_liking(self,
  1175.                            enabled=None,
  1176.                            max=None,
  1177.                            min=None):
  1178.  
  1179.         self.delimit_liking = True if enabled is True else False
  1180.         self.max_likes = max
  1181.         self.min_likes = min
  1182.  
  1183.     def set_delimit_commenting(self,
  1184.                                enabled=False,
  1185.                                max=None,
  1186.                                min=None,
  1187.                                comments_mandatory_words=[]):
  1188.  
  1189.         self.delimit_commenting = True if enabled is True else False
  1190.         self.max_comments = max
  1191.         self.min_comments = min
  1192.  
  1193.         # comment only when the image description contain at least one of
  1194.         # those words
  1195.         self.comments_mandatory_words = comments_mandatory_words
  1196.  
  1197.     def set_simulation(self, enabled=True, percentage=100):
  1198.         """ Sets aside simulation parameters """
  1199.         if enabled not in [True, False]:
  1200.             self.logger.info(
  1201.                 "Invalid simulation parameter! Please use correct syntax "
  1202.                 "with accepted values.")
  1203.  
  1204.         elif enabled is False:
  1205.             self.simulation["enabled"] = False
  1206.  
  1207.         else:
  1208.             percentage = 0 if percentage is None else percentage
  1209.             self.simulation = {"enabled": True, "percentage": percentage}
  1210.  
  1211.     def like_by_locations(self,
  1212.                           locations=None,
  1213.                           amount=50,
  1214.                           media=None,
  1215.                           skip_top_posts=True):
  1216.         """Likes (default) 50 images per given locations"""
  1217.         if self.aborting:
  1218.             return self
  1219.  
  1220.         liked_img = 0
  1221.         already_liked = 0
  1222.         inap_img = 0
  1223.         commented = 0
  1224.         followed = 0
  1225.         not_valid_users = 0
  1226.  
  1227.         locations = locations or []
  1228.         self.quotient_breach = False
  1229.  
  1230.         for index, location in enumerate(locations):
  1231.             if self.quotient_breach:
  1232.                 break
  1233.  
  1234.             self.logger.info('Location [{}/{}]'
  1235.                              .format(index + 1, len(locations)))
  1236.             self.logger.info('--> {}'.format(location.encode('utf-8')))
  1237.  
  1238.             try:
  1239.                 links = get_links_for_location(self.browser,
  1240.                                                location,
  1241.                                                amount,
  1242.                                                self.logger,
  1243.                                                media,
  1244.                                                skip_top_posts)
  1245.             except NoSuchElementException as exc:
  1246.                 self.logger.warning(
  1247.                     "Error occurred while getting images from location: {}  "
  1248.                     "~maybe too few images exist\n\t{}\n".format(location, str(
  1249.                         exc).encode("utf-8")))
  1250.                 continue
  1251.  
  1252.             for i, link in enumerate(links):
  1253.                 if self.jumps["consequent"]["likes"] >= self.jumps["limit"][
  1254.                     "likes"]:
  1255.                     self.logger.warning(
  1256.                         "--> Like quotient reached its peak!\t~leaving "
  1257.                         "Like-By-Locations activity\n")
  1258.                     self.quotient_breach = True
  1259.                     # reset jump counter after a breach report
  1260.                     self.jumps["consequent"]["likes"] = 0
  1261.                     break
  1262.  
  1263.                 self.logger.info('[{}/{}]'.format(i + 1, len(links)))
  1264.                 self.logger.info(link)
  1265.  
  1266.                 try:
  1267.                     inappropriate, user_name, is_video, reason, scope = (
  1268.                         check_link(self.browser,
  1269.                                    link,
  1270.                                    self.dont_like,
  1271.                                    self.mandatory_words,
  1272.                                    self.mandatory_language,
  1273.                                    self.is_mandatory_character,
  1274.                                    self.mandatory_character,
  1275.                                    self.check_character_set,
  1276.                                    self.ignore_if_contains,
  1277.                                    self.logger))
  1278.  
  1279.                     if not inappropriate and self.delimit_liking:
  1280.                         self.liking_approved = verify_liking(self.browser,
  1281.                                                              self.max_likes,
  1282.                                                              self.min_likes,
  1283.                                                              self.logger)
  1284.  
  1285.                     if not inappropriate and self.liking_approved:
  1286.                         # validate user
  1287.                         validation, details = self.validate_user_call(
  1288.                             user_name)
  1289.  
  1290.                         if validation is not True:
  1291.                             self.logger.info(
  1292.                                 "--> Not a valid user: {}".format(details))
  1293.                             not_valid_users += 1
  1294.                             continue
  1295.                         else:
  1296.                             web_address_navigator(self.browser, link)
  1297.  
  1298.                         # try to like
  1299.                         like_state, msg = like_image(self.browser,
  1300.                                                      user_name,
  1301.                                                      self.blacklist,
  1302.                                                      self.logger,
  1303.                                                      self.logfolder)
  1304.  
  1305.                         if like_state is True:
  1306.                             liked_img += 1
  1307.                             # reset jump counter after a successful like
  1308.                             self.jumps["consequent"]["likes"] = 0
  1309.  
  1310.                             checked_img = True
  1311.                             temp_comments = []
  1312.  
  1313.                             commenting = random.randint(
  1314.                                 0, 100) <= self.comment_percentage
  1315.                             following = random.randint(
  1316.                                 0, 100) <= self.follow_percentage
  1317.  
  1318.                             if self.use_clarifai and (following or commenting):
  1319.                                 try:
  1320.                                     checked_img, temp_comments, \
  1321.                                     clarifai_tags = (
  1322.                                         self.query_clarifai())
  1323.  
  1324.                                 except Exception as err:
  1325.                                     self.logger.error(
  1326.                                         'Image check error: {}'.format(err))
  1327.  
  1328.                             # comments
  1329.                             if (self.do_comment and
  1330.                                     user_name not in self.dont_include and
  1331.                                     checked_img and
  1332.                                     commenting):
  1333.  
  1334.                                 if self.delimit_commenting:
  1335.                                     (self.commenting_approved,
  1336.                                      disapproval_reason) = verify_commenting(
  1337.                                         self.browser,
  1338.                                         self.max_comments,
  1339.                                         self.min_comments,
  1340.                                         self.comments_mandatory_words,
  1341.                                         self.logger)
  1342.                                 if self.commenting_approved:
  1343.                                     # smart commenting
  1344.                                     comments = self.fetch_smart_comments(
  1345.                                         is_video,
  1346.                                         temp_comments)
  1347.                                     if comments:
  1348.                                         comment_state, msg = comment_image(
  1349.                                             self.browser,
  1350.                                             user_name,
  1351.                                             comments,
  1352.                                             self.blacklist,
  1353.                                             self.logger,
  1354.                                             self.logfolder)
  1355.                                         if comment_state is True:
  1356.                                             commented += 1
  1357.  
  1358.                                 else:
  1359.                                     self.logger.info(disapproval_reason)
  1360.  
  1361.                             else:
  1362.                                 self.logger.info('--> Not commented')
  1363.                                 sleep(1)
  1364.  
  1365.                             # following
  1366.                             if (self.do_follow and
  1367.                                     user_name not in self.dont_include and
  1368.                                     checked_img and
  1369.                                     following and
  1370.                                     not follow_restriction("read", user_name,
  1371.                                                            self.follow_times,
  1372.                                                            self.logger)):
  1373.  
  1374.                                 follow_state, msg = follow_user(self.browser,
  1375.                                                                 "post",
  1376.                                                                 self.username,
  1377.                                                                 user_name,
  1378.                                                                 None,
  1379.                                                                 self.blacklist,
  1380.                                                                 self.logger,
  1381.                                                                 self.logfolder)
  1382.                                 if follow_state is True:
  1383.                                     followed += 1
  1384.  
  1385.                             else:
  1386.                                 self.logger.info('--> Not following')
  1387.                                 sleep(1)
  1388.  
  1389.                         elif msg == "already liked":
  1390.                             already_liked += 1
  1391.  
  1392.                         elif msg == "jumped":
  1393.                             # will break the loop after certain consecutive
  1394.                             # jumps
  1395.                             self.jumps["consequent"]["likes"] += 1
  1396.  
  1397.                     else:
  1398.                         self.logger.info(
  1399.                             '--> Image not liked: {}'.format(
  1400.                                 reason.encode('utf-8')))
  1401.                         inap_img += 1
  1402.  
  1403.                 except NoSuchElementException as err:
  1404.                     self.logger.error('Invalid Page: {}'.format(err))
  1405.  
  1406.             self.logger.info('Location: {}'.format(location.encode('utf-8')))
  1407.             self.logger.info('Liked: {}'.format(liked_img))
  1408.             self.logger.info('Already Liked: {}'.format(already_liked))
  1409.             self.logger.info('Commented: {}'.format(commented))
  1410.             self.logger.info('Followed: {}'.format(followed))
  1411.             self.logger.info('Inappropriate: {}'.format(inap_img))
  1412.             self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  1413.  
  1414.         self.followed += followed
  1415.         self.liked_img += liked_img
  1416.         self.already_liked += already_liked
  1417.         self.commented += commented
  1418.         self.inap_img += inap_img
  1419.         self.not_valid_users += not_valid_users
  1420.  
  1421.         return self
  1422.  
  1423.     def comment_by_locations(self,
  1424.                              locations=None,
  1425.                              amount=50,
  1426.                              media=None,
  1427.                              skip_top_posts=True):
  1428.         """Likes (default) 50 images per given locations"""
  1429.         if self.aborting:
  1430.             return self
  1431.  
  1432.         commented = 0
  1433.         followed = 0
  1434.         inap_img = 0
  1435.         not_valid_users = 0
  1436.  
  1437.         locations = locations or []
  1438.         self.quotient_breach = False
  1439.  
  1440.         for index, location in enumerate(locations):
  1441.             if self.quotient_breach:
  1442.                 break
  1443.  
  1444.             self.logger.info('Location [{}/{}]'
  1445.                              .format(index + 1, len(locations)))
  1446.             self.logger.info('--> {}'.format(location.encode('utf-8')))
  1447.  
  1448.             try:
  1449.                 links = get_links_for_location(self.browser,
  1450.                                                location,
  1451.                                                amount,
  1452.                                                self.logger,
  1453.                                                media,
  1454.                                                skip_top_posts)
  1455.             except NoSuchElementException:
  1456.                 self.logger.warning('Too few images, skipping this location')
  1457.                 continue
  1458.  
  1459.             for i, link in enumerate(links):
  1460.                 if self.jumps["consequent"]["comments"] >= self.jumps["limit"][
  1461.                     "comments"]:
  1462.                     self.logger.warning(
  1463.                         "--> Comment quotient reached its peak!\t~leaving "
  1464.                         "Comment-By-Locations activity\n")
  1465.                     self.quotient_breach = True
  1466.                     # reset jump counter after a breach report
  1467.                     self.jumps["consequent"]["comments"] = 0
  1468.                     break
  1469.  
  1470.                 self.logger.info('[{}/{}]'.format(i + 1, len(links)))
  1471.                 self.logger.info(link)
  1472.  
  1473.                 try:
  1474.                     inappropriate, user_name, is_video, reason, scope = (
  1475.                         check_link(self.browser,
  1476.                                    link,
  1477.                                    self.dont_like,
  1478.                                    self.mandatory_words,
  1479.                                    self.mandatory_language,
  1480.                                    self.is_mandatory_character,
  1481.                                    self.mandatory_character,
  1482.                                    self.check_character_set,
  1483.                                    self.ignore_if_contains,
  1484.                                    self.logger))
  1485.                     if not inappropriate:
  1486.                         # validate user
  1487.                         validation, details = self.validate_user_call(
  1488.                             user_name)
  1489.                         if validation is not True:
  1490.                             self.logger.info(details)
  1491.                             not_valid_users += 1
  1492.                             continue
  1493.                         else:
  1494.                             web_address_navigator(self.browser, link)
  1495.  
  1496.                         # try to comment
  1497.                         self.logger.info(
  1498.                             "--> Image not liked: Likes are disabled for the "
  1499.                             "'Comment-By-Locations' feature")
  1500.  
  1501.                         checked_img = True
  1502.                         temp_comments = []
  1503.                         commenting = random.randint(
  1504.                             0, 100) <= self.comment_percentage
  1505.                         following = random.randint(
  1506.                             0, 100) <= self.follow_percentage
  1507.  
  1508.                         if not commenting:
  1509.                             self.logger.info(
  1510.                                 "--> Image not commented: skipping out of "
  1511.                                 "given comment percentage")
  1512.                             continue
  1513.  
  1514.                         if self.use_clarifai:
  1515.                             try:
  1516.                                 checked_img, temp_comments, clarifai_tags = (
  1517.                                     self.query_clarifai())
  1518.  
  1519.                             except Exception as err:
  1520.                                 self.logger.error(
  1521.                                     'Image check error: {}'.format(err))
  1522.  
  1523.                         if (self.do_comment and
  1524.                                 user_name not in self.dont_include and
  1525.                                 checked_img):
  1526.  
  1527.                             if self.delimit_commenting:
  1528.                                 (self.commenting_approved,
  1529.                                  disapproval_reason) = verify_commenting(
  1530.                                     self.browser,
  1531.                                     self.max_comments,
  1532.                                     self.min_comments,
  1533.                                     self.comments_mandatory_words,
  1534.                                     self.logger)
  1535.                             if self.commenting_approved:
  1536.                                 # smart commenting
  1537.                                 comments = self.fetch_smart_comments(is_video,
  1538.                                                                      temp_comments)
  1539.                                 if comments:
  1540.                                     comment_state, msg = comment_image(
  1541.                                         self.browser,
  1542.                                         user_name,
  1543.                                         comments,
  1544.                                         self.blacklist,
  1545.                                         self.logger,
  1546.                                         self.logfolder)
  1547.                                     if comment_state is True:
  1548.                                         commented += 1
  1549.                                         # reset jump counter after a
  1550.                                         # successful comment
  1551.                                         self.jumps["consequent"][
  1552.                                             "comments"] = 0
  1553.  
  1554.                                         # try to follow
  1555.                                         if (self.do_follow and
  1556.                                                 user_name not in
  1557.                                                 self.dont_include and
  1558.                                                 checked_img and
  1559.                                                 following and
  1560.                                                 not follow_restriction("read",
  1561.                                                                        user_name,
  1562.                                                                        self.follow_times,
  1563.                                                                        self.logger)):
  1564.  
  1565.                                             follow_state, msg = follow_user(
  1566.                                                 self.browser,
  1567.                                                 "post",
  1568.                                                 self.username,
  1569.                                                 user_name,
  1570.                                                 None,
  1571.                                                 self.blacklist,
  1572.                                                 self.logger,
  1573.                                                 self.logfolder)
  1574.                                             if follow_state is True:
  1575.                                                 followed += 1
  1576.  
  1577.                                         else:
  1578.                                             self.logger.info(
  1579.                                                 '--> Not following')
  1580.                                             sleep(1)
  1581.  
  1582.                                 elif msg == "jumped":
  1583.                                     # will break the loop after certain
  1584.                                     # consecutive jumps
  1585.                                     self.jumps["consequent"]["comments"] += 1
  1586.  
  1587.                             else:
  1588.                                 self.logger.info(disapproval_reason)
  1589.  
  1590.                         else:
  1591.                             self.logger.info('--> Not commented')
  1592.                             sleep(1)
  1593.  
  1594.                     else:
  1595.                         self.logger.info(
  1596.                             '--> Image not commented: {}'.format(
  1597.                                 reason.encode('utf-8')))
  1598.                         inap_img += 1
  1599.  
  1600.                 except NoSuchElementException as err:
  1601.                     self.logger.error('Invalid Page: {}'.format(err))
  1602.  
  1603.         self.logger.info('Location: {}'.format(location.encode('utf-8')))
  1604.         self.logger.info('Commented: {}'.format(commented))
  1605.         self.logger.info('Followed: {}'.format(followed))
  1606.         self.logger.info('Inappropriate: {}'.format(inap_img))
  1607.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  1608.  
  1609.         self.followed += followed
  1610.         self.not_valid_users += not_valid_users
  1611.  
  1612.         return self
  1613.  
  1614.     def like_by_tags(self,
  1615.                      tags=None,
  1616.                      amount=50,
  1617.                      skip_top_posts=True,
  1618.                      use_smart_hashtags=False,
  1619.                      interact=False,
  1620.                      randomize=False,
  1621.                      media=None):
  1622.         """Likes (default) 50 images per given tag"""
  1623.         if self.aborting:
  1624.             return self
  1625.  
  1626.         webhookurl = "https://api.telegram.org/botXXXXXXX/sendMessage"
  1627.  
  1628.         payload = {
  1629.             "chat_id": "-378468384",
  1630.             "text": "Liking " + format(amount) + " images for the tags: " + ' '.join(tags),
  1631.             "parse_mode": "Markdown"
  1632.         }
  1633.  
  1634.         requests.post(webhookurl, json=payload,
  1635.                       headers={"Content-type": "application/json;charset=UTF-8"})
  1636.  
  1637.         liked_img = 0
  1638.         already_liked = 0
  1639.         inap_img = 0
  1640.         commented = 0
  1641.         followed = 0
  1642.         not_valid_users = 0
  1643.  
  1644.         # if smart hashtag is enabled
  1645.         if use_smart_hashtags is True and self.smart_hashtags is not []:
  1646.             print('Using smart hashtags')
  1647.             tags = self.smart_hashtags
  1648.  
  1649.         # deletes white spaces in tags
  1650.         tags = [tag.strip() for tag in tags]
  1651.         tags = tags or []
  1652.         self.quotient_breach = False
  1653.  
  1654.         for index, tag in enumerate(tags):
  1655.             if self.quotient_breach:
  1656.                 break
  1657.  
  1658.             self.logger.info('Tag [{}/{}]'.format(index + 1, len(tags)))
  1659.             self.logger.info('--> {}'.format(tag.encode('utf-8')))
  1660.  
  1661.             try:
  1662.                 links = get_links_for_tag(self.browser,
  1663.                                           tag,
  1664.                                           amount,
  1665.                                           skip_top_posts,
  1666.                                           randomize,
  1667.                                           media,
  1668.                                           self.logger)
  1669.             except NoSuchElementException:
  1670.                 self.logger.info('Too few images, skipping this tag')
  1671.                 continue
  1672.  
  1673.             for i, link in enumerate(links):
  1674.                 if self.jumps["consequent"]["likes"] >= self.jumps["limit"][
  1675.                     "likes"]:
  1676.                     self.logger.warning(
  1677.                         "--> Like quotient reached its peak!\t~leaving "
  1678.                         "Like-By-Tags activity\n")
  1679.                     self.quotient_breach = True
  1680.                     # reset jump counter after a breach report
  1681.                     self.jumps["consequent"]["likes"] = 0
  1682.                     break
  1683.  
  1684.                 self.logger.info('[{}/{}]'.format(i + 1, len(links)))
  1685.                 self.logger.info(link)
  1686.  
  1687.                 try:
  1688.                     inappropriate, user_name, is_video, reason, scope = (
  1689.                         check_link(self.browser,
  1690.                                    link,
  1691.                                    self.dont_like,
  1692.                                    self.mandatory_words,
  1693.                                    self.mandatory_language,
  1694.                                    self.is_mandatory_character,
  1695.                                    self.mandatory_character,
  1696.                                    self.check_character_set,
  1697.                                    self.ignore_if_contains,
  1698.                                    self.logger)
  1699.                     )
  1700.  
  1701.                     if not inappropriate and self.delimit_liking:
  1702.                         self.liking_approved = verify_liking(self.browser,
  1703.                                                              self.max_likes,
  1704.                                                              self.min_likes,
  1705.                                                              self.logger)
  1706.  
  1707.                     if not inappropriate and self.liking_approved:
  1708.                         # validate user
  1709.                         validation, details = self.validate_user_call(
  1710.                             user_name)
  1711.                         if validation is not True:
  1712.                             self.logger.info(details)
  1713.                             not_valid_users += 1
  1714.                             continue
  1715.                         else:
  1716.                             web_address_navigator(self.browser, link)
  1717.  
  1718.                         # try to like
  1719.                         like_state, msg = like_image(self.browser,
  1720.                                                      user_name,
  1721.                                                      self.blacklist,
  1722.                                                      self.logger,
  1723.                                                      self.logfolder)
  1724.  
  1725.                         if like_state is True:
  1726.                             liked_img += 1
  1727.                             # reset jump counter after a successful like
  1728.                             self.jumps["consequent"]["likes"] = 0
  1729.  
  1730.                             token = "XXXXXXX"
  1731.  
  1732.                             webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  1733.  
  1734.                             payload = {
  1735.                                 "chat_id": "-378468384",
  1736.                                 "text": "*Liked an image* for the tag _" + format(tag) + "_: [" + format(
  1737.                                     link) + "](" + format(link) + ")",
  1738.                                 "parse_mode": "Markdown"
  1739.                             }
  1740.  
  1741.                             requests.post(webhookurl, json=payload,
  1742.                                           headers={"Content-type": "application/json;charset=UTF-8"})
  1743.  
  1744.                             checked_img = True
  1745.                             temp_comments = []
  1746.  
  1747.                             commenting = (random.randint(0, 100) <=
  1748.                                           self.comment_percentage)
  1749.                             following = (random.randint(0, 100) <=
  1750.                                          self.follow_percentage)
  1751.  
  1752.                             if self.use_clarifai and (following or commenting):
  1753.                                 try:
  1754.                                     checked_img, temp_comments, \
  1755.                                     clarifai_tags = (
  1756.                                         self.query_clarifai())
  1757.  
  1758.                                 except Exception as err:
  1759.                                     self.logger.error(
  1760.                                         'Image check error: {}'.format(err))
  1761.  
  1762.                             # comments
  1763.                             if (self.do_comment and
  1764.                                     user_name not in self.dont_include and
  1765.                                     checked_img and
  1766.                                     commenting):
  1767.  
  1768.                                 if self.delimit_commenting:
  1769.                                     (self.commenting_approved,
  1770.                                      disapproval_reason) = verify_commenting(
  1771.                                         self.browser,
  1772.                                         self.max_comments,
  1773.                                         self.min_comments,
  1774.                                         self.comments_mandatory_words,
  1775.                                         self.logger)
  1776.                                 if self.commenting_approved:
  1777.                                     # smart commenting
  1778.                                     comments = self.fetch_smart_comments(
  1779.                                         is_video,
  1780.                                         temp_comments)
  1781.                                     if comments:
  1782.                                         comment_state, msg = comment_image(
  1783.                                             self.browser,
  1784.                                             user_name,
  1785.                                             comments,
  1786.                                             self.blacklist,
  1787.                                             self.logger,
  1788.                                             self.logfolder)
  1789.                                         if comment_state is True:
  1790.                                             commented += 1
  1791.  
  1792.                                 else:
  1793.                                     self.logger.info(disapproval_reason)
  1794.  
  1795.                             else:
  1796.                                 self.logger.info('--> Not commented')
  1797.                                 sleep(1)
  1798.  
  1799.                             # following
  1800.                             if (self.do_follow and
  1801.                                     user_name not in self.dont_include and
  1802.                                     checked_img and
  1803.                                     following and
  1804.                                     not follow_restriction("read", user_name,
  1805.                                                            self.follow_times,
  1806.                                                            self.logger)):
  1807.  
  1808.                                 follow_state, msg = follow_user(self.browser,
  1809.                                                                 "post",
  1810.                                                                 self.username,
  1811.                                                                 user_name,
  1812.                                                                 None,
  1813.                                                                 self.blacklist,
  1814.                                                                 self.logger,
  1815.                                                                 self.logfolder)
  1816.                                 if follow_state is True:
  1817.                                     followed += 1
  1818.                                     token = "XXXXXXX"
  1819.  
  1820.                                     webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  1821.  
  1822.                                     payload = {
  1823.                                         "chat_id": "-378468384",
  1824.                                         "text": "*Followed* the user [" + format(
  1825.                                             user_name) + "](https://www.instagram.com/" + format(user_name) + "/)",
  1826.                                         "parse_mode": "Markdown"
  1827.                                     }
  1828.  
  1829.                                     requests.post(webhookurl, json=payload,
  1830.                                                   headers={"Content-type": "application/json;charset=UTF-8"})
  1831.                             else:
  1832.                                 self.logger.info('--> Not following')
  1833.                                 sleep(1)
  1834.  
  1835.                             # interactions (if any)
  1836.                             if interact:
  1837.                                 self.logger.info(
  1838.                                     "--> User gonna be interacted: '{}'"
  1839.                                     .format(user_name))
  1840.  
  1841.                                 token = "XXXXXXX"
  1842.  
  1843.                                 webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  1844.  
  1845.                                 payload = {
  1846.                                     "chat_id": "-378468384",
  1847.                                     "text": "*Interacting with user*: [" + format(
  1848.                                         user_name) + "](https://instagram.com/" + format(user_name) + "/)",
  1849.                                     "parse_mode": "Markdown"
  1850.                                 }
  1851.  
  1852.                                 requests.post(webhookurl, json=payload,
  1853.                                               headers={"Content-type": "application/json;charset=UTF-8"})
  1854.  
  1855.                                 self.like_by_users(user_name,
  1856.                                                    self.user_interact_amount,
  1857.                                                    self.user_interact_random,
  1858.                                                    self.user_interact_media)
  1859.  
  1860.                         elif msg == "already liked":
  1861.                             already_liked += 1
  1862.  
  1863.                         elif msg == "jumped":
  1864.                             # will break the loop after certain consecutive
  1865.                             # jumps
  1866.                             self.jumps["consequent"]["likes"] += 1
  1867.  
  1868.                     else:
  1869.                         self.logger.info(
  1870.                             '--> Image not liked: {}'.format(
  1871.                                 reason.encode('utf-8')))
  1872.                         inap_img += 1
  1873.  
  1874.                 except NoSuchElementException as err:
  1875.                     self.logger.error('Invalid Page: {}'.format(err))
  1876.  
  1877.         self.logger.info('Tag: {}'.format(tag.encode('utf-8')))
  1878.         self.logger.info('Liked: {}'.format(liked_img))
  1879.         self.logger.info('Already Liked: {}'.format(already_liked))
  1880.         self.logger.info('Commented: {}'.format(commented))
  1881.         self.logger.info('Followed: {}'.format(followed))
  1882.         self.logger.info('Inappropriate: {}'.format(inap_img))
  1883.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  1884.  
  1885.         self.liked_img += liked_img
  1886.         self.already_liked += already_liked
  1887.         self.commented += commented
  1888.         self.followed += followed
  1889.         self.inap_img += inap_img
  1890.         self.not_valid_users += not_valid_users
  1891.  
  1892.         return self
  1893.  
  1894.     def like_by_users(self, usernames, amount=10, randomize=False, media=None):
  1895.         """Likes some amounts of images for each usernames"""
  1896.         if self.aborting:
  1897.             return self
  1898.  
  1899.         if not isinstance(usernames, list):
  1900.             usernames = [usernames]
  1901.  
  1902.         liked_img = 0
  1903.         total_liked_img = 0
  1904.         already_liked = 0
  1905.         inap_img = 0
  1906.         commented = 0
  1907.         followed = 0
  1908.         not_valid_users = 0
  1909.  
  1910.         usernames = usernames or []
  1911.         self.quotient_breach = False
  1912.  
  1913.         for index, username in enumerate(usernames):
  1914.             if self.quotient_breach:
  1915.                 break
  1916.  
  1917.             self.logger.info("Username [{}/{}]"
  1918.                              .format(index + 1, len(usernames)))
  1919.             self.logger.info("--> {}"
  1920.                              .format(username.encode('utf-8')))
  1921.  
  1922.             following = random.randint(0, 100) <= self.follow_percentage
  1923.  
  1924.             validation, details = self.validate_user_call(username)
  1925.             if not validation:
  1926.                 self.logger.info("--> Not a valid user: {}".format(details))
  1927.                 not_valid_users += 1
  1928.                 continue
  1929.  
  1930.             try:
  1931.                 links = get_links_for_username(
  1932.                     self.browser,
  1933.                     self.username,
  1934.                     username,
  1935.                     amount,
  1936.                     self.logger,
  1937.                     self.logfolder,
  1938.                     randomize,
  1939.                     media)
  1940.  
  1941.             except NoSuchElementException:
  1942.                 self.logger.error('Element not found, skipping this username')
  1943.                 continue
  1944.  
  1945.             if (self.do_follow and
  1946.                     username not in self.dont_include and
  1947.                     following and
  1948.                     not follow_restriction("read",
  1949.                                            username,
  1950.                                            self.follow_times,
  1951.                                            self.logger)):
  1952.                 follow_state, msg = follow_user(self.browser,
  1953.                                                 "profile",
  1954.                                                 self.username,
  1955.                                                 username,
  1956.                                                 None,
  1957.                                                 self.blacklist,
  1958.                                                 self.logger,
  1959.                                                 self.logfolder)
  1960.                 if follow_state is True:
  1961.                     followed += 1
  1962.             else:
  1963.                 self.logger.info('--> Not following')
  1964.                 sleep(1)
  1965.  
  1966.             if links is False:
  1967.                 continue
  1968.  
  1969.             # Reset like counter for every username
  1970.             liked_img = 0
  1971.  
  1972.             for i, link in enumerate(links):
  1973.                 # Check if target has reached
  1974.                 if liked_img >= amount:
  1975.                     self.logger.info('-------------')
  1976.                     self.logger.info("--> Total liked image reached it's "
  1977.                                      "amount given: {}".format(liked_img))
  1978.                     break
  1979.  
  1980.                 if self.jumps["consequent"]["likes"] >= self.jumps["limit"][
  1981.                     "likes"]:
  1982.                     self.logger.warning(
  1983.                         "--> Like quotient reached its peak!\t~leaving "
  1984.                         "Like-By-Users activity\n")
  1985.                     self.quotient_breach = True
  1986.                     # reset jump counter after a breach report
  1987.                     self.jumps["consequent"]["likes"] = 0
  1988.                     break
  1989.  
  1990.                 self.logger.info('Post [{}/{}]'.format(liked_img + 1, amount))
  1991.                 self.logger.info(link)
  1992.  
  1993.                 try:
  1994.                     inappropriate, user_name, is_video, reason, scope = (
  1995.                         check_link(self.browser,
  1996.                                    link,
  1997.                                    self.dont_like,
  1998.                                    self.mandatory_words,
  1999.                                    self.mandatory_language,
  2000.                                    self.is_mandatory_character,
  2001.                                    self.mandatory_character,
  2002.                                    self.check_character_set,
  2003.                                    self.ignore_if_contains,
  2004.                                    self.logger))
  2005.  
  2006.                     if not inappropriate and self.delimit_liking:
  2007.                         self.liking_approved = verify_liking(self.browser,
  2008.                                                              self.max_likes,
  2009.                                                              self.min_likes,
  2010.                                                              self.logger)
  2011.  
  2012.                     if not inappropriate and self.liking_approved:
  2013.                         like_state, msg = like_image(self.browser,
  2014.                                                      user_name,
  2015.                                                      self.blacklist,
  2016.                                                      self.logger,
  2017.                                                      self.logfolder)
  2018.                         if like_state is True:
  2019.                             total_liked_img += 1
  2020.                             liked_img += 1
  2021.                             # reset jump counter after a successful like
  2022.                             self.jumps["consequent"]["likes"] = 0
  2023.  
  2024.                             token = "XXXXXXX"
  2025.  
  2026.                             webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  2027.  
  2028.                             payload = {
  2029.                                 "chat_id": "-378468384",
  2030.                                 "text": "*Liked an image* by user _" + format(
  2031.                                     user_name) + "_ [" + format(link) + "](" + format(link) + ")",
  2032.                                 "parse_mode": "Markdown"
  2033.                             }
  2034.  
  2035.                             requests.post(webhookurl, json=payload,
  2036.                                           headers={"Content-type": "application/json;charset=UTF-8"})
  2037.  
  2038.                             checked_img = True
  2039.                             temp_comments = []
  2040.  
  2041.                             commenting = random.randint(
  2042.                                 0, 100) <= self.comment_percentage
  2043.  
  2044.                             if self.use_clarifai and (following or commenting):
  2045.                                 try:
  2046.                                     checked_img, temp_comments, \
  2047.                                     clarifai_tags = (
  2048.                                         self.query_clarifai())
  2049.  
  2050.                                 except Exception as err:
  2051.                                     self.logger.error(
  2052.                                         'Image check error: {}'.format(err))
  2053.  
  2054.                             if (self.do_comment and
  2055.                                     user_name not in self.dont_include and
  2056.                                     checked_img and
  2057.                                     commenting):
  2058.  
  2059.                                 if self.delimit_commenting:
  2060.                                     (self.commenting_approved,
  2061.                                      disapproval_reason) = verify_commenting(
  2062.                                         self.browser,
  2063.                                         self.max_comments,
  2064.                                         self.min_comments,
  2065.                                         self.comments_mandatory_words,
  2066.                                         self.logger)
  2067.                                 if self.commenting_approved:
  2068.                                     # smart commenting
  2069.                                     comments = self.fetch_smart_comments(
  2070.                                         is_video,
  2071.                                         temp_comments)
  2072.                                     if comments:
  2073.                                         comment_state, msg = comment_image(
  2074.                                             self.browser,
  2075.                                             user_name,
  2076.                                             comments,
  2077.                                             self.blacklist,
  2078.                                             self.logger,
  2079.                                             self.logfolder)
  2080.                                         if comment_state is True:
  2081.                                             commented += 1
  2082.  
  2083.                                 else:
  2084.                                     self.logger.info(disapproval_reason)
  2085.  
  2086.                             else:
  2087.                                 self.logger.info('--> Not commented')
  2088.                                 sleep(1)
  2089.  
  2090.                         elif msg == "already liked":
  2091.                             already_liked += 1
  2092.  
  2093.                         elif msg == "jumped":
  2094.                             # will break the loop after certain consecutive
  2095.                             # jumps
  2096.                             self.jumps["consequent"]["likes"] += 1
  2097.  
  2098.                     else:
  2099.                         self.logger.info(
  2100.                             '--> Image not liked: {}'.format(
  2101.                                 reason.encode('utf-8')))
  2102.                         inap_img += 1
  2103.  
  2104.                 except NoSuchElementException as err:
  2105.                     self.logger.error('Invalid Page: {}'.format(err))
  2106.  
  2107.             if liked_img < amount:
  2108.                 self.logger.info('-------------')
  2109.                 self.logger.info("--> Given amount not fullfilled, "
  2110.                                  "image pool reached its end\n")
  2111.  
  2112.         self.logger.info('User: {}'.format(username.encode('utf-8')))
  2113.         self.logger.info('Liked: {}'.format(total_liked_img))
  2114.         self.logger.info('Already Liked: {}'.format(already_liked))
  2115.         self.logger.info('Commented: {}'.format(commented))
  2116.         self.logger.info('Inappropriate: {}'.format(inap_img))
  2117.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  2118.  
  2119.         self.liked_img += liked_img
  2120.         self.already_liked += already_liked
  2121.         self.commented += commented
  2122.         self.inap_img += inap_img
  2123.         self.not_valid_users += not_valid_users
  2124.  
  2125.         return self
  2126.  
  2127.     def interact_by_users(self,
  2128.                           usernames,
  2129.                           amount=10,
  2130.                           randomize=False,
  2131.                           media=None):
  2132.         """Likes some amounts of images for each usernames"""
  2133.         if self.aborting:
  2134.             return self
  2135.  
  2136.         message = "Starting to interact by users.."
  2137.         highlight_print(self.username, message, "feature", "info", self.logger)
  2138.  
  2139.         if not isinstance(usernames, list):
  2140.             usernames = [usernames]
  2141.  
  2142.         # standalone means this feature is started by the user
  2143.         standalone = True if "interact_by_users" not in \
  2144.                              self.internal_usage.keys() else False
  2145.         # skip validation in case of it is already accomplished
  2146.         users_validated = True if not standalone and not \
  2147.         self.internal_usage["interact_by_users"]["validate"] else False
  2148.  
  2149.         total_liked_img = 0
  2150.         already_liked = 0
  2151.         inap_img = 0
  2152.         commented = 0
  2153.         followed = 0
  2154.         already_followed = 0
  2155.         not_valid_users = 0
  2156.  
  2157.         self.quotient_breach = False
  2158.  
  2159.         for index, username in enumerate(usernames):
  2160.             if self.quotient_breach:
  2161.                 # keep `quotient_breach` active to break the internal
  2162.                 # iterators of the caller
  2163.                 self.quotient_breach = True if not standalone else False
  2164.                 break
  2165.  
  2166.             self.logger.info(
  2167.                 'Username [{}/{}]'.format(index + 1, len(usernames)))
  2168.             self.logger.info('--> {}'.format(username.encode('utf-8')))
  2169.  
  2170.             if not users_validated:
  2171.                 validation, details = self.validate_user_call(username)
  2172.                 if not validation:
  2173.                     self.logger.info(
  2174.                         "--> not a valid user: {}".format(details))
  2175.                     not_valid_users += 1
  2176.                     continue
  2177.  
  2178.             # decision making
  2179.             # static conditions
  2180.             not_dont_include = username not in self.dont_include
  2181.             follow_restricted = follow_restriction("read", username,
  2182.                                                    self.follow_times,
  2183.                                                    self.logger)
  2184.             counter = 0
  2185.             while True:
  2186.                 following = (random.randint(0,
  2187.                                             100) <= self.follow_percentage and
  2188.                              self.do_follow and
  2189.                              not_dont_include and
  2190.                              not follow_restricted)
  2191.                 commenting = (random.randint(0,
  2192.                                              100) <= self.comment_percentage
  2193.                               and
  2194.                               self.do_comment and
  2195.                               not_dont_include)
  2196.                 liking = (random.randint(0, 100) <= self.like_percentage)
  2197.  
  2198.                 counter += 1
  2199.  
  2200.                 # if we have only one image to like/comment
  2201.                 if commenting and not liking and amount == 1:
  2202.                     continue
  2203.                 if following or commenting or liking:
  2204.                     self.logger.info(
  2205.                         'username actions: following={} commenting={} '
  2206.                         'liking={}'.format(
  2207.                             following, commenting, liking))
  2208.                     break
  2209.                 # if for some reason we have no actions on this user
  2210.                 if counter > 5:
  2211.                     self.logger.info(
  2212.                         'username={} could not get interacted'.format(
  2213.                             username))
  2214.                     break
  2215.  
  2216.             try:
  2217.                 links = get_links_for_username(self.browser,
  2218.                                                self.username,
  2219.                                                username,
  2220.                                                amount,
  2221.                                                self.logger,
  2222.                                                self.logfolder,
  2223.                                                randomize,
  2224.                                                media)
  2225.             except NoSuchElementException:
  2226.                 self.logger.error('Element not found, skipping this username')
  2227.                 continue
  2228.  
  2229.             if links is False:
  2230.                 continue
  2231.  
  2232.             # Reset like counter for every username
  2233.             liked_img = 0
  2234.  
  2235.             for i, link in enumerate(links[:amount]):
  2236.                 if self.jumps["consequent"]["likes"] >= self.jumps["limit"][
  2237.                     "likes"]:
  2238.                     self.logger.warning(
  2239.                         "--> Like quotient reached its peak!\t~leaving "
  2240.                         "Interact-By-Users activity\n")
  2241.                     self.quotient_breach = True
  2242.                     # reset jump counter after a breach report
  2243.                     self.jumps["consequent"]["likes"] = 0
  2244.                     break
  2245.  
  2246.                 # Check if target has reached
  2247.                 if liked_img >= amount:
  2248.                     self.logger.info('-------------')
  2249.                     self.logger.info("--> Total liked image reached it's "
  2250.                                      "amount given: {}".format(liked_img))
  2251.                     break
  2252.  
  2253.                 self.logger.info(
  2254.                     'Post [{}/{}]'.format(liked_img + 1, len(links[:amount])))
  2255.                 self.logger.info(link)
  2256.  
  2257.                 try:
  2258.                     inappropriate, user_name, is_video, reason, scope = (
  2259.                         check_link(self.browser,
  2260.                                    link,
  2261.                                    self.dont_like,
  2262.                                    self.mandatory_words,
  2263.                                    self.mandatory_language,
  2264.                                    self.is_mandatory_character,
  2265.                                    self.mandatory_character,
  2266.                                    self.check_character_set,
  2267.                                    self.ignore_if_contains,
  2268.                                    self.logger))
  2269.  
  2270.                     if not inappropriate:
  2271.                         # after first image we roll again
  2272.                         if i > 0:
  2273.                             liking = (random.randint(0,
  2274.                                                      100) <=
  2275.                                       self.like_percentage)
  2276.                             commenting = (random.randint(0,
  2277.                                                          100) <=
  2278.                                           self.comment_percentage and
  2279.                                           self.do_comment and
  2280.                                           not_dont_include)
  2281.  
  2282.                         # like
  2283.                         if self.do_like and liking and self.delimit_liking:
  2284.                             self.liking_approved = verify_liking(self.browser,
  2285.                                                                  self.max_likes,
  2286.                                                                  self.min_likes,
  2287.                                                                  self.logger)
  2288.  
  2289.                         if self.do_like and liking and self.liking_approved:
  2290.                             like_state, msg = like_image(self.browser,
  2291.                                                          user_name,
  2292.                                                          self.blacklist,
  2293.                                                          self.logger,
  2294.                                                          self.logfolder)
  2295.                             if like_state is True:
  2296.                                 total_liked_img += 1
  2297.                                 liked_img += 1
  2298.                                 # reset jump counter after a successful like
  2299.                                 self.jumps["consequent"]["likes"] = 0
  2300.  
  2301.                                 # comment
  2302.                                 checked_img = True
  2303.                                 temp_comments = []
  2304.  
  2305.                                 if self.use_clarifai and commenting:
  2306.                                     try:
  2307.                                         checked_img, temp_comments, \
  2308.                                         clarifai_tags = (
  2309.                                             self.query_clarifai())
  2310.  
  2311.                                     except Exception as err:
  2312.                                         self.logger.error(
  2313.                                             'Image check error: {}'.format(
  2314.                                                 err))
  2315.  
  2316.                                 if commenting and checked_img:
  2317.  
  2318.                                     if self.delimit_commenting:
  2319.                                         (self.commenting_approved,
  2320.                                          disapproval_reason) = \
  2321.                                             verify_commenting(
  2322.                                                 self.browser,
  2323.                                                 self.max_comments,
  2324.                                                 self.min_comments,
  2325.                                                 self.comments_mandatory_words,
  2326.                                                 self.logger)
  2327.                                     if self.commenting_approved:
  2328.                                         # smart commenting
  2329.                                         comments = self.fetch_smart_comments(
  2330.                                             is_video,
  2331.                                             temp_comments)
  2332.                                         if comments:
  2333.                                             comment_state, msg = comment_image(
  2334.                                                 self.browser,
  2335.                                                 user_name,
  2336.                                                 comments,
  2337.                                                 self.blacklist,
  2338.                                                 self.logger,
  2339.                                                 self.logfolder)
  2340.                                             if comment_state is True:
  2341.                                                 commented += 1
  2342.  
  2343.                                     else:
  2344.                                         self.logger.info(disapproval_reason)
  2345.  
  2346.                                 else:
  2347.                                     self.logger.info('--> Not commented')
  2348.                                     sleep(1)
  2349.  
  2350.                             elif msg == "already liked":
  2351.                                 already_liked += 1
  2352.  
  2353.                             elif msg == "jumped":
  2354.                                 # will break the loop after certain
  2355.                                 # consecutive jumps
  2356.                                 self.jumps["consequent"]["likes"] += 1
  2357.  
  2358.                     else:
  2359.                         self.logger.info(
  2360.                             '--> Image not liked: {}'.format(
  2361.                                 reason.encode('utf-8')))
  2362.                         inap_img += 1
  2363.  
  2364.                 except NoSuchElementException as err:
  2365.                     self.logger.info('Invalid Page: {}'.format(err))
  2366.  
  2367.             # follow
  2368.             if following and not (self.dont_follow_inap_post and inap_img > 0):
  2369.  
  2370.                 follow_state, msg = follow_user(
  2371.                     self.browser,
  2372.                     "post",
  2373.                     self.username,
  2374.                     username,
  2375.                     None,
  2376.                     self.blacklist,
  2377.                     self.logger,
  2378.                     self.logfolder)
  2379.                 if follow_state is True:
  2380.                     followed += 1
  2381.  
  2382.                 elif msg == "already followed":
  2383.                     already_followed += 1
  2384.  
  2385.             else:
  2386.                 self.logger.info('--> Not following')
  2387.                 sleep(1)
  2388.  
  2389.             if liked_img < amount:
  2390.                 self.logger.info('-------------')
  2391.                 self.logger.info("--> Given amount not fullfilled, image pool "
  2392.                                  "reached its end\n")
  2393.  
  2394.         # final words
  2395.         interacted_media_size = (len(usernames) * amount - inap_img)
  2396.         self.logger.info(
  2397.             "Finished interacting on total of {} images from {} users! xD\n"
  2398.             .format(interacted_media_size, len(usernames)))
  2399.  
  2400.         # print results
  2401.         self.logger.info('Liked: {}'.format(total_liked_img))
  2402.         self.logger.info('Already Liked: {}'.format(already_liked))
  2403.         self.logger.info('Commented: {}'.format(commented))
  2404.         self.logger.info('Followed: {}'.format(followed))
  2405.         self.logger.info('Already Followed: {}'.format(already_followed))
  2406.         self.logger.info('Inappropriate: {}'.format(inap_img))
  2407.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  2408.  
  2409.         self.liked_img += total_liked_img
  2410.         self.already_liked += already_liked
  2411.         self.commented += commented
  2412.         self.followed += followed
  2413.         self.already_followed += already_followed
  2414.         self.inap_img += inap_img
  2415.         self.not_valid_users += not_valid_users
  2416.  
  2417.         return self
  2418.  
  2419.     def interact_by_users_tagged_posts(self,
  2420.                                        usernames,
  2421.                                        amount=10,
  2422.                                        randomize=False,
  2423.                                        media=None):
  2424.         """Likes some amounts of tagged images for each usernames"""
  2425.         if self.aborting:
  2426.             return self
  2427.  
  2428.         if not isinstance(usernames, list):
  2429.             usernames = [usernames]
  2430.  
  2431.         # standalone means this feature is started by the user
  2432.         standalone = True if "interact_by_users" not in \
  2433.                              self.internal_usage.keys() else False
  2434.         # skip validation in case of it is already accomplished
  2435.         users_validated = True if not standalone and not \
  2436.         self.internal_usage["interact_by_users"]["validate"] else False
  2437.  
  2438.         total_liked_img = 0
  2439.         already_liked = 0
  2440.         inap_img = 0
  2441.         commented = 0
  2442.         followed = 0
  2443.         already_followed = 0
  2444.         not_valid_users = 0
  2445.  
  2446.         self.quotient_breach = False
  2447.  
  2448.         for index, username in enumerate(usernames):
  2449.             if self.quotient_breach:
  2450.                 # keep `quotient_breach` active to break the internal
  2451.                 # iterators of the caller
  2452.                 self.quotient_breach = True if not standalone else False
  2453.                 break
  2454.  
  2455.             self.logger.info(
  2456.                 'Username [{}/{}]'.format(index + 1, len(usernames)))
  2457.             self.logger.info('--> {}'.format(username.encode('utf-8')))
  2458.  
  2459.             if not users_validated and username != self.username:
  2460.                 validation, details = self.validate_user_call(username)
  2461.                 if not validation:
  2462.                     self.logger.info(
  2463.                         "--> not a valid user: {}".format(details))
  2464.                     not_valid_users += 1
  2465.                     continue
  2466.  
  2467.             # decision making
  2468.             # static conditions
  2469.             not_dont_include = username not in self.dont_include
  2470.             follow_restricted = follow_restriction("read", username,
  2471.                                                    self.follow_times,
  2472.                                                    self.logger)
  2473.             counter = 0
  2474.             while True:
  2475.                 following = (random.randint(0,
  2476.                                             100) <= self.follow_percentage and
  2477.                              self.do_follow and
  2478.                              not_dont_include and
  2479.                              not follow_restricted)
  2480.                 commenting = (random.randint(0,
  2481.                                              100) <= self.comment_percentage
  2482.                               and
  2483.                               self.do_comment and
  2484.                               not_dont_include)
  2485.                 liking = (random.randint(0, 100) <= self.like_percentage)
  2486.  
  2487.                 counter += 1
  2488.  
  2489.                 # if we have only one image to like/comment
  2490.                 if commenting and not liking and amount == 1:
  2491.                     continue
  2492.                 if following or commenting or liking:
  2493.                     self.logger.info(
  2494.                         'username actions: following={} commenting={} '
  2495.                         'liking={}'.format(
  2496.                             following, commenting, liking))
  2497.                     break
  2498.                 # if for some reason we have no actions on this user
  2499.                 if counter > 5:
  2500.                     self.logger.info(
  2501.                         'username={} could not get interacted'.format(
  2502.                             username))
  2503.                     break
  2504.  
  2505.             try:
  2506.                 links = get_links_for_username(self.browser,
  2507.                                                self.username,
  2508.                                                username,
  2509.                                                amount,
  2510.                                                self.logger,
  2511.                                                self.logfolder,
  2512.                                                randomize,
  2513.                                                media,
  2514.                                                taggedImages=True)
  2515.             except NoSuchElementException:
  2516.                 self.logger.error('Element not found, skipping this username')
  2517.                 continue
  2518.  
  2519.             if links is False:
  2520.                 continue
  2521.  
  2522.             # Reset like counter for every username
  2523.             liked_img = 0
  2524.  
  2525.             for i, link in enumerate(links[:amount]):
  2526.                 if self.jumps["consequent"]["likes"] >= self.jumps["limit"][
  2527.                     "likes"]:
  2528.                     self.logger.warning(
  2529.                         "--> Like quotient reached its peak!\t~leaving "
  2530.                         "Interact-By-Users activity\n")
  2531.                     self.quotient_breach = True
  2532.                     # reset jump counter after a breach report
  2533.                     self.jumps["consequent"]["likes"] = 0
  2534.                     break
  2535.  
  2536.                 # Check if target has reached
  2537.                 if liked_img >= amount:
  2538.                     self.logger.info('-------------')
  2539.                     self.logger.info("--> Total liked image reached it's "
  2540.                                      "amount given: {}".format(liked_img))
  2541.                     break
  2542.  
  2543.                 self.logger.info(
  2544.                     'Post [{}/{}]'.format(liked_img + 1, len(links[:amount])))
  2545.                 self.logger.info(link)
  2546.  
  2547.                 try:
  2548.                     inappropriate, user_name, is_video, reason, scope = (
  2549.                         check_link(self.browser,
  2550.                                    link,
  2551.                                    self.dont_like,
  2552.                                    self.mandatory_words,
  2553.                                    self.mandatory_language,
  2554.                                    self.is_mandatory_character,
  2555.                                    self.mandatory_character,
  2556.                                    self.check_character_set,
  2557.                                    self.ignore_if_contains,
  2558.                                    self.logger))
  2559.  
  2560.                     if not inappropriate:
  2561.                         # after first image we roll again
  2562.                         if i > 0:
  2563.                             liking = (
  2564.                                     random.randint(0,
  2565.                                                    100) <=
  2566.                                     self.like_percentage)
  2567.                             commenting = (
  2568.                                     random.randint(0,
  2569.                                                    100) <=
  2570.                                     self.comment_percentage and
  2571.                                     self.do_comment and
  2572.                                     not_dont_include)
  2573.  
  2574.                         # like
  2575.                         if self.do_like and liking and self.delimit_liking:
  2576.                             self.liking_approved = verify_liking(
  2577.                                 self.browser,
  2578.                                 self.max_likes,
  2579.                                 self.min_likes,
  2580.                                 self.logger)
  2581.  
  2582.                         if self.do_like and liking and self.liking_approved:
  2583.                             like_state, msg = like_image(self.browser,
  2584.                                                          user_name,
  2585.                                                          self.blacklist,
  2586.                                                          self.logger,
  2587.                                                          self.logfolder)
  2588.                             if like_state is True:
  2589.                                 total_liked_img += 1
  2590.                                 liked_img += 1
  2591.                                 # reset jump counter after a successful like
  2592.                                 self.jumps["consequent"]["likes"] = 0
  2593.  
  2594.                                 # comment
  2595.                                 checked_img = True
  2596.                                 temp_comments = []
  2597.  
  2598.                                 if self.use_clarifai and commenting:
  2599.                                     try:
  2600.                                         (checked_img,
  2601.                                          temp_comments,
  2602.                                          clarifai_tags) = self.query_clarifai()
  2603.                                     except Exception as err:
  2604.                                         self.logger.error(
  2605.                                             'Image check error: {}'.format(
  2606.                                                 err))
  2607.  
  2608.                                 if commenting and checked_img:
  2609.  
  2610.                                     if self.delimit_commenting:
  2611.                                         (self.commenting_approved,
  2612.                                          disapproval_reason) = \
  2613.                                             verify_commenting(
  2614.                                                 self.browser,
  2615.                                                 self.max_comments,
  2616.                                                 self.min_comments,
  2617.                                                 self.comments_mandatory_words,
  2618.                                                 self.logger)
  2619.                                     if self.commenting_approved:
  2620.                                         if temp_comments:
  2621.                                             # use clarifai related comments
  2622.                                             # only!
  2623.                                             comments = temp_comments
  2624.  
  2625.                                         elif is_video:
  2626.                                             comments = (self.comments +
  2627.                                                         self.video_comments)
  2628.  
  2629.                                         else:
  2630.                                             comments = (self.comments +
  2631.                                                         self.photo_comments)
  2632.  
  2633.                                         comment_state, msg = comment_image(
  2634.                                             self.browser,
  2635.                                             user_name,
  2636.                                             comments,
  2637.                                             self.blacklist,
  2638.                                             self.logger,
  2639.                                             self.logfolder)
  2640.                                         if comment_state is True:
  2641.                                             commented += 1
  2642.  
  2643.                                     else:
  2644.                                         self.logger.info(disapproval_reason)
  2645.  
  2646.                                 else:
  2647.                                     self.logger.info('--> Not commented')
  2648.                                     sleep(1)
  2649.  
  2650.                             elif msg == "already liked":
  2651.                                 already_liked += 1
  2652.  
  2653.                             elif msg == "jumped":
  2654.                                 # will break the loop after certain
  2655.                                 # consecutive jumps
  2656.                                 self.jumps["consequent"]["likes"] += 1
  2657.  
  2658.                     else:
  2659.                         self.logger.info(
  2660.                             '--> Image not liked: {}'.format(
  2661.                                 reason.encode('utf-8')))
  2662.                         inap_img += 1
  2663.  
  2664.                 except NoSuchElementException as err:
  2665.                     self.logger.info('Invalid Page: {}'.format(err))
  2666.  
  2667.             # follow
  2668.             if following and not (self.dont_follow_inap_post and inap_img > 0):
  2669.  
  2670.                 follow_state, msg = follow_user(
  2671.                     self.browser,
  2672.                     "profile",
  2673.                     self.username,
  2674.                     username,
  2675.                     None,
  2676.                     self.blacklist,
  2677.                     self.logger,
  2678.                     self.logfolder)
  2679.                 if follow_state is True:
  2680.                     followed += 1
  2681.  
  2682.                 elif msg == "already followed":
  2683.                     already_followed += 1
  2684.  
  2685.             else:
  2686.                 self.logger.info('--> Not following')
  2687.                 sleep(1)
  2688.  
  2689.             if liked_img < amount:
  2690.                 self.logger.info('-------------')
  2691.                 self.logger.info("--> Given amount not fullfilled, image pool "
  2692.                                  "reached its end\n")
  2693.  
  2694.         # final words
  2695.         interacted_media_size = (len(usernames) * amount - inap_img)
  2696.         self.logger.info(
  2697.             "Finished interacting on total of {} images from {} users! xD\n"
  2698.             .format(interacted_media_size, len(usernames)))
  2699.  
  2700.         # print results
  2701.         self.logger.info('Liked: {}'.format(total_liked_img))
  2702.         self.logger.info('Already Liked: {}'.format(already_liked))
  2703.         self.logger.info('Commented: {}'.format(commented))
  2704.         self.logger.info('Followed: {}'.format(followed))
  2705.         self.logger.info('Already Followed: {}'.format(already_followed))
  2706.         self.logger.info('Inappropriate: {}'.format(inap_img))
  2707.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  2708.  
  2709.         self.liked_img += total_liked_img
  2710.         self.already_liked += already_liked
  2711.         self.commented += commented
  2712.         self.followed += followed
  2713.         self.already_followed += already_followed
  2714.         self.inap_img += inap_img
  2715.         self.not_valid_users += not_valid_users
  2716.  
  2717.         return self
  2718.  
  2719.     def like_from_image(self, url, amount=50, media=None):
  2720.         """Gets the tags from an image and likes 50 images for each tag"""
  2721.         if self.aborting:
  2722.             return self
  2723.  
  2724.         try:
  2725.             if not url:
  2726.                 urls = self.browser.find_elements_by_xpath(
  2727.                     "//main//article//div//div[1]//div[1]//a[1]")
  2728.                 url = urls[0].get_attribute("href")
  2729.                 self.logger.info("new url {}".format(url))
  2730.             tags = get_tags(self.browser, url)
  2731.             self.logger.info(tags)
  2732.             self.like_by_tags(tags, amount, media)
  2733.         except TypeError as err:
  2734.             self.logger.error('Sorry, an error occurred: {}'.format(err))
  2735.             self.aborting = True
  2736.             return self
  2737.  
  2738.         return self
  2739.  
  2740.     def interact_user_followers(self, usernames, amount=10, randomize=False):
  2741.  
  2742.         if self.aborting:
  2743.             return self
  2744.  
  2745.         if self.do_follow is not True and self.do_like is not True:
  2746.             self.logger.info(
  2747.                 "Please enable following or liking in settings in order to "
  2748.                 "do interactions.")
  2749.             return self
  2750.  
  2751.         elif self.user_interact_amount <= 0:
  2752.             self.logger.info(
  2753.                 "Please choose an amount higher than zero in "
  2754.                 "`set_user_interact` in order to do interactions.")
  2755.             return self
  2756.  
  2757.         if not isinstance(usernames, list):
  2758.             usernames = [usernames]
  2759.  
  2760.         interacted_all = 0
  2761.         not_valid_users = 0
  2762.         simulated_unfollow = 0
  2763.  
  2764.         # hold the current global values for differentiating at the end
  2765.         liked_init = self.liked_img
  2766.         already_liked_init = self.already_liked
  2767.         commented_init = self.commented
  2768.         followed_init = self.followed
  2769.         inap_img_init = self.inap_img
  2770.  
  2771.         self.quotient_breach = False
  2772.  
  2773.         for index, user in enumerate(usernames):
  2774.             if self.quotient_breach:
  2775.                 break
  2776.  
  2777.             self.logger.info("User '{}' [{}/{}]"
  2778.                              .format((user),
  2779.                                      index + 1,
  2780.                                      len(usernames)))
  2781.             try:
  2782.                 person_list, simulated_list = get_given_user_followers(
  2783.                     self.browser,
  2784.                     self.username,
  2785.                     user,
  2786.                     amount,
  2787.                     self.dont_include,
  2788.                     randomize,
  2789.                     self.blacklist,
  2790.                     self.follow_times,
  2791.                     self.simulation,
  2792.                     self.jumps,
  2793.                     self.logger,
  2794.                     self.logfolder)
  2795.  
  2796.             except (TypeError, RuntimeWarning) as err:
  2797.                 if isinstance(err, RuntimeWarning):
  2798.                     self.logger.warning(
  2799.                         u'Warning: {} , skipping to next user'.format(err))
  2800.                     continue
  2801.  
  2802.                 else:
  2803.                     self.logger.error(
  2804.                         'Sorry, an error occurred: {}'.format(err))
  2805.                     self.aborting = True
  2806.                     return self
  2807.  
  2808.             print('')
  2809.             self.logger.info(
  2810.                 "Grabbed {} usernames from '{}'s `Followers` to do "
  2811.                 "interaction."
  2812.                 .format(len(person_list), user))
  2813.  
  2814.             interacted_personal = 0
  2815.  
  2816.             for index, person in enumerate(person_list):
  2817.                 if self.quotient_breach:
  2818.                     self.logger.warning(
  2819.                         "--> Like quotient reached its peak!"
  2820.                         "\t~leaving Interact-User-Followers activity\n")
  2821.                     break
  2822.  
  2823.                 self.logger.info("User '{}' [{}/{}]".format((person),
  2824.                                                             index + 1,
  2825.                                                             len(person_list)))
  2826.  
  2827.                 validation, details = self.validate_user_call(person)
  2828.                 if validation is not True:
  2829.                     self.logger.info(details)
  2830.                     not_valid_users += 1
  2831.  
  2832.                     if person in simulated_list:
  2833.                         self.logger.warning(
  2834.                             "--> Simulated Unfollow {}:"
  2835.                             " unfollowing '{}' due to mismatching "
  2836.                             "validation..."
  2837.                             .format(simulated_unfollow + 1, person))
  2838.                         unfollow_state, msg = unfollow_user(self.browser,
  2839.                                                             "profile",
  2840.                                                             self.username,
  2841.                                                             person,
  2842.                                                             None,
  2843.                                                             None,
  2844.                                                             self.relationship_data,
  2845.                                                             self.logger,
  2846.                                                             self.logfolder)
  2847.                         if unfollow_state is True:
  2848.                             simulated_unfollow += 1
  2849.  
  2850.                     continue
  2851.  
  2852.                 # Do interactions if any
  2853.                 do_interact = random.randint(0,
  2854.                                              100) <= \
  2855.                               self.user_interact_percentage
  2856.  
  2857.                 if do_interact is False:
  2858.                     self.logger.info(
  2859.                         "Skipping user '{}' due to the interaction "
  2860.                         "percentage of {}"
  2861.                         .format(person, self.user_interact_percentage))
  2862.                     continue
  2863.  
  2864.                 else:
  2865.                     interacted_all += 1
  2866.                     interacted_personal += 1
  2867.  
  2868.                     self.logger.info(
  2869.                         "Interaction [{}/{}]  |  Total Interaction: {}"
  2870.                         .format(interacted_personal,
  2871.                                 len(person_list),
  2872.                                 interacted_all))
  2873.  
  2874.                     with self.feature_in_feature("interact_by_users", False):
  2875.                         self.interact_by_users(person,
  2876.                                                self.user_interact_amount,
  2877.                                                self.user_interact_random,
  2878.                                                self.user_interact_media)
  2879.                     if self.aborting:
  2880.                         return self
  2881.                     sleep(1)
  2882.  
  2883.         # final words
  2884.         self.logger.info(
  2885.             "Finished interacting {} people from {} users' `Followers`! xD\n"
  2886.             .format(interacted_all, len(usernames)))
  2887.  
  2888.         # find the feature-wide action sizes by taking a difference
  2889.         liked = (self.liked_img - liked_init)
  2890.         already_liked = (self.already_liked - already_liked_init)
  2891.         commented = (self.commented - commented_init)
  2892.         followed = (self.followed - followed_init)
  2893.         inap_img = (self.inap_img - inap_img_init)
  2894.  
  2895.         # print results
  2896.         self.logger.info('Liked: {}'.format(liked))
  2897.         self.logger.info('Already Liked: {}'.format(already_liked))
  2898.         self.logger.info('Commented: {}'.format(commented))
  2899.         self.logger.info('Followed: {}'.format(followed))
  2900.         self.logger.info('Inappropriate: {}'.format(inap_img))
  2901.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  2902.  
  2903.         self.not_valid_users += not_valid_users
  2904.  
  2905.         return self
  2906.  
  2907.     def interact_user_following(self, usernames, amount=10, randomize=False):
  2908.  
  2909.         if self.aborting:
  2910.             return self
  2911.  
  2912.         if self.do_follow is not True and self.do_like is not True:
  2913.             self.logger.info("Please enable following or liking in settings"
  2914.                              " in order to do interactions.")
  2915.             return self
  2916.  
  2917.         elif self.user_interact_amount <= 0:
  2918.             self.logger.info(
  2919.                 "Please choose an amount higher than zero in"
  2920.                 " `set_user_interact` in order to do interactions.")
  2921.             return self
  2922.  
  2923.         if not isinstance(usernames, list):
  2924.             usernames = [usernames]
  2925.  
  2926.         interacted_all = 0
  2927.         not_valid_users = 0
  2928.         simulated_unfollow = 0
  2929.  
  2930.         # hold the current global values for differentiating at the end
  2931.         liked_init = self.liked_img
  2932.         already_liked_init = self.already_liked
  2933.         commented_init = self.commented
  2934.         followed_init = self.followed
  2935.         inap_img_init = self.inap_img
  2936.  
  2937.         self.quotient_breach = False
  2938.  
  2939.         for index, user in enumerate(usernames):
  2940.             if self.quotient_breach:
  2941.                 break
  2942.  
  2943.             self.logger.info("User '{}' [{}/{}]"
  2944.                              .format((user),
  2945.                                      index + 1,
  2946.                                      len(usernames)))
  2947.             try:
  2948.                 person_list, simulated_list = get_given_user_following(
  2949.                     self.browser,
  2950.                     self.username,
  2951.                     user,
  2952.                     amount,
  2953.                     self.dont_include,
  2954.                     randomize,
  2955.                     self.blacklist,
  2956.                     self.follow_times,
  2957.                     self.simulation,
  2958.                     self.jumps,
  2959.                     self.logger,
  2960.                     self.logfolder)
  2961.  
  2962.             except (TypeError, RuntimeWarning) as err:
  2963.                 if isinstance(err, RuntimeWarning):
  2964.                     self.logger.warning(
  2965.                         u'Warning: {} , skipping to next user'.format(err))
  2966.                     continue
  2967.  
  2968.                 else:
  2969.                     self.logger.error(
  2970.                         'Sorry, an error occurred: {}'.format(err))
  2971.                     self.aborting = True
  2972.                     return self
  2973.  
  2974.             print('')
  2975.             self.logger.info(
  2976.                 "Grabbed {} usernames from '{}'s `Following` to do "
  2977.                 "interaction."
  2978.                 .format(len(person_list), user))
  2979.             interacted_personal = 0
  2980.  
  2981.             for index, person in enumerate(person_list):
  2982.                 if self.quotient_breach:
  2983.                     self.logger.warning(
  2984.                         "--> Like quotient reached its peak!"
  2985.                         "\t~leaving Interact-User-Following activity\n")
  2986.                     break
  2987.  
  2988.                 self.logger.info("User '{}' [{}/{}]".format((person),
  2989.                                                             index + 1,
  2990.                                                             len(person_list)))
  2991.  
  2992.                 validation, details = self.validate_user_call(person)
  2993.                 if validation is not True:
  2994.                     self.logger.info(details)
  2995.                     not_valid_users += 1
  2996.  
  2997.                     if person in simulated_list:
  2998.                         self.logger.warning(
  2999.                             "--> Simulated Unfollow {}:"
  3000.                             " unfollowing '{}' due to mismatching "
  3001.                             "validation..."
  3002.                             .format(simulated_unfollow + 1, person))
  3003.  
  3004.                         unfollow_state, msg = unfollow_user(
  3005.                             self.browser,
  3006.                             "profile",
  3007.                             self.username,
  3008.                             person,
  3009.                             None,
  3010.                             None,
  3011.                             self.relationship_data,
  3012.                             self.logger,
  3013.                             self.logfolder)
  3014.                         if unfollow_state is True:
  3015.                             simulated_unfollow += 1
  3016.  
  3017.                     continue
  3018.  
  3019.                 # Do interactions if any
  3020.                 do_interact = random.randint(0,
  3021.                                              100) <= \
  3022.                               self.user_interact_percentage
  3023.  
  3024.                 if do_interact is False:
  3025.                     self.logger.info("Skipping user '{}' due to"
  3026.                                      " the interaction percentage of {}"
  3027.                                      .format(person,
  3028.                                              self.user_interact_percentage))
  3029.                     continue
  3030.  
  3031.                 else:
  3032.                     interacted_all += 1
  3033.                     interacted_personal += 1
  3034.  
  3035.                     self.logger.info(
  3036.                         "Interaction [{}/{}]  |  Total Interaction: {}"
  3037.                         .format(interacted_personal,
  3038.                                 len(person_list),
  3039.                                 interacted_all))
  3040.  
  3041.                     with self.feature_in_feature("interact_by_users", False):
  3042.                         self.interact_by_users(person,
  3043.                                                self.user_interact_amount,
  3044.                                                self.user_interact_random,
  3045.                                                self.user_interact_media)
  3046.                     if self.aborting:
  3047.                         return self
  3048.                     sleep(1)
  3049.  
  3050.         # final words
  3051.         self.logger.info("Finished interacting {} people"
  3052.                          " from {} users' `Following`! xD\n"
  3053.                          .format(interacted_all,
  3054.                                  len(usernames)))
  3055.  
  3056.         # find the feature-wide action sizes by taking a difference
  3057.         liked = (self.liked_img - liked_init)
  3058.         already_liked = (self.already_liked - already_liked_init)
  3059.         commented = (self.commented - commented_init)
  3060.         followed = (self.followed - followed_init)
  3061.         inap_img = (self.inap_img - inap_img_init)
  3062.  
  3063.         # print results
  3064.         self.logger.info('Liked: {}'.format(liked))
  3065.         self.logger.info('Already Liked: {}'.format(already_liked))
  3066.         self.logger.info('Commented: {}'.format(commented))
  3067.         self.logger.info('Followed: {}'.format(followed))
  3068.         self.logger.info('Inappropriate: {}'.format(inap_img))
  3069.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  3070.  
  3071.         self.not_valid_users += not_valid_users
  3072.  
  3073.         return self
  3074.  
  3075.     def follow_user_followers(self,
  3076.                               usernames,
  3077.                               amount=10,
  3078.                               randomize=False,
  3079.                               interact=False,
  3080.                               sleep_delay=600):
  3081.         """ Follow the `Followers` of given users """
  3082.         if self.aborting:
  3083.             return self
  3084.  
  3085.         message = "Starting to follow user `Followers`.."
  3086.         highlight_print(self.username, message, "feature", "info", self.logger)
  3087.  
  3088.         if not isinstance(usernames, list):
  3089.             usernames = [usernames]
  3090.  
  3091.         followed_all = 0
  3092.         followed_new = 0
  3093.         not_valid_users = 0
  3094.  
  3095.         # below, you can use some static value `10` instead of random ones..
  3096.         relax_point = random.randint(7, 14)
  3097.  
  3098.         # hold the current global values for differentiating at the end
  3099.         already_followed_init = self.already_followed
  3100.         liked_init = self.liked_img
  3101.         already_liked_init = self.already_liked
  3102.         commented_init = self.commented
  3103.         inap_img_init = self.inap_img
  3104.  
  3105.         self.quotient_breach = False
  3106.  
  3107.         for index, user in enumerate(usernames):
  3108.             if self.quotient_breach:
  3109.                 break
  3110.  
  3111.             self.logger.info(
  3112.                 "User '{}' [{}/{}]".format((user), index + 1, len(usernames)))
  3113.  
  3114.             try:
  3115.                 person_list, simulated_list = get_given_user_followers(
  3116.                     self.browser,
  3117.                     self.username,
  3118.                     user,
  3119.                     amount,
  3120.                     self.dont_include,
  3121.                     randomize,
  3122.                     self.blacklist,
  3123.                     self.follow_times,
  3124.                     self.simulation,
  3125.                     self.jumps,
  3126.                     self.logger,
  3127.                     self.logfolder)
  3128.  
  3129.             except (TypeError, RuntimeWarning) as err:
  3130.                 if isinstance(err, RuntimeWarning):
  3131.                     self.logger.warning(
  3132.                         u'Warning: {} , skipping to next user'.format(err))
  3133.                     continue
  3134.  
  3135.                 else:
  3136.                     self.logger.error(
  3137.                         'Sorry, an error occurred: {}'.format(err))
  3138.                     self.aborting = True
  3139.                     return self
  3140.  
  3141.             print('')
  3142.             self.logger.info(
  3143.                 "Grabbed {} usernames from '{}'s `Followers` to do following\n"
  3144.                     .format(len(person_list), user))
  3145.  
  3146.             followed_personal = 0
  3147.             simulated_unfollow = 0
  3148.  
  3149.             for index, person in enumerate(person_list):
  3150.                 if self.quotient_breach:
  3151.                     self.logger.warning(
  3152.                         "--> Follow quotient reached its peak!"
  3153.                         "\t~leaving Follow-User-Followers activity\n")
  3154.                     break
  3155.  
  3156.                 self.logger.info(
  3157.                     "Ongoing Follow [{}/{}]: now following '{}'..."
  3158.                     .format(index + 1, len(person_list), person))
  3159.  
  3160.                 validation, details = self.validate_user_call(person)
  3161.                 if validation is not True:
  3162.                     self.logger.info(details)
  3163.                     not_valid_users += 1
  3164.  
  3165.                     if person in simulated_list:
  3166.                         self.logger.warning(
  3167.                             "--> Simulated Unfollow {}: unfollowing"
  3168.                             " '{}' due to mismatching validation...\n"
  3169.                             .format(simulated_unfollow + 1, person))
  3170.  
  3171.                         unfollow_state, msg = unfollow_user(
  3172.                             self.browser,
  3173.                             "profile",
  3174.                             self.username,
  3175.                             person,
  3176.                             None,
  3177.                             None,
  3178.                             self.relationship_data,
  3179.                             self.logger,
  3180.                             self.logfolder)
  3181.                         if unfollow_state is True:
  3182.                             simulated_unfollow += 1
  3183.                     # skip this [non-validated] user
  3184.                     continue
  3185.  
  3186.                 # go ahead and follow, then interact (if any)
  3187.                 with self.feature_in_feature("follow_by_list", False):
  3188.                     followed = self.follow_by_list(person,
  3189.                                                    self.follow_times,
  3190.                                                    sleep_delay,
  3191.                                                    interact)
  3192.                 sleep(1)
  3193.  
  3194.                 if followed > 0:
  3195.                     followed_all += 1
  3196.                     followed_new += 1
  3197.                     followed_personal += 1
  3198.  
  3199.                 self.logger.info("Follow per user: {}  |  Total Follow: {}\n"
  3200.                                  .format(followed_personal, followed_all))
  3201.  
  3202.                 # take a break after a good following
  3203.                 if followed_new >= relax_point:
  3204.                     delay_random = random.randint(
  3205.                         ceil(sleep_delay * 0.85),
  3206.                         ceil(sleep_delay * 1.14))
  3207.                     sleep_time = ("{} seconds".format(delay_random) if
  3208.                                   delay_random < 60 else
  3209.                                   "{} minutes".format(truncate_float(
  3210.                                       delay_random / 60, 2)))
  3211.                     self.logger.info(
  3212.                         "------=>  Followed {} new users ~sleeping about {}\n"
  3213.                         .format(followed_new, sleep_time))
  3214.                     sleep(delay_random)
  3215.                     relax_point = random.randint(7, 14)
  3216.                     followed_new = 0
  3217.  
  3218.         # final words
  3219.         self.logger.info("Finished following {} users' `Followers`! xD\n"
  3220.                          .format(len(usernames)))
  3221.         # find the feature-wide action sizes by taking a difference
  3222.         already_followed = (self.already_followed - already_followed_init)
  3223.         inap_img = (self.inap_img - inap_img_init)
  3224.         liked = (self.liked_img - liked_init)
  3225.         already_liked = (self.already_liked - already_liked_init)
  3226.         commented = (self.commented - commented_init)
  3227.  
  3228.         # print results
  3229.         self.logger.info("Followed: {}".format(followed_all))
  3230.         self.logger.info("Already followed: {}".format(already_followed))
  3231.         self.logger.info("Not valid users: {}".format(not_valid_users))
  3232.  
  3233.         if interact is True:
  3234.             print('')
  3235.             # print results out of interactions
  3236.             self.logger.info("Liked: {}".format(liked))
  3237.             self.logger.info("Already Liked: {}".format(already_liked))
  3238.             self.logger.info("Commented: {}".format(commented))
  3239.             self.logger.info("Inappropriate: {}".format(inap_img))
  3240.  
  3241.         self.not_valid_users += not_valid_users
  3242.  
  3243.         return self
  3244.  
  3245.     def follow_user_following(self,
  3246.                               usernames,
  3247.                               amount=10,
  3248.                               randomize=False,
  3249.                               interact=False,
  3250.                               sleep_delay=600):
  3251.         """ Follow the `Following` of given users """
  3252.         if self.aborting:
  3253.             return self
  3254.  
  3255.         message = "Starting to follow user `Following`.."
  3256.         highlight_print(self.username, message, "feature", "info", self.logger)
  3257.  
  3258.         if not isinstance(usernames, list):
  3259.             usernames = [usernames]
  3260.  
  3261.         followed_all = 0
  3262.         followed_new = 0
  3263.         not_valid_users = 0
  3264.  
  3265.         # hold the current global values for differentiating at the end
  3266.         already_followed_init = self.already_followed
  3267.         liked_init = self.liked_img
  3268.         already_liked_init = self.already_liked
  3269.         commented_init = self.commented
  3270.         inap_img_init = self.inap_img
  3271.  
  3272.         # below, can use a static value instead of from random range..
  3273.         relax_point = random.randint(7, 14)
  3274.         self.quotient_breach = False
  3275.  
  3276.         for index, user in enumerate(usernames):
  3277.             if self.quotient_breach:
  3278.                 break
  3279.  
  3280.             self.logger.info("User '{}' [{}/{}]".format((user),
  3281.                                                         index + 1,
  3282.                                                         len(usernames)))
  3283.             try:
  3284.                 person_list, simulated_list = get_given_user_following(
  3285.                     self.browser,
  3286.                     self.username,
  3287.                     user,
  3288.                     amount,
  3289.                     self.dont_include,
  3290.                     randomize,
  3291.                     self.blacklist,
  3292.                     self.follow_times,
  3293.                     self.simulation,
  3294.                     self.jumps,
  3295.                     self.logger,
  3296.                     self.logfolder)
  3297.  
  3298.             except (TypeError, RuntimeWarning) as err:
  3299.                 if isinstance(err, RuntimeWarning):
  3300.                     self.logger.warning(
  3301.                         u'Warning: {} , skipping to next user'.format(err))
  3302.                     continue
  3303.  
  3304.                 else:
  3305.                     self.logger.error(
  3306.                         'Sorry, an error occurred: {}'.format(err))
  3307.                     self.aborting = True
  3308.                     return self
  3309.  
  3310.             print('')
  3311.             self.logger.info(
  3312.                 "Grabbed {} usernames from '{}'s `Following` to do following\n"
  3313.                 .format(len(person_list), user))
  3314.  
  3315.             followed_personal = 0
  3316.             simulated_unfollow = 0
  3317.  
  3318.             for index, person in enumerate(person_list):
  3319.                 if self.quotient_breach:
  3320.                     self.logger.warning(
  3321.                         "--> Follow quotient reached its peak!"
  3322.                         "\t~leaving Follow-User-Following activity\n")
  3323.                     break
  3324.  
  3325.                 self.logger.info(
  3326.                     "Ongoing Follow [{}/{}]: now following '{}'..."
  3327.                     .format(index + 1, len(person_list), person)
  3328.                 )
  3329.  
  3330.                 validation, details = self.validate_user_call(person)
  3331.                 if validation is not True:
  3332.                     self.logger.info(details)
  3333.                     not_valid_users += 1
  3334.  
  3335.                     if person in simulated_list:
  3336.                         self.logger.warning(
  3337.                             "--> Simulated Unfollow {}:"
  3338.                             " unfollowing '{}' due to mismatching "
  3339.                             "validation...\n"
  3340.                             .format(simulated_unfollow + 1, person))
  3341.  
  3342.                         unfollow_state, msg = unfollow_user(
  3343.                             self.browser,
  3344.                             "profile",
  3345.                             self.username,
  3346.                             person,
  3347.                             None,
  3348.                             None,
  3349.                             self.relationship_data,
  3350.                             self.logger,
  3351.                             self.logfolder)
  3352.                         if unfollow_state is True:
  3353.                             simulated_unfollow += 1
  3354.                     # skip the [non-validated] user
  3355.                     continue
  3356.  
  3357.                 # go ahead and follow, then interact (if any)
  3358.                 with self.feature_in_feature("follow_by_list", False):
  3359.                     followed = self.follow_by_list(person,
  3360.                                                    self.follow_times,
  3361.                                                    sleep_delay,
  3362.                                                    interact)
  3363.                 sleep(1)
  3364.  
  3365.                 if followed > 0:
  3366.                     followed_all += 1
  3367.                     followed_new += 1
  3368.                     followed_personal += 1
  3369.  
  3370.                 self.logger.info("Follow per user: {}  |  Total Follow: {}\n"
  3371.                                  .format(followed_personal, followed_all))
  3372.  
  3373.                 # take a break after a good following
  3374.                 if followed_new >= relax_point:
  3375.                     delay_random = random.randint(
  3376.                         ceil(sleep_delay * 0.85),
  3377.                         ceil(sleep_delay * 1.14))
  3378.                     sleep_time = ("{} seconds".format(delay_random) if
  3379.                                   delay_random < 60 else
  3380.                                   "{} minutes".format(truncate_float(
  3381.                                       delay_random / 60, 2)))
  3382.                     self.logger.info(
  3383.                         "------=>  Followed {} new users ~sleeping about {}\n"
  3384.                         .format(followed_new, sleep_time))
  3385.                     sleep(delay_random)
  3386.                     relax_point = random.randint(7, 14)
  3387.                     followed_new = 0
  3388.  
  3389.         # final words
  3390.         self.logger.info("Finished following {} users' `Following`! xD\n"
  3391.                          .format(len(usernames)))
  3392.  
  3393.         # find the feature-wide action sizes by taking a difference
  3394.         already_followed = (self.already_followed - already_followed_init)
  3395.         inap_img = (self.inap_img - inap_img_init)
  3396.         liked = (self.liked_img - liked_init)
  3397.         already_liked = (self.already_liked - already_liked_init)
  3398.         commented = (self.commented - commented_init)
  3399.  
  3400.         # print results
  3401.         self.logger.info("Followed: {}".format(followed_all))
  3402.         self.logger.info("Already followed: {}".format(already_followed))
  3403.         self.logger.info("Not valid users: {}".format(not_valid_users))
  3404.  
  3405.         if interact is True:
  3406.             print('')
  3407.             # print results out of interactions
  3408.             self.logger.info("Liked: {}".format(liked))
  3409.             self.logger.info("Already Liked: {}".format(already_liked))
  3410.             self.logger.info("Commented: {}".format(commented))
  3411.             self.logger.info("Inappropriate: {}".format(inap_img))
  3412.  
  3413.         self.not_valid_users += not_valid_users
  3414.  
  3415.         return self
  3416.  
  3417.     def unfollow_users(self,
  3418.                        amount=10,
  3419.                        customList=(False, [], "all"),
  3420.                        InstapyFollowed=(False, "all"),
  3421.                        nonFollowers=False,
  3422.                        allFollowing=False,
  3423.                        style="FIFO",
  3424.                        unfollow_after=None,
  3425.                        sleep_delay=600):
  3426.         """Unfollows (default) 10 users from your following list"""
  3427.  
  3428.         if self.aborting:
  3429.             return self
  3430.  
  3431.         message = "Starting to unfollow users.."
  3432.         highlight_print(self.username, message,
  3433.                         "feature", "info", self.logger)
  3434.  
  3435.         token = "XXXXXXX"
  3436.  
  3437.         webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  3438.  
  3439.         payload = {
  3440.             "chat_id": "-378468384",
  3441.             "text": "Starting to *unfollow* users",
  3442.             "parse_mode": "Markdown"
  3443.         }
  3444.  
  3445.         requests.post(webhookurl, json=payload,
  3446.                       headers={"Content-type": "application/json;charset=UTF-8"})
  3447.  
  3448.         if unfollow_after is not None:
  3449.             if not python_version().startswith(('2.7', '3')):
  3450.                 self.logger.warning(
  3451.                     "`unfollow_after` parameter is not"
  3452.                     " available for Python versions below 2.7")
  3453.                 unfollow_after = None
  3454.  
  3455.         self.automatedFollowedPool = set_automated_followed_pool(
  3456.             self.username,
  3457.             unfollow_after,
  3458.             self.logger,
  3459.             self.logfolder)
  3460.  
  3461.         try:
  3462.             unfollowed = unfollow(self.browser,
  3463.                                   self.username,
  3464.                                   amount,
  3465.                                   customList,
  3466.                                   InstapyFollowed,
  3467.                                   nonFollowers,
  3468.                                   allFollowing,
  3469.                                   style,
  3470.                                   self.automatedFollowedPool,
  3471.                                   self.relationship_data,
  3472.                                   self.dont_include,
  3473.                                   self.white_list,
  3474.                                   sleep_delay,
  3475.                                   self.jumps,
  3476.                                   self.logger,
  3477.                                   self.logfolder)
  3478.             self.logger.info(
  3479.                 "--> Total people unfollowed : {}\n".format(unfollowed))
  3480.             self.unfollowed += unfollowed
  3481.  
  3482.         except Exception as exc:
  3483.             if isinstance(exc, RuntimeWarning):
  3484.                 self.logger.warning(
  3485.                     u'Warning: {} , stopping unfollow_users'.format(exc))
  3486.                 return self
  3487.  
  3488.             else:
  3489.                 self.logger.error('Sorry, an error occurred: {}'.format(exc))
  3490.                 self.aborting = True
  3491.                 return self
  3492.  
  3493.         return self
  3494.  
  3495.     def remove_follow_requests(self,
  3496.                                amount=200,
  3497.                                sleep_delay=600):
  3498.         """Remove user unaccepted follow requests"""
  3499.  
  3500.         if self.aborting:
  3501.             return self
  3502.  
  3503.         message = "Starting to get follow requests.."
  3504.         highlight_print(self.username,
  3505.                         message,
  3506.                         "feature",
  3507.                         "info",
  3508.                         self.logger)
  3509.  
  3510.         follow_requests = get_follow_requests(self.browser,
  3511.                                               amount,
  3512.                                               sleep_delay,
  3513.                                               self.logger,
  3514.                                               self.logfolder)
  3515.  
  3516.         unfollow_count = 0
  3517.  
  3518.         for person in follow_requests:
  3519.             self.logger.warning(
  3520.                 "--> Unfollow {}/{}:"
  3521.                 " Removing request for: '{}' "
  3522.                 .format(unfollow_count + 1, len(follow_requests), person))
  3523.  
  3524.             unfollow_state, msg = unfollow_user(self.browser,
  3525.                                                 "profile",
  3526.                                                 self.username,
  3527.                                                 person,
  3528.                                                 None,
  3529.                                                 None,
  3530.                                                 self.relationship_data,
  3531.                                                 self.logger,
  3532.                                                 self.logfolder)
  3533.  
  3534.             if unfollow_state is True:
  3535.                 unfollow_count += 1
  3536.                 self.unfollowed += 1
  3537.  
  3538.         return self
  3539.  
  3540.     def like_by_feed(self, **kwargs):
  3541.         """Like the users feed"""
  3542.  
  3543.         if self.aborting:
  3544.             return self
  3545.  
  3546.         for i in self.like_by_feed_generator(**kwargs):
  3547.             pass
  3548.  
  3549.         return self
  3550.  
  3551.     def like_by_feed_generator(self,
  3552.                                amount=50,
  3553.                                randomize=False,
  3554.                                unfollow=False,
  3555.                                interact=False):
  3556.         """Like the users feed"""
  3557.  
  3558.         if self.aborting:
  3559.             return
  3560.  
  3561.         liked_img = 0
  3562.         already_liked = 0
  3563.         inap_img = 0
  3564.         inap_unfollow = 0
  3565.         commented = 0
  3566.         followed = 0
  3567.         skipped_img = 0
  3568.         num_of_search = 0
  3569.         not_valid_users = 0
  3570.         link_not_found_loop_error = 0
  3571.  
  3572.         history = []
  3573.         self.quotient_breach = False
  3574.  
  3575.         while liked_img < amount:
  3576.             if self.quotient_breach:
  3577.                 break
  3578.  
  3579.             try:
  3580.                 # Gets another load of links to be tested
  3581.                 links = get_links_from_feed(self.browser,
  3582.                                             amount,
  3583.                                             num_of_search,
  3584.                                             self.logger)
  3585.  
  3586.                 if len(links) > 0:
  3587.                     link_not_found_loop_error = 0
  3588.  
  3589.                 if len(links) == 0:
  3590.                     link_not_found_loop_error += 1
  3591.                     if link_not_found_loop_error >= 10:
  3592.                         self.logger.warning(
  3593.                             "Loop error, 0 links"
  3594.                             " for 10 times consecutively, exit loop")
  3595.                         break
  3596.  
  3597.             except NoSuchElementException:
  3598.                 self.logger.warning('Too few images, aborting')
  3599.                 self.aborting = True
  3600.                 return
  3601.  
  3602.             num_of_search += 1
  3603.  
  3604.             for i, link in enumerate(links):
  3605.                 if liked_img == amount:
  3606.                     break
  3607.  
  3608.                 if (self.jumps["consequent"]["likes"]
  3609.                         >= self.jumps["limit"]["likes"]):
  3610.                     self.logger.warning("--> Like quotient reached its peak!"
  3611.                                         "\t~leaving Like-By-Feed activity\n")
  3612.                     self.quotient_breach = True
  3613.                     # reset jump counter after a breach report
  3614.                     self.jumps["consequent"]["likes"] = 0
  3615.                     break
  3616.  
  3617.                 if randomize and random.choice([True, False]):
  3618.                     self.logger.warning('Post Randomly Skipped...\n')
  3619.                     skipped_img += 1
  3620.                     continue
  3621.                 else:
  3622.                     if link in history:
  3623.                         self.logger.info('This link has already '
  3624.                                          'been visited: {}'
  3625.                                          .format(link))
  3626.                         continue
  3627.                     else:
  3628.                         self.logger.info('New link found...')
  3629.                         history.append(link)
  3630.                         self.logger.info('[{} posts liked /{} amount]'
  3631.                                          .format(liked_img, amount))
  3632.                         self.logger.info(link)
  3633.  
  3634.                         try:
  3635.                             (inappropriate, user_name,
  3636.                              is_video, reason, scope) = check_link(
  3637.                                 self.browser,
  3638.                                 link,
  3639.                                 self.dont_like,
  3640.                                 self.mandatory_words,
  3641.                                 self.mandatory_language,
  3642.                                 self.is_mandatory_character,
  3643.                                 self.mandatory_character,
  3644.                                 self.check_character_set,
  3645.                                 self.ignore_if_contains,
  3646.                                 self.logger)
  3647.  
  3648.                             if not inappropriate and self.delimit_liking:
  3649.                                 self.liking_approved = verify_liking(
  3650.                                     self.browser,
  3651.                                     self.max_likes,
  3652.                                     self.min_likes,
  3653.                                     self.logger)
  3654.                             if not inappropriate and self.liking_approved:
  3655.                                 # validate user
  3656.                                 validation, details = self.validate_user_call(
  3657.                                     user_name)
  3658.                                 if validation is not True:
  3659.                                     self.logger.info(details)
  3660.                                     not_valid_users += 1
  3661.                                     continue
  3662.                                 else:
  3663.                                     web_address_navigator(self.browser, link)
  3664.  
  3665.                                 # try to like
  3666.                                 like_state, msg = like_image(self.browser,
  3667.                                                              user_name,
  3668.                                                              self.blacklist,
  3669.                                                              self.logger,
  3670.                                                              self.logfolder)
  3671.  
  3672.                                 if like_state is True:
  3673.                                     liked_img += 1
  3674.                                     # reset jump counter after a successful
  3675.                                     # like
  3676.                                     self.jumps["consequent"]["likes"] = 0
  3677.  
  3678.                                     token = "XXXXXXX"
  3679.  
  3680.                                     webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  3681.  
  3682.                                     payload = {
  3683.                                         "chat_id": "-378468384",
  3684.                                         "text": "*Liked an image* _in the feed_ [" + format(link) + "](" + format(
  3685.                                             link) + ")",
  3686.                                         "parse_mode": "Markdown"
  3687.                                     }
  3688.  
  3689.                                     requests.post(webhookurl, json=payload,
  3690.                                                   headers={"Content-type": "application/json;charset=UTF-8"})
  3691.  
  3692.                                     checked_img = True
  3693.                                     temp_comments = []
  3694.  
  3695.                                     commenting = random.randint(
  3696.                                         0, 100) <= self.comment_percentage
  3697.                                     following = random.randint(
  3698.                                         0, 100) <= self.follow_percentage
  3699.  
  3700.                                     if (self.use_clarifai and
  3701.                                             (following or commenting)):
  3702.                                         try:
  3703.                                             (checked_img,
  3704.                                              temp_comments,
  3705.                                              clarifai_tags) = \
  3706.                                                 self.query_clarifai()
  3707.  
  3708.                                         except Exception as err:
  3709.                                             self.logger.error(
  3710.                                                 'Image check error:'
  3711.                                                 ' {}'.format(err))
  3712.  
  3713.                                     # commenting
  3714.                                     if (self.do_comment and
  3715.                                             user_name not in
  3716.                                             self.dont_include and
  3717.                                             checked_img and
  3718.                                             commenting):
  3719.                                         if self.delimit_commenting:
  3720.                                             (self.commenting_approved,
  3721.                                              disapproval_reason) = \
  3722.                                                 verify_commenting(
  3723.                                                     self.browser,
  3724.                                                     self.max_comments,
  3725.                                                     self.min_comments,
  3726.                                                     self.comments_mandatory_words,
  3727.                                                     self.logger)
  3728.  
  3729.                                         if self.commenting_approved:
  3730.                                             # smart commenting
  3731.                                             comments = \
  3732.                                                 self.fetch_smart_comments(
  3733.                                                     is_video,
  3734.                                                     temp_comments)
  3735.                                             if comments:
  3736.                                                 comment_state, \
  3737.                                                 msg = comment_image(
  3738.                                                     self.browser,
  3739.                                                     user_name,
  3740.                                                     comments,
  3741.                                                     self.blacklist,
  3742.                                                     self.logger,
  3743.                                                     self.logfolder)
  3744.                                             if comment_state is True:
  3745.                                                 commented += 1
  3746.  
  3747.                                         else:
  3748.                                             self.logger.info(
  3749.                                                 disapproval_reason)
  3750.  
  3751.                                     else:
  3752.                                         self.logger.info('--> Not commented')
  3753.                                         sleep(1)
  3754.  
  3755.                                     # following
  3756.                                     if (self.do_follow and
  3757.                                             user_name not in
  3758.                                             self.dont_include and
  3759.                                             checked_img and
  3760.                                             following and
  3761.                                             not follow_restriction(
  3762.                                                 "read", user_name,
  3763.                                                 self.follow_times,
  3764.                                                 self.logger)):
  3765.                                         follow_state, msg = follow_user(
  3766.                                             self.browser,
  3767.                                             "post",
  3768.                                             self.username,
  3769.                                             user_name,
  3770.                                             None,
  3771.                                             self.blacklist,
  3772.                                             self.logger,
  3773.                                             self.logfolder)
  3774.                                         if follow_state is True:
  3775.                                             followed += 1
  3776.                                     else:
  3777.                                         self.logger.info('--> Not following')
  3778.                                         sleep(1)
  3779.  
  3780.                                     # interactions (if any)
  3781.                                     if interact:
  3782.                                         self.logger.info(
  3783.                                             "--> User gonna be interacted: "
  3784.                                             "'{}'".format(user_name))
  3785.  
  3786.                                         self.like_by_users(
  3787.                                             user_name,
  3788.                                             self.user_interact_amount,
  3789.                                             self.user_interact_random,
  3790.                                             self.user_interact_media)
  3791.                                     # :P
  3792.                                     yield self
  3793.  
  3794.                                 elif msg == "already liked":
  3795.                                     already_liked += 1
  3796.  
  3797.                                 elif msg == "jumped":
  3798.                                     # will break the loop after
  3799.                                     # certain consecutive jumps
  3800.                                     self.jumps["consequent"]["likes"] += 1
  3801.  
  3802.                             elif inappropriate:
  3803.                                 inap_img += 1
  3804.                                 self.logger.info(
  3805.                                     "--> Image not liked: {}"
  3806.                                     .format(reason.encode('utf-8')))
  3807.  
  3808.                                 if "Inappropriate" in reason and unfollow:
  3809.                                     # example of unfollowing
  3810.                                     # directly from a post page (faster)
  3811.                                     self.logger.warning(
  3812.                                         "--> Ongoing Unfollow {}: unfollowing"
  3813.                                         " '{}' due to inappropriate content..."
  3814.                                         .format(inap_unfollow + 1, user_name))
  3815.  
  3816.                                     unfollow_state, msg = unfollow_user(
  3817.                                         self.browser,
  3818.                                         "post",
  3819.                                         self.username,
  3820.                                         user_name,
  3821.                                         None,
  3822.                                         None,
  3823.                                         self.relationship_data,
  3824.                                         self.logger,
  3825.                                         self.logfolder)
  3826.  
  3827.                                     if unfollow_state is True:
  3828.                                         inap_unfollow += 1
  3829.  
  3830.                         except NoSuchElementException as err:
  3831.                             self.logger.error('Invalid Page: {}'.format(err))
  3832.  
  3833.         self.logger.info('Liked: {}'.format(liked_img))
  3834.         self.logger.info('Already Liked: {}'.format(already_liked))
  3835.         self.logger.info('Commented: {}'.format(commented))
  3836.         self.logger.info('Followed: {}'.format(followed))
  3837.         self.logger.info('Inappropriate: {}'.format(inap_img))
  3838.         self.logger.info('Not valid users: {}'.format(not_valid_users))
  3839.         self.logger.info('Randomly Skipped: {}\n'.format(skipped_img))
  3840.  
  3841.         self.liked_img += liked_img
  3842.         self.already_liked += already_liked
  3843.         self.commented += commented
  3844.         self.followed += followed
  3845.         self.inap_img += inap_img
  3846.         self.not_valid_users += not_valid_users
  3847.  
  3848.         return
  3849.  
  3850.     def set_dont_unfollow_active_users(self,
  3851.                                        enabled=False,
  3852.                                        posts=4,
  3853.                                        boundary=500):
  3854.         """Prevents unfollow followers who have liked one of
  3855.        your latest X posts"""
  3856.  
  3857.         if self.aborting:
  3858.             return
  3859.  
  3860.         # do nothing
  3861.         if not enabled:
  3862.             return
  3863.  
  3864.         # list of users who liked our media
  3865.         active_users = get_active_users(self.browser,
  3866.                                         self.username,
  3867.                                         posts,
  3868.                                         boundary,
  3869.                                         self.logger)
  3870.  
  3871.         # include active user to not unfollow list
  3872.         self.dont_include.update(active_users)
  3873.  
  3874.     def set_blacklist(self, enabled, campaign):
  3875.         """
  3876.         Enable/disable blacklist. If enabled, adds users to a blacklist
  3877.        after interact with and adds users to dont_include list
  3878.        """
  3879.  
  3880.         if enabled is False:
  3881.             self.dont_include = self.white_list
  3882.             return
  3883.  
  3884.         self.blacklist['enabled'] = True
  3885.         self.blacklist['campaign'] = campaign
  3886.  
  3887.         try:
  3888.             with open('{}blacklist.csv'.format(self.logfolder), 'r') \
  3889.                     as blacklist:
  3890.                 reader = csv.DictReader(blacklist)
  3891.                 for row in reader:
  3892.                     if row['campaign'] == campaign:
  3893.                         self.dont_include.add(row['username'])
  3894.         # except:
  3895.         except Exception:
  3896.             self.logger.info('Campaign {} first run'.format(campaign))
  3897.  
  3898.     def grab_followers(self,
  3899.                        username=None,
  3900.                        amount=None,
  3901.                        live_match=False,
  3902.                        store_locally=True):
  3903.         """
  3904.         Gets and returns `followers` information of given user
  3905.        in desired amount, also, saves locally
  3906.        """
  3907.  
  3908.         message = "Starting to get the `Followers` data.."
  3909.         highlight_print(self.username, message, "feature", "info", self.logger)
  3910.  
  3911.         if username is None:
  3912.             self.logger.warning(
  3913.                 "Please provide a username to grab `Followers` data"
  3914.                 "  ~e.g. your own username or somebody else's")
  3915.             return self
  3916.  
  3917.         elif amount is None:
  3918.             self.logger.warning("Please put amount to grab `Followers` data")
  3919.             return self
  3920.  
  3921.         elif amount != "full" and \
  3922.                 (type(amount) != int or (
  3923.                 (type(amount) == int and amount <= 0))):
  3924.             self.logger.info("Please provide a valid amount bigger than"
  3925.                              " zero (0) to grab `Followers` data")
  3926.             return self
  3927.  
  3928.         # Get `followers` data
  3929.         grabbed_followers = get_followers(self.browser,
  3930.                                           username,
  3931.                                           amount,
  3932.                                           self.relationship_data,
  3933.                                           live_match,
  3934.                                           store_locally,
  3935.                                           self.logger,
  3936.                                           self.logfolder)
  3937.         return grabbed_followers
  3938.  
  3939.     def grab_following(self,
  3940.                        username=None,
  3941.                        amount=None,
  3942.                        live_match=False,
  3943.                        store_locally=True):
  3944.         """
  3945.         Gets and returns `following` information of given user
  3946.        in desired amount, also, saves locally
  3947.        """
  3948.  
  3949.         message = "Starting to get the `Following` data.."
  3950.         highlight_print(self.username, message, "feature", "info", self.logger)
  3951.  
  3952.         if username is None:
  3953.             self.logger.warning(
  3954.                 "Please provide a username to grab `Following` data"
  3955.                 "  ~e.g. your own username or somebody else's")
  3956.             return self
  3957.  
  3958.         elif amount is None:
  3959.             self.logger.warning("Please put amount to grab `Following` data")
  3960.             return self
  3961.  
  3962.         elif amount != "full" and \
  3963.                 (type(amount) != int or (
  3964.                 (type(amount) == int and amount <= 0))):
  3965.             self.logger.info("Please provide a valid amount bigger than"
  3966.                              " zero (0) to grab `Following` data")
  3967.             return self
  3968.  
  3969.         # get `following` data
  3970.         grabbed_following = get_following(self.browser,
  3971.                                           username,
  3972.                                           amount,
  3973.                                           self.relationship_data,
  3974.                                           live_match,
  3975.                                           store_locally,
  3976.                                           self.logger,
  3977.                                           self.logfolder)
  3978.         return grabbed_following
  3979.  
  3980.     def pick_unfollowers(self,
  3981.                          username=None,
  3982.                          compare_by="latest",
  3983.                          compare_track="first",
  3984.                          live_match=False,
  3985.                          store_locally=True,
  3986.                          print_out=True):
  3987.         """
  3988.         Compares the `followers` stored in a latest local
  3989.        copy against either lively generated data or previous
  3990.        local copy and returns absent followers
  3991.        """
  3992.  
  3993.         message = "Starting to pick Unfollowers of {}..".format(username)
  3994.         highlight_print(self.username, message, "feature", "info", self.logger)
  3995.  
  3996.         # get all and active Unfollowers
  3997.         all_unfollowers, active_unfollowers = get_unfollowers(
  3998.             self.browser,
  3999.             username,
  4000.             compare_by,
  4001.             compare_track,
  4002.             self.relationship_data,
  4003.             live_match,
  4004.             store_locally,
  4005.             print_out,
  4006.             self.logger,
  4007.             self.logfolder)
  4008.  
  4009.         return all_unfollowers, active_unfollowers
  4010.  
  4011.     def pick_nonfollowers(self,
  4012.                           username=None,
  4013.                           live_match=False,
  4014.                           store_locally=True):
  4015.         """ Returns Nonfollowers data of a given user """
  4016.  
  4017.         message = "Starting to pick Nonfollowers of {}..".format(username)
  4018.         highlight_print(self.username, message, "feature", "info", self.logger)
  4019.  
  4020.         # get Nonfollowers
  4021.         nonfollowers = get_nonfollowers(self.browser,
  4022.                                         username,
  4023.                                         self.relationship_data,
  4024.                                         live_match,
  4025.                                         store_locally,
  4026.                                         self.logger,
  4027.                                         self.logfolder)
  4028.  
  4029.         return nonfollowers
  4030.  
  4031.     def pick_fans(self,
  4032.                   username=None,
  4033.                   live_match=False,
  4034.                   store_locally=True):
  4035.         """
  4036.         Returns Fans data- all of the usernames who do follow
  4037.        the user WHOM user itself do not follow back
  4038.        """
  4039.  
  4040.         message = "Starting to pick Fans of {}..".format(username)
  4041.         highlight_print(self.username, message, "feature", "info", self.logger)
  4042.  
  4043.         # get Fans
  4044.         fans = get_fans(self.browser,
  4045.                         username,
  4046.                         self.relationship_data,
  4047.                         live_match,
  4048.                         store_locally,
  4049.                         self.logger,
  4050.                         self.logfolder)
  4051.  
  4052.         return fans
  4053.  
  4054.     def pick_mutual_following(self,
  4055.                               username=None,
  4056.                               live_match=False,
  4057.                               store_locally=True):
  4058.         """
  4059.         Returns Mutual Following data- all of the usernames who
  4060.        do follow the user WHOM user itself also do follow back
  4061.        """
  4062.  
  4063.         message = "Starting to pick Mutual Following of {}..".format(username)
  4064.         highlight_print(self.username, message, "feature", "info", self.logger)
  4065.  
  4066.         # get Mutual Following
  4067.         mutual_following = get_mutual_following(self.browser,
  4068.                                                 username,
  4069.                                                 self.relationship_data,
  4070.                                                 live_match,
  4071.                                                 store_locally,
  4072.                                                 self.logger,
  4073.                                                 self.logfolder)
  4074.  
  4075.         return mutual_following
  4076.  
  4077.     def end(self):
  4078.         """Closes the current session"""
  4079.  
  4080.         close_browser(self.browser, False, self.logger)
  4081.  
  4082.         with interruption_handler():
  4083.             # close virtual display
  4084.             if self.nogui:
  4085.                 self.display.stop()
  4086.  
  4087.             # write useful information
  4088.             dump_follow_restriction(self.username,
  4089.                                     self.logger,
  4090.                                     self.logfolder)
  4091.             dump_record_activity(self.username,
  4092.                                  self.logger,
  4093.                                  self.logfolder)
  4094.  
  4095.             with open('{}followed.txt'.format(self.logfolder), 'w') \
  4096.                     as followFile:
  4097.                 followFile.write(str(self.followed))
  4098.  
  4099.             # output live stats before leaving
  4100.             self.live_report()
  4101.  
  4102.             message = "Session ended!"
  4103.             highlight_print(self.username, message, "end", "info", self.logger)
  4104.             print("\n\n")
  4105.  
  4106.     def follow_by_tags(self,
  4107.                        tags=None,
  4108.                        amount=50,
  4109.                        skip_top_posts=True,
  4110.                        use_smart_hashtags=False,
  4111.                        randomize=False,
  4112.                        media=None):
  4113.         if self.aborting:
  4114.             return self
  4115.  
  4116.         inap_img = 0
  4117.         followed = 0
  4118.         not_valid_users = 0
  4119.  
  4120.         # if smart hashtag is enabled
  4121.         if use_smart_hashtags is True and self.smart_hashtags is not []:
  4122.             print('Using smart hashtags')
  4123.             tags = self.smart_hashtags
  4124.  
  4125.         # deletes white spaces in tags
  4126.         tags = [tag.strip() for tag in tags]
  4127.         tags = tags or []
  4128.         self.quotient_breach = False
  4129.  
  4130.         for index, tag in enumerate(tags):
  4131.             if self.quotient_breach:
  4132.                 break
  4133.  
  4134.             self.logger.info('Tag [{}/{}]'.format(index + 1, len(tags)))
  4135.             self.logger.info('--> {}'.format(tag.encode('utf-8')))
  4136.  
  4137.             try:
  4138.                 links = get_links_for_tag(self.browser,
  4139.                                           tag,
  4140.                                           amount,
  4141.                                           skip_top_posts,
  4142.                                           randomize,
  4143.                                           media,
  4144.                                           self.logger)
  4145.             except NoSuchElementException:
  4146.                 self.logger.info('Too few images, skipping this tag')
  4147.                 continue
  4148.  
  4149.             for i, link in enumerate(links):
  4150.                 if (self.jumps["consequent"]["follows"]
  4151.                         >= self.jumps["limit"]["follows"]):
  4152.                     self.logger.warning("--> Follow quotient reached its peak!"
  4153.                                         "\t~leaving Follow-By-Tags activity\n")
  4154.                     self.quotient_breach = True
  4155.                     # reset jump counter after a breach report
  4156.                     self.jumps["consequent"]["follows"] = 0
  4157.                     break
  4158.  
  4159.                 self.logger.info('[{}/{}]'.format(i + 1, len(links)))
  4160.                 self.logger.info(link)
  4161.  
  4162.                 try:
  4163.                     inappropriate, user_name, is_video, reason, scope = (
  4164.                         check_link(self.browser,
  4165.                                    link,
  4166.                                    self.dont_like,
  4167.                                    self.mandatory_words,
  4168.                                    self.mandatory_language,
  4169.                                    self.is_mandatory_character,
  4170.                                    self.mandatory_character,
  4171.                                    self.check_character_set,
  4172.                                    self.ignore_if_contains,
  4173.                                    self.logger)
  4174.                     )
  4175.  
  4176.                     if not inappropriate:
  4177.                         # validate user
  4178.                         validation, details = self.validate_user_call(
  4179.                             user_name)
  4180.                         if validation is not True:
  4181.                             self.logger.info(details)
  4182.                             not_valid_users += 1
  4183.                             continue
  4184.                         else:
  4185.                             web_address_navigator(self.browser, link)
  4186.  
  4187.                         # try to follow
  4188.                         follow_state, msg = follow_user(self.browser,
  4189.                                                         "post",
  4190.                                                         self.username,
  4191.                                                         user_name,
  4192.                                                         None,
  4193.                                                         self.blacklist,
  4194.                                                         self.logger,
  4195.                                                         self.logfolder)
  4196.                         if follow_state is True:
  4197.                             followed += 1
  4198.                             # reset jump counter after a successful follow
  4199.                             self.jumps["consequent"]["follows"] = 0
  4200.  
  4201.                         elif msg == "jumped":
  4202.                             # will break the loop after certain consecutive
  4203.                             # jumps
  4204.                             self.jumps["consequent"]["follows"] += 1
  4205.  
  4206.                     else:
  4207.                         self.logger.info(
  4208.                             '--> User not followed: {}'.format(reason))
  4209.                         inap_img += 1
  4210.  
  4211.                 except NoSuchElementException as err:
  4212.                     self.logger.error('Invalid Page: {}'.format(err))
  4213.  
  4214.         self.logger.info('Followed: {}'.format(followed))
  4215.         self.logger.info('Inappropriate: {}'.format(inap_img))
  4216.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  4217.  
  4218.         self.followed += followed
  4219.         self.inap_img += inap_img
  4220.         self.not_valid_users += not_valid_users
  4221.  
  4222.         return self
  4223.  
  4224.     def interact_by_URL(self,
  4225.                         urls=[],
  4226.                         randomize=False,
  4227.                         interact=False):
  4228.         """ Interact on posts at given URLs """
  4229.  
  4230.         if self.aborting:
  4231.             return self
  4232.  
  4233.         message = "Starting to interact by given URLs.."
  4234.         highlight_print(self.username, message, "feature", "info", self.logger)
  4235.  
  4236.         if not isinstance(urls, list):
  4237.             urls = [urls]
  4238.  
  4239.         if randomize is True:
  4240.             random.shuffle(urls)
  4241.  
  4242.         liked_img = 0
  4243.         already_liked = 0
  4244.         inap_img = 0
  4245.         commented = 0
  4246.         followed = 0
  4247.         not_valid_users = 0
  4248.  
  4249.         for index, url in enumerate(urls):
  4250.             if (self.jumps["consequent"]["likes"]
  4251.                     >= self.jumps["limit"]["likes"]):
  4252.                 self.logger.warning("--> Like quotient reached its peak!"
  4253.                                     "\t~leaving Interact-By-URL activity\n")
  4254.                 # reset jump counter before breaking the loop
  4255.                 self.jumps["consequent"]["likes"] = 0
  4256.                 # we have not used `quotient_breach` here
  4257.                 # cos this method has just one iterator
  4258.                 break
  4259.  
  4260.             if "https://www.instagram.com/p/" not in url:
  4261.                 url = "https://www.instagram.com/p/" + url
  4262.  
  4263.             self.logger.info('URL [{}/{}]'.format(index + 1, len(urls)))
  4264.             self.logger.info('--> {}'.format(url.encode('utf-8')))
  4265.  
  4266.             try:
  4267.                 inappropriate, user_name, is_video, reason, scope = (
  4268.                     check_link(self.browser,
  4269.                                url,
  4270.                                self.dont_like,
  4271.                                self.mandatory_words,
  4272.                                self.mandatory_language,
  4273.                                self.is_mandatory_character,
  4274.                                self.mandatory_character,
  4275.                                self.check_character_set,
  4276.                                self.ignore_if_contains,
  4277.                                self.logger))
  4278.  
  4279.                 if not inappropriate and self.delimit_liking:
  4280.                     self.liking_approved = verify_liking(self.browser,
  4281.                                                          self.max_likes,
  4282.                                                          self.min_likes,
  4283.                                                          self.logger)
  4284.  
  4285.                 if not inappropriate and self.liking_approved:
  4286.                     # validate user
  4287.                     validation, details = self.validate_user_call(user_name)
  4288.                     if validation is not True:
  4289.                         self.logger.info(details)
  4290.                         not_valid_users += 1
  4291.                         continue
  4292.                     else:
  4293.                         web_address_navigator(self.browser, url)
  4294.  
  4295.                     # try to like
  4296.                     like_state, msg = like_image(self.browser,
  4297.                                                  user_name,
  4298.                                                  self.blacklist,
  4299.                                                  self.logger,
  4300.                                                  self.logfolder)
  4301.  
  4302.                     if like_state is True:
  4303.                         liked_img += 1
  4304.                         # reset jump counter after a successful like
  4305.                         self.jumps["consequent"]["likes"] = 0
  4306.  
  4307.                         token = "XXXXXXX"
  4308.  
  4309.                         webhookurl = "https://api.telegram.org/bot" + token + "/sendMessage"
  4310.  
  4311.                         payload = {
  4312.                             "chat_id": "-378468384",
  4313.                             "text": "*Liked an image* by [" + format(
  4314.                                 user_name) + "](https://www.instagram.com" + format(user_name) + "/) [" + format(
  4315.                                 link) + "](" + format(
  4316.                                 link) + ")",
  4317.                             "parse_mode": "Markdown"
  4318.                         }
  4319.  
  4320.                         requests.post(webhookurl, json=payload,
  4321.                                       headers={"Content-type": "application/json;charset=UTF-8"})
  4322.  
  4323.                         checked_img = True
  4324.                         temp_comments = []
  4325.  
  4326.                         commenting = (random.randint(0, 100) <=
  4327.                                       self.comment_percentage)
  4328.                         following = (random.randint(0, 100) <=
  4329.                                      self.follow_percentage)
  4330.  
  4331.                         if self.use_clarifai and (following or commenting):
  4332.                             try:
  4333.                                 (checked_img,
  4334.                                  temp_comments,
  4335.                                  clarifai_tags) = self.query_clarifai()
  4336.  
  4337.                             except Exception as err:
  4338.                                 self.logger.error(
  4339.                                     'Image check error: {}'.format(err))
  4340.  
  4341.                         if (self.do_comment and
  4342.                                 user_name not in self.dont_include and
  4343.                                 checked_img and
  4344.                                 commenting):
  4345.  
  4346.                             if self.delimit_commenting:
  4347.                                 (self.commenting_approved,
  4348.                                  disapproval_reason) = verify_commenting(
  4349.                                     self.browser,
  4350.                                     self.max_comments,
  4351.                                     self.min_comments,
  4352.                                     self.comments_mandatory_words,
  4353.                                     self.logger)
  4354.                             if self.commenting_approved:
  4355.                                 # smart commenting
  4356.                                 comments = self.fetch_smart_comments(is_video,
  4357.                                                                      temp_comments)
  4358.                                 if comments:
  4359.                                     comment_state, msg = comment_image(
  4360.                                         self.browser,
  4361.                                         user_name,
  4362.                                         comments,
  4363.                                         self.blacklist,
  4364.                                         self.logger,
  4365.                                         self.logfolder)
  4366.  
  4367.                                     if comment_state is True:
  4368.                                         commented += 1
  4369.                             else:
  4370.                                 self.logger.info(disapproval_reason)
  4371.  
  4372.                         else:
  4373.                             self.logger.info('--> Not commented')
  4374.                             sleep(1)
  4375.  
  4376.                         if (self.do_follow and
  4377.                                 user_name not in self.dont_include and
  4378.                                 checked_img and following and
  4379.                                 not follow_restriction("read",
  4380.                                                        user_name,
  4381.                                                        self.follow_times,
  4382.                                                        self.logger)):
  4383.                             follow_state, msg = follow_user(
  4384.                                 self.browser,
  4385.                                 "post",
  4386.                                 self.username,
  4387.                                 user_name,
  4388.                                 None,
  4389.                                 self.blacklist,
  4390.                                 self.logger,
  4391.                                 self.logfolder)
  4392.  
  4393.                             if follow_state is True:
  4394.                                 followed += 1
  4395.                         else:
  4396.                             self.logger.info('--> Not following')
  4397.                             sleep(1)
  4398.  
  4399.                         # check if interaction is expected
  4400.                         if interact is True:
  4401.                             do_interact = (random.randint(0, 100)
  4402.                                            <= self.user_interact_percentage)
  4403.                             # do interactions if any
  4404.                             if do_interact and self.user_interact_amount > 0:
  4405.                                 self.interact_by_users(
  4406.                                     user_name,
  4407.                                     self.user_interact_amount,
  4408.                                     self.user_interact_random,
  4409.                                     self.user_interact_media)
  4410.  
  4411.                     elif msg == "already liked":
  4412.                         already_liked += 1
  4413.  
  4414.                     elif msg == "jumped":
  4415.                         # will break the loop after certain consecutive jumps
  4416.                         self.jumps["consequent"]["likes"] += 1
  4417.  
  4418.                 else:
  4419.                     self.logger.info(
  4420.                         '--> Image not liked: {}'.format(
  4421.                             reason.encode('utf-8')))
  4422.                     inap_img += 1
  4423.  
  4424.             except NoSuchElementException as err:
  4425.                 self.logger.error('Invalid Page: {}'.format(err))
  4426.  
  4427.         self.logger.info('Liked: {}'.format(liked_img))
  4428.         self.logger.info('Already Liked: {}'.format(already_liked))
  4429.         self.logger.info('Commented: {}'.format(commented))
  4430.         self.logger.info('Followed: {}'.format(followed))
  4431.         self.logger.info('Inappropriate: {}'.format(inap_img))
  4432.         self.logger.info('Not valid users: {}\n'.format(not_valid_users))
  4433.  
  4434.         self.liked_img += liked_img
  4435.         self.already_liked += already_liked
  4436.         self.commented += commented
  4437.         self.followed += followed
  4438.         self.inap_img += inap_img
  4439.         self.not_valid_users += not_valid_users
  4440.  
  4441.         return self
  4442.  
  4443.     def set_quota_supervisor(self,
  4444.                              enabled=False,
  4445.                              sleep_after=[],
  4446.                              sleepyhead=False,
  4447.                              stochastic_flow=False,
  4448.                              notify_me=False,
  4449.                              peak_likes=(None, None),
  4450.                              peak_comments=(None, None),
  4451.                              peak_follows=(None, None),
  4452.                              peak_unfollows=(None, None),
  4453.                              peak_server_calls=(None, None)):
  4454.         """
  4455.         Sets aside QS configuration ANY time in a session
  4456.        """
  4457.  
  4458.         # take a reference of the global configuration
  4459.         configuration = Settings.QS_config
  4460.  
  4461.         # strong type checking on peaks entered
  4462.         peak_values_combined = [peak_likes, peak_comments, peak_follows,
  4463.                                 peak_unfollows, peak_server_calls]
  4464.         peaks_are_tuple = all(type(item) is tuple for
  4465.                               item in peak_values_combined)
  4466.  
  4467.         if peaks_are_tuple:
  4468.             peak_values_merged = [i for sub in peak_values_combined
  4469.                                   for i in sub]
  4470.             integers_filtered = filter(lambda e: isinstance(e, int),
  4471.                                        peak_values_merged)
  4472.  
  4473.             peaks_are_provided = all(len(item) == 2 for
  4474.                                      item in peak_values_combined)
  4475.             peaks_are_valid = all(type(item) is int or type(item) is
  4476.                                   type(None) for item in peak_values_merged)
  4477.             peaks_are_good = all(item >= 0 for item in integers_filtered)
  4478.  
  4479.         # set QS if peak values are eligible
  4480.         if (peaks_are_tuple and
  4481.                 peaks_are_provided and
  4482.                 peaks_are_valid and
  4483.                 peaks_are_good):
  4484.  
  4485.             peaks = {"likes": {"hourly": peak_likes[0],
  4486.                                "daily": peak_likes[1]},
  4487.                      "comments": {"hourly": peak_comments[0],
  4488.                                   "daily": peak_comments[1]},
  4489.                      "follows": {"hourly": peak_follows[0],
  4490.                                  "daily": peak_follows[1]},
  4491.                      "unfollows": {"hourly": peak_unfollows[0],
  4492.                                    "daily": peak_unfollows[1]},
  4493.                      "server_calls": {"hourly": peak_server_calls[0],
  4494.                                       "daily": peak_server_calls[1]}}
  4495.  
  4496.             if not isinstance(sleep_after, list):
  4497.                 sleep_after = [sleep_after]
  4498.  
  4499.             rt = time.time()
  4500.             latesttime = {"hourly": rt, "daily": rt}
  4501.             orig_peaks = deepcopy(peaks)  # original peaks always remain static
  4502.             stochasticity = {"enabled": stochastic_flow,
  4503.                              "latesttime": latesttime,
  4504.                              "original_peaks": orig_peaks}
  4505.  
  4506.             if (platform.startswith("win32") and
  4507.                     python_version().startswith(('2', '3.7'))):
  4508.                 # UPDATE ME: remove this block [below] once
  4509.                 # plyer>1.3.0 is released to PyPI
  4510.                 notify_me = False
  4511.  
  4512.             # update QS configuration with the fresh settings
  4513.             configuration.update({"state": enabled,
  4514.                                   "sleep_after": sleep_after,
  4515.                                   "sleepyhead": sleepyhead,
  4516.                                   "stochasticity": stochasticity,
  4517.                                   "notify": notify_me,
  4518.                                   "peaks": peaks})
  4519.  
  4520.         else:
  4521.             # turn off QS for the rest of the session
  4522.             # since peak values are ineligible
  4523.             configuration.update(state="False")
  4524.  
  4525.             # user should be warned only if has had QS turned on
  4526.             if enabled is True:
  4527.                 self.logger.warning("Quota Supervisor: peak rates are misfit! "
  4528.                                     "Please use supported formats."
  4529.                                     "\t~disabled QS")
  4530.  
  4531.     @contextmanager
  4532.     def feature_in_feature(self, feature, validate_users):
  4533.         """
  4534.         Use once a host feature calls a guest
  4535.        feature WHERE guest needs special behaviour(s)
  4536.        """
  4537.  
  4538.         try:
  4539.             # add the guest which is gonna be used by the host :)
  4540.             self.internal_usage[feature] = {"validate": validate_users}
  4541.             yield
  4542.  
  4543.         finally:
  4544.             # remove the guest just after using it
  4545.             self.internal_usage.pop(feature)
  4546.  
  4547.     def live_report(self):
  4548.         """ Report live sessional statistics """
  4549.  
  4550.         print('')
  4551.  
  4552.         stats = [self.liked_img, self.already_liked,
  4553.                  self.commented,
  4554.                  self.followed, self.already_followed,
  4555.                  self.unfollowed,
  4556.                  self.inap_img,
  4557.                  self.not_valid_users]
  4558.  
  4559.         webhookurl = "https://api.telegram.org/botXXXXXXX/sendMessage"
  4560.  
  4561.         payload = {
  4562.             "chat_id": "-378468384",
  4563.             "text": "Session *finished* " + datetime.datetime.now().strftime(
  4564.                 "%H:%M %d/%m/%Y") + " for _" + format(self.username) + "_\n\n*Images liked*: " + format(
  4565.                 self.liked_img) + "\n*Followed*: " + format(self.followed) + "\n*Unfollowed*: " + format(
  4566.                 self.unfollowed) + "\n*Followers*: " + format(self.followed_by) + "\n*Following*: " + format(
  4567.                 self.following_num) + "\n\n-------------------------------",
  4568.             "parse_mode": "Markdown"
  4569.         }
  4570.  
  4571.         requests.post(webhookurl, json=payload,
  4572.                       headers={"Content-type": "application/json;charset=UTF-8"})
  4573.  
  4574.         if self.following_num and self.followed_by:
  4575.             owner_relationship_info = (
  4576.                 "On session start was FOLLOWING {} users"
  4577.                 " & had {} FOLLOWERS"
  4578.                     .format(self.following_num,
  4579.                             self.followed_by))
  4580.         else:
  4581.             owner_relationship_info = ''
  4582.  
  4583.         sessional_run_time = self.run_time()
  4584.         run_time_info = ("{} seconds".format(sessional_run_time) if
  4585.                          sessional_run_time < 60 else
  4586.                          "{} minutes".format(truncate_float(
  4587.                              sessional_run_time / 60, 2)) if
  4588.                          sessional_run_time < 3600 else
  4589.                          "{} hours".format(truncate_float(
  4590.                              sessional_run_time / 60 / 60, 2)))
  4591.         run_time_msg = "[Session lasted {}]".format(run_time_info)
  4592.  
  4593.         if any(stat for stat in stats):
  4594.             self.logger.info(
  4595.                 "Sessional Live Report:\n"
  4596.                 "\t|> LIKED {} images  |  ALREADY LIKED: {}\n"
  4597.                 "\t|> COMMENTED on {} images\n"
  4598.                 "\t|> FOLLOWED {} users  |  ALREADY FOLLOWED: {}\n"
  4599.                 "\t|> UNFOLLOWED {} users\n"
  4600.                 "\t|> LIKED {} comments\n"
  4601.                 "\t|> REPLIED to {} comments\n"
  4602.                 "\t|> INAPPROPRIATE images: {}\n"
  4603.                 "\t|> NOT VALID users: {}\n"
  4604.                 "\n{}\n{}"
  4605.                 .format(self.liked_img,
  4606.                         self.already_liked,
  4607.                         self.commented,
  4608.                         self.followed,
  4609.                         self.already_followed,
  4610.                         self.unfollowed,
  4611.                         self.liked_comments,
  4612.                         self.replied_to_comments,
  4613.                         self.inap_img,
  4614.                         self.not_valid_users,
  4615.                         owner_relationship_info,
  4616.                         run_time_msg))
  4617.         else:
  4618.             self.logger.info("Sessional Live Report:\n"
  4619.                              "\t|> No any statistics to show\n"
  4620.                              "\n{}\n{}"
  4621.                              .format(owner_relationship_info,
  4622.                                      run_time_msg))
  4623.  
  4624.     def set_do_reply_to_comments(self,
  4625.                                  enabled=False,
  4626.                                  percentage=0):
  4627.         """ Define if the comments on posts should be replied """
  4628.  
  4629.         self.do_reply_to_comments = enabled
  4630.         self.reply_to_comments_percent = percentage
  4631.  
  4632.         return self
  4633.  
  4634.     def set_comment_replies(self,
  4635.                             replies=[],
  4636.                             media=None):
  4637.         """ Set the replies to comments """
  4638.  
  4639.         if not replies:
  4640.             self.logger.info(
  4641.                 "Please, provide some comment replies for use next time.")
  4642.             self.comment_replies = None
  4643.             self.photo_comment_replies = None
  4644.             self.video_comment_replies = None
  4645.  
  4646.             return self
  4647.  
  4648.         if media in ["Photo", "Video"]:
  4649.             attr = "{}_comment_replies".format(media.lower())
  4650.             setattr(self, attr, replies)
  4651.  
  4652.         else:
  4653.             if media is not None:
  4654.                 self.logger.warning("Unkown media type set at"
  4655.                                     " comment replies! Treating as 'any'.")
  4656.  
  4657.             self.comment_replies = replies
  4658.  
  4659.     def set_use_meaningcloud(self,
  4660.                              enabled=False,
  4661.                              license_key=None,
  4662.                              polarity="P",
  4663.                              agreement=None,
  4664.                              subjectivity=None,
  4665.                              confidence=100):
  4666.         """ Set MeaningCloud Sentiment Analysis API configuration """
  4667.  
  4668.         if license_key is None:
  4669.             license_key = os.environ.get("MEANINGCLOUD_LIC_KEY")
  4670.  
  4671.         if polarity.upper() not in ['P', "P+", "NEU", 'N', "N+"]:
  4672.             self.logger.info("Oh no! Please provide a valid polarity "
  4673.                              "score tag for MeaningCloud"
  4674.                              "\t~service will not operate")
  4675.             polarity = None
  4676.  
  4677.         if enabled and license_key and polarity:
  4678.             Settings.meaningcloud_config.update(
  4679.                 enabled=enabled,
  4680.                 license_key=license_key,
  4681.                 score_tag=polarity.upper(),
  4682.                 agreement=agreement.upper() if agreement else None,
  4683.                 subjectivity=subjectivity.upper() if subjectivity else None,
  4684.                 confidence=int(confidence) if confidence else None)
  4685.  
  4686.         else:
  4687.             # turn off MeaningCloud service if not enabled or wrongly
  4688.             # configured
  4689.             Settings.meaningcloud_config.update(enabled=False)
  4690.  
  4691.     def set_use_yandex(self,
  4692.                        enabled=False,
  4693.                        API_key=None,
  4694.                        match_language=False,
  4695.                        language_code="en"):
  4696.         """ Set Yandex Translate API configuration """
  4697.  
  4698.         if API_key is None:
  4699.             API_key = os.environ.get("YANDEX_API_KEY")
  4700.  
  4701.         if enabled and API_key:
  4702.             Settings.yandex_config.update(
  4703.                 enabled=enabled,
  4704.                 API_key=API_key)
  4705.  
  4706.             if match_language is True and language_code:
  4707.                 supported_langs = yandex_supported_languages()
  4708.  
  4709.                 if (not supported_langs or
  4710.                         language_code.lower() not in supported_langs):
  4711.                     msg = ("Oh no! Failed to get the list of supported"
  4712.                            " languages by Yandex Translate :("
  4713.                            if not supported_langs else
  4714.                            "Oh no! The language with '{}' code is not"
  4715.                            " supported by Yandex Translate :/"
  4716.                            .format(language_code))
  4717.                     self.logger.info("{}\t~text language won't be matched"
  4718.                                      .format(msg))
  4719.                     match_language = False
  4720.  
  4721.             Settings.yandex_config.update(
  4722.                 match_language=match_language if language_code else False,
  4723.                 language_code=language_code.lower() if language_code else None)
  4724.  
  4725.         else:
  4726.             # turn off Yandex service if not enabled or wrongly configured
  4727.             Settings.yandex_config.update(enabled=False)
  4728.  
  4729.     def interact_by_comments(self,
  4730.                              usernames=None,
  4731.                              posts_amount=10,
  4732.                              comments_per_post=1,
  4733.                              reply=False,
  4734.                              interact=False,
  4735.                              randomize=False,
  4736.                              media=None):
  4737.         """
  4738.         Like comments of people on posts, reply to them
  4739.        and also interact with those commenters
  4740.        """
  4741.  
  4742.         message = "Starting to interact by comments.."
  4743.         highlight_print(self.username, message, "feature", "info", self.logger)
  4744.  
  4745.         if not isinstance(usernames, list):
  4746.             usernames = [usernames]
  4747.  
  4748.         if media not in ["Photo", "Video", None]:
  4749.             self.logger.warning("Unkown media type- '{}' set at"
  4750.                                 " Interact-By-Comments!\t~treating as any.."
  4751.                                 .format(media))
  4752.             media = None
  4753.  
  4754.         # hold the current global values for differentiating at the end
  4755.         liked_init = self.liked_img
  4756.         already_liked_init = self.already_liked
  4757.         liked_comments_init = self.liked_comments
  4758.         commented_init = self.commented
  4759.         replied_to_comments_init = self.replied_to_comments
  4760.         followed_init = self.followed
  4761.         already_followed_init = self.already_followed
  4762.         inap_img_init = self.inap_img
  4763.         not_valid_users_init = self.not_valid_users
  4764.  
  4765.         overall_posts_count = 0
  4766.         self.quotient_breach = False
  4767.         like_failures_tracker = {"consequent": {"post_likes": 0,
  4768.                                                 "comment_likes": 0},
  4769.                                  "limit": {"post_likes": 5,
  4770.                                            "comment_likes": 10}}
  4771.  
  4772.         leave_msg = "\t~leaving Interact-By-Comments activity\n"
  4773.  
  4774.         # start the interaction!
  4775.         for s, username in enumerate(usernames):
  4776.             if self.quotient_breach:
  4777.                 break
  4778.  
  4779.             message = "User: [{}/{}]".format(s + 1, len(usernames))
  4780.             highlight_print(
  4781.                 self.username, message, "user iteration", "info", self.logger)
  4782.  
  4783.             validation, details = self.validate_user_call(username)
  4784.             if validation is not True:
  4785.                 self.logger.info("--> Not a valid user: {}"
  4786.                                  .format(details))
  4787.                 self.not_valid_users += 1
  4788.                 continue
  4789.  
  4790.             per_user_liked_comments = 0
  4791.             per_user_replied_to_comments = 0
  4792.             per_user_used_replies = []
  4793.  
  4794.             try:
  4795.                 links = get_links_for_username(self.browser,
  4796.                                                self.username,
  4797.                                                username,
  4798.                                                posts_amount,
  4799.                                                self.logger,
  4800.                                                self.logfolder,
  4801.                                                randomize,
  4802.                                                media)
  4803.             except NoSuchElementException:
  4804.                 self.logger.error("Element not found, skipping this user.")
  4805.                 continue
  4806.  
  4807.             if links is False:
  4808.                 continue
  4809.  
  4810.             else:
  4811.                 if randomize:
  4812.                     random.shuffle(links)
  4813.                 links = links[:posts_amount]
  4814.                 overall_posts_count += len(links)
  4815.  
  4816.             for i, link in enumerate(links):
  4817.                 if self.quotient_breach:
  4818.                     break
  4819.  
  4820.                 elif (self.jumps["consequent"]["comments"]
  4821.                       >= self.jumps["limit"]["comments"]):
  4822.                     self.logger.warning(
  4823.                         "--> Comment quotient reached its peak!{}"
  4824.                         .format(leave_msg))
  4825.                     self.quotient_breach = True
  4826.                     # reset jump counter after a breach report
  4827.                     self.jumps["consequent"]["comments"] = 0
  4828.                     break
  4829.  
  4830.                 elif (like_failures_tracker["consequent"]["post_likes"]
  4831.                       >= like_failures_tracker["limit"]["post_likes"]):
  4832.                     self.logger.warning(
  4833.                         "--> Too many failures to like posts!{}"
  4834.                         .format(leave_msg))
  4835.                     # this example shows helpful usage of
  4836.                     # quotient breach outside QS needs..
  4837.                     self.quotient_breach = True
  4838.                     break
  4839.  
  4840.                 message = "Post: [{}/{}]".format(i + 1, len(links))
  4841.                 highlight_print(self.username, message, "post iteration",
  4842.                                 "info", self.logger)
  4843.  
  4844.                 (inappropriate, user_name,
  4845.                  is_video, reason, scope) = check_link(
  4846.                     self.browser,
  4847.                     link,
  4848.                     self.dont_like,
  4849.                     self.mandatory_words,
  4850.                     self.mandatory_language,
  4851.                     self.is_mandatory_character,
  4852.                     self.mandatory_character,
  4853.                     self.check_character_set,
  4854.                     self.ignore_if_contains,
  4855.                     self.logger)
  4856.  
  4857.                 if inappropriate:
  4858.                     self.logger.info(
  4859.                         "--> Post not interacted. {}\n"
  4860.                         .format(reason.encode('utf-8')))
  4861.                     self.inap_img += 1
  4862.                     continue
  4863.  
  4864.                 # go go!
  4865.                 per_post_liked_comments = 0
  4866.                 per_post_replied_to_comments = 0
  4867.                 per_post_interacted_commenters = []
  4868.  
  4869.                 # get comments (if any)
  4870.                 comment_data = get_comments_on_post(self.browser,
  4871.                                                     self.username,
  4872.                                                     username,
  4873.                                                     comments_per_post,
  4874.                                                     link,
  4875.                                                     self.ignore_users,
  4876.                                                     randomize,
  4877.                                                     self.logger)
  4878.                 if not comment_data:
  4879.                     self.logger.info("No interaction did happen.\n")
  4880.                     continue
  4881.  
  4882.                 # like the post before interacting on comments
  4883.                 image_like_state, msg = like_image(self.browser,
  4884.                                                    user_name,
  4885.                                                    self.blacklist,
  4886.                                                    self.logger,
  4887.                                                    self.logfolder)
  4888.                 if image_like_state is True:
  4889.                     like_failures_tracker["consequent"]["post_likes"] = 0
  4890.                     self.liked_img += 1