def timeout(self, timeout_length, warning_module=None, use_warnings=True): """ Returns a tuple with the follow data: How long to timeout the user for, and what the punishment string is set to. The punishment string is used to clarify whether this was a warning or the real deal. """ punishment = 'timed out for {} seconds'.format(timeout_length) if use_warnings and warning_module is not None: redis = RedisManager.get() """ How many chances the user has before receiving a full timeout. """ total_chances = warning_module.settings['total_chances'] warning_keys = self.get_warning_keys(total_chances, warning_module.settings['redis_prefix']) warnings = self.get_warnings(redis, warning_keys) chances_used = self.get_chances_used(warnings) if chances_used < total_chances: """ The user used up one of his warnings. Calculate for how long we should time him out. """ timeout_length = warning_module.settings['base_timeout'] * (chances_used + 1) punishment = 'timed out for {} seconds (warning)'.format(timeout_length) self.add_warning(redis, warning_module.settings['length'], warning_keys, warnings) return (timeout_length, punishment)
def test(): redis = RedisManager.get() current_quest_key = '{streamer}:current_quest'.format(streamer=StreamHelper.get_streamer()) current_quest_id = redis.get(current_quest_key) current_quest = module_manager[current_quest_id] current_quest.load_data() return render_template('test.html', current_quest=current_quest)
def start_quest(self): HandlerManager.add_handler('on_duel_complete', self.on_duel_complete) redis = RedisManager.get() self.progress = {} old_progress = redis.hgetall(self.progress_key) for user, progress in old_progress.items(): try: self.progress[user.decode('utf8')] = int(progress) except (TypeError, ValueError): pass self.points_required = redis.get(self.points_required_key) try: self.points_required = int(self.points_required) except (TypeError, ValueError): pass if self.points_required is None: try: self.points_required = random.randint(self.settings['min_value'], self.settings['max_value'] + 1) except ValueError: # someone f****d up self.points_required = 500 redis.set(self.points_required_key, self.points_required)
def start_quest(self): HandlerManager.add_handler('on_message', self.on_message) redis = RedisManager.get() self.load_progress(redis=redis) self.load_data(redis=redis)
def on_stream_stop(self): if self.current_quest is None: log.info('No quest active on stream stop.') return False self.current_quest.stop_quest() self.current_quest = None self.bot.say('Stream ended, quest has been reset.') redis = RedisManager.get() # Remove any mentions of the current quest redis.delete(self.current_quest_key) last_stream_id = StreamHelper.get_last_stream_id() if last_stream_id is False: log.error('No last stream ID found.') # No last stream ID found. why? return False # XXX: Should we use a pipeline for any of this? # Go through user tokens and remove any from more than 2 streams ago for key in redis.keys('{streamer}:*:tokens'.format(streamer=StreamHelper.get_streamer())): all_tokens = redis.hgetall(key) for stream_id_str in all_tokens: try: stream_id = int(stream_id_str) except (TypeError, ValueError): log.error('Invalid stream id in tokens by {}'.format(key)) continue if last_stream_id - stream_id > 1: log.info('Removing tokens for stream {}'.format(stream_id)) redis.hdel(key, stream_id)
def start_quest(self): HandlerManager.add_handler('on_user_win_hs_bet', self.on_user_win_hs_bet) redis = RedisManager.get() self.load_progress(redis=redis)
def stop_quest(self): HandlerManager.remove_handler('on_user_win_hs_bet', self.on_user_win_hs_bet) redis = RedisManager.get() self.reset_progress(redis=redis)
def stop_quest(self): HandlerManager.remove_handler('on_duel_complete', self.on_duel_complete) redis = RedisManager.get() self.reset_progress(redis=redis) redis.delete(self.points_required_key)
def progress_quest(self, amount, limit, completion_reward): """ Progress the quest for `stream_id` by `amount`. We load data from redis in case no progress has been made yet. """ streamer = StreamHelper.get_streamer() stream_id = StreamHelper.get_current_stream_id() if stream_id is False: return False redis = RedisManager.get() quest_progress_key = '{streamer}:{stream_id}:{username}:quest_progress'.format( streamer=streamer, stream_id=stream_id, username=self.username) if stream_id not in self.quest_progress: # Load the old progress, or set it to 0 if no progress was found self.init_quest_progress() if self.quest_progress[stream_id] >= limit: # The user has already completed this quest. return False self.quest_progress[stream_id] += amount if self.quest_progress[stream_id] >= limit: # The user just completed the quest for the first time self.award_tokens(completion_reward, redis=redis) return False redis.set(quest_progress_key, self.quest_progress[stream_id])
def award_tokens(self, tokens, redis=None, force=False): """ Returns True if tokens were awarded properly. Returns False if not. Tokens can only be rewarded once per stream ID. """ streamer = StreamHelper.get_streamer() stream_id = StreamHelper.get_current_stream_id() if stream_id is False: return False if redis is None: redis = RedisManager.get() key = '{streamer}:{username}:tokens'.format(streamer=streamer, username=self.username) if force: res = True redis.hset(key, stream_id, tokens) else: res = True if redis.hsetnx(key, stream_id, tokens) == 1 else False if res is True: HandlerManager.trigger('on_user_gain_tokens', self, tokens) return res
def get_follow_relationship(self, username, streamer): """Returns the follow relationship between the user and a streamer. Returns False if `username` is not following `streamer`. Otherwise, return a datetime object. This value is cached in Redis for 2 minutes. """ redis = RedisManager.get() fr_key = "fr_{username}_{streamer}".format(username=username, streamer=streamer) follow_relationship = redis.get(fr_key) if follow_relationship is None: try: data = self.get(endpoints=["users", username, "follows", "channels", streamer], base=self.kraken_url) created_at = data["created_at"] redis.setex(fr_key, time=120, value=created_at) return TwitchAPI.parse_datetime(created_at) except urllib.error.HTTPError: redis.setex(fr_key, time=120, value="-1") return False except: log.exception("Unhandled exception in get_follow_relationship") return False else: if follow_relationship == b"-1": return False else: return TwitchAPI.parse_datetime(follow_relationship.decode("utf-8"))
def spend_tokens(self, tokens_to_spend, redis=None): if redis is None: redis = RedisManager.get() user_token_key = '{streamer}:{username}:tokens'.format( streamer=StreamHelper.get_streamer(), username=self.username) token_dict = redis.hgetall(user_token_key) for stream_id in token_dict: try: num_tokens = int(token_dict[stream_id]) except (TypeError, ValueError): continue if num_tokens == 0: continue decrease_by = min(tokens_to_spend, num_tokens) tokens_to_spend -= decrease_by num_tokens -= decrease_by redis.hset(user_token_key, stream_id, num_tokens) if tokens_to_spend == 0: return True return False
def award_tokens(self, tokens, redis=None, force=False): """ Returns True if tokens were awarded properly. Returns False if not. Tokens can only be rewarded once per stream ID. """ streamer = StreamHelper.get_streamer() stream_id = StreamHelper.get_current_stream_id() if stream_id is False: return False if redis is None: redis = RedisManager.get() key = '{streamer}:{username}:tokens'.format( streamer=streamer, username=self.username) if force: res = True redis.hset(key, stream_id, tokens) else: res = True if redis.hsetnx(key, stream_id, tokens) == 1 else False if res is True: HandlerManager.trigger('on_user_gain_tokens', self, tokens) return res
def get_follow_relationship(self, username, streamer): """Returns the follow relationship between the user and a streamer. Returns False if `username` is not following `streamer`. Otherwise, return a datetime object. This value is cached in Redis for 2 minutes. """ redis = RedisManager.get() fr_key = 'fr_{username}_{streamer}'.format(username=username, streamer=streamer) follow_relationship = redis.get(fr_key) if follow_relationship is None: try: data = self.get(endpoints=['users', username, 'follows', 'channels', streamer], base=self.kraken_url) created_at = data['created_at'] redis.setex(fr_key, time=120, value=created_at) return TwitchAPI.parse_datetime(created_at) except urllib.error.HTTPError: redis.setex(fr_key, time=120, value='-1') return False except: log.exception('Unhandled exception in get_follow_relationship') return False else: if follow_relationship == '-1': return False else: return TwitchAPI.parse_datetime(follow_relationship)
def stop_quest(self): HandlerManager.remove_handler('on_message', self.on_message) redis = RedisManager.get() self.reset_progress(redis=redis) redis.delete(self.current_emote_key)
def stop_quest(self): HandlerManager.remove_handler('on_duel_complete', self.on_duel_complete) redis = RedisManager.get() self.reset_progress(redis=redis)
def on_duel_complete(self, winner, loser, points_won, points_bet): if points_won < 1: # This duel did not award any points. # That means it's entirely irrelevant to us return if winner.username in self.progress: total_points_won = self.progress[winner.username] if total_points_won >= self.points_required: # The user has already won enough points, and been rewarded already. return else: total_points_won = 0 # If we get here, this means the user has not completed the quest yet. # And the user won some points in this duel total_points_won += points_won redis = RedisManager.get() if total_points_won >= self.points_required: # Reward the user with some tokens winner.award_tokens(self.REWARD, redis=redis) # Save the users "points won" progress self.progress[winner.username] = total_points_won redis.hset(self.progress_key, winner.username, total_points_won)
def get_tags(self, redis=None): if redis is None: redis = RedisManager.get() val = redis.hget('global:usertags', self.username) if val: return json.loads(val) else: return {}
def start_quest(self): HandlerManager.add_handler('on_duel_complete', self.on_duel_complete) redis = RedisManager.get() self.load_progress(redis=redis) self.load_data(redis=redis) self.LIMIT = self.points_required
def start_quest(self): HandlerManager.add_handler('on_user_win_hs_bet', self.on_user_win_hs_bet) redis = RedisManager.get() self.load_progress(redis=redis) self.load_data(redis=redis) self.LIMIT = self.hsbet_points_required
def redis_test2(self, username): redis = RedisManager.get() values = [ redis.hget('pajlada:users:points', username), redis.hget('pajlada:users:ignored', username), redis.hget('pajlada:users:banned', username), redis.hget('pajlada:users:last_active', username), ] return values
def load_config(self, config): self.config = config self.nickname = config['main'].get('nickname', 'pajbot') self.password = config['main'].get('password', 'abcdef') self.timezone = config['main'].get('timezone', 'UTC') self.trusted_mods = config.getboolean('main', 'trusted_mods') TimeManager.init_timezone(self.timezone) 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.wolfram = None try: if 'wolfram' in config['main']: import wolframalpha self.wolfram = wolframalpha.Client(config['main']['wolfram']) except: pass self.silent = False self.dev = False if 'flags' in config: self.silent = True if 'silent' in config['flags'] and config[ 'flags']['silent'] == '1' else self.silent self.dev = True if 'dev' in config['flags'] and config['flags'][ 'dev'] == '1' else self.dev DBManager.init(self.config['main']['db']) redis_options = {} if 'redis' in config: log.info(config._sections['redis']) redis_options = config._sections['redis'] RedisManager.init(**redis_options)
def load_progress(self, redis=None): if redis is None: redis = RedisManager.get() self.progress = {} old_progress = redis.hgetall(self.progress_key) for user, progress in old_progress.items(): try: self.progress[user.decode('utf8')] = int(progress) except (TypeError, ValueError): pass
def load_config(self, config): self.config = config self.domain = config['web'].get('domain', 'localhost') self.nickname = config['main'].get('nickname', 'pajbot') self.password = config['main'].get('password', 'abcdef') self.timezone = config['main'].get('timezone', 'UTC') self.trusted_mods = config.getboolean('main', 'trusted_mods') TimeManager.init_timezone(self.timezone) 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.wolfram = None try: if 'wolfram' in config['main']: import wolframalpha self.wolfram = wolframalpha.Client(config['main']['wolfram']) except: pass self.silent = False self.dev = False if 'flags' in config: self.silent = True if 'silent' in config['flags'] and config['flags']['silent'] == '1' else self.silent self.dev = True if 'dev' in config['flags'] and config['flags']['dev'] == '1' else self.dev DBManager.init(self.config['main']['db']) redis_options = {} if 'redis' in config: redis_options = config._sections['redis'] RedisManager.init(**redis_options)
def load_progress(self, redis=None): if redis is None: redis = RedisManager.get() self.progress = {} old_progress = redis.hgetall(self.progress_key) for user, progress in old_progress.items(): try: self.progress[user] = int(progress) except (TypeError, ValueError): pass
def update_chatters_stage2(self, chatters): points = 1 if self.bot.is_online else 0 log.debug('Updating {0} chatters'.format(len(chatters))) self.bot.stream_manager.update_chatters(chatters, self.update_chatters_interval) with RedisManager.pipeline_context() as pipeline: with DBManager.create_session_scope() as db_session: user_models = UserManager.get().bulk_load_user_models(chatters, db_session) users = [] for username in chatters: user_model = user_models.get(username, None) user = UserManager.get().get_user(username, db_session=db_session, user_model=user_model, redis=pipeline) users.append(user) more_update_data = {} if self.bot.is_online: more_update_data['minutes_in_chat_online'] = self.update_chatters_interval else: more_update_data['minutes_in_chat_offline'] = self.update_chatters_interval points_to_give_out = {} dt_now = datetime.datetime.now().timestamp() for user in users: user._set_last_seen(dt_now) num_points = points if user.subscriber: num_points *= 5 # TODO: Load user tags during the pipeline redis data fetch if self.bot.streamer == 'forsenlol' and 'trumpsc_sub' in user.get_tags(): num_points *= 0.5 num_points = int(num_points) if num_points not in points_to_give_out: points_to_give_out[num_points] = [] points_to_give_out[num_points].append(user.username) user.save(save_to_db=False) for num_points, usernames in points_to_give_out.items(): payload = { User.points: User.points + num_points, } if self.bot.is_online: payload[User.minutes_in_chat_online] = User.minutes_in_chat_online + self.update_chatters_interval else: payload[User.minutes_in_chat_offline] = User.minutes_in_chat_offline + self.update_chatters_interval db_session.query(User).filter(User.username.in_(usernames)).\ update(payload, synchronize_session=False) pipeline.execute()
def redis_test(self, username): try: pipeline = RedisManager.get().pipeline() pipeline.hget('pajlada:users:points', username) pipeline.hget('pajlada:users:ignored', username) pipeline.hget('pajlada:users:banned', username) pipeline.hget('pajlada:users:last_active', username) except: pipeline.reset() finally: b = pipeline.execute() log.info(b)
def load_data(self, redis=None): if redis is None: redis = RedisManager.get() self.current_emote = redis.get(self.current_emote_key) if self.current_emote is None: # randomize an emote global_twitch_emotes = self.bot.emotes.get_global_emotes() self.current_emote = random.choice(global_twitch_emotes) redis.set(self.current_emote_key, self.current_emote) else: self.current_emote = self.current_emote
def home(): redis = RedisManager.get() values = redis.hgetall("stream_data") for x in bots: x.online = values.get("{streamer}:online".format(streamer=x.streamer[0])) == "True" try: x.viewers = int(values.get("{streamer}:viewers".format(streamer=x.streamer[0]))) except: x.viewers = -1 x.viewers_str = millify(x.viewers) x.game = values.get("{streamer}:game".format(streamer=x.streamer[0])) bots.sort(key=lambda x: (x.online, x.viewers, x.bot), reverse=True) return render_template("home.html", bots=bots, values=values)
def home(): custom_content = '' redis = RedisManager.get() streamer = StreamHelper.get_streamer() keys = ('online', 'viewers', 'game') stream_data_keys = [ '{streamer}:{key}'.format(streamer=streamer, key=key) for key in keys ] stream_data_list = redis.hmget('stream_data', stream_data_keys) stream_data = { keys[x]: stream_data_list[x] for x in range(0, len(keys)) } keys = StreamHelper.social_keys streamer_info_keys = [ '{streamer}:{key}'.format(streamer=streamer, key=key) for key in keys.keys() ] log.info(streamer_info_keys) streamer_info_list = redis.hmget('streamer_info', streamer_info_keys) streamer_info = collections.OrderedDict() for key in keys: value = streamer_info_list.pop(0) if value: streamer_info[key] = { 'value': keys[key]['format'].format(value), 'title': keys[key]['title'], 'format': keys[key]['format'], } current_quest_key = '{streamer}:current_quest'.format( streamer=StreamHelper.get_streamer()) current_quest_id = redis.get(current_quest_key) if current_quest_id is not None: current_quest = app.module_manager[current_quest_id] if current_quest: current_quest.load_data() else: current_quest = None return render_template('home.html', custom_content=custom_content, current_quest=current_quest, stream_data=stream_data, streamer_info=streamer_info)
def refresh_stream_status(self): try: status = self.bot.twitchapi.get_status(self.bot.streamer) if status['error'] is True: log.error('An error occured while fetching stream status') return redis = RedisManager.get() redis.hmset( 'stream_data', { '{streamer}:online'.format(streamer=self.bot.streamer): status['online'], '{streamer}:viewers'.format(streamer=self.bot.streamer): status['viewers'], '{streamer}:game'.format(streamer=self.bot.streamer): status['game'], }) self.num_viewers = status['viewers'] if status['online']: if self.current_stream is None: self.create_stream(status) if self.current_stream_chunk is None: self.create_stream_chunk(status) if self.current_stream_chunk.broadcast_id != status[ 'broadcast_id']: log.debug('Detected a new chunk!') self.create_stream_chunk(status) self.num_offlines = 0 self.first_offline = None self.bot.ascii_timeout_duration = 120 self.bot.msg_length_timeout_duration = 120 else: self.bot.ascii_timeout_duration = 10 self.bot.msg_length_timeout_duration = 10 if self.online is True: log.info('Offline. {0}'.format(self.num_offlines)) if self.first_offline is None: self.first_offline = datetime.datetime.now() if self.num_offlines >= 10: log.info('Switching to offline state!') self.go_offline() self.num_offlines += 1 except: log.exception('Uncaught exception while refreshing stream status')
def on_stream_start(self): available_quests = list(filter(lambda m: m.ID.startswith('quest-'), self.submodules)) if len(available_quests) == 0: log.error('No quests enabled.') return False self.current_quest = random.choice(available_quests) self.current_quest.start_quest() redis = RedisManager.get() redis.set(self.current_quest_key, self.current_quest.ID) self.bot.say('Stream started, new quest has been chosen!') self.bot.say('Current quest objective: {}'.format(self.current_quest.get_objective()))
def start_quest(self): HandlerManager.add_handler('on_message', self.on_message) redis = RedisManager.get() self.load_progress(redis=redis) self.current_emote = redis.get(self.current_emote_key) if self.current_emote is None: # randomize an emote global_twitch_emotes = self.bot.emotes.get_global_emotes() self.current_emote = random.choice(global_twitch_emotes) redis.set(self.current_emote_key, self.current_emote) else: self.current_emote = self.current_emote.decode('utf8')
def load_data(self, redis=None): if redis is None: redis = RedisManager.get() self.hsbet_points_required = redis.get(self.hsbet_points_key) try: self.hsbet_points_required = int(self.hsbet_points_required) except (TypeError, ValueError): pass if self.hsbet_points_required is None: try: self.hsbet_points_required = random.randint(self.settings['min_value'], self.settings['max_value'] + 1) except ValueError: self.hsbet_points_required = 500 redis.set(self.hsbet_points_key, self.hsbet_points_required)
def load_data(self, redis=None): if redis is None: redis = RedisManager.get() self.points_required = redis.get(self.points_required_key) try: self.points_required = int(self.points_required) except (TypeError, ValueError): pass if self.points_required is None: try: self.points_required = random.randint(self.settings['min_value'], self.settings['max_value'] + 1) except ValueError: # someone f****d up self.points_required = 500 redis.set(self.points_required_key, self.points_required)
def on_duel_complete(self, winner, loser, points_won, points_bet): if points_won < 1: return user_progress = self.get_user_progress(winner.username, default=0) if user_progress >= self.get_limit(): return user_progress += 1 redis = RedisManager.get() if user_progress == self.get_limit(): winner.award_tokens(self.REWARD, redis=redis) self.set_user_progress(winner.username, user_progress, redis=redis)
def upgrade(): bind = op.get_bind() session = Session(bind=bind) with RedisManager.pipeline_context() as pipeline: streamer = pb_config['main']['streamer'] count_key = '{streamer}:emotes:count'.format(streamer=streamer) epmrecord_key = '{streamer}:emotes:epmrecord'.format(streamer=streamer) pipeline.delete(count_key, epmrecord_key) for emote in session.query(Emote): if emote.stats: pipeline.zincrby(count_key, emote.code, emote.stats.count) pipeline.zincrby(epmrecord_key, emote.code, emote.stats.tm_record) op.drop_table('tb_emote_stats') op.drop_table('tb_emote')
def on_user_win_hs_bet(self, user, points_reward): if points_reward < 1: return user_progress = self.get_user_progress(user.username, default=0) if user_progress >= self.hsbet_points_required: return user_progress += points_reward redis = RedisManager.get() if user_progress >= self.hsbet_points_required: user.award_tokens(self.REWARD, redis=redis) self.set_user_progress(user.username, user_progress, redis=redis)
def get_tokens(self, redis=None): streamer = StreamHelper.get_streamer() if redis is None: redis = RedisManager.get() tokens = redis.hgetall('{streamer}:{username}:tokens'.format( streamer=streamer, username=self.username)) num_tokens = 0 for token_value in tokens.values(): try: num_tokens += int(token_value) except (TypeError, ValueError): log.warn('Invalid value for tokens, user {}'.format(self.username)) return num_tokens
def on_message(self, source, message, emotes, whisper, urls, event): if len(message) >= self.get_quest_message_length(): if event.type == 'action': user_progress = self.get_user_progress(source.username, default=0) if user_progress >= self.get_limit(): return user_progress += 1 redis = RedisManager.get() if user_progress == self.get_limit(): source.award_tokens(self.REWARD, redis=redis) self.set_user_progress(source.username, user_progress, redis=redis)
def init_quest_progress(self, redis=None): """ Initialize quest progress for the current stream. """ streamer = StreamHelper.get_streamer() stream_id = StreamHelper.get_current_stream_id() if redis is None: redis = RedisManager.get() quest_progress_key = '{streamer}:{stream_id}:{username}:quest_progress'.format( streamer=streamer, stream_id=stream_id, username=self.username) old_progress = 0 try: old_progress = int(redis.get(quest_progress_key)) except (TypeError, ValueError): pass self.quest_progress[stream_id] = old_progress
def on_message(self, source, message, emotes, whisper, urls, event): for emote in emotes: if emote['code'] == self.current_emote: user_progress = self.get_user_progress(source.username, default=0) + 1 if user_progress > self.get_limit(): log.debug('{} has already complete the quest. Moving along.'.format(source.username)) # no need to do more return redis = RedisManager.get() if user_progress == self.get_limit(): source.award_tokens(self.REWARD, redis=redis) self.set_user_progress(source.username, user_progress, redis=redis) return
def get_viewer_data(self, redis=None): if self.offline: return False if not redis: redis = RedisManager.get() data = redis.hget( '{streamer}:viewer_data'.format(streamer=self.bot.streamer), self.current_stream.id) if data is None: data = {} else: data = json.loads(data) return data
def get_tokens(self, redis=None): streamer = StreamHelper.get_streamer() if redis is None: redis = RedisManager.get() tokens = redis.hgetall('{streamer}:{username}:tokens'.format( streamer=streamer, username=self.username)) num_tokens = 0 for token_value in tokens.values(): try: num_tokens += int(token_value) except (TypeError, ValueError): log.warn('Invalid value for tokens, user {}'.format( self.username)) return num_tokens