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 create_stream(self, status): log.info('Attempting to create a stream!') with DBManager.create_session_scope(expire_on_commit=False) as db_session: stream_chunk = db_session.query(StreamChunk).filter_by(broadcast_id=status['broadcast_id']).one_or_none() new_stream = False if stream_chunk is not None: stream = stream_chunk.stream else: log.info('checking if there is an active stream already') stream = db_session.query(Stream).filter_by(ended=False).order_by(Stream.stream_start.desc()).first() new_stream = stream is None if new_stream: log.info('No active stream, create new!') stream = Stream(status['created_at'], title=status['title']) db_session.add(stream) db_session.commit() log.info('Successfully added stream!') stream_chunk = StreamChunk(stream, status['broadcast_id'], status['created_at']) db_session.add(stream_chunk) db_session.commit() stream.stream_chunks.append(stream_chunk) log.info('Created stream chunk') self.current_stream = stream self.current_stream_chunk = stream_chunk db_session.expunge_all() if new_stream: HandlerManager.trigger('on_stream_start', stop_on_false=False) log.info('Successfully created a stream')
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 commit_all(self): log.info('Commiting all...') for key, manager in self.commitable.items(): log.info('Commiting {0}'.format(key)) manager.commit() log.info('Done with {0}'.format(key)) log.info('ok!') HandlerManager.trigger('on_commit', stop_on_false=False)
def base_paid_timeout(self, bot, source, message, _time, _cost): if message is None or len(message) == 0: return False username = message.split(' ')[0] if len(username) < 2: return False with bot.users.find_context(username) as victim: if victim is None: bot.whisper(source.username, 'This user does not exist FailFish') return False if victim.last_active is None or (datetime.datetime.now() - victim._last_active).total_seconds() > 10 * 60: bot.whisper(source.username, 'This user has not been active in chat within the last 10 minutes.') return False """ if victim == source: bot.whisper(source.username, 'You can\'t timeout yourself FailFish') return False """ if victim.moderator is True: bot.whisper(source.username, 'This person has mod privileges, timeouting this person is not worth it.') return False if victim.level >= self.settings['bypass_level']: bot.whisper(source.username, 'This person\'s user level is too high, you can\'t timeout this person.') return False now = datetime.datetime.now() if victim.timed_out is True and victim.timeout_end > now: victim.timeout_end += datetime.timedelta(seconds=_time) bot.whisper(victim.username, '{victim.username}, you were timed out for an additional {time} seconds by {source.username}'.format( victim=victim, source=source, time=_time)) bot.whisper(source.username, 'You just used {0} points to time out {1} for an additional {2} seconds.'.format(_cost, username, _time)) num_seconds = int((victim.timeout_end - now).total_seconds()) bot._timeout(username, num_seconds) else: bot.whisper(source.username, 'You just used {0} points to time out {1} for {2} seconds.'.format(_cost, username, _time)) bot.whisper(username, '{0} just timed you out for {1} seconds. /w {2} !$unbanme to unban yourself for points forsenMoney'.format(source.username, _time, bot.nickname)) bot._timeout(username, _time) victim.timed_out = True victim.timeout_start = now victim.timeout_end = now + datetime.timedelta(seconds=_time) if self.settings['show_on_clr']: payload = {'user': source.username, 'victim': victim.username} bot.websocket_manager.emit('timeout', payload) HandlerManager.trigger('on_paid_timeout', source, victim, _cost, stop_on_false=False)
def on_message(self, source, msg_raw, message_emotes, whisper, urls, event): if len(message_emotes) > 0: if hasattr(self, 'bingo_running') and self.bingo_running is True: if len(message_emotes) == 1 and len(msg_raw.split(' ')) == 1: if message_emotes[0]['code'] == self.bingo_target: HandlerManager.trigger('on_bingo_win', source, self.bingo_points, self.bingo_target) self.bingo_running = False self.bingo_bttv_twitch_running = False self.bot.me('{0} won the bingo! {1} was the target. Congrats, {2} points to you PogChamp'.format(source.username_raw, self.bingo_target, self.bingo_points)) source.points += self.bingo_points log.info('{0} won the bingo for {1} points!'.format(source.username_raw, self.bingo_points))
def accept_duel(self, **options): """ Accepts any active duel requests you've received. How to add: !add funccommand accept accept_duel --cd 0 --usercd 0 How to use: !accept """ bot = options['bot'] source = options['source'] init_dueling_variables(source) duel_tax = 0.3 # 30% tax if source.duel_request is not False: if not source.can_afford(source.duel_price) or not source.duel_request.can_afford(source.duel_price): bot.whisper(source.username, 'Your duel request with {} was cancelled due to one of you not having enough points.'.format(source.duel_request.username_raw)) bot.whisper(source.duel_request.username, 'Your duel request with {} was cancelled due to one of you not having enough points.'.format(source.username_raw)) source.duel_request.duel_target = False source.duel_request = False return False source.points -= source.duel_price source.duel_request.points -= source.duel_price winning_pot = int(source.duel_price * (1.0 - duel_tax)) participants = [source, source.duel_request] winner = random.choice(participants) participants.remove(winner) loser = participants.pop() winner.points += source.duel_price winner.points += winning_pot bot.duel_manager.user_won(winner, winning_pot) bot.duel_manager.user_lost(loser, source.duel_price) arguments = { 'winner': winner.username, 'loser': loser.username, 'total_pot': source.duel_price, 'extra_points': winning_pot, } if source.duel_price > 0: message = self.get_phrase('message_won_points', **arguments) if source.duel_price >= 500: bot.websocket_manager.emit('notification', {'message': '{} won the duel vs {}'.format(winner.username_raw, loser.username_raw)}) else: message = self.get_phrase('message_won', **arguments) bot.say(message) source.duel_request.duel_target = False source.duel_request = False source.duel_price = 0 HandlerManager.trigger('on_duel_complete', winner, loser, winning_pot, source.duel_price)
def parse_message(self, msg_raw, source, event, tags={}, whisper=False): msg_lower = msg_raw.lower() emote_tag = None for tag in tags: if tag['key'] == 'subscriber' and event.target == self.channel: source.subscriber = tag['value'] == '1' elif tag['key'] == 'emotes' and tag['value']: emote_tag = tag['value'] elif tag['key'] == 'display-name' and tag['value']: source.username_raw = tag['value'] elif tag['key'] == 'user-type': source.moderator = tag['value'] == 'mod' or source.username == self.streamer # source.num_lines += 1 if source is None: log.error('No valid user passed to parse_message') return False if source.banned: self.ban(source.username) return False # If a user types when timed out, we assume he's been unbanned for a good reason and remove his flag. if source.timed_out is True: source.timed_out = False # Parse emotes in the message message_emotes = self.emotes.parse_message_twitch_emotes(source, msg_raw, emote_tag, whisper) urls = self.find_unique_urls(msg_raw) # log.debug('{2}{0}: {1}'.format(source.username, msg_raw, '<w>' if whisper else '')) res = HandlerManager.trigger('on_message', source, msg_raw, message_emotes, whisper, urls, event, stop_on_false=True) if res is False: return False source.last_seen = datetime.datetime.now() source.last_active = datetime.datetime.now() if source.ignored: return False if msg_lower[:1] == '!': msg_lower_parts = msg_lower.split(' ') trigger = msg_lower_parts[0][1:] msg_raw_parts = msg_raw.split(' ') remaining_message = ' '.join(msg_raw_parts[1:]) if len(msg_raw_parts) > 1 else None if trigger in self.commands: command = self.commands[trigger] extra_args = { 'emotes': message_emotes, 'trigger': trigger, } command.run(self, source, remaining_message, event=event, args=extra_args, whisper=whisper)
def on_message(self, source, message, emotes, whisper, urls, event): if whisper is False and source.username in self.valid_usernames: # Did twitchnotify tell us about a new sub? m = self.new_sub_regex.search(message) if m: username = m.group(1) user = self.bot.users[username] self.on_new_sub(user) HandlerManager.trigger('on_user_sub', user) else: # Did twitchnotify tell us about a resub? m = self.resub_regex.search(message) if m: username = m.group(1) num_months = int(m.group(2)) user = self.bot.users[username] self.on_resub(user, num_months) HandlerManager.trigger('on_user_resub', user, num_months)
def go_offline(self): with DBManager.create_session_scope(expire_on_commit=False) as db_session: self.current_stream.ended = True self.current_stream.stream_end = self.first_offline self.current_stream_chunk.chunk_end = self.first_offline db_session.add(self.current_stream) db_session.add(self.current_stream_chunk) db_session.commit() db_session.expunge_all() self.last_stream = self.current_stream self.current_stream = None self.current_stream_chunk = None HandlerManager.trigger('on_stream_stop', stop_on_false=False)
def go_offline(self): with DBManager.create_session_scope( expire_on_commit=False) as db_session: self.current_stream.ended = True self.current_stream.stream_end = self.first_offline self.current_stream_chunk.chunk_end = self.first_offline db_session.add(self.current_stream) db_session.add(self.current_stream_chunk) db_session.commit() db_session.expunge_all() self.last_stream = self.current_stream self.current_stream = None self.current_stream_chunk = None HandlerManager.trigger('on_stream_stop', stop_on_false=False)
def end_raffle(self): if not self.raffle_running: return False self.raffle_running = False if len(self.raffle_users) == 0: self.bot.me('Wow, no one joined the raffle DansGame') return False winner = random.choice(self.raffle_users) self.raffle_users = [] self.bot.websocket_manager.emit('notification', {'message': '{} won {} points in the raffle!'.format(winner.username_raw, self.raffle_points)}) self.bot.me('The raffle has finished! {0} won {1} points! PogChamp'.format(winner.username_raw, self.raffle_points)) winner.points += self.raffle_points HandlerManager.trigger('on_raffle_win', winner, self.raffle_points)
def on_pubmsg(self, chatconn, event): if event.source.user == self.nickname: return False # We use .lower() in case twitch ever starts sending non-lowercased usernames source = self.users[event.source.user.lower()] res = HandlerManager.trigger('on_pubmsg', source, event.arguments[0], stop_on_false=True) if res is False: return False self.parse_message(event.arguments[0], source, event, tags=event.tags)
def create_stream(self, status): log.info('Attempting to create a stream!') with DBManager.create_session_scope( expire_on_commit=False) as db_session: stream_chunk = db_session.query(StreamChunk).filter_by( broadcast_id=status['broadcast_id']).one_or_none() new_stream = False if stream_chunk is not None: stream = stream_chunk.stream else: log.info('checking if there is an active stream already') stream = db_session.query(Stream).filter_by( ended=False).order_by(Stream.stream_start.desc()).first() new_stream = stream is None if new_stream: log.info('No active stream, create new!') stream = Stream(status['created_at'], title=status['title']) db_session.add(stream) db_session.commit() log.info('Successfully added stream!') stream_chunk = StreamChunk(stream, status['broadcast_id'], status['created_at']) db_session.add(stream_chunk) db_session.commit() stream.stream_chunks.append(stream_chunk) log.info('Created stream chunk') self.current_stream = stream self.current_stream_chunk = stream_chunk db_session.expunge_all() if new_stream: HandlerManager.trigger('on_stream_start', stop_on_false=False) log.info('Successfully created a stream')
def parse_message(self, msg_raw, source, event, tags={}, whisper=False): msg_lower = msg_raw.lower() if source is None: log.error('No valid user passed to parse_message') return False if source.banned: self.ban(source.username) return False # If a user types when timed out, we assume he's been unbanned for a good reason and remove his flag. if source.timed_out is True: source.timed_out = False message_emotes = [] for tag in tags: if tag['key'] == 'subscriber' and event.target == self.channel: if source.subscriber and tag['value'] == '0': source.subscriber = False elif not source.subscriber and tag['value'] == '1': source.subscriber = True elif tag['key'] == 'emotes' and tag['value']: emote_data = tag['value'].split('/') for emote in emote_data: try: emote_id, emote_occurrence = emote.split(':') emote_indices = emote_occurrence.split(',') emote_count = len(emote_indices) emote = self.emotes[int(emote_id)] first_index, last_index = emote_indices[0].split('-') first_index = int(first_index) last_index = int(last_index) emote_code = msg_raw[first_index:last_index + 1] if emote_code[0] == ':': emote_code = emote_code.upper() message_emotes.append({ 'code': emote_code, 'twitch_id': emote_id, 'start': first_index, 'end': last_index, }) tag_as = None if emote_code.startswith('trump'): tag_as = 'trump_sub' elif emote_code.startswith('eloise'): tag_as = 'eloise_sub' elif emote_code.startswith('forsen'): tag_as = 'forsen_sub' elif emote_code.startswith('nostam'): tag_as = 'nostam_sub' elif emote_code.startswith('reynad'): tag_as = 'reynad_sub' elif emote_code.startswith('athene'): tag_as = 'athene_sub' elif emote_id in [ 12760, 35600, 68498, 54065, 59411, 59412, 59413, 62683, 70183, 70181, 68499, 70429, 70432, 71432, 71433 ]: tag_as = 'massan_sub' if tag_as is not None: if source.tag_as(tag_as) is True: self.execute_delayed(60 * 60 * 24, source.remove_tag, (tag_as, )) if emote.id is None and emote.code is None: # The emote we just detected is new, set its code. emote.code = emote_code if emote.code not in self.emotes: self.emotes[emote.code] = emote emote.add(emote_count, self.reactor) except: log.exception( 'Exception caught while splitting emote data') log.error('Emote data: {}'.format(emote_data)) log.error('msg_raw: {}'.format(msg_raw)) elif tag['key'] == 'display-name' and tag['value']: try: source.update_username(tag['value']) except: log.exception( 'Exception caught while updating a users username') elif tag['key'] == 'user-type': source.moderator = tag[ 'value'] == 'mod' or source.username == self.streamer for emote in self.emotes.custom_data: num = 0 for match in emote.regex.finditer(msg_raw): num += 1 message_emotes.append({ 'code': emote.code, 'bttv_hash': emote.emote_hash, 'start': match.span()[0], 'end': match.span()[1] - 1, # don't ask me }) if num > 0: emote.add(num, self.reactor) urls = self.find_unique_urls(msg_raw) log.debug('{2}{0}: {1}'.format(source.username, msg_raw, '<w>' if whisper else '')) res = HandlerManager.trigger('on_message', source, msg_raw, message_emotes, whisper, urls, event, stop_on_false=True) if res is False: return False source.last_seen = datetime.datetime.now() source.last_active = datetime.datetime.now() if source.ignored: return False if msg_lower[:1] == '!': msg_lower_parts = msg_lower.split(' ') trigger = msg_lower_parts[0][1:] msg_raw_parts = msg_raw.split(' ') remaining_message = ' '.join( msg_raw_parts[1:]) if len(msg_raw_parts) > 1 else None if trigger in self.commands: command = self.commands[trigger] extra_args = { 'emotes': message_emotes, 'trigger': trigger, } command.run(self, source, remaining_message, event=event, args=extra_args, whisper=whisper)
def accept_duel(self, **options): """ Accepts any active duel requests you've received. How to add: !add funccommand accept accept_duel --cd 0 --usercd 0 How to use: !accept """ bot = options['bot'] source = options['source'] init_dueling_variables(source) duel_tax = 0.3 # 30% tax if source.duel_request is not False: if not source.can_afford( source.duel_price) or not source.duel_request.can_afford( source.duel_price): bot.whisper( source.username, 'Your duel request with {} was cancelled due to one of you not having enough points.' .format(source.duel_request.username_raw)) bot.whisper( source.duel_request.username, 'Your duel request with {} was cancelled due to one of you not having enough points.' .format(source.username_raw)) source.duel_request.duel_target = False source.duel_request = False return False source.points -= source.duel_price source.duel_request.points -= source.duel_price winning_pot = int(source.duel_price * (1.0 - duel_tax)) participants = [source, source.duel_request] winner = random.choice(participants) participants.remove(winner) loser = participants.pop() winner.points += source.duel_price winner.points += winning_pot bot.duel_manager.user_won(winner, winning_pot) bot.duel_manager.user_lost(loser, source.duel_price) arguments = { 'winner': winner.username, 'loser': loser.username, 'total_pot': source.duel_price, 'extra_points': winning_pot, } if source.duel_price > 0: message = self.get_phrase('message_won_points', **arguments) if source.duel_price >= 500: bot.websocket_manager.emit( 'notification', { 'message': '{} won the duel vs {}'.format( winner.username_raw, loser.username_raw) }) else: message = self.get_phrase('message_won', **arguments) bot.say(message) source.duel_request.duel_target = False source.duel_request = False source.duel_price = 0 HandlerManager.trigger('on_duel_complete', winner, loser, winning_pot, source.duel_price)
def multi_end_raffle(self): if not self.raffle_running: return False self.raffle_running = False if len(self.raffle_users) == 0: self.bot.me('Wow, no one joined the raffle DansGame') return False # Shuffle the list of participants random.shuffle(self.raffle_users) num_participants = len(self.raffle_users) abs_points = abs(self.raffle_points) max_winners = min(num_participants, 200) min_point_award = 100 negative = self.raffle_points < 0 # Decide how we should pick the winners log.info('Num participants: {}'.format(num_participants)) for winner_percentage in [x * 0.01 for x in range(1, 26)]: log.info('Winner percentage: {}'.format(winner_percentage)) num_winners = math.ceil(num_participants * winner_percentage) points_per_user = math.ceil(abs_points / num_winners) log.info('nw: {}, ppu: {}'.format(num_winners, points_per_user)) if num_winners > max_winners: num_winners = max_winners points_per_user = math.ceil(abs_points / num_winners) break elif points_per_user < min_point_award: num_winners = max(1, min(math.floor(abs_points / min_point_award), num_participants)) points_per_user = math.ceil(abs_points / num_winners) break log.info('k done. got {} winners'.format(num_winners)) winners = self.raffle_users[:num_winners] self.raffle_users = [] if negative: points_per_user *= -1 self.bot.me('The multi-raffle has finished! {0} users won {1} points each! PogChamp'.format(len(winners), points_per_user)) winners_arr = [] for winner in winners: winner.points += points_per_user winners_arr.append(winner) winners_str = generate_winner_list(winners_arr) if len(winners_str) > 300: self.bot.me('{} won {} points each!'.format(winners_str, points_per_user)) winners_arr = [] winner.save() if len(winners_arr) > 0: winners_str = generate_winner_list(winners_arr) self.bot.me('{} won {} points each!'.format(winners_str, points_per_user)) HandlerManager.trigger('on_multiraffle_win', winners, points_per_user)
def roulette(self, **options): if self.settings['only_roulette_after_sub']: if self.last_sub is None: return False if datetime.datetime.now() - self.last_sub > datetime.timedelta(seconds=self.settings['after_sub_roulette_time']): return False message = options['message'] user = options['source'] bot = options['bot'] if message is None: bot.whisper(user.username, 'I didn\'t recognize your bet! Usage: !roulette 150 to bet 150 points') return False msg_split = message.split(' ') if msg_split[0].lower() in ('all', 'allin'): bet = user.points_available() elif msg_split[0].endswith('%'): try: percentage = int(msg_split[0][:-1]) if percentage < 1 or percentage > 100: bot.whisper(user.username, 'To bet with percentages you need to specify a number between 1 and 100 (like !roulette 50%)') return False bet = math.floor(user.points_available() * (percentage / 100)) except (ValueError, TypeError): bot.whisper(user.username, 'Invalid percentage specified haHAA') return False else: try: message = message.lower() message = message.replace('k', '000') message = message.replace('m', '000000') bet = int(message.split(' ')[0]) except (ValueError, TypeError): bot.whisper(user.username, 'I didn\'t recognize your bet! Usage: !roulette 150 to bet 150 points') return False if not user.can_afford(bet): bot.whisper(user.username, 'You don\'t have enough points to do a roulette for {} points :('.format(bet)) return False if bet < self.settings['min_roulette_amount']: bot.whisper(user.username, 'You have to bet at least {} point! :('.format(self.settings['min_roulette_amount'])) return False # Calculating the result result = self.rigged_random_result() points = bet if result else -bet user.points += points with DBManager.create_session_scope() as db_session: r = Roulette(user.id, points) db_session.add(r) arguments = { 'bet': bet, 'user': user.username_raw, 'points': user.points_available() } if points > 0: out_message = self.get_phrase('message_won', **arguments) else: out_message = self.get_phrase('message_lost', **arguments) if self.settings['options_output'] == '1. Show results in chat': bot.me(out_message) if self.settings['options_output'] == '2. Show results in whispers': bot.whisper(user.username, out_message) if self.settings['options_output'] == '3. Show results in chat if it\'s over X points else it will be whispered.': if abs(points) >= self.settings['min_show_points']: bot.me(out_message) else: bot.whisper(user.username, out_message) HandlerManager.trigger('on_roulette_finish', user, points)
def parse_message(self, msg_raw, source, event, tags={}, whisper=False): msg_lower = msg_raw.lower() if source is None: log.error('No valid user passed to parse_message') return False if source.banned: self.ban(source.username) return False # If a user types when timed out, we assume he's been unbanned for a good reason and remove his flag. if source.timed_out is True: source.timed_out = False emote_tag = None for tag in tags: if tag['key'] == 'subscriber' and event.target == self.channel: if source.subscriber and tag['value'] == '0': source.subscriber = False elif not source.subscriber and tag['value'] == '1': source.subscriber = True elif tag['key'] == 'emotes' and tag['value']: emote_tag = tag['value'] elif tag['key'] == 'display-name' and tag['value']: try: source.update_username(tag['value']) except: log.exception( 'Exception caught while updating a users username') elif tag['key'] == 'user-type': source.moderator = tag[ 'value'] == 'mod' or source.username == self.streamer # Parse emotes in the message message_emotes = self.emotes.parse_message_twitch_emotes( source, msg_raw, emote_tag) urls = self.find_unique_urls(msg_raw) log.debug('{2}{0}: {1}'.format(source.username, msg_raw, '<w>' if whisper else '')) res = HandlerManager.trigger('on_message', source, msg_raw, message_emotes, whisper, urls, event, stop_on_false=True) if res is False: return False source.last_seen = datetime.datetime.now() source.last_active = datetime.datetime.now() if source.ignored: return False if msg_lower[:1] == '!': msg_lower_parts = msg_lower.split(' ') trigger = msg_lower_parts[0][1:] msg_raw_parts = msg_raw.split(' ') remaining_message = ' '.join( msg_raw_parts[1:]) if len(msg_raw_parts) > 1 else None if trigger in self.commands: command = self.commands[trigger] extra_args = { 'emotes': message_emotes, 'trigger': trigger, } command.run(self, source, remaining_message, event=event, args=extra_args, whisper=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.tbutil.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) self.duel_manager = DuelManager(self) HandlerManager.trigger('on_managers_loaded') # Reloadable managers self.reloadable = { 'filters': self.filters, } # Commitable managers self.commitable = { 'commands': self.commands, 'filters': self.filters, '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)
def paid_timeout(self, **options): message = options['message'] bot = options['bot'] source = options['source'] _time = self.settings['timeout_length'] _cost = self.settings['cost'] if message is None or len(message) == 0: return False username = message.split(' ')[0] if len(username) < 2: return False victim = bot.users.find(username) if victim is None: bot.whisper(source.username, 'This user does not exist FailFish') return False if victim.last_active is None or ( datetime.datetime.now() - victim._last_active).total_seconds() > 10 * 60: bot.whisper( source.username, 'This user has not been active in chat within the last 10 minutes.' ) return False """ if victim == source: bot.whisper(source.username, 'You can\'t timeout yourself FailFish') return False """ if victim.moderator is True: bot.whisper( source.username, 'This person has mod privileges, timeouting this person is not worth it.' ) return False if victim.level >= self.settings['bypass_level']: bot.whisper( source.username, 'This person\'s user level is too high, you can\'t timeout this person.' ) return False now = datetime.datetime.now() if victim.timed_out is True and victim.timeout_end > now: victim.timeout_end += datetime.timedelta(seconds=_time) bot.whisper( victim.username, '{victim.username}, you were timed out for an additional {time} seconds by {source.username}' .format(victim=victim, source=source, time=_time)) bot.whisper( source.username, 'You just used {0} points to time out {1} for an additional {2} seconds.' .format(_cost, username, _time)) num_seconds = int((victim.timeout_end - now).total_seconds()) bot._timeout(username, num_seconds) else: bot.whisper( source.username, 'You just used {0} points to time out {1} for {2} seconds.'. format(_cost, username, _time)) bot.whisper( username, '{0} just timed you out for {1} seconds. /w {2} !$unbanme to unban yourself for points forsenMoney' .format(source.username, _time, bot.nickname)) bot._timeout(username, _time) victim.timed_out = True victim.timeout_start = now victim.timeout_end = now + datetime.timedelta(seconds=_time) payload = {'user': source.username, 'victim': victim.username} bot.websocket_manager.emit('timeout', payload) HandlerManager.trigger('on_paid_timeout', source, victim, _cost, stop_on_false=False)
def multi_end_raffle(self): if not self.raffle_running: return False self.raffle_running = False if len(self.raffle_users) == 0: self.bot.me('Wow, no one joined the raffle DansGame') return False # Shuffle the list of participants random.shuffle(self.raffle_users) num_participants = len(self.raffle_users) abs_points = abs(self.raffle_points) max_winners = min(num_participants, 200) min_point_award = 100 negative = self.raffle_points < 0 # Decide how we should pick the winners log.info('Num participants: {}'.format(num_participants)) for winner_percentage in [x * 0.01 for x in range(1, 26)]: log.info('Winner percentage: {}'.format(winner_percentage)) num_winners = math.ceil(num_participants * winner_percentage) points_per_user = math.ceil(abs_points / num_winners) log.info('nw: {}, ppu: {}'.format(num_winners, points_per_user)) if num_winners > max_winners: num_winners = max_winners points_per_user = math.ceil(abs_points / num_winners) break elif points_per_user < min_point_award: num_winners = max(1, min(math.floor(abs_points / min_point_award), num_participants)) points_per_user = math.ceil(abs_points / num_winners) break log.info('k done. got {} winners'.format(num_winners)) winners = self.raffle_users[:num_winners] self.raffle_users = [] if negative: points_per_user *= -1 self.bot.me('The multi-raffle has finished! {0} users won {1} points each! PogChamp'.format(len(winners), points_per_user)) winners_arr = [] for winner in winners: winner.points += points_per_user winners_arr.append(winner) winners_str = generate_winner_list(winners_arr) if len(winners_str) > 300: self.bot.me('{} won {} points each!'.format(winners_str, points_per_user)) winners_arr = [] if len(winners_arr) > 0: winners_str = generate_winner_list(winners_arr) self.bot.me('{} won {} points each!'.format(winners_str, points_per_user)) HandlerManager.trigger('on_multiraffle_win', winners, points_per_user)
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.tbutil.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) 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) 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)
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)
def poll_trackobot_stage2(self, game_data): latest_game = game_data['history'][0] if latest_game['id'] != self.last_game_id: winners = [] losers = [] total_winning_points = 0 total_losing_points = 0 points_bet = { 'win': 0, 'loss': 0, } bet_game_id = None with DBManager.create_session_scope() as db_session: bet_game = HSBetGame(latest_game['id'], latest_game['result']) db_session.add(bet_game) db_session.flush() bet_game_id = bet_game.id db_bets = {} for username in self.bets: bet_for_win, points = self.bets[username] """ self.bot.me('{} bet {} points on the last game to end up as a {}'.format( username, points, 'win' if bet_for_win else 'loss')) """ user = self.bot.users[username] correct_bet = (latest_game['result'] == 'win' and bet_for_win is True) or (latest_game['result'] == 'loss' and bet_for_win is False) points_bet['win' if bet_for_win else 'loss'] += points db_bets[username] = HSBetBet(bet_game_id, user.id, 'win' if bet_for_win else 'loss', points, 0) if correct_bet: winners.append((user, points)) total_winning_points += points user.remove_debt(points) else: losers.append((user, points)) total_losing_points += points user.pay_debt(points) db_bets[username].profit = -points self.bot.whisper(user.username, 'You bet {} points on the wrong outcome, so you lost it all. :('.format( points)) for obj in losers: user, points = obj log.debug('{} lost {} points!'.format(user, points)) for obj in winners: points_reward = 0 user, points = obj if points == 0: # If you didn't bet any points, you don't get a part of the cut. HandlerManager.trigger('on_user_win_hs_bet', user, points_reward) continue pot_cut = points / total_winning_points points_reward = int(pot_cut * total_losing_points) db_bets[user.username].profit = points_reward user.points += points_reward HandlerManager.trigger('on_user_win_hs_bet', user, points_reward) self.bot.whisper(user.username, 'You bet {} points on the right outcome, that rewards you with a profit of {} points! (Your bet was {:.2f}% of the total pool)'.format( points, points_reward, pot_cut * 100)) """ self.bot.me('{} bet {} points, and made a profit of {} points by correctly betting on the HS game!'.format( user.username_raw, points, points_reward)) """ with DBManager.create_session_scope() as db_session: for username in db_bets: bet = db_bets[username] db_session.add(bet) self.bot.me('A new game has begun! Vote with !hsbet win/lose POINTS') self.bets = {} self.last_game_id = latest_game['id'] self.last_game_start = datetime.datetime.now() + datetime.timedelta(seconds=self.settings['time_until_bet_closes']) payload = { 'time_left': self.settings['time_until_bet_closes'], 'win': 0, 'loss': 0, } self.bot.websocket_manager.emit('hsbet_new_game', data=payload) # stats about the game ratio = 0.0 try: ratio = (total_losing_points / total_winning_points) * 100.0 except: pass self.bot.me('The game ended as a {result}. {points_bet[win]} points bet on win, {points_bet[loss]} points bet on loss. Winners can expect a {ratio:.2f}% return on their bet points.'.format(ratio=ratio, result=latest_game['result'], points_bet=points_bet)) redis = RedisManager.get() redis.set('{streamer}:last_hsbet_game_id'.format(streamer=StreamHelper.get_streamer()), self.last_game_id) redis.set('{streamer}:last_hsbet_game_start'.format(streamer=StreamHelper.get_streamer()), self.last_game_start.timestamp())
def poll_trackobot_stage2(self, game_data): latest_game = game_data['history'][0] if latest_game['id'] != self.last_game_id: winners = [] losers = [] total_winning_points = 0 total_losing_points = 0 points_bet = { 'win': 0, 'loss': 0, } bet_game_id = None with DBManager.create_session_scope() as db_session: bet_game = HSBetGame(latest_game['id'], latest_game['result']) db_session.add(bet_game) db_session.flush() bet_game_id = bet_game.id db_bets = {} for username in self.bets: bet_for_win, points = self.bets[username] """ self.bot.me('{} bet {} points on the last game to end up as a {}'.format( username, points, 'win' if bet_for_win else 'loss')) """ user = self.bot.users[username] correct_bet = (latest_game['result'] == 'win' and bet_for_win is True) or (latest_game['result'] == 'loss' and bet_for_win is False) points_bet['win' if bet_for_win else 'loss'] += points db_bets[username] = HSBetBet(bet_game_id, user.id, 'win' if bet_for_win else 'loss', points, 0) if correct_bet: winners.append((user, points)) total_winning_points += points user.remove_debt(points) else: losers.append((user, points)) total_losing_points += points user.pay_debt(points) db_bets[username].profit = -points self.bot.whisper( user.username, 'You bet {} points on the wrong outcome, so you lost it all. :(' .format(points)) for obj in losers: user, points = obj log.debug('{} lost {} points!'.format(user, points)) for obj in winners: points_reward = 0 user, points = obj if points == 0: # If you didn't bet any points, you don't get a part of the cut. HandlerManager.trigger('on_user_win_hs_bet', user, points_reward) continue pot_cut = points / total_winning_points points_reward = int(pot_cut * total_losing_points) db_bets[user.username].profit = points_reward user.points += points_reward HandlerManager.trigger('on_user_win_hs_bet', user, points_reward) self.bot.whisper( user.username, 'You bet {} points on the right outcome, that rewards you with a profit of {} points! (Your bet was {:.2f}% of the total pool)' .format(points, points_reward, pot_cut * 100)) """ self.bot.me('{} bet {} points, and made a profit of {} points by correctly betting on the HS game!'.format( user.username_raw, points, points_reward)) """ with DBManager.create_session_scope() as db_session: for username in db_bets: bet = db_bets[username] db_session.add(bet) self.bot.me( 'A new game has begun! Vote with !hsbet win/lose POINTS') self.bets = {} self.last_game_id = latest_game['id'] self.last_game_start = datetime.datetime.now( ) + datetime.timedelta( seconds=self.settings['time_until_bet_closes']) payload = { 'time_left': self.settings['time_until_bet_closes'], 'win': 0, 'loss': 0, } self.bot.websocket_manager.emit('hsbet_new_game', data=payload) # stats about the game ratio = 0.0 try: ratio = (total_losing_points / total_winning_points) * 100.0 except: pass self.bot.me( 'The game ended as a {result}. {points_bet[win]} points bet on win, {points_bet[loss]} points bet on loss. Winners can expect a {ratio:.2f}% return on their bet points.' .format(ratio=ratio, result=latest_game['result'], points_bet=points_bet)) redis = RedisManager.get() redis.set( '{streamer}:last_hsbet_game_id'.format( streamer=StreamHelper.get_streamer()), self.last_game_id) redis.set( '{streamer}:last_hsbet_game_start'.format( streamer=StreamHelper.get_streamer()), self.last_game_start.timestamp())