def test_validation_with_nested_configmanager():
    schema = [{
        "name": "user id ",
        "key": "user.id",
        "type": "integer",
        "required": True
    }, {
        "name": "user name ",
        "key": "user.name",
        "type": "string",
        "required": True
    }
    ]
    config_sample = {'user': {'id': 1234}}
    with pytest.raises(ValidationError):
        _ = ConfigManager(schema=schema, defaults=config_sample)

    os.environ.update({"BARBEROUSSE_USER_NAME": "user"})
    config_manager = ConfigManager(schema=schema, prefix="BARBEROUSSE", defaults=config_sample)
    assert config_manager.get('user.name') == 'user'
    assert config_manager.get('user.id') == 1234

    config_sample.update({'config42': {'env': {'prefix': 'BARBEROUSSE'}}})
    config_manager = ConfigManager(schema=schema, defaults=config_sample)

    assert config_manager.get('user.name') == 'user'
    assert config_manager.get('user.id') == 1234
def test_configuration_replace(default_config, sample_config):
    config_manager = ConfigManager()

    config_manager.set_many(sample_config)
    assert sample_config['simple'] == config_manager.get('simple')
    config_manager.replace(default_config)
    assert config_manager.get('simple') is None
    assert default_config['defaultkey1'] == config_manager.get('defaultkey1')
def test_configuration_setting_nested_keys(sample_config):
    config_manager = ConfigManager()
    config_manager.set_many(sample_config)
    config_manager.set('key1', 'simple')
    config_manager.set('key2.key2', 'simple')
    config_manager.set('key3.key3.key3', 'simple')

    assert 'simple' == config_manager.get('key1')
    assert 'simple' == config_manager.get('key2.key2')
    assert 'simple' == config_manager.get('key3.key3.key3')
def test_change_nesting_key(cwd):
    defaults = {
        'config43': {
            'file': {
                'path': cwd + "/files/nested-config.yml"
            }
        }
    }
    config_manager = ConfigManager(defaults=defaults,
                                   nested_configuration_key="config43")
    assert config_manager.get('config43') is not None
    assert config_manager.get('nested') == "value"
Example #5
0
def test_literals_etcd(script_runner):
    keyspace = '/' + str(uuid4())
    ret = script_runner.run(
        'config42',
        *args('-l key=value nested.key=value -c etcd --etcd-keyspace ' +
              keyspace))
    assert ret.success
    config_manager = ConfigManager(keyspace=keyspace)

    assert config_manager.get('key') == 'value'
    assert config_manager.get('nested').get('key') == 'value'
    assert config_manager.get('nested.key') == 'value'
Example #6
0
def test_apply_literals(script_runner):
    keyspace = '/' + str(uuid4())
    _ = script_runner.run(
        'config42',
        *args('-l key=value key2=value -c etcd --etcd-keyspace ' + keyspace))
    ret = script_runner.run(
        'config42',
        *args('-a apply -l key=value2 -c etcd --etcd-keyspace ' + keyspace))
    assert ret.success
    config_manager = ConfigManager(keyspace=keyspace)
    assert config_manager.get('key') == 'value2'
    assert config_manager.get('key2') is None
    assert len(config_manager.as_dict()) == 1
Example #7
0
def test_apply_raw(script_runner, tmp_path):
    tmp_dir = tmp_path / 'raw'
    tmp_dir.mkdir()
    path = str(tmp_dir)
    _ = script_runner.run(
        'config42', *args('-l key=value key2=value -c raw --raw-path ' + path))
    ret = script_runner.run(
        'config42', *args('-a apply -l key=value2 -c raw --raw-path ' + path))
    assert ret.success
    config_manager = ConfigManager(path=path)
    assert config_manager.get('key') == 'value2'
    assert config_manager.get('key') == 'value2'
    assert config_manager.get('key2') is None
    assert len(config_manager.as_dict()) == 1
def test_validation_casting():
    schema = [{
        "name": "user id ",
        "key": "user.id",
        "type": "integer",
    }, {
        "name": "user code ",
        "key": "user.code",
        "type": "string"
    }
    ]

    config_manager = ConfigManager(schema=schema, defaults={'user': {'id': '12345', 'code': 12345}})
    assert config_manager.get('user.code') == '12345'
    assert config_manager.get('user.id') == 12345
def test_configuration_content(cwd):
    defaults = {
        'config42': {
            'file': {
                'path': cwd + "/files/nested-config.yml"
            }
        }
    }
    os.environ.update({"BARBEROUSSE_ENV1": "TEST1"})
    config_manager = ConfigManager(defaults=defaults)

    assert config_manager.get('config42') is not None
    assert len(config_manager.get('config42')) > 1
    assert config_manager.get('env1') == "TEST1"
    assert config_manager.get('nested') == "value"
Example #10
0
def test_configuration_update_default_value(default_config):
    config_manager = ConfigManager(defaults=default_config)
    assert config_manager.get('absentkey3.absentkey3.absentkey3') is None
    config_manager.set('absentkey3.absentkey3.absentkey3', "value", default=True)
    config_manager.set('absentkey4', "value", default=True)

    assert config_manager.as_dict().get('absentkey3') is None
Example #11
0
def main():
    config = ConfigManager()
    config.set_many(DEFAULT_CONFIG)
    _config = ConfigManager(schema=schema, defaults=defaults)
    config.set_many(_config.as_dict())
    config.set_many(
        ConfigManager(schema=schema,
                      path=_config.get('config.file')).as_dict())
    config.set_many(_config.as_dict())
    config.commit()

    if config.get('dump_configuration'):
        conf = config.as_dict()
        conf.pop('config42')
        print(yaml.dump(conf))
        exit(0)
    if config.get('show_version'):
        print("Installed version {}".format(instabot_py.__version__))
        exit(0)

    if not config.get('ignore_updates'):
        last_version = get_last_version()
        if last_version and last_version != instabot_py.__version__:
            print(
                "Newer version available: {}, The current version: {}".format(
                    last_version, instabot_py.__version__))
            print(
                "To update, please type \n python3 -m pip install instabot-py --upgrade --no-cache-dir "
            )
            print("")
            print(
                "  > You can ignore warning, run the instabot with --ignore-updates flag"
            )
            exit(0)

    if config.get('verbosity'):
        verbosity = int(config.get('verbosity'))
        if verbosity == 1:
            level = logging.INFO
        elif verbosity > 1:
            level = logging.DEBUG
        config.set("logging.root.level", level)

    logging.config.dictConfig(config.get("logging"))
    try:
        bot = InstaBot(config=config)
    except CredsMissing:
        print(
            "You didn't provide your Instagram login & password or you didn't specify the configuration file"
        )
        print("Try again :")
        print("")
        print("   instabot-py --login YOUR_LOGIN --password YOUR_PASSWORD")
        print("   instabot-py -c your-config.yml")
        print(
            "You can export and modify the default configuration by typing the command below"
        )
        print("    instabot-py --dump")
        exit(1)
    bot.mainloop()
Example #12
0
def main():
    config = ConfigManager()
    config.set_many(DEFAULT_CONFIG)
    _config = ConfigManager(schema=schema, defaults=defaults)
    config.set_many(_config.as_dict())
    config.set_many(ConfigManager(path=_config.get('config.file')).as_dict())
    config.set_many(_config.as_dict())
    config.commit()

    if config.get('dump_configuration'):
        conf = config.as_dict()
        conf.pop('config42')
        print(yaml.dump(conf))
        exit(0)
    if config.get('show_version'):
        print(instabot_py.__version__)
        exit(0)

    if config.get('verbosity'):
        verbosity = int(config.get('verbosity'))
        if verbosity == 1:
            level = logging.INFO
        elif verbosity > 1:
            level = logging.DEBUG
        config.set("logging.root.level", level)

    logging.config.dictConfig(config.get("logging"))
    bot = InstaBot(config=config)
    bot.mainloop()
