Beispiel #1
0
    async def delete(self,
                     ctx: commands.Context,
                     game: db.Game,
                     *,
                     args: str = None):
        """*Owner*: delete an in progress/yet to be started game"""
        game: db.Game
        args = args or ''

        if not game:
            return await ctx.send('Game ID not provided.')

        if game.is_complete and '-override' not in args.lower():
            return await ctx.send(f'Please unwin the game first.')

        db.GameLog.write(
            game_id=game.id,
            message=
            f'{db.GameLog.member_string(ctx.author)} manually deleted the game.'
        )

        db.delete(game)

        logger.info(f'Game {game.id} deleted.')

        await ctx.send(f'Game {game.id} deleted.')
Beispiel #2
0
    async def close_signups(self, manual=False):
        signup_message: db.SignupMessage = db.SignupMessage.query().filter_by(
            is_open=True).first()
        channel: TextChannel = self.bot.get_channel(
            int(self.conf['channels']['announcements']))
        logger.debug(signup_message.message_id)
        msg = await channel.fetch_message(signup_message.message_id)

        await msg.edit(content=settings.messages.SIGNUPS_CLOSED_MESSAGE.format(
            msg.content))
        await msg.clear_reactions()

        signup_message.is_open = False
        signup_message.save()
        self.message_id = None

        logger.info(
            f'Signups have been {"automatically " if not manual else ""}'
            f'closed.')

        if not manual:
            try:
                await self.create_matchups()
            except Exception as e:
                await settings.discord_channel_log(
                    f'Unhandled error (notifying <@{settings.owner_id}>): {e}')
Beispiel #3
0
    async def open_signups(self, manual=False, ping: str = None):
        if ping == 'noping':
            ping = ''
        else:
            ping = ping or '@everyone'
        # Get the channel
        announcements: TextChannel = self.bot.get_channel(
            int(self.conf['channels']['announcements']))

        # Send the message, add the reactions
        msg = await announcements.send(
            settings.messages.SIGNUP_MESSAGE.format(
                ping if ping != '' else 'everyone'),
            allowed_mentions=AllowedMentions(everyone=True))
        await msg.add_reaction(settings.emojis.white_check_mark)
        await msg.add_reaction(settings.emojis.blue_check_mark)

        day = settings.next_day(0)

        db.SignupMessage(message_id=msg.id, is_open=True, close_at=day).save()

        self.message_id = msg.id

        logger.info(
            f'Signups have been {"automatically " if not manual else ""}'
            f'opened. They will close at {day}, UTC time.')
Beispiel #4
0
 async def remove_random(member_list: List, platform):
     pl = member_list.pop(random.randint(0, len(member_list) - 1))
     m: User = self.bot.get_user(pl.id)
     try:
         await m.send(
             f'You have been randomly removed from the {platform} matchups for this week\'s PolyLadder '
             f'games. Sorry!')
     except discord.Forbidden:
         pass
     logger.info(
         f'{m.name}#{m.discriminator}/{m.id} kicked from {platform} matches.'
     )
Beispiel #5
0
 async def signup_loop(self):
     signupmessage = db.SignupMessage.query().filter_by(
         is_open=True).first()
     if signupmessage:
         # Signups are open. Check if we can close them.
         if signupmessage.close_at < datetime.datetime.utcnow():
             logger.info('Closing signups.')
             await self.close_signups()
     else:
         if datetime.datetime.utcnow().weekday() == 5:
             signupmessages = db.SignupMessage.query().filter(
                 db.SignupMessage.close_at > datetime.datetime.utcnow()
             ).count()
             if not signupmessages:
                 # It's saturday, open the signups
                 logger.info('Opening signups')
                 await self.open_signups()
             else:
                 logger.info(
                     'Not opening signups, as there is a signupmessage object with a closing date later than now.'
                 )
Beispiel #6
0
 async def quit(self, ctx: commands.Context):
     """Stop the bot's process."""
     db.session.close()
     logger.info('Shutting down.')
     await ctx.send('Shutting down...')
     await self.bot.close()
Beispiel #7
0
 async def on_ready(self):
     s = f'Online. Logged in as {self.bot.user.name}/{self.bot.user.id}, PID {os.getpid()}'
     print(s)
     logger.info(s)
