コード例 #1
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}')
コード例 #2
0
    async def remove_signup(member: Member, signupmessage, mobile):
        signup: db.Signup = db.Signup.query().filter_by(
            signup_id=signupmessage.id, player_id=member.id,
            mobile=mobile).first()

        if signup:
            signup.delete()

            await member.send(
                f'You have been removed from the list of players for next week\'s **{"mobile" if mobile else "steam"}**'
                f' games. You can sign back up by reacting to the signup message again.'
            )
            logger.debug(
                f'{member.name} removed from signups for the {"steam" if not mobile else "mobile"} '
                f'matchups of signup id {signupmessage.id}')
コード例 #3
0
    async def signups(self, ctx: commands.Context, ping: str = None):
        """
        Open or close signups manually.
        
        - [p]open_signups - open signups
        - [p]close_signups - close signups
        """

        if ctx.invoked_with == 'close_signups':

            # Make sure there are actually signups open.
            if not db.SignupMessage.query().filter_by(is_open=True).first():
                return await ctx.send(
                    'Signups are not open, and therefore cannot be closed.')

            logger.debug(
                f'Close signups triggered manually by {ctx.author.name}/{ctx.author.id}'
            )

            await self.close_signups(manual=True)
            return await ctx.send(
                f'Signups manually closed by {ctx.author.mention}')

        elif ctx.invoked_with == 'open_signups':

            # Make sure there aren't signups already open
            if db.SignupMessage.query().filter_by(is_open=True).first():
                return await ctx.send(
                    'Signups are already open, and more cannot be opened.')

            logger.debug(
                f'Open signups triggered manually by {ctx.author.name}/{ctx.author.id}'
            )

            await self.open_signups(manual=True, ping=ping)
            return await ctx.send(
                f'Signups manually opened by {ctx.author.mention}')
        else:
            await ctx.send(f'Run `{ctx.prefix}help signups` please.')
コード例 #4
0
    async def check_rungs(self,
                          ctx: commands.Context,
                          member: discord.Member = None):

        await ctx.send(
            'Checking that all rung changes have been applied correctly.')
        logger.debug('Checking rungs...')

        done = 0

        for player in (db.Player.query() if not member else
                       db.Player.query().filter_by(id=member.id)):
            player: db.Player

            games = db.Game.query().filter(
                db.or_(db.Game.host_id == player.id, db.Game.away_id ==
                       player.id) & db.Game.is_confirmed.is_(True)).order_by(
                           db.Game.win_claimed_ts.asc())

            player_rung = 1

            msg = f'{player.name}: {player_rung}'
            for game in games:
                game: db.Game
                logger.debug(
                    f'{player.name} ({player.id}) --- game {game.id} confirmed status {game.is_confirmed}, '
                    f'with {game.host_step_change} for the host, and {game.away_step_change} for away.'
                )
                if game.host_id == player.id:
                    n = game.host_step_change
                else:
                    n = game.away_step_change

                msg += f'+{n}'
                player_rung += n

                if player_rung < 1:
                    player_rung = 1
                elif player_rung > 12:
                    player_rung = 12

            await ctx.send(
                f'Rung calculation for {msg} = {player_rung}. Actual rung = {player.rung}'
            )

            if is_bad := (player_rung != player.rung):
                await ctx.send(
                    f'Player {player.name} ({player.id}) had rung {player.rung} in the database, '
                    f'but should have had {player_rung}. Fixing...')

                db.GameLog.write(
                    f'{db.GameLog.member_string(player)} - rung changed to {player_rung} from {player.rung} as part of '
                    f'a rung check.')

                player.rung = player_rung

                player.save()

                if (m := ctx.guild.get_member(player.id)) and is_bad:
                    self.bot.loop.create_task(settings.fix_roles(m))

                done += 1
コード例 #5
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()
コード例 #6
0
class League(commands.Cog):
    def __init__(self, bot, conf):
        self.bot = bot
        self.conf = conf

        self.message_id = None

        self.relevant_emojis = [
            settings.emojis.blue_check_mark, settings.emojis.white_check_mark
        ]

        self.signup_loop.start()

    def cog_unload(self):
        self.signup_loop.cancel()

    @commands.Cog.listener()
    async def on_raw_reaction_add(self, payload: RawReactionActionEvent):
        signupmessage: db.SignupMessage = db.SignupMessage.query().filter(
            db.SignupMessage.is_open.is_(True)).first()

        if self.message_id != getattr(signupmessage, 'message_id',
                                      self.message_id):
            self.message_id = signupmessage.message_id

        if payload.message_id != self.message_id:
            return

        if payload.user_id == self.bot.user.id:
            return

        channel = payload.member.guild.get_channel(payload.channel_id)
        message: Message = await channel.fetch_message(payload.message_id)
        emoji = self.bot.get_emoji(
            payload.emoji.id) if payload.emoji.id else payload.emoji

        if emoji.name not in self.relevant_emojis:
            await message.remove_reaction(emoji, payload.member)

        if emoji.name == settings.emojis.white_check_mark:
            await self.add_signup(payload.member,
                                  signupmessage,
                                  message,
                                  emoji,
                                  mobile=True)
        elif emoji.name == settings.emojis.blue_check_mark:
            await self.add_signup(payload.member,
                                  signupmessage,
                                  message,
                                  emoji,
                                  mobile=False)

    @commands.Cog.listener()
    async def on_raw_reaction_remove(self, payload: RawReactionActionEvent):
        msg: db.SignupMessage = db.SignupMessage.query().filter(
            db.SignupMessage.is_open.is_(True)).first()

        if self.message_id != getattr(msg, 'message_id', self.message_id):
            self.message_id = msg.message_id

        if payload.message_id != self.message_id:
            return

        if payload.user_id == self.bot.user.id:
            return

        if payload.emoji.name not in self.relevant_emojis:
            return

        member = self.bot.get_user(payload.user_id)

        if payload.emoji.name == settings.emojis.white_check_mark:
            await self.remove_signup(member, msg, mobile=True)
        elif payload.emoji.name == settings.emojis.blue_check_mark:
            await self.remove_signup(member, msg, mobile=False)

    @staticmethod
    async def add_signup(member: Member, signupmessage, message: Message,
                         emoji, mobile):

        p: db.Player = db.Player.get(member.id)
        if not p:
            await message.remove_reaction(emoji, member)
            return await member.send(
                f'You must be registered with me to signup for matches.')
        elif p.ign is None and mobile:
            await message.remove_reaction(emoji, member)
            return await member.send(f'You have not set your mobile name.')
        elif p.steam_name is None and not mobile:
            await message.remove_reaction(emoji, member)
            return await member.send(f'You have not set your steam name.')

        if (s := db.Signup.query().filter(
                db.Signup.player_id == member.id).first()) is not None:
            await message.remove_reaction(emoji, member)
            platform_str = 'mobile' if s.mobile else 'steam'
            return await member.send(
                f'You are already signed up for {platform_str}. You cannot signup for both platforms.'
            )

        signup = db.Signup(signup_id=signupmessage.id,
                           player_id=member.id,
                           mobile=mobile)

        signup.save()

        await member.send(
            f'You are now signed up for next week\'s **{"mobile" if mobile else "steam"}** games. '
            f'If you would like to remove yourself, just remove the reaction you just placed.'
        )
        logger.debug(
            f'{member.name} signed up for {"steam" if not mobile else "mobile"} matchups, signupmessage id '
            f'{signupmessage.id}')