Example #13
0
def test_configuration_setting_raise_exception(sample_config):
    config_manager = ConfigManager()
    config_manager.set_many(sample_config)
    with pytest.raises(AttributeError):
        config_manager.set('nested_list.0.1', 'simple')

    config_manager.set('absentkey4', "value")
    with pytest.raises(AttributeError):
        assert config_manager.get('absentkey4.absentkey3.absentkey3') == "value"
def test_validation_defaults_configmanager():
    schema = [{
        "name": "user id ",
        "key": "user.id",
        "type": "integer",
        "default": 1234
    }, {
        "name": "user name ",
        "key": "user.name",
    }
    ]

    with pytest.raises(ValidationError):
        _ = ConfigManager(schema=schema)

    config_manager = ConfigManager(schema=schema, defaults={'user': {'name': 'user'}})
    assert config_manager.get('user.name') == 'user'
    assert config_manager.get('user.id') == 1234
Example #15
0
def test_configuration_trigger_commit(sample_config):
    config_manager = ConfigManager()

    config_manager.set_many(sample_config)
    config_manager.commit()
    assert sample_config['simple'] == config_manager.get('simple')

    config_manager.set('new_key', 'new_value')
    assert config_manager.handler.updated is False
    config_manager.set('new_key2', 'new_value2', trigger_commit=False)
    assert config_manager.handler.updated is True
    config_manager.commit()
    assert config_manager.handler.updated is False

    config_manager.set('new_key', 'new_value', trigger_commit=True)
    assert config_manager.handler.updated is False
Example #16
0
def main():
    config = ConfigManager()
    config.set_many(DEFAULT_CONFIG)
    _config = ConfigManager(schema=schema, defaults=defaults)
    config.set_many(_config.as_dict())
    config_file = _config.get('config.file')
    config.set_many(ConfigManager(schema=schema, path=config_file).as_dict())
    config.set_many(_config.as_dict())
    config.commit()

    configure_logging(config)
    if config.get('dump_configuration'):
        conf = config.as_dict()
        conf.pop('config42')
        conf.pop('dump_configuration')
        print(yaml.dump(conf))
        exit(0)
    if config.get('show_version'):
        print("Installed version {}".format(src.__version__))
        exit(0)

    if not config.get('ignore_updates'):
        last_version = get_last_version()
        if last_version and last_version != src.__version__:
            print("Newer version available: {}, The current version: {}".format(last_version, src.__version__))
            print("To update, please type \n python3 -m pip install instabot-py --upgrade --no-cache-dir ")
            print("")
            print("  > You can ignore warning, run the instabot with --ignore-updates flag")
            exit(0)

    try:
        bot = InstaBot(config=config)
        if config_file:
            bot.logger.info(f"Reading configuration ({len(_config.as_dict())} settings) from {config_file}")
        else:
            bot.logger.info(f"Use the default configuration, add '-c your-config.yml' to specify your config")

    except CredsMissing:
        print("You didn't provide your Instagram login & password or you didn't specify the configuration file")
        print("Try again :")
        print("")
        print("   instabot-py --login YOUR_LOGIN --password YOUR_PASSWORD")
        print("   instabot-py -c your-config.yml")
        print("You can export and modify the default configuration by typing the command below")
        print("    instabot-py --dump")
        exit(1)
    bot.mainloop()
def test_validation_with_configmanager():
    schema = [{
        "name": "user id ",
        "key": "user.id",
        "type": "integer",
        "required": True
    }, {
        "name": "user name ",
        "key": "user.name",
        "type": "string",
        "required": True
    }
    ]
    config_sample = {'user': {'id': 1234, 'name': 'user'}}

    config_manager = ConfigManager(schema=schema, defaults=config_sample)
    assert config_manager.get('user.name') == 'user'

    with pytest.raises(ValidationError):
        _ = ConfigManager(schema=schema, defaults={})

    # Disable validator
    _ = ConfigManager(schema=schema, validator=False, defaults={})
Example #18
0
    def create(*, project_name, encoding='utf-8', environment=None, defaults=None):
        if defaults is None:
            defaults = {}
        # load defaults from home directory
        config_file = Config._get_config_file_path(project_name, "default")
        found_config_file = False
        if os.path.exists(config_file):
            logging.getLogger(__name__).info("loading default configurations from %s", config_file)
            config = ConfigManager(path=config_file, encoding=encoding, defaults=defaults)
            found_config_file = True
        # load environment configurations from environment variables
        # fix prefix to be SC
        prefix = "SC"
        env_config = ConfigManager(prefix=prefix)

        key_env = "environment"
        if environment is None:
            environment = env_config.get(key_env)
            if environment is None:
                # use production configuration if not specified environment
                environment = "production"
                logging.getLogger(__name__).info("did not specify environment, using %s", environment)
        else:
            logging.getLogger(__name__).info("using environment: %s", environment)

        # load environment configurations from /var/opt/sc directory
        env_config_file = Config._get_config_file_path(project_name, environment)
        if os.path.exists(env_config_file):
            logging.getLogger(__name__).info("loading environmental configurations from %s", env_config_file)
            if not found_config_file:
                config = ConfigManager(path=env_config_file, encoding=encoding, defaults=defaults)
                found_config_file = True
            else:
                config.set_many(ConfigManager(path=env_config_file, encoding=encoding).as_dict())

        # load environment configurations from user directory
        user_config_file = Config._get_user_dir_config_file_path(project_name, environment)
        if os.path.exists(user_config_file):
            logging.getLogger(__name__).info("loading user directory configurations from %s", user_config_file)
            if not found_config_file:
                config = ConfigManager(path=user_config_file, encoding=encoding, defaults=defaults)
                found_config_file = True
            else:
                config.set_many(ConfigManager(path=user_config_file, encoding=encoding).as_dict())

        # load environment configurations from current directory
        current_dir_config_file = Config._get_cur_dir_config_file_path(environment)
        if os.path.exists(current_dir_config_file):
            logging.getLogger(__name__).info("loading current directory configurations from %s", current_dir_config_file)
            logging.getLogger(__name__).info(f"found_config_file: {found_config_file}")
            if not found_config_file:
                config = ConfigManager(path=current_dir_config_file, encoding=encoding, defaults=defaults)
                found_config_file = True
            else:
                config.set_many(ConfigManager(path=current_dir_config_file, encoding=encoding).as_dict())
        
        if not found_config_file:
            config = ConfigManager(defaults=defaults)
        config.set_many(env_config.as_dict())
        config.set(key_env, environment)
        return config
Example #19
0
from pprint import pprint

from config42 import ConfigManager
from config42.handlers import FileHandler

config = ConfigManager(handler=FileHandler, path='files/config1.yml')
CONFIG = config.handler.as_dict()

print("Configuration has been loaded")
pprint(CONFIG)

# Access to configuration via the ConfigManager getter
print("application_name : {}".format(config.get('application_name')))
print("nested key : {}".format(config.get('nested.nestedkey.key2')))

# Access to configuration via the as dict utility; it will dump configuration file to data store if updated
print("user : {}".format(config.handler.as_dict()['user']))

# Access to configuration via the classic CONFIG global variable
print("application_name : {}".format(CONFIG['application_name']))
print("nested key : {}".format(CONFIG['nested']['nestedkey']['key2']))
Example #20
0
def test_configuration_default_values(default_config):
    config_manager = ConfigManager(defaults=default_config)

    assert default_config['defaultkey1'] == config_manager.get('defaultkey1')
    assert default_config['defaultkey2']['defaultkey2'] == config_manager.get('defaultkey2.defaultkey2')
    assert config_manager.get('absentkey3.absentkey3.absentkey3') is None
