def __init__(self, config, args): self.config = config self.args = args ScheduleManager.init() DBManager.init(self.config["main"]["db"]) # redis redis_options = {} if "redis" in config: redis_options = dict(config.items("redis")) RedisManager.init(**redis_options) utils.wait_for_redis_data_loaded(RedisManager.get()) self.nickname = config["main"].get("nickname", "pajbot") if config["main"].getboolean("verified", False): self.tmi_rate_limits = TMIRateLimits.VERIFIED elif config["main"].getboolean("known", False): self.tmi_rate_limits = TMIRateLimits.KNOWN else: self.tmi_rate_limits = TMIRateLimits.BASE self.whisper_output_mode = WhisperOutputMode.from_config_value( config["main"].get("whisper_output_mode", "normal") ) # phrases self.phrases = { "welcome": ["{nickname} {version} running! HeyGuys"], "quit": ["{nickname} {version} shutting down... BibleThump"], } if "phrases" in config: phrases = config["phrases"] if "welcome" in phrases: self.phrases["welcome"] = phrases["welcome"].splitlines() if "quit" in phrases: self.phrases["quit"] = phrases["quit"].splitlines() # Remembers whether the "welcome" phrases have already been said. We don't want to send the # welcome messages to chat again on a reconnect. self.welcome_messages_sent = False # streamer if "streamer" in config["main"]: self.streamer = config["main"]["streamer"] self.channel = "#" + self.streamer elif "target" in config["main"]: self.channel = config["main"]["target"] self.streamer = self.channel[1:] self.bot_domain = self.config["web"]["domain"] self.streamer_display = self.config["web"]["streamer_name"] log.debug("Loaded config") # do this earlier since schema upgrade can depend on the helix api self.api_client_credentials = ClientCredentials( self.config["twitchapi"]["client_id"], self.config["twitchapi"]["client_secret"], self.config["twitchapi"]["redirect_uri"], ) self.twitch_id_api = TwitchIDAPI(self.api_client_credentials) self.twitch_tmi_api = TwitchTMIAPI() self.app_token_manager = AppAccessTokenManager(self.twitch_id_api, RedisManager.get()) self.twitch_helix_api = TwitchHelixAPI(RedisManager.get(), self.app_token_manager) self.twitch_v5_api = TwitchKrakenV5API(self.api_client_credentials, RedisManager.get()) self.bot_user_id = self.twitch_helix_api.get_user_id(self.nickname) if self.bot_user_id is None: raise ValueError("The bot login name you entered under [main] does not exist on twitch.") self.streamer_user_id = self.twitch_helix_api.get_user_id(self.streamer) if self.streamer_user_id is None: raise ValueError("The streamer login name you entered under [main] does not exist on twitch.") self.streamer_access_token_manager = UserAccessTokenManager( api=self.twitch_id_api, redis=RedisManager.get(), username=self.streamer, user_id=self.streamer_user_id ) StreamHelper.init_streamer(self.streamer, self.streamer_user_id, self.streamer_display) # SQL migrations with DBManager.create_dbapi_connection_scope() as sql_conn: sql_migratable = DatabaseMigratable(sql_conn) sql_migration = Migration(sql_migratable, pajbot.migration_revisions.db, self) sql_migration.run() # Redis migrations redis_migratable = RedisMigratable(redis_options=redis_options, namespace=self.streamer) redis_migration = Migration(redis_migratable, pajbot.migration_revisions.redis, self) redis_migration.run() # Thread pool executor for async actions self.action_queue = ActionQueue() # refresh points_rank and num_lines_rank regularly UserRanksRefreshManager.start(self.action_queue) self.reactor = irc.client.Reactor() # SafeDefaultScheduler makes the bot not exit on exception in the main thread # e.g. on actions via bot.execute_now, etc. self.reactor.scheduler_class = SafeDefaultScheduler self.reactor.scheduler = SafeDefaultScheduler() self.start_time = utils.now() ActionParser.bot = self HandlerManager.init_handlers() self.socket_manager = SocketManager(self.streamer, self.execute_now) self.stream_manager = StreamManager(self) StreamHelper.init_stream_manager(self.stream_manager) self.decks = DeckManager() self.banphrase_manager = BanphraseManager(self).load() self.timer_manager = TimerManager(self).load() self.kvi = KVIManager() # bot access token if "password" in self.config["main"]: log.warning( "DEPRECATED - Using bot password/oauth token from file. " "You should authenticate in web gui using route /bot_login " "and remove password from config file" ) access_token = self.config["main"]["password"] if access_token.startswith("oauth:"): access_token = access_token[6:] self.bot_token_manager = UserAccessTokenManager( api=None, redis=None, username=self.nickname, user_id=self.bot_user_id, token=UserAccessToken.from_implicit_auth_flow_token(access_token), ) else: self.bot_token_manager = UserAccessTokenManager( api=self.twitch_id_api, redis=RedisManager.get(), username=self.nickname, user_id=self.bot_user_id ) self.emote_manager = EmoteManager(self.twitch_v5_api, self.action_queue) self.epm_manager = EpmManager() self.ecount_manager = EcountManager() if "twitter" in self.config and self.config["twitter"].get("streaming_type", "twitter") == "tweet-provider": self.twitter_manager = PBTwitterManager(self) else: self.twitter_manager = TwitterManager(self) self.module_manager = ModuleManager(self.socket_manager, bot=self).load() self.commands = CommandManager( socket_manager=self.socket_manager, module_manager=self.module_manager, bot=self ).load() self.websocket_manager = WebSocketManager(self) HandlerManager.trigger("on_managers_loaded") # Commitable managers self.commitable = {"commands": self.commands, "banphrases": self.banphrase_manager} self.execute_every(60, self.commit_all) self.execute_every(1, self.do_tick) # promote the admin to level 2000 self.admin = self.config["main"].get("admin", None) if self.admin is None: log.warning("No admin user specified. See the [main] section in the example config for its usage.") else: with DBManager.create_session_scope() as db_session: admin_user = User.find_or_create_from_login(db_session, self.twitch_helix_api, self.admin) if admin_user is None: log.warning( "The login name you entered for the admin user does not exist on twitch. " "No admin user has been created." ) else: admin_user.level = 2000 # silent mode self.silent = ( "flags" in config and "silent" in config["flags"] and config["flags"]["silent"] == "1" ) or args.silent if self.silent: log.info("Silent mode enabled") # dev mode self.dev = "flags" in config and "dev" in config["flags"] and config["flags"]["dev"] == "1" if self.dev: self.version_long = utils.extend_version_if_possible(VERSION) else: self.version_long = VERSION self.irc = IRCManager(self) relay_host = self.config["main"].get("relay_host", None) relay_password = self.config["main"].get("relay_password", None) if relay_host is not None or relay_password is not None: log.warning( "DEPRECATED - Relaybroker support is no longer implemented. relay_host and relay_password are ignored" ) self.data = { "broadcaster": self.streamer, "version": self.version_long, "version_brief": VERSION, "bot_name": self.nickname, "bot_domain": self.bot_domain, "streamer_display": self.streamer_display, } self.data_cb = { "status_length": self.c_status_length, "stream_status": self.c_stream_status, "bot_uptime": self.c_uptime, "current_time": self.c_current_time, "molly_age_in_years": self.c_molly_age_in_years, } self.user_agent = f"pajbot1/{VERSION} ({self.nickname})"
def __init__(self, config, args=None): # Load various configuration variables from the given config object # The config object that should be passed through should # come from pajbot.utils.load_config self.load_config(config) # Update the database scheme if necessary using alembic # In case of errors, i.e. if the database is out of sync or the alembic # binary can't be called, we will shut down the bot. pajbot.utils.alembic_upgrade() # Actions in this queue are run in a separate thread. # This means actions should NOT access any database-related stuff. self.action_queue = ActionQueue() self.action_queue.start() self.reactor = irc.client.Reactor(self.on_connect) self.start_time = datetime.datetime.now() ActionParser.bot = self HandlerManager.init_handlers() self.socket_manager = SocketManager(self) self.stream_manager = StreamManager(self) StreamHelper.init_bot(self, self.stream_manager) ScheduleManager.init() self.users = UserManager() self.decks = DeckManager() self.module_manager = ModuleManager(self.socket_manager, bot=self).load() self.commands = CommandManager( socket_manager=self.socket_manager, module_manager=self.module_manager, bot=self).load() self.filters = FilterManager().reload() self.banphrase_manager = BanphraseManager(self).load() self.timer_manager = TimerManager(self).load() self.kvi = KVIManager() self.emotes = EmoteManager(self) self.twitter_manager = TwitterManager(self) HandlerManager.trigger('on_managers_loaded') # Reloadable managers self.reloadable = { 'filters': self.filters, } # Commitable managers self.commitable = { 'commands': self.commands, 'filters': self.filters, 'banphrases': self.banphrase_manager, } self.execute_every(10 * 60, self.commit_all) self.execute_every(1, self.do_tick) try: self.admin = self.config['main']['admin'] except KeyError: log.warning('No admin user specified. See the [main] section in config.example.ini for its usage.') if self.admin: with self.users.get_user_context(self.admin) as user: pass # user.level = 2000 self.parse_version() relay_host = self.config['main'].get('relay_host', None) relay_password = self.config['main'].get('relay_password', None) if relay_host is None or relay_password is None: self.irc = MultiIRCManager(self) else: self.irc = SingleIRCManager(self) self.reactor.add_global_handler('all_events', self.irc._dispatcher, -10) twitch_client_id = None twitch_oauth = None if 'twitchapi' in self.config: twitch_client_id = self.config['twitchapi'].get('client_id', None) twitch_oauth = self.config['twitchapi'].get('oauth', None) # A client ID is required for the bot to work properly now, give an error for now if twitch_client_id is None: log.error('MISSING CLIENT ID, SET "client_id" VALUE UNDER [twitchapi] SECTION IN CONFIG FILE') self.twitchapi = TwitchAPI(twitch_client_id, twitch_oauth) self.data = {} self.data_cb = {} self.url_regex = re.compile(self.url_regex_str, re.IGNORECASE) self.data['broadcaster'] = self.streamer self.data['version'] = self.version self.data['version_brief'] = self.version_brief self.data['bot_name'] = self.nickname self.data_cb['status_length'] = self.c_status_length self.data_cb['stream_status'] = self.c_stream_status self.data_cb['bot_uptime'] = self.c_uptime self.data_cb['current_time'] = self.c_current_time self.silent = True if args.silent else self.silent if self.silent: log.info('Silent mode enabled') """ For actions that need to access the main thread, we can use the mainthread_queue. """ self.mainthread_queue = ActionQueue() self.execute_every(1, self.mainthread_queue.parse_action) self.websocket_manager = WebSocketManager(self) try: if self.config['twitchapi']['update_subscribers'] == '1': self.execute_every(30 * 60, self.action_queue.add, (self.update_subscribers_stage1, )) except: pass # XXX: TEMPORARY UGLY CODE HandlerManager.add_handler('on_user_gain_tokens', self.on_user_gain_tokens) HandlerManager.add_handler('send_whisper', self.whisper)
def __init__(self, config, args=None): # Load various configuration variables from the given config object # The config object that should be passed through should # come from pajbot.utils.load_config self.load_config(config) log.debug("Loaded config") # streamer is additionally initialized here so streamer can be accessed by the DB migrations # before StreamHelper.init_bot() is called later (which depends on an upgraded DB because # StreamManager accesses the DB) StreamHelper.init_streamer(self.streamer) # Update the database (and partially redis) scheme if necessary using alembic # In case of errors, i.e. if the database is out of sync or the alembic # binary can't be called, we will shut down the bot. pajbot.utils.alembic_upgrade() log.debug("ran db upgrade") # Actions in this queue are run in a separate thread. # This means actions should NOT access any database-related stuff. self.action_queue = ActionQueue() self.action_queue.start() self.reactor = irc.client.Reactor(self.on_connect) self.start_time = pajbot.utils.now() ActionParser.bot = self HandlerManager.init_handlers() self.socket_manager = SocketManager(self.streamer) self.stream_manager = StreamManager(self) StreamHelper.init_bot(self, self.stream_manager) ScheduleManager.init() self.users = UserManager() self.decks = DeckManager() self.banphrase_manager = BanphraseManager(self).load() self.timer_manager = TimerManager(self).load() self.kvi = KVIManager() twitch_client_id = None twitch_oauth = None if "twitchapi" in self.config: twitch_client_id = self.config["twitchapi"].get("client_id", None) twitch_oauth = self.config["twitchapi"].get("oauth", None) # A client ID is required for the bot to work properly now, give an error for now if twitch_client_id is None: log.error( 'MISSING CLIENT ID, SET "client_id" VALUE UNDER [twitchapi] SECTION IN CONFIG FILE' ) self.twitchapi = TwitchAPI(twitch_client_id, twitch_oauth) self.emote_manager = EmoteManager(twitch_client_id) self.epm_manager = EpmManager() self.ecount_manager = EcountManager() self.twitter_manager = TwitterManager(self) self.module_manager = ModuleManager(self.socket_manager, bot=self).load() self.commands = CommandManager(socket_manager=self.socket_manager, module_manager=self.module_manager, bot=self).load() HandlerManager.trigger("on_managers_loaded") # Reloadable managers self.reloadable = {} # Commitable managers self.commitable = { "commands": self.commands, "banphrases": self.banphrase_manager } self.execute_every(10 * 60, self.commit_all) self.execute_every(1, self.do_tick) try: self.admin = self.config["main"]["admin"] except KeyError: log.warning( "No admin user specified. See the [main] section in config.example.ini for its usage." ) if self.admin: with self.users.get_user_context(self.admin) as user: user.level = 2000 self.parse_version() relay_host = self.config["main"].get("relay_host", None) relay_password = self.config["main"].get("relay_password", None) if relay_host is None or relay_password is None: self.irc = MultiIRCManager(self) else: self.irc = SingleIRCManager(self) self.reactor.add_global_handler("all_events", self.irc._dispatcher, -10) self.data = {} self.data_cb = {} self.url_regex = re.compile(self.url_regex_str, re.IGNORECASE) self.data["broadcaster"] = self.streamer self.data["version"] = self.version self.data["version_brief"] = self.version_brief self.data["bot_name"] = self.nickname self.data_cb["status_length"] = self.c_status_length self.data_cb["stream_status"] = self.c_stream_status self.data_cb["bot_uptime"] = self.c_uptime self.data_cb["current_time"] = self.c_current_time self.silent = True if args.silent else self.silent if self.silent: log.info("Silent mode enabled") """ For actions that need to access the main thread, we can use the mainthread_queue. """ self.mainthread_queue = ActionQueue() self.execute_every(1, self.mainthread_queue.parse_action) self.websocket_manager = WebSocketManager(self) try: if self.config["twitchapi"]["update_subscribers"] == "1": self.execute_every(30 * 60, self.action_queue.add, (self.update_subscribers_stage1, )) except: pass
def __init__(self, config, args): self.config = config self.args = args self.last_ping = utils.now() self.last_pong = utils.now() DBManager.init(self.config["main"]["db"]) # redis redis_options = {} if "redis" in config: redis_options = dict(config.items("redis")) RedisManager.init(**redis_options) wait_for_redis_data_loaded(RedisManager.get()) # Pepega SE points sync pajbot.models.user.Config.se_sync_token = config["main"].get( "se_sync_token", None) pajbot.models.user.Config.se_channel = config["main"].get( "se_channel", None) self.nickname = config["main"].get("nickname", "pajbot") self.timezone = config["main"].get("timezone", "UTC") if config["main"].getboolean("verified", False): TMI.promote_to_verified() # phrases self.phrases = { "welcome": ["{nickname} {version} running!"], "quit": ["{nickname} {version} shutting down..."] } if "phrases" in config: phrases = config["phrases"] if "welcome" in phrases: self.phrases["welcome"] = phrases["welcome"].splitlines() if "quit" in phrases: self.phrases["quit"] = phrases["quit"].splitlines() TimeManager.init_timezone(self.timezone) # streamer if "streamer" in config["main"]: self.streamer = config["main"]["streamer"] self.channel = "#" + self.streamer elif "target" in config["main"]: self.channel = config["main"]["target"] self.streamer = self.channel[1:] StreamHelper.init_streamer(self.streamer) log.debug("Loaded config") # do this earlier since schema upgrade can depend on the helix api self.api_client_credentials = ClientCredentials( self.config["twitchapi"]["client_id"], self.config["twitchapi"]["client_secret"], self.config["twitchapi"]["redirect_uri"], ) self.twitch_id_api = TwitchIDAPI(self.api_client_credentials) self.app_token_manager = AppAccessTokenManager(self.twitch_id_api, RedisManager.get()) self.twitch_helix_api = TwitchHelixAPI(RedisManager.get(), self.app_token_manager) self.twitch_v5_api = TwitchKrakenV5API(self.api_client_credentials, RedisManager.get()) self.twitch_legacy_api = TwitchLegacyAPI(self.api_client_credentials, RedisManager.get()) self.twitch_tmi_api = TwitchTMIAPI() self.bot_user_id = self.twitch_helix_api.get_user_id(self.nickname) if self.bot_user_id is None: raise ValueError( "The bot login name you entered under [main] does not exist on twitch." ) self.streamer_user_id = self.twitch_helix_api.get_user_id( self.streamer) if self.streamer_user_id is None: raise ValueError( "The streamer login name you entered under [main] does not exist on twitch." ) # SQL migrations sql_conn = DBManager.engine.connect().connection sql_migratable = DatabaseMigratable(sql_conn) sql_migration = Migration(sql_migratable, pajbot.migration_revisions.db, self) sql_migration.run() # Redis migrations redis_migratable = RedisMigratable(redis_options=redis_options, namespace=self.streamer) redis_migration = Migration(redis_migratable, pajbot.migration_revisions.redis, self) redis_migration.run() # Actions in this queue are run in a separate thread. # This means actions should NOT access any database-related stuff. self.action_queue = ActionQueue() self.action_queue.start() self.reactor = irc.client.Reactor(self.on_connect) self.start_time = utils.now() ActionParser.bot = self HandlerManager.init_handlers() self.socket_manager = SocketManager(self.streamer, self.execute_now) self.stream_manager = StreamManager(self) StreamHelper.init_bot(self, self.stream_manager) ScheduleManager.init() self.users = UserManager() self.decks = DeckManager() self.banphrase_manager = BanphraseManager(self).load() self.timer_manager = TimerManager(self).load() self.kvi = KVIManager() # bot access token if "password" in self.config["main"]: log.warning( "DEPRECATED - Using bot password/oauth token from file. " "You should authenticate in web gui using route /bot_login " "and remove password from config file") access_token = self.config["main"]["password"] if access_token.startswith("oauth:"): access_token = access_token[6:] self.bot_token_manager = UserAccessTokenManager( api=None, redis=None, username=self.nickname, user_id=self.bot_user_id, token=UserAccessToken.from_implicit_auth_flow_token( access_token), ) else: self.bot_token_manager = UserAccessTokenManager( api=self.twitch_id_api, redis=RedisManager.get(), username=self.nickname, user_id=self.bot_user_id) self.emote_manager = EmoteManager(self.twitch_v5_api, self.twitch_legacy_api, self.action_queue) self.epm_manager = EpmManager() self.ecount_manager = EcountManager() self.twitter_manager = TwitterManager(self) self.module_manager = ModuleManager(self.socket_manager, bot=self).load() self.commands = CommandManager(socket_manager=self.socket_manager, module_manager=self.module_manager, bot=self).load() self.websocket_manager = WebSocketManager(self) HandlerManager.trigger("on_managers_loaded") # Commitable managers self.commitable = { "commands": self.commands, "banphrases": self.banphrase_manager } self.execute_every(10 * 60, self.commit_all) self.execute_every(1, self.do_tick) # promote the admin to level 2000 admin = None try: admin = self.config["main"]["admin"] except KeyError: log.warning( "No admin user specified. See the [main] section in the example config for its usage." ) if admin is not None: with self.users.get_user_context(admin) as user: user.level = 2000 # silent mode self.silent = ("flags" in config and "silent" in config["flags"] and config["flags"]["silent"] == "1") or args.silent if self.silent: log.info("Silent mode enabled") # dev mode self.dev = "flags" in config and "dev" in config["flags"] and config[ "flags"]["dev"] == "1" if self.dev: self.version_long = extend_version_if_possible(VERSION) else: self.version_long = VERSION self.irc = IRCManager(self) relay_host = self.config["main"].get("relay_host", None) relay_password = self.config["main"].get("relay_password", None) if relay_host is not None or relay_password is not None: log.warning( "DEPRECATED - Relaybroker support is no longer implemented. relay_host and relay_password are ignored" ) self.reactor.add_global_handler("all_events", self.irc._dispatcher, -10) self.data = { "broadcaster": self.streamer, "version": self.version_long, "version_brief": VERSION, "bot_name": self.nickname, } self.data_cb = { "status_length": self.c_status_length, "stream_status": self.c_stream_status, "bot_uptime": self.c_uptime, "current_time": self.c_current_time, "molly_age_in_years": self.c_molly_age_in_years, }
def __init__(self, config, args=None): self.load_config(config) self.last_ping = datetime.datetime.now() self.last_pong = datetime.datetime.now() self.load_default_phrases() self.db_session = DBManager.create_session() try: subprocess.check_call( ['alembic', 'upgrade', 'head'] + ['--tag="{0}"'.format(' '.join(sys.argv[1:]))]) except subprocess.CalledProcessError: log.exception('aaaa') log.error( 'Unable to call `alembic upgrade head`, this means the database could be out of date. Quitting.' ) sys.exit(1) except PermissionError: log.error( 'No permission to run `alembic upgrade head`. This means your user probably doesn\'t have execution rights on the `alembic` binary.' ) log.error( 'The error can also occur if it can\'t find `alembic` in your PATH, and instead tries to execute the alembic folder.' ) sys.exit(1) except FileNotFoundError: log.error( 'Could not found an installation of alembic. Please install alembic to continue.' ) sys.exit(1) except: log.exception('Unhandled exception when calling db update') sys.exit(1) # Actions in this queue are run in a separate thread. # This means actions should NOT access any database-related stuff. self.action_queue = ActionQueue() self.action_queue.start() self.reactor = irc.client.Reactor(self.on_connect) self.start_time = datetime.datetime.now() ActionParser.bot = self HandlerManager.init_handlers() self.socket_manager = SocketManager(self) self.stream_manager = StreamManager(self) StreamHelper.init_bot(self, self.stream_manager) ScheduleManager.init() self.users = UserManager() self.decks = DeckManager() self.module_manager = ModuleManager(self.socket_manager, bot=self).load() self.commands = CommandManager(socket_manager=self.socket_manager, module_manager=self.module_manager, bot=self).load() self.filters = FilterManager().reload() self.banphrase_manager = BanphraseManager(self).load() self.timer_manager = TimerManager(self).load() self.kvi = KVIManager() self.emotes = EmoteManager(self).reload() self.twitter_manager = TwitterManager(self) self.duel_manager = DuelManager(self) HandlerManager.trigger('on_managers_loaded') # Reloadable managers self.reloadable = { 'filters': self.filters, 'emotes': self.emotes, } # Commitable managers self.commitable = { 'commands': self.commands, 'filters': self.filters, 'emotes': self.emotes, 'users': self.users, 'banphrases': self.banphrase_manager, } self.execute_every(10 * 60, self.commit_all) try: self.admin = self.config['main']['admin'] except KeyError: log.warning( 'No admin user specified. See the [main] section in config.example.ini for its usage.' ) if self.admin: self.users[self.admin].level = 2000 self.parse_version() relay_host = self.config['main'].get('relay_host', None) relay_password = self.config['main'].get('relay_password', None) if relay_host is None or relay_password is None: self.irc = MultiIRCManager(self) else: self.irc = SingleIRCManager(self) self.reactor.add_global_handler('all_events', self.irc._dispatcher, -10) twitch_client_id = None twitch_oauth = None if 'twitchapi' in self.config: twitch_client_id = self.config['twitchapi'].get('client_id', None) twitch_oauth = self.config['twitchapi'].get('oauth', None) self.twitchapi = TwitchAPI(twitch_client_id, twitch_oauth) self.ascii_timeout_duration = 120 self.msg_length_timeout_duration = 120 self.data = {} self.data_cb = {} self.url_regex = re.compile(self.url_regex_str, re.IGNORECASE) self.data['broadcaster'] = self.streamer self.data['version'] = self.version self.data_cb['status_length'] = self.c_status_length self.data_cb['stream_status'] = self.c_stream_status self.data_cb['bot_uptime'] = self.c_uptime self.data_cb['current_time'] = self.c_current_time self.silent = True if args.silent else self.silent if self.silent: log.info('Silent mode enabled') self.reconnection_interval = 5 """ For actions that need to access the main thread, we can use the mainthread_queue. """ self.mainthread_queue = ActionQueue() self.execute_every(1, self.mainthread_queue.parse_action) self.websocket_manager = WebSocketManager(self) try: if self.config['twitchapi']['update_subscribers'] == '1': self.execute_every(30 * 60, self.action_queue.add, (self.update_subscribers_stage1, )) except: pass # XXX: TEMPORARY UGLY CODE HandlerManager.add_handler('on_user_gain_tokens', self.on_user_gain_tokens)