Beispiel #8
0
    async def create_matchups(self):
        logger.debug(f'Generating matchups')

        mobile_signups = db.Signup.query().filter(
            db.Signup.mobile.is_(True), db.Signup.player_id.isnot(None))
        steam_signups = db.Signup.query().filter(
            db.Signup.mobile.is_(False), db.Signup.player_id.isnot(None))

        if not mobile_signups.count() and not steam_signups.count():
            return logger.info('Not creating matchups - no one has signed up.')
        elif not mobile_signups.count():
            logger.info('Not creating matches for mobile - no signups')
        elif not steam_signups.count():
            logger.info('Not creating matches for steam - no signups')

        # First of all, get all the players.
        mobile_players = list(
            filter(lambda x: x.user is not None,
                   [x.player for x in mobile_signups.all()]))
        steam_players = list(
            filter(lambda x: x.user is not None,
                   [x.player for x in steam_signups.all()]))

        mobile_players_even = len(mobile_players) % 2 == 0
        steam_players_even = len(steam_players) % 2 == 0

        if not mobile_players_even or not steam_players_even:

            async def remove_random(member_list: List, platform):
                pl = member_list.pop(random.randint(0, len(member_list) - 1))
                m: User = self.bot.get_user(pl.id)
                try:
                    await m.send(
                        f'You have been randomly removed from the {platform} matchups for this week\'s PolyLadder '
                        f'games. Sorry!')
                except discord.Forbidden:
                    pass
                logger.info(
                    f'{m.name}#{m.discriminator}/{m.id} kicked from {platform} matches.'
                )

            # We don't have an even number of players. Kick one of them.
            duplicates = set(mobile_players) & set(steam_players)
            if not duplicates:
                # No players have signed up for both steam and mobile
                pass
                if not mobile_players_even:
                    await remove_random(mobile_players, 'mobile')
                if not steam_players_even:
                    await remove_random(steam_players, 'steam')
            else:
                # There are duplicate players - player's who have signed up for both platforms.
                if len(duplicates) > 1:
                    if not mobile_players_even:
                        # mobile
                        p = duplicates.pop()
                        member = self.bot.get_user(p.id)
                        await member.send(
                            'You have been removed from mobile matchups for this week\'s PolyLadder matches, '
                            'due to player limits and to you signing up for both steam and mobile.'
                        )
                        mobile_players.remove(p)
                        logger.info(
                            f'{member.name}#{member.discriminator}/{member.id} kicked from mobile matches.'
                        )
                    if not steam_players_even:
                        # steam
                        p = duplicates.pop()
                        member = self.bot.get_user(p.id)
                        await member.send(
                            'You have been removed from steam matchups for this week\'s PolyLadder matches, '
                            'due to player limits and to you signing up for both steam and mobile.'
                        )
                        steam_players.remove(p)
                        logger.info(
                            f'{member.name}#{member.discriminator}/{member.id} kicked from steam matches.'
                        )
                else:
                    if not mobile_players_even:
                        # Only 1 duplicate
                        p = duplicates.pop()
                        member = self.bot.get_user(p.id)
                        await member.send(
                            'You have been removed from mobile matchups for this week\'s PolyLadder matches, '
                            'due to player limits and to you signing up for both steam and mobile.'
                        )
                        mobile_players.remove([p, member])
                        logger.info(
                            f'{member.name}#{member.discriminator}/{member.id} kicked from mobile matches.'
                        )
                    if not steam_players_even:
                        await remove_random(steam_players, 'steam')

        # Load the discord member objects
        mobile: List[List[db.Player, Member]] = list()
        steam: List[List[db.Player, Member]] = list()

        for player in mobile_players:
            user = self.bot.get_user(player.id)
            if player is None or user is None:
                continue
            mobile.append([player, user])
        for player in steam_players:
            user = self.bot.get_user(player.id)
            if player is None or user is None:
                continue
            steam.append([player, user])

        # Now that we have an even number of players in each tier, start generating matchups
        # Create the tier objects
        mobile_tiers: Dict[int, list] = {x: [] for x in range(1, 13)}
        steam_tiers: Dict[int, list] = {x: [] for x in range(1, 13)}

        # Place people in their initial rungs.
        for player, member in mobile:
            mobile_tiers[player.rung].append((player, member))

        for player, member in steam:
            steam_tiers[player.rung].append((player, member))

        # Iterate through each rung, from the bottom up, and move people up if there's an odd
        # number of people in that rung.

        for tiers in [mobile_tiers, steam_tiers]:
            for r in range(1, 13):
                rung = tiers[r]
                if len(rung) % 2 == 0:
                    continue
                rung.sort(key=lambda x: x[0].wins().count())
                tiers[r + 1].append(rung.pop(0))

        # Rungs are sorted, create the matchups
        mobile_games = self.make_games(mobile_tiers, True)
        steam_games = self.make_games(steam_tiers, False)

        platform_msg_source = Template("""
**{{platform}}**:\n
{% for tier, games in gms.items() if games is not none %}
Tier {{tier}} matchups:
{% for game in games %}
<@{{game.host.id}}> ({{game.host_step}}) hosts vs <@{{game.away_id}}> ({{game.away_step}}) - Game {{game.id}}
{% endfor %}


{% endfor %}
""")

        if mobile_games or steam_games:
            chan: TextChannel = self.bot.get_channel(
                int(self.conf['channels']['matchups']))
            tribe_tier = random.randint(1, 3)
            await chan.send(
                f'A new week of games has been generated!\nWe will be using **Level {tribe_tier}** tribes this week.\n'
                f'Here are your games:')
            if mobile_games:
                message = f'\n\n{platform_msg_source.render(gms=mobile_games, platform="Mobile")}'

                for block in settings.split_string(message):
                    await chan.send(block)
            if steam_games:
                message = f'\n\n{platform_msg_source.render(gms=steam_games, platform="Steam")}'
                for block in settings.split_string(message):
                    await chan.send(block)
            await chan.send(
                '\n\nPlease create your games as soon as possible. '
                'If your game has not $started in the next 72 hours (3 days), '
                'the away player will become the host. If the new host does not '
                'start the game 72 hours after that, the game will be cancelled.'
            )
            mobile_signups.delete()
            steam_signups.delete()

            db.save()