Example #21
0
def test_configuration_content_absent_keys():
    config_manager = ConfigManager()
    assert config_manager.get('absentkey1') is None
    assert config_manager.get('absentkey2.absentkey2') is None
    assert config_manager.get('absentkey3.absentkey3.absentkey3') is None
Example #22
0
def main():
    global config
    try:
        config = ConfigManager(handler=ArgParse,
                               schema=schema,
                               prog="config42")
        if not config.get('verbosity'):
            level = 100  # Disbaled
        elif config.get('verbosity') == 1:
            level = logging.INFO
        else:
            level = logging.DEBUG

        logging.basicConfig(level=level,
                            format="[%(name)s/%(levelname)s] - %(message)s")

        configuration = config.get('configuration')
        from_configuration = config.get('from_configuration')
        action = config.get('action')

        if not action:
            if configuration and (from_configuration
                                  or config.get('literals')):
                action = ACTION_MERGE
            elif not configuration and not from_configuration and not config.get(
                    'literals'):
                config.handler.parser.print_help()
                raise SystemExit
            else:
                action = ACTION_READ

        if action == ACTION_READ:
            parsed_config = read_from_configuration(configuration)
            if config.get('output_format') == 'json':
                print(json.dumps(parsed_config, indent=2))
            else:
                print(yaml.dump(parsed_config))
        else:
            config_manager = load_configmanager(configuration)
            if action == ACTION_DESTROY:
                config_manager.handler.destroy()
                logging.info("{}/configuration has been destroyed, {}".format(
                    configuration.capitalize(), config.get(configuration)))
            elif action == ACTION_APPLY:
                parsed_config = read_from_configuration(from_configuration)
                config_manager.handler.destroy()
                config_manager.replace(parsed_config)
                config_manager.commit()
                logging.info(
                    "{}/ previous configuration has been flushed, {}".format(
                        configuration.capitalize(), config.get(configuration)))
            elif action in ACTION_MERGE:
                # update a configuration
                parsed_config = read_from_configuration(from_configuration)
                config_manager.set_many(parsed_config)
                config_manager.commit()
                logging.info("{}/configuration has been updated, {}".format(
                    configuration.capitalize(), config.get(configuration)))
    except SystemExit:
        exit(1)
    except KeyboardInterrupt:
        exit(127)
    except Exception as exc:
        logging.exception(exc)
        exit(1)
Example #23
0
import logging.config
import os

from config42 import ConfigManager

from log_analyzer.default_config import DEFAULT_CONFIG

LOGGER = logging.getLogger(__name__)

env_config = ConfigManager(prefix="LOGANALYZER")
logging.basicConfig(
    level=logging.DEBUG if env_config.get("debug") else logging.INFO)
config = ConfigManager(defaults=DEFAULT_CONFIG)
config.set_many(env_config.as_dict())
config_file = config.get("config.file")
config_etcd = config.get("config.etcd")
if config_file:
    if config_file.startswith("/"):
        config_path = config_file
    else:
        cwd = os.getcwd()
        config_path = cwd + "/" + config_file
    config.set_many(
        ConfigManager(path=config_path.replace('//', '/')).as_dict())
    LOGGER.info("Setting configuration from {} : OK".format(config_file))

if config_etcd:
    if not config_etcd.get("keyspace"):
        raise Exception("etcd Keyspace is mandatory")
    try:
        config.set_many(ConfigManager(**config_etcd).as_dict())
Example #24
0
config = ConfigManager(schema=schema,
                       defaults={
                           'config42':
                           OrderedDict([
                               ('argv', dict(handler=ArgParse, schema=schema)),
                               ('env', {
                                   'prefix': 'C42'
                               }),
                               ('file', {
                                   'path': 'local.yml'
                               }),
                           ])
                       })

if not config.get('verbosity'):
    level = 100  # Disbaled
elif config.get('verbosity') == 1:
    level = logging.INFO
else:
    level = logging.DEBUG

logging.basicConfig(level=level,
                    format="[%(name)s/%(levelname)s] - %(message)s")


def base36_encode(number):
    assert number >= 0, "positive integer required"
    if number == 0:
        return "0"
    base36 = []
Example #25
0
from config42 import ConfigManager

env_config = ConfigManager(prefix="MYAPP")
# Access to configuration via the ConfigManager getter
print("username : {}".format(env_config.get('username')))
print("nested key  : {}".format(env_config.get('secret.one')))
Example #26
0
def test_configuration_content_index_error(sample_config):
    config_manager = ConfigManager()
    config_manager.set_many(sample_config)
    assert config_manager.get('nested_list.0.4') is None
Example #27
0
def main():
    config = ConfigManager()
    config.set_many(DEFAULT_CONFIG)
    _config = ConfigManager(schema=schema, defaults=defaults)
    config.set_many(_config.as_dict())
    config_file = _config.get('config.file')
    config.set_many(ConfigManager(schema=schema, path=config_file).as_dict())
    config.set_many(_config.as_dict())
    config.commit()

    configure_logging(config)
    if config.get('dump_configuration'):
        conf = config.as_dict()
        conf.pop('config42')
        conf.pop('dump_configuration')
        print(yaml.dump(conf))
        exit(0)

    if config.get('create_configuration'):
        create_configuration()
        exit(0)

    if config.get('show_version'):
        print("Installed version {}".format(instabot_py.__version__))
        exit(0)

    if not config.get('ignore_updates'):
        last_version = get_last_version()
        current_version = instabot_py.__version__
        if last_version and last_version != current_version:
            print(f"""Newer version is available: {last_version}. The current \
version is: {current_version}.
To update instabot-py, please perform: 
    python3 -m pip install instabot-py --upgrade --no-cache-dir
                
    > You can also ignore this warning and upgrade instabot-py later. In this \
case, please run the instabot with '--ignore-updates' flag.""")
            exit(0)

    if config_file:
        print(f"Reading configuration ({len(_config.as_dict())} settings) from"
              f" {config_file}")
    elif os.path.isfile('instabot.config.yml'):
        print("Using 'instabot.config.yml' as a configuration, add "
              "'-c your-config.yml' if you want to use your config file")
    else:
        print("Configuration file has not been found. Please run the instabot "
              "with '--create-config' flag.")
        exit(0)

    try:
        bot = InstaBot(config=config)
    except CredsMissing:
        print(
            """We could not find your Instagram login and/or password. Maybe \
you did not change the default ones in the config file.
You can specify them either directly, correct them in the default config file \
or use your own config file:

    instabot-py --login YOUR_LOGIN --password YOUR_PASSWORD    
or
    instabot-py -c your-config.yml
""")
        exit(1)

    bot.mainloop()
