Example #1
0
    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
Example #2
0
    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')
Example #3
0
    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
Example #4
0
    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)
Example #5
0
    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)
Example #6
0
    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)
Example #7
0
 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))
Example #8
0
    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)
Example #9
0
    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)
Example #10
0
 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)
Example #11
0
    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)
Example #12
0
    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)
Example #13
0
    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)
Example #14
0
    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)
Example #15
0
    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')
Example #16
0
    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)
Example #17
0
    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)
Example #18
0
    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)
Example #19
0
    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)
Example #20
0
    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)
Example #21
0
    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)
Example #22
0
    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)
Example #23
0
    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)
Example #24
0
    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)
Example #25
0
    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)
Example #26
0
    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)
Example #27
0
    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())
Example #28
0
    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())