Example #28
0
class InstaBot:
    """
    Instabot.py

    """

    url = "https://www.instagram.com/"
    url_tag = "https://www.instagram.com/explore/tags/%s/?__a=1"
    url_location = "https://www.instagram.com/explore/locations/%s/?__a=1"
    url_likes = "https://www.instagram.com/web/likes/%s/like/"
    url_unlike = "https://www.instagram.com/web/likes/%s/unlike/"
    url_comment = "https://www.instagram.com/web/comments/%s/add/"
    url_follow = "https://www.instagram.com/web/friendships/%s/follow/"
    url_unfollow = "https://www.instagram.com/web/friendships/%s/unfollow/"
    url_login = "******"
    url_logout = "https://www.instagram.com/accounts/logout/"
    url_media_detail = "https://www.instagram.com/p/%s/?__a=1"
    url_media = "https://www.instagram.com/p/%s/"
    url_user_detail = "https://www.instagram.com/%s/"
    api_user_detail = "https://i.instagram.com/api/v1/users/%s/info/"

    def __init__(self, config=None, **kwargs):
        self.logger = logging.getLogger(self.__class__.__name__)
        if not config:
            self.config = ConfigManager(defaults=DEFAULT_CONFIG)
            self.config.set_many(kwargs)
        else:
            self.config = config

        login = self.config.get("login")
        password = self.config.get("password")
        if login is None or password is None:
            raise CredsMissing()

        self.persistence = PersistenceManager(self.config.get("database"))
        self.persistence.bot = self
        self.session_file = self.config.get("session_file")

        self.user_agent = random.sample(self.config.get("list_of_ua"), 1)[0]
        self.bot_start = datetime.datetime.now()
        self.bot_start_ts = time.time()
        self.start_at_h = self.config.get("start_at_h")
        self.start_at_m = self.config.get("start_at_m")
        self.end_at_h = self.config.get("end_at_h")
        self.end_at_m = self.config.get("end_at_m")
        self.window_check_every = self.config.get("window_check_every")
        self.unfollow_break_min = self.config.get("unfollow_break_min")
        self.unfollow_break_max = self.config.get("unfollow_break_max")
        self.user_blacklist = self.config.get("user_blacklist")
        self.tag_blacklist = self.config.get("tag_blacklist")
        self.unfollow_whitelist = self.config.get("unfollow_whitelist")
        self.comment_list = self.config.get("comment_list")

        self.instaloader = instaloader.Instaloader()

        # Unfollow Criteria & Options
        self.unfollow_recent_feed = self.str2bool(
            self.config.get("unfollow_recent_feed"))
        self.unfollow_not_following = self.str2bool(
            self.config.get("unfollow_not_following"))
        self.unfollow_inactive = self.str2bool(
            self.config.get("unfollow_inactive"))
        self.unfollow_probably_fake = self.str2bool(
            self.config.get("unfollow_probably_fake"))
        self.unfollow_selebgram = self.str2bool(
            self.config.get("unfollow_selebgram"))
        self.unfollow_everyone = self.str2bool(
            self.config.get("unfollow_everyone"))

        self.time_in_day = 24 * 60 * 60
        # Like
        self.like_per_run = int(self.config.get("like_per_run"))

        if self.like_per_run > 0:
            self.like_delay = self.time_in_day / self.like_per_run

        # Unlike
        self.time_till_unlike = self.config.get("time_till_unlike")
        self.unlike_per_run = int(self.config.get("unlike_per_run"))
        if self.unlike_per_run > 0:
            self.unlike_delay = self.time_in_day / self.unlike_per_run

        # Follow
        self.follow_time = self.config.get("follow_time")
        self.follow_per_run = int(self.config.get("follow_per_run"))
        self.follow_delay = self.config.get("follow_delay")
        if self.follow_per_run > 0 and not self.follow_delay:
            self.follow_delay = self.time_in_day / self.follow_per_run

        # Unfollow
        self.unfollow_per_run = int(self.config.get("unfollow_per_run"))
        self.unfollow_delay = self.config.get("unfollow_delay")
        if self.unfollow_per_run > 0 and not self.unfollow_delay:
            self.unfollow_delay = self.time_in_day / self.unfollow_per_run

        # Comment
        self.comments_per_run = int(self.config.get("comments_per_run"))
        self.comments_delay = self.config.get("comments_delay")
        if self.comments_per_run > 0 and not self.comments_delay:
            self.comments_delay = self.time_in_day / self.comments_per_run

        # Don't like if media have more than n likes.
        self.media_max_like = self.config.get("media_max_like")
        # Don't like if media have less than n likes.
        self.media_min_like = self.config.get("media_min_like")
        # Don't follow if user have more than n followers.
        self.user_max_follow = self.config.get("user_max_follow")
        # Don't follow if user have less than n followers.
        self.user_min_follow = self.config.get("user_min_follow")

        # Auto mod seting:
        # Default list of tag.
        self.tag_list = self.config.get("tag_list")
        # Default keywords.
        self.keywords = self.config.get("keywords")
        # Get random tag, from tag_list, and like (1 to n) times.
        self.max_like_for_one_tag = self.config.get("max_like_for_one_tag")
        # log_mod 0 to console, 1 to file
        self.log_mod = self.config.get("log_mod")

        self.s = requests.Session()
        self.c = requests.Session()

        self.proxies = self.config.get('proxies')
        if self.proxies:
            self.s.proxies.update(self.proxies)
            self.c.proxies.update(self.proxies)

        # All counters.
        self.like_counter = 0
        self.unlike_counter = 0
        self.follow_counter = 0
        self.unfollow_counter = 0
        self.comments_counter = 0
        self.current_index = 0
        self.current_id = "abcds"
        # List of user_id, that bot follow
        self.user_info_list = []
        self.user_list = []
        self.ex_user_list = []
        self.unwanted_username_list = []
        self.is_checked = False
        self.is_selebgram = False
        self.is_fake_account = False
        self.is_active_user = False
        self.is_following = False
        self.is_follower = False
        self.is_rejected = False
        self.is_self_checking = False
        self.is_by_tag = False
        self.is_follower_number = 0

        self.user_id = 0
        self.login_status = False
        self.by_location = False

        self.user_login = login.lower()
        self.user_password = password
        self.unfollow_from_feed = False
        self.medias = []

        self.media_on_feed = []
        self.media_by_user = []
        self.current_owner = ""
        self.error_400 = 0
        self.error_400_to_ban = self.config.get("error_400_to_ban")
        self.ban_sleep_time = self.config.get("ban_sleep_time")
        self.unwanted_username_list = self.config.get("unwanted_username_list")
        now_time = datetime.datetime.now()
        self.logger.info("Instabot v{} started at {}:".format(
            instabot_py.__version__, now_time.strftime("%d.%m.%Y %H:%M")))
        self.prog_run = True
        self.next_iteration = {
            "Like": 0,
            "Unlike": 0,
            "Follow": 0,
            "Unfollow": 0,
            "Comments": 0,
            "Populate": 0,
        }

        self.populate_user_blacklist()
        self.login()
        signal.signal(signal.SIGINT, self.cleanup)
        signal.signal(signal.SIGTERM, self.cleanup)
        atexit.register(self.cleanup)

    def url_user(self, username):
        return self.url_user_detail % username

    def get_user_id_by_username(self, user_name):
        url_info = self.url_user_detail % (user_name)
        info = self.s.get(url_info)
        json_info = json.loads(
            re.search("window._sharedData = (.*?);</script>", info.text,
                      re.DOTALL).group(1))
        id_user = json_info["entry_data"]["ProfilePage"][0]["graphql"]["user"][
            "id"]
        return id_user

    def populate_user_blacklist(self):
        for user in self.user_blacklist:
            user_id_url = self.url_user_detail % (user)
            info = self.s.get(user_id_url)

            # prevent error if 'Account of user was deleted or link is invalid
            from json import JSONDecodeError

            try:
                all_data = json.loads(
                    re.search("window._sharedData = (.*?);</script>",
                              info.text, re.DOTALL).group(1))
            except JSONDecodeError as e:
                self.logger.info(
                    f"Account of user {user} was deleted or link is "
                    "invalid")
            else:
                # prevent exception if user have no media
                id_user = all_data["entry_data"]["ProfilePage"][0]["graphql"][
                    "user"]["id"]
                # Update the user_name with the user_id
                self.user_blacklist[user] = id_user
                self.logger.info(
                    f"Blacklisted user {user} added with ID: {id_user}")
                time.sleep(5 * random.random())

    def login(self):

        successfulLogin = False

        self.s.headers.update({
            "Accept":
            "*/*",
            "Accept-Language":
            self.config.get("accept_language"),
            "Accept-Encoding":
            "gzip, deflate, br",
            "Connection":
            "keep-alive",
            "Host":
            "www.instagram.com",
            "Origin":
            "https://www.instagram.com",
            "Referer":
            "https://www.instagram.com/",
            "User-Agent":
            self.user_agent,
            "X-Instagram-AJAX":
            "1",
            "Content-Type":
            "application/x-www-form-urlencoded",
            "X-Requested-With":
            "XMLHttpRequest",
        })

        if self.session_file and os.path.isfile(self.session_file):
            self.logger.info(f"Found session file {self.session_file}")
            successfulLogin = True
            with open(self.session_file, "rb") as i:
                cookies = pickle.load(i)
                self.s.cookies.update(cookies)
        else:
            self.logger.info("Trying to login as {}...".format(
                self.user_login))
            self.login_post = {
                "username": self.user_login,
                "password": self.user_password,
            }
            r = self.s.get(self.url)
            csrf_token = re.search('(?<="csrf_token":")\w+', r.text).group(0)
            self.s.headers.update({"X-CSRFToken": csrf_token})
            time.sleep(5 * random.random())
            login = self.s.post(self.url_login,
                                data=self.login_post,
                                allow_redirects=True)
            if login.status_code not in (200, 400):
                # Handling Other Status Codes and making debug easier!!
                self.logger.debug(
                    "Login Request didn't return 200 as status code!")
                self.logger.debug(
                    "Here is more info for debugging or creating an issue"
                    "==============="
                    "Response Status:{login.status_code}"
                    "==============="
                    "Response Content:{login.text}"
                    "==============="
                    "Response Header:{login.headers}"
                    "===============")
                return
            else:
                self.logger.debug("Login request succeeded ")

            loginResponse = login.json()
            try:
                self.csrftoken = login.cookies["csrftoken"]
                self.s.headers.update({"X-CSRFToken": self.csrftoken})
            except Exception as exc:
                self.logger.warning("Something wrong with login")
                self.logger.debug(login.text)
                self.logger.exception(exc)
            if loginResponse.get("errors"):
                self.logger.error(
                    "Something is wrong with Instagram! Please try again later..."
                )
                self.logger.error(loginResponse["errors"]["error"])

            elif loginResponse.get("message") == "checkpoint_required":
                try:
                    if "instagram.com" in loginResponse["checkpoint_url"]:
                        challenge_url = loginResponse["checkpoint_url"]
                    else:
                        challenge_url = f"https://instagram.com{loginResponse['checkpoint_url']}"
                    self.logger.info(f"Challenge required at {challenge_url}")
                    with self.s as clg:
                        clg.headers.update({
                            "Accept":
                            "*/*",
                            "Accept-Language":
                            self.config.get("accept_language"),
                            "Accept-Encoding":
                            "gzip, deflate, br",
                            "Connection":
                            "keep-alive",
                            "Host":
                            "www.instagram.com",
                            "Origin":
                            "https://www.instagram.com",
                            "User-Agent":
                            self.user_agent,
                            "X-Instagram-AJAX":
                            "1",
                            "Content-Type":
                            "application/x-www-form-urlencoded",
                            "x-requested-with":
                            "XMLHttpRequest",
                        })
                        # Get challenge page
                        challenge_request_explore = clg.get(challenge_url)

                        # Get CSRF Token from challenge page
                        challenge_csrf_token = re.search(
                            '(?<="csrf_token":")\w+',
                            challenge_request_explore.text).group(0)
                        # Get Rollout Hash from challenge page
                        rollout_hash = re.search(
                            '(?<="rollout_hash":")\w+',
                            challenge_request_explore.text).group(0)

                        # Ask for option 1 from challenge, which is usually Email or Phone
                        challenge_post = {"choice": 1}

                        # Update headers for challenge submit page
                        clg.headers.update(
                            {"X-CSRFToken": challenge_csrf_token})
                        clg.headers.update({"Referer": challenge_url})

                        # Request instagram to send a code
                        challenge_request_code = clg.post(challenge_url,
                                                          data=challenge_post,
                                                          allow_redirects=True)

                        # User should receive a code soon, ask for it
                        challenge_userinput_code = input(
                            "Challenge Required.\n\nEnter the code sent to your mail/phone: "
                        )
                        challenge_security_post = {
                            "security_code": int(challenge_userinput_code)
                        }

                        complete_challenge = clg.post(
                            challenge_url,
                            data=challenge_security_post,
                            allow_redirects=True,
                        )
                        if complete_challenge.status_code != 200:
                            self.logger.info(
                                "Entered code is wrong, Try again later!")
                            return
                        self.csrftoken = complete_challenge.cookies[
                            "csrftoken"]
                        self.s.headers.update({
                            "X-CSRFToken": self.csrftoken,
                            "X-Instagram-AJAX": "1"
                        })
                        successfulLogin = complete_challenge.status_code == 200

                except Exception as err:
                    self.logger.debug(
                        f"Login failed, response: \n\n{login.text} {err}")
                    return False
            elif loginResponse.get("authenticated") is False:
                self.logger.error("Login error! Check your login data!")
                return

            else:
                rollout_hash = re.search('(?<="rollout_hash":")\w+',
                                         r.text).group(0)
                self.s.headers.update({"X-Instagram-AJAX": rollout_hash})
                successfulLogin = True
            # ig_vw=1536; ig_pr=1.25; ig_vh=772;  ig_or=landscape-primary;
            self.s.cookies["csrftoken"] = self.csrftoken
            self.s.cookies["ig_vw"] = "1536"
            self.s.cookies["ig_pr"] = "1.25"
            self.s.cookies["ig_vh"] = "772"
            self.s.cookies["ig_or"] = "landscape-primary"
            time.sleep(5 * random.random())

        if successfulLogin:
            r = self.s.get("https://www.instagram.com/")
            self.csrftoken = re.search('(?<="csrf_token":")\w+',
                                       r.text).group(0)
            self.s.cookies["csrftoken"] = self.csrftoken
            self.s.headers.update({"X-CSRFToken": self.csrftoken})
            finder = r.text.find(self.user_login)
            if finder != -1:
                self.user_id = self.get_user_id_by_username(self.user_login)
                self.login_status = True
                self.logger.info(f"{self.user_login} login success!\n")
                if self.session_file is not None:
                    self.logger.info(
                        f"Saving cookies to session file {self.session_file}")
                    with open(self.session_file, "wb") as output:
                        pickle.dump(self.s.cookies, output,
                                    pickle.HIGHEST_PROTOCOL)
            else:
                self.login_status = False
                self.logger.error("Login error! Check your login data!")
                if self.session_file and os.path.isfile(self.session_file):
                    try:
                        os.remove(self.session_file)
                    except:
                        self.logger.info(
                            "Could not delete session file. Please delete manually"
                        )

                self.prog_run = False
        else:
            self.logger.error("Login error! Connection error!")

    def logout(self):
        now_time = datetime.datetime.now()
        log_string = (
            "Logout: likes - %i, Unlikes -%i, Follows - %i, Unfollows - %i, Comments - %i."
            % (
                self.like_counter,
                self.unlike_counter,
                self.follow_counter,
                self.unfollow_counter,
                self.comments_counter,
            ))
        self.logger.info(log_string)
        work_time = now_time - self.bot_start
        self.logger.info(f"Bot work time: {work_time}")

        try:
            _ = self.s.post(self.url_logout,
                            data={"csrfmiddlewaretoken": self.csrftoken})
            self.logger.info("Logout success!")
            self.login_status = False
        except Exception as exc:
            logging.error("Logout error!")
            logging.exception("exc")

    def cleanup(self, *_):

        if self.login_status and self.session_file is None:
            self.logout()
        self.prog_run = False

    def get_media_id_by_tag(self, tag):
        """ Get media ID set, by your hashtag or location """
        medias = None
        if tag.startswith("l:"):
            tag = tag.replace("l:", "")
            self.logger.info(f"Get Media by location: {tag}")
            url_location = self.url_location % (tag)
            r = self.s.get(url_location)
            try:
                all_data = json.loads(r.text)
                medias = list(all_data["graphql"]["location"]
                              ["edge_location_to_media"]["edges"])
            except Exception as exc:

                self.logger.exception(exc)
        else:
            self.logger.debug(f"Get Media by tag: {tag}")
            url_tag = self.url_tag % (tag)
            r = self.s.get(url_tag)
            try:
                all_data = json.loads(r.text)
                medias = list(all_data["graphql"]["hashtag"]
                              ["edge_hashtag_to_media"]["edges"])
            except Exception as exc:
                self.logger.exception(exc)

        return medias

    def get_media_url(self, media_id=None, shortcode=None):
        """ Get Media Code or Full Url from Media ID """
        if shortcode:
            return self.url_media % shortcode
        elif media_id:
            media_id = int(media_id)
            alphabet = (
                "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
            )
            shortened_id = ""
            while media_id > 0:
                media_id, idx = divmod(media_id, 64)
                shortened_id = alphabet[idx] + shortened_id

            return self.url_media % shortened_id

    def get_username_by_user_id(self, user_id):
        try:
            profile = instaloader.Profile.from_id(self.instaloader.context,
                                                  user_id)
            return profile.username
        except Exception as exc:
            logging.exception(exc)

    def media_contains_blacklisted_tag(self, media):
        try:
            if len(media["node"]["edge_media_to_caption"]["edges"]) > 1:
                caption = media["node"]["edge_media_to_caption"]["edges"][0][
                    "node"]["text"].encode("ascii", errors="ignore")

                tag_blacklist = set(self.tag_blacklist)
                tags = {
                    tag.decode("ASCII").strip("#").lower()
                    for tag in caption.split()
                    if (tag.decode("ASCII")).startswith("#")
                }
                matching_tags = tags.intersection(tag_blacklist)
                if matching_tags:
                    self.logger.debug("Media ignored tag(s): {}".format(
                        ", ".join(matching_tags)))
                    return True
        except Exception as exc:
            self.logger.warning("Except on media_contains_blacklisted_tag")
            self.logger.exception(exc)

    def verify_media_misc(self, media):
        if media["node"]["owner"]["id"] == self.user_id:
            self.logger.debug("Keep calm - It's your own media ;)")
            return True

        if self.persistence.check_already_liked(media_id=media["node"]["id"]):
            self.logger.info("Keep calm - It's already liked ;)")
            return True

    def verify_media_owner_blacklisted(self, media):

        for username, userid, in self.user_blacklist.items():
            if media["node"]["owner"]["id"] == userid:
                self.logger.debug(
                    f"Media owned by blacklisted user: {username}")
                return True

    def verify_media_number_of_likes(self, media):
        like_count = media["node"]["edge_liked_by"]["count"]
        return (like_count <= self.media_max_like and like_count >= self.media_min_like) \
               or (self.media_max_like == 0 and like_count >= self.media_min_like) \
               or (self.media_min_like == 0 and like_count <= self.media_max_like) \
               or (self.media_min_like == 0 and self.media_max_like == 0)

    def verify_media(self, media):
        return not self.verify_media_misc(media) \
               and not self.media_contains_blacklisted_tag(media) \
               and not self.verify_media_number_of_likes(media) \
               and not self.verify_media_owner_blacklisted(media)

    def like(self, media_id):
        """ Send http request to like media by ID """
        media_to_like_url = self.get_media_url(media_id)

        try:
            self.logger.debug(
                f"Trying to like media: id: {media_id}, url: {media_to_like_url}"
            )
            resp = self.s.post(self.url_likes % (media_id))
        except Exception as exc:
            logging.exception(exc)
            return False

        if resp.status_code == 200:
            # Like is successful, all is ok!
            self.error_400 = 0
            self.like_counter += 1
            self.persistence.insert_media(media_id=media_id, status="200")
            self.logger.info(f"Liked media #{self.like_counter}: id: {media_id}, " \
                             f"url: {media_to_like_url}")
            return True
        elif resp.status_code == 400:
            self.logger.info(f"Could not like media: id: {media_id}, "
                             f"url: {media_to_like_url}. Reason: {resp.text}")
            self.persistence.insert_media(
                media_id=media_id,
                status="400",
            )

        else:
            self.persistence.insert_media(
                media_id=media_id,
                status=str(resp.status_code),
            )
            self.logger.debug(
                f"Could not like media: id: {media_id}, "
                f"url: {media_to_like_url}, status code: {resp.status_code}. "
                f"Reason: {resp.text}")

        return False

    def unlike(self, media_id):
        """ Send http request to unlike media by ID """
        url_unlike = self.url_unlike % (media_id)
        try:
            resp = self.s.post(url_unlike)
        except Exception as exc:
            logging.exception(exc)
            return None

        if resp.status_code == 200:
            self.persistence.update_media_complete(media_id)
            self.unlike_counter += 1
            self.logger.info(
                f"Media Unliked: # {self.unlike_counter} id: {media_id}, url: {self.get_media_url(media_id)}"
            )
            return True
        elif resp.status_code == 400 and resp.text == 'missing media':
            self.persistence.update_media_complete(media_id)
            self.logger.info(
                f"Could not unlike media: id: {media_id}, url: {self.get_media_url(media_id)}. It seems "
                f"this media is no longer exist.")
        else:
            self.logger.critical(
                f"Could not unlike media: id: {media_id}, url: {self.get_media_url(media_id)}. "
                f"Status code : {resp.status_code} Reason: {resp.text}")

        return False

    def comment(self, media_id, comment_text):
        """ Send http request to comment """
        self.logger.info(
            f"Trying to comment: {media_id} {self.get_media_url(media_id)}")
        url_comment = self.url_comment % (media_id)
        try:
            resp = self.s.post(url_comment,
                               data={"comment_text": comment_text})
        except Exception as exc:
            logging.exception(exc)
            return False

        if resp.status_code == 200:
            self.comments_counter += 1
            self.logger.info(
                f"Comment: {comment_text}. #{self.comments_counter}.")
            return True

    def follow(self, user_id, username=None):
        """ Send http request to follow """
        if self.login_status:
            url_follow = self.url_follow % user_id
            if username is None:
                username = self.get_username_by_user_id(user_id=user_id)
            try:
                resp = self.s.post(url_follow)
                if resp.status_code == 200:
                    self.follow_counter += 1
                    self.logger.info(
                        f"Followed: {self.url_user(username)} #{self.follow_counter}."
                    )
                    self.persistence.insert_username(user_id=user_id,
                                                     username=username)
                return resp
            except:
                logging.exception("Except on follow!")
        return False

    def unfollow(self, user_id, username=""):
        """ Send http request to unfollow """
        try:
            resp = self.s.post(self.url_unfollow % (user_id))
        except Exception as exc:
            logging.critical("Error while requesting the unfollow endpoint")
            logging.exception(exc)
            return False

        if resp.status_code == 200:
            self.unfollow_counter += 1
            self.logger.info(
                f"Unfollowed: {self.url_user(username)} #{self.unfollow_counter}."
            )
            self.persistence.insert_unfollow_count(user_id=user_id)
            return True
        else:
            return False

    # Backwards Compatibility for old example.py files
    def auto_mod(self):
        self.mainloop()

    def new_auto_mod(self):
        self.mainloop()

    def run_during_time_window(self):
        # TODO this method is subject of deprecation

        now = datetime.datetime.now()
        # distance between start time and now
        dns = self.time_dist(datetime.time(self.start_at_h, self.start_at_m),
                             now.time())
        # distance between end time and now
        dne = self.time_dist(datetime.time(self.end_at_h, self.end_at_m),
                             now.time())
        if not ((dns == 0 or dne < dns) and dne != 0):
            self.logger.info(f"Pause for {self.ban_sleep_time} seconds")
            time.sleep(self.window_check_every)
            return False
        else:
            return True

    def loop_controller(self):
        # 400 errors,
        if self.error_400 >= self.error_400_to_ban:
            self.logger.info(
                f"Bot receives {self.error_400} HTTP_400_Error(s), You're maybe banned! "
            )
            self.logger.info(f"Pause for {self.ban_sleep_time} seconds")
            time.sleep(self.generate_time(self.ban_sleep_time))
            self.error_400 = 0

        # exceed counters, program halt
        if self.like_counter > self.like_per_run \
                and self.follow_counter > self.follow_per_run \
                and self.unfollow_counter > self.unfollow_per_run \
                and self.comments_counter > self.comments_per_run:
            self.prog_run = False

        if self.iteration_ready("follow") or self.iteration_ready("unfollow") \
                or self.iteration_ready("unlike") or self.iteration_ready("like") \
                or self.iteration_ready("comments"):
            return True
        else:
            time.sleep(1)
            return False

    def mainloop(self):
        medias = []
        while self.prog_run and self.login_status:
            if not self.run_during_time_window():
                continue
            if not self.loop_controller():
                continue

            if len(medias) == 0:
                tag = random.choice(self.tag_list)
                medias_raw = self.get_media_id_by_tag(tag)
                self.logger.debug(f"Retrieved {len(medias_raw)} medias")
                max_tag_like_count = random.randint(1,
                                                    self.max_like_for_one_tag)
                medias = self.remove_already_liked_medias(
                    medias_raw)[:max_tag_like_count]
                self.logger.debug(
                    f"Select {max_tag_like_count} medias to process. Increase max_like_for_one_tag value for more processing medias "
                )
                continue

            media = medias.pop()
            self.new_auto_mod_like(media)
            self.new_auto_mod_unlike()
            self.new_auto_mod_follow(media)
            self.new_auto_mod_unfollow()
            self.new_auto_mod_comments(media)

        self.logger.info("Exit from loop GoodBye")

    def remove_already_liked_medias(self, medias):
        return [
            media for media in medias
            if not self.persistence.check_already_liked(
                media_id=media["node"]["id"])
        ]

    def new_auto_mod_like(self, media):
        if self.iteration_ready("like") and media:
            self.init_next_interation("like")
            media_id = media['node']['id']
            if self.like(media_id):
                return True

    def new_auto_mod_unlike(self):
        if self.iteration_ready("unlike"):
            self.init_next_interation("unlike")
            media_id = self.persistence.get_medias_to_unlike()
            if media_id:
                self.logger.debug("Trying to unlike media")
                if self.unlike(media_id):
                    return True
            else:
                self.logger.debug("Nothing to unlike")

    def get_followers_count(self, username):
        try:
            resp = self.s.get(self.url_user_detail % (username))
            all_data = json.loads(
                re.search("window._sharedData = (.*?);</script>", resp.text,
                          re.DOTALL).group(1))
            followers_count = all_data["entry_data"]["ProfilePage"][0][
                "graphql"]["user"]["edge_followed_by"]["count"]
        except Exception as exc:
            self.logger.exception(exc)
            followers_count = 0
        return followers_count

    def verify_account_name(self, username):
        if not (self.keywords and len(self.keywords) > 0):
            return True

        for keyword in self.keywords:
            if username.find(keyword) >= 0:
                return True

        try:
            url = self.url_user_detail % (username)
            r = self.s.get(url)
            all_data = json.loads(
                re.search(
                    "window._sharedData = (.*?);</script>",
                    r.text,
                    re.DOTALL,
                ).group(1))
            biography = all_data["entry_data"]["ProfilePage"][0]["graphql"][
                "user"]["biography"]

            if biography:
                for keyword in self.keywords:
                    if biography.find(keyword) >= 0:
                        return True
        except Exception as exc:
            self.logger.debug(f"Cannot retrieve user:{username}'s biography")
            self.logger.exception(exc)

        self.logger.debug(
            f"Won't follow {username}: does not meet keywords requirement. Keywords not found."
        )

    def verify_account_followers(self, username):
        if self.user_min_follow == 0 and self.user_max_follow == 0:
            return True

        try:
            followers = self.get_followers_count(username)
            if followers < self.user_min_follow:
                self.logger.info(
                    f"Won't follow {username}: does not meet user_min_follow requirement"
                )
                return

            if self.user_max_follow != 0 and followers > self.user_max_follow:
                self.logger.info(
                    f"Won't follow {username}: does not meet user_max_follow requirement"
                )
                return

        except Exception as exc:
            self.logger.exception(exc)

    def verify_account(self, username):
        return username != self.login \
               and self.verify_account_name(username) \
               and self.verify_account_followers(username)

    def new_auto_mod_follow(self, media):
        if self.iteration_ready(
                "follow") and self.follow_per_run != 0 and media:
            self.init_next_interation("follow")
            user_id = media["node"]["owner"]["id"]
            username = self.get_username_by_user_id(user_id)

            if not self.verify_account(username):
                self.logger.debug(
                    f"Not following {username}, the account doesn't meet requirements"
                )
                return False

            if self.persistence.check_already_followed(user_id=user_id):
                self.logger.debug(f"Already followed before {username}")
                return False

            log_string = f"Trying to follow: {username}"
            self.logger.debug(log_string)

            if self.follow(user_id=user_id, username=username):
                return True

    def populate_from_feed(self):
        medias = self.get_medias_from_recent_feed()

        try:
            for mediafeed_user in medias:
                feed_username = mediafeed_user["node"]["owner"]["username"]
                feed_user_id = mediafeed_user["node"]["owner"]["id"]
                # print(self.persistence.check_if_userid_exists( userid=feed_user_id))
                if not self.persistence.check_if_userid_exists(
                        userid=feed_user_id):
                    self.persistence.insert_username(user_id=feed_user_id,
                                                     username=feed_username)
                    self.logger.debug(
                        f"Inserted user {feed_username} from recent feed")
        except Exception as exc:
            self.logger.warning("Notice: could not populate from recent feed")
            self.logger.exception(exc)

    def new_auto_mod_unfollow(self):
        if self.iteration_ready("unfollow"):
            self.init_next_interation("unfollow")
            user = self.persistence.get_username_to_unfollow_random()
            if user:
                self.logger.debug(
                    f"Trying to unfollow #{self.unfollow_counter + 1}: {user}")
                if self.auto_unfollow(user):
                    return True

    # new Method splitted from new_auto_mod_unfollow
    def new_auto_mod_unfollow_from_feed(self):
        if self.unfollow_from_feed:
            try:
                if (time.time() > self.next_iteration["Populate"]
                        and self.unfollow_recent_feed is True):
                    self.populate_from_feed()
                    self.next_iteration["Populate"] = time.time() + (
                        self.generate_time(360))
            except Exception as exc:
                self.logger.warning(
                    "Notice: Could not populate from recent feed right now")
                self.logger.exception(exc)

            log_string = f"Trying to unfollow #{self.unfollow_counter + 1}:"
            self.logger.debug(log_string)
            self.auto_unfollow()
            self.next_iteration["Unfollow"] = time.time() + self.generate_time(
                self.unfollow_delay)

    def auto_unfollow(self, user):
        user_id = user.id
        user_name = user.username
        if not user_name:
            _username = self.get_username_by_user_id(user_id=user_id)
            if _username:
                user_name = _username
            else:
                self.logger.debug(
                    f"Cannot resolve username from user id: {current_id}")
                return False

        if self.verify_unfollow(user_name):
            return self.unfollow(user_id, user_name)
        else:
            self.persistence.insert_unfollow_count(user_id=user_id)
            return True

    def verify_unfollow(self, user_name):
        user_info = self.get_user_info(user_name)
        if not user_info:
            return False

        if self.unfollow_everyone:
            self.logger.debug(
                "Ignore verifications, Unfollow everyone flag is set")
            return True

        self.logger.debug(f"Getting user info : {user_name} - "
                          f"Followers : {user_info.get('followers')} - "
                          f"Following : {user_info.get('follows')} - "
                          f"Media : {user_info.get('medias')}")

        if user_name in self.unfollow_whitelist:
            self.logger.debug(
                "This account {user_name} is marked in the whitelist")
            return False
        if not self.account_is_followed_by_you(user_info):
            self.logger.debug("You're not follwing this account")
            return False

        if self.account_is_selebgram(user_info) and self.unfollow_selebgram:
            self.logger.debug("This account is a selebgram account")
            return True

        if self.account_is_fake(user_info) and self.unfollow_probably_fake:
            self.logger.debug("This account is a fake account")
            return True

        if not self.account_is_active(user_info) and self.unfollow_inactive:
            self.logger.debug("This account is not active")
            return True

        if not self.account_is_following_you(
                user_info) and self.unfollow_not_following:
            self.logger.debug("This account is not following you")
            return True

        return False

    def get_user_info(self, user_name):
        url_tag = self.url_user_detail % (user_name)
        try:
            r = self.s.get(url_tag)
            if r.text.find(
                    "The link you followed may be broken, or the page may have been removed."
            ) >= 0:
                self.logger.debug(f"This account was deleted : {user_name}")
                return False
            raw_data = re.search("window._sharedData = (.*?);</script>",
                                 r.text, re.DOTALL).group(1)
            user_data = json.loads(
                raw_data)["entry_data"]["ProfilePage"][0]["graphql"]["user"]
            user_info = dict(
                follows=user_data["edge_follow"]["count"],
                followers=user_data["edge_followed_by"]["count"],
                medias=user_data["edge_owner_to_timeline_media"]["count"],
                follows_viewer=user_data["follows_viewer"],
                followed_by_viewer=user_data["followed_by_viewer"],
                requested_by_viewer=user_data["requested_by_viewer"],
                has_requested_viewer=user_data["has_requested_viewer"])
            return user_info
        except Exception as exc:
            self.logger.exception(exc)
            return None

    def account_is_selebgram(self, user_info):
        return user_info.get("follows") == 0 or (
            user_info.get("followers") / user_info.get("follows") > 2)

    def account_is_fake(self, user_info):
        return user_info.get("followers") == 0 or (
            user_info.get("follows") / user_info.get("followers") > 2)

    def account_is_active(self, user_info):
        return user_info.get("medias") > 0 \
               and (user_info.get("follows") / user_info.get("medias") < 25) \
               and (user_info.get("followers") / user_info.get("medias") < 25)

    def account_is_following_you(self, user_info):
        return user_info.get("follows_viewer") or user_info.get(
            "has_requested_viewer")

    def account_is_followed_by_you(self, user_info):
        return user_info.get("followed_by_viewer") or user_info.get(
            "requested_by_viewer")

    def new_auto_mod_comments(self, media):
        if self.iteration_ready(
                "comments") and self.verify_media_before_comment(media):
            self.init_next_interation("comments")
            comment_text = self.generate_comment()
            if "@username@" in comment_text:
                comment_text = comment_text.replace(
                    "@username@", media["node"]["owner"]["username"])

            media_id = media["node"]["id"]

            if not self.comment(media_id, comment_text):
                self.persistence.insert_media(media["node"]["id"], "Error")

    def init_next_interation(self, action):
        self.next_iteration[action] = self.generate_time(
            getattr(self, action + "_delay", -2 * time.time())) + time.time()

    def iteration_ready(self, action):
        action_counter = getattr(self, action + "_counter", 0)
        action_counter_per_run = getattr(self, action + "_per_run", 0)
        registered_time = self.next_iteration.get(action, 0)
        return action_counter < action_counter_per_run and registered_time >= 0 and time.time(
        ) > registered_time

    def generate_time(self, time):
        """ Make some random for next iteration"""
        return time * 0.9 + time * 0.2 * random.random()

    def generate_comment(self):
        c_list = list(itertools.product(*self.comment_list))

        repl = [("  ", " "), (" .", "."), (" !", "!")]
        res = " ".join(random.choice(c_list))
        for s, r in repl:
            res = res.replace(s, r)
        return res.capitalize()

    def verify_media_before_comment(self, media):
        media_code = media["node"]["shortcode"]
        url_check = self.url_media % (media_code)
        try:
            resp = self.s.get(url_check)
        except Exception as exc:
            self.logger.warning(f"Couldn't comment post {url_check}")
            self.logger.exception(exc)
            return False

        if "dialog-404" in resp.text:
            self.logger.warning(
                f"Tried to comment {media_code} but it doesn't exist (404). Resuming..."
            )
            return False

        if resp.status_code == 200:
            raw_data = re.search("window._sharedData = (.*?);", resp.text,
                                 re.DOTALL).group(1)

            if not '"entry_data":{"PostPage"' in raw_data:
                self.logger.critical("Invalid data from media.")
                return False

            all_data = json.loads(raw_data)["entry_data"]["PostPage"][0]

            if all_data["graphql"]["shortcode_media"]["owner"][
                    "id"] == self.user_id:
                self.logger.debug("This media is yours.")
                return False
            try:
                edges = all_data["graphql"]["shortcode_media"].get(
                    "edge_media_to_comment", None)
                if not edges:
                    edges = all_data["graphql"]["shortcode_media"].get(
                        "edge_media_to_parent_comment", None)

                comments = list(edges["edges"])
            except Exception as exc:
                self.logger.critical("Cannot retrieve comments from media. ")
                self.logger.exception(exc)

            for comment in comments:
                if comment["node"]["owner"]["id"] == self.user_id:
                    self.logger.debug("Media is already commented")
                    return False

            return True
        elif resp.status_code == 404:
            self.logger.warning(f"{media_code} doesn't exist (404).")
            return False

    def get_medias_from_recent_feed(self):
        self.logger.debug(f"{self.user_login} : Get media id on recent feed")
        url_tag = "https://www.instagram.com/"
        try:
            r = self.s.get(url_tag)
            jsondata = re.search("additionalDataLoaded\('feed',({.*})\);",
                                 r.text).group(1)
            all_data = json.loads(jsondata.strip())
            media_on_feed = list(
                all_data["user"]["edge_web_feed_timeline"]["edges"])
            self.logger.debug(f"Media in recent feed = {len(media_on_feed)}")

        except Exception as exc:
            logging.exception(exc)
            media_on_feed = []
        return media_on_feed

    @staticmethod
    def time_dist(to_time, from_time):
        """
        Method to compare time.
        In terms of minutes result is
        from_time + result == to_time
        Args:
            to_time: datetime.time() object.
            from_time: datetime.time() object.
        Returns: int
            how much minutes between from_time and to_time
            if to_time < from_time then it means that
                to_time is on the next day.
        """
        to_t = to_time.hour * 60 + to_time.minute
        from_t = from_time.hour * 60 + from_time.minute
        midnight_t = 24 * 60
        return (midnight_t - from_t) + to_t if to_t < from_t else to_t - from_t

    @staticmethod
    def str2bool(value):
        return str(value).lower() in ["yes", "true"]