Exemplo n.º 1
0
    async def pingall(self, ctx, *, message: str = None):
        """ Ping everyone in all of your incomplete games

        Not useable by all players.

         **Examples**
        `[p]pingall My phone died and I will make all turns tomorrow`
        Send a message to everyone in all of your incomplete games
        `[p]pingall @Glouc3stershire Glouc is in Tahiti and will play again tomorrow`
        *Staff:* Send a message to everyone in another player's games

        """
        if not message:
            return await ctx.send(f'Message is required.')

        m = utilities.string_to_user_id(message.split()[0])

        if m:
            logger.debug('Third party use of pingall')
            # Staff member using command on third party
            if settings.get_user_level(ctx) <= 3:
                logger.debug('insufficient user level')
                return await ctx.send(f'You do not have permission to use this command on another player\'s games.')
            message = ' '.join(message.split()[1:])  # remove @Mention first word of message
            target = str(m)
        else:
            logger.debug('first party usage of pingall')
            # Play using command on their own games
            if settings.get_user_level(ctx) <= 2:
                logger.debug('insufficient user level')
                return await ctx.send(f'You do not have permission to use this command. You can ask a server staff member to use this command on your games for you.')
            target = str(ctx.author.id)

        logger.debug(f'pingall target is {target}')

        try:
            player_match = models.Player.get_or_except(player_string=target, guild_id=ctx.guild.id)
        except exceptions.NoSingleMatch:
            return await ctx.send(f'User <@{target}> is not a registered ELO player.')

        game_list = models.Game.search(player_filter=[player_match], status_filter=2, guild_id=ctx.guild.id)
        logger.debug(f'{len(game_list)} incomplete games for target')

        list_of_players = []
        for g in game_list:
            list_of_players += [f'<@{l.player.discord_member.discord_id}>' for l in g.lineup]

        list_of_players = list(set(list_of_players))
        logger.debug(f'{len(list_of_players)} unique opponents for target')
        clean_message = utilities.escape_role_mentions(message)
        if len(list_of_players) > 100:
            await ctx.send(f'*Warning:* More than 100 unique players are addressed. Only the first 100 will be mentioned.')
        await ctx.send(f'Message to all players in unfinished games for <@{target}>: *{clean_message}*')

        recipient_message = f'Message recipients: {" ".join(list_of_players[:100])}'
        return await ctx.send(recipient_message[:2000])
Exemplo n.º 2
0
    async def ping(self, ctx, *, args=None):
        """ Ping everyone in one of your games with a message

         **Examples**
        `[p]ping 100 I won't be able to take my turn today` - Send a message to everyone in game 100
        `[p]ping This game is amazing!` - You can omit the game ID if you send the command from a game-specific channel

        See `[p]help pingall` for a command to ping ALL incomplete games simultaneously.

        """

        usage = (f'**Example usage:** `{ctx.prefix}ping 100 Here\'s a nice note for everyone in game 100.`\n'
                    'You can also omit the game ID if you use the command from a game-specific channel.')
        if not args:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(usage)

        if settings.is_mod(ctx):
            ctx.command.reset_cooldown(ctx)

        args = args.split()
        try:
            game_id = int(args[0])
            message = ' '.join(args[1:])
        except ValueError:
            game_id = None
            message = ' '.join(args)

        inferred_game = None
        if not game_id:
            try:
                inferred_game = models.Game.by_channel_id(chan_id=ctx.message.channel.id)
            except exceptions.TooManyMatches:
                logger.error(f'More than one game with matching channel {ctx.message.channel.id}')
                return await ctx.send('Error looking up game based on current channel - please contact the bot owner.')
            except exceptions.NoMatches:
                ctx.command.reset_cooldown(ctx)
                return await ctx.send(f'Game ID was not included. {usage}')
            logger.debug(f'Inferring game {inferred_game.id} from ping command used in channel {ctx.message.channel.id}')

        if not message:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(f'Message was not included. {usage}')

        if ctx.message.attachments:
            attachment_urls = '\n'.join([attachment.url for attachment in ctx.message.attachments])
            message += f'\n{attachment_urls}'

        message = utilities.escape_role_mentions(message)

        if inferred_game:
            game = inferred_game
        else:
            game = await PolyGame().convert(ctx, int(game_id), allow_cross_guild=True)

        if not game.player(discord_id=ctx.author.id) and not settings.is_staff(ctx):
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(f'You are not a player in game {game.id}')

        permitted_channels = settings.guild_setting(game.guild_id, 'bot_channels')
        permitted_channels_private = []
        if settings.guild_setting(game.guild_id, 'game_channel_categories'):
            if game.game_chan:
                permitted_channels = [game.game_chan] + permitted_channels
            if game.smallest_team() > 1:
                permitted_channels_private = [gs.team_chan for gs in game.gamesides]
                permitted_channels = permitted_channels_private + permitted_channels
                # allows ping command to be used in private team channels - only if there is no solo squad in the game which would mean they cant see the message
                # this also adjusts where the @Mention is placed (sent to all team channels instead of simply in the ctx.channel)
            elif ctx.channel.id in [gs.team_chan for gs in game.gamesides]:
                channel_tags = [f'<#{chan_id}>' for chan_id in permitted_channels]
                ctx.command.reset_cooldown(ctx)
                return await ctx.send(f'This command cannot be used in this channel because there is at least one solo player without access to a team channel.\n'
                    f'Permitted channels: {" ".join(channel_tags)}')

        if ctx.channel.id not in permitted_channels and ctx.channel.id not in settings.guild_setting(game.guild_id, 'bot_channels_private'):
            channel_tags = [f'<#{chan_id}>' for chan_id in permitted_channels]
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(f'This command can not be used in this channel. Permitted channels: {" ".join(channel_tags)}')

        player_mentions = [f'<@{l.player.discord_member.discord_id}>' for l in game.lineup]
        full_message = f'Message from {ctx.author.mention} (**{ctx.author.name}**) regarding game {game.id} **{game.name}**:\n*{message}*'

        if ctx.channel.id in permitted_channels_private:
            logger.debug(f'Ping triggered in private channel {ctx.channel.id}')
            await game.update_squad_channels(self.bot.guilds, game.guild_id, message=f'{full_message}\n{" ".join(player_mentions)}')
        else:
            logger.debug(f'Ping triggered in non-private channel {ctx.channel.id}')
            await game.update_squad_channels(self.bot.guilds, ctx.guild.id, message=full_message)
            await ctx.send(f'{full_message}\n{" ".join(player_mentions)}')
Exemplo n.º 3
0
    async def ping(self, ctx, game_id: int = None, *, message: str = None):
        """ Ping everyone in one of your games with a message

         **Examples**
        `[p]ping 100 I won't be able to take my turn today` - Send a message to everyone in game 100

        See `[p]help pingall` for a command to ping ALL incomplete games simultaneously.

        """
        if not game_id:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                f'Game ID was not included. Example usage: `{ctx.prefix}ping 100 Here\'s a nice note for everyone in game 100.`'
            )

        if not message:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                f'Message was not included. Example usage: `{ctx.prefix}ping 100 Here\'s a nice note`'
            )

        if ctx.message.attachments:
            attachment_urls = '\n'.join(
                [attachment.url for attachment in ctx.message.attachments])
            message += f'\n{attachment_urls}'

        # message = discord.utils.escape_mentions(message)  # to prevent @everyone vulnerability
        message = utilities.escape_role_mentions(message)

        try:
            game = models.Game.get(id=int(game_id))
        except ValueError:
            return await ctx.send(f'Invalid game ID "{game_id}".')
        except peewee.DoesNotExist:
            return await ctx.send(f'Game with ID {game_id} cannot be found.')

        if not game.player(
                discord_id=ctx.author.id) and not settings.is_staff(ctx):
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(f'You are not a player in game {game.id}')

        permitted_channels = settings.guild_setting(game.guild_id,
                                                    'bot_channels')
        permitted_channels_private = []
        if settings.guild_setting(game.guild_id, 'game_channel_categories'):
            if game.game_chan:
                permitted_channels = [game.game_chan] + permitted_channels
            if game.smallest_team() > 1:
                permitted_channels_private = [
                    gs.team_chan for gs in game.gamesides
                ]
                permitted_channels = permitted_channels_private + permitted_channels
                # allows ping command to be used in private team channels - only if there is no solo squad in the game which would mean they cant see the message
                # this also adjusts where the @Mention is placed (sent to all team channels instead of simply in the ctx.channel)
            elif ctx.channel.id in [gs.team_chan for gs in game.gamesides]:
                channel_tags = [
                    f'<#{chan_id}>' for chan_id in permitted_channels
                ]
                ctx.command.reset_cooldown(ctx)
                return await ctx.send(
                    f'This command cannot be used in this channel because there is at least one solo player without access to a team channel.\n'
                    f'Permitted channels: {" ".join(channel_tags)}')

        if ctx.channel.id not in permitted_channels and ctx.channel.id not in settings.guild_setting(
                game.guild_id, 'bot_channels_private'):
            channel_tags = [f'<#{chan_id}>' for chan_id in permitted_channels]
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(
                f'This command can not be used in this channel. Permitted channels: {" ".join(channel_tags)}'
            )

        player_mentions = [
            f'<@{l.player.discord_member.discord_id}>' for l in game.lineup
        ]
        full_message = f'Message from {ctx.author.mention} (**{ctx.author.name}**) regarding game {game.id} **{game.name}**:\n*{message}*'

        if ctx.channel.id in permitted_channels_private:
            logger.debug(f'Ping triggered in private channel {ctx.channel.id}')
            await game.update_squad_channels(
                self.bot.guilds,
                game.guild_id,
                message=f'{full_message}\n{" ".join(player_mentions)}')
        else:
            logger.debug(
                f'Ping triggered in non-private channel {ctx.channel.id}')
            await game.update_squad_channels(self.bot.guilds,
                                             ctx.guild.id,
                                             message=full_message)
            await ctx.send(f'{full_message}\n{" ".join(player_mentions)}')
Exemplo n.º 4
0
    async def pingall(self, ctx, *message):
        """ Ping everyone in all of your incomplete games

        Not useable by all players.

         **Examples**
        `[p]pingall My phone died and I will make all turns tomorrow`
        Send a message to everyone in all of your incomplete games
        `[p]pingall @Glouc3stershire Glouc is in Tahiti and will play again tomorrow`
        *Staff:* Send a message to everyone in another player's games

        """

        if not message:
            return await ctx.send(f'Message is required.')

        m = re.match(r"<@[!]?([0-9]{17,21})>", message[0])

        if m:
            # Staff member using command on third party
            if settings.get_user_level(ctx) <= 4:
                return await ctx.send(
                    f'You do not have permission to use this command on another player\'s games.'
                )
            message = message[1:]
            target = m[1]
        else:
            # Play using command on their own games
            if settings.get_user_level(ctx) <= 3:
                return await ctx.send(
                    f'You do not have permission to use this command. You can ask a server staff member to use this command on your games for you.'
                )
            target = str(ctx.author.id)

        try:
            player_match = models.Player.get_or_except(player_string=target,
                                                       guild_id=ctx.guild.id)
        except exceptions.NoSingleMatch:
            return await ctx.send(
                f'User <@{target}> is not a registered ELO player.')

        game_list = models.Game.search(player_filter=[player_match],
                                       status_filter=2,
                                       guild_id=ctx.guild.id)

        list_of_players = []
        for g in game_list:
            list_of_players += [
                f'<@{l.player.discord_member.discord_id}>' for l in g.lineup
            ]

        list_of_players = list(set(list_of_players))
        clean_message = utilities.escape_role_mentions(' '.join(message))
        if len(list_of_players) > 100:
            await ctx.send(
                f'*Warning:* More than 100 unique players are addressed. Only the first 100 will be mentioned.'
            )
        await ctx.send(
            f'Message to all players in unfinished games for <@{target}>: *{clean_message}*'
        )

        return await ctx.send(
            f'Message recipients: {" ".join(list_of_players[:100])}')
Exemplo n.º 5
0
    async def player(self, ctx, *args):
        """See your own player card or the card of another player

        **Examples**
        `[p]player` - See your own player card
        `[p]player Nelluk` - See Nelluk's card
        """

        args_list = list(args)
        if len(args_list) == 0:
            # Player looking for info on themselves
            args_list.append(f'<@{ctx.author.id}>')

        # Otherwise look for a player matching whatever they entered
        player_mention = ' '.join(args_list)
        player_mention_safe = utilities.escape_role_mentions(player_mention)

        player_results = Player.string_matches(player_string=player_mention)
        if len(player_results) > 1:
            p_names = [p.name for p in player_results]
            p_names_str = '**, **'.join(p_names[:10])
            return await ctx.send(
                f'Found {len(player_results)} players matching *{player_mention_safe}*. Be more specific or use an @Mention.\nFound: **{p_names_str}**'
            )
        elif len(player_results) == 0:
            # No Player matches - check for guild membership
            guild_matches = await utilities.get_guild_member(
                ctx, player_mention)
            if len(guild_matches) > 1:
                p_names = [p.display_name for p in guild_matches]
                p_names_str = '**, **'.join(p_names[:10])
                return await ctx.send(
                    f'There is more than one member found with name *{player_mention_safe}*. Be more specific or use an @Mention.\nFound: **{p_names_str}**'
                )
            if len(guild_matches) == 0:
                return await ctx.send(
                    f'Could not find *{player_mention_safe}* by Discord name or ID.'
                )

            return await ctx.send(
                f'*{guild_matches[0].display_name}* was found but has no game history.'
            )
        else:
            player = player_results[0]

        def async_create_player_embed():
            utilities.connect()
            wins, losses = player.get_record()
            rank, lb_length = player.leaderboard_rank(settings.date_cutoff)

            if rank is None:
                rank_str = 'Unranked'
            else:
                rank_str = f'{rank} of {lb_length}'

            max_str = f'(Max: {player.elo_max})\n' if player.elo_max > player.elo else ''
            results_str = f'ELO: {player.elo}\n{max_str}W\u00A0{wins}\u00A0/\u00A0L\u00A0{losses}'

            embed = discord.Embed(
                description=f'__Player card for <@{player.discord_id}>__')
            embed.add_field(name='**Results**', value=results_str)
            embed.add_field(name='**Ranking**', value=rank_str)

            guild_member = ctx.guild.get_member(player.discord_id)
            if guild_member:
                embed.set_thumbnail(url=guild_member.avatar_url_as(size=512))

            return embed

        embed = await self.bot.loop.run_in_executor(None,
                                                    async_create_player_embed)
        await ctx.send(embed=embed)
Exemplo n.º 6
0
    async def ping(self, ctx, *, args=''):
        """ Ping everyone in one of your games with a message

         **Examples**
        `[p]ping 100 I won't be able to take my turn today` - Send a message to everyone in game 100
        `[p]ping This game is amazing!` - You can omit the game ID if you send the command from a game-specific channel

        See `[p]help pingall` for a command to ping ALL incomplete games simultaneously.

        """

        usage = (
            f'**Example usage:** `{ctx.prefix}ping 100 Here\'s a nice note for everyone in game 100.`\n'
            'You can also omit the game ID if you use the command from a game-specific channel.'
        )

        if ctx.message.attachments:
            attachment_urls = '\n'.join(
                [attachment.url for attachment in ctx.message.attachments])
            args += f'\n{attachment_urls}'

        if not args:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(usage)

        if settings.is_mod(ctx.author):
            ctx.command.reset_cooldown(ctx)

        args = args.split()
        try:
            game_id = int(args[0])
            message = ' '.join(args[1:])
        except ValueError:
            game_id = None
            message = ' '.join(args)

        # TODO:  should prioritize inferred game above an integer. currently something like '$ping 1 city island plz restart'
        # will try to ping game ID #1 even if done within a game channel

        inferred_game = None
        if not game_id:
            try:
                inferred_game = models.Game.by_channel_id(
                    chan_id=ctx.message.channel.id)
            except exceptions.TooManyMatches:
                logger.error(
                    f'More than one game with matching channel {ctx.message.channel.id}'
                )
                return await ctx.send(
                    'Error looking up game based on current channel - please contact the bot owner.'
                )
            except exceptions.NoMatches:
                ctx.command.reset_cooldown(ctx)
                logger.debug('Could not infer game from current channel.')
                return await ctx.send(f'Game ID was not included. {usage}')
            logger.debug(
                f'Inferring game {inferred_game.id} from ping command used in channel {ctx.message.channel.id}'
            )

        if not message:
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(f'Message was not included. {usage}')

        message = utilities.escape_role_mentions(message)

        if inferred_game:
            game = inferred_game
        else:
            game = await PolyGame().convert(ctx,
                                            int(game_id),
                                            allow_cross_guild=True)

        if not game.player(discord_id=ctx.author.id) and not settings.is_staff(
                ctx.author):
            ctx.command.reset_cooldown(ctx)
            return await ctx.send(f'You are not a player in game {game.id}')

        permitted_channels = settings.guild_setting(game.guild_id,
                                                    'bot_channels').copy()
        if game.game_chan:
            permitted_channels.append(game.game_chan)

        game_player_ids = [
            l.player.discord_member.discord_id for l in game.lineup
        ]
        game_members = [ctx.guild.get_member(p_id) for p_id in game_player_ids]
        player_mentions = [f'<@{p_id}>' for p_id in game_player_ids]

        game_channels = [gs.team_chan for gs in game.gamesides]
        game_channels = [chan for chan in game_channels
                         if chan]  # remove Nones

        mention_players_in_current_channel = True  # False when done from game channel, True otherwise

        if ctx.channel.id in game_channels and len(game_channels) >= len(
                game.gamesides):
            logger.debug(
                'Allowing ping since it is within a game channel, and all sides have a game channel'
            )
            mention_players_in_current_channel = False
        elif settings.is_mod(
                ctx.author) and len(game_channels) >= len(game.gamesides):
            logger.debug(
                'Allowing ping since it is from a mod and all sides have a game channel'
            )
            mention_players_in_current_channel = False
        elif None not in game_members and all(
                ctx.channel.permissions_for(member).read_messages
                for member in game_members):
            logger.debug(
                'Allowing ping since all members have read access to current channel'
            )
            mention_players_in_current_channel = True
        elif ctx.channel.id in permitted_channels:
            logger.debug(
                'Allowing ping since it is a bot channel or central game channel'
            )
            mention_players_in_current_channel = True
        else:
            logger.debug(f'Not allowing ping in {ctx.channel.id}')
            if len(game_channels) >= len(game.gamesides):
                permitted_channels = game_channels + permitted_channels

            channel_tags = [f'<#{chan_id}>' for chan_id in permitted_channels]
            ctx.command.reset_cooldown(ctx)

            if len(game_channels) < len(game.gamesides):
                error_str = 'Not all sides have access to a private channel. '
            else:
                error_str = ''

            return await ctx.send(
                f'This command can not be used in this channel. {error_str}Permitted channels: {" ".join(channel_tags)}'
            )

        full_message = f'Message from **{ctx.author.display_name}** regarding game {game.id} **{game.name}**:\n*{message}*'
        models.GameLog.write(
            game_id=game,
            guild_id=game.guild_id,
            message=
            f'{models.GameLog.member_string(ctx.author)} pinged the game with message: *{discord.utils.escape_markdown(message)}*'
        )

        try:
            if mention_players_in_current_channel:
                logger.debug(
                    f'Ping triggered in non-private channel {ctx.channel.id}')
                await game.update_squad_channels(self.bot.guilds,
                                                 ctx.guild.id,
                                                 message=full_message,
                                                 suppress_errors=True)
                await ctx.send(f'{full_message}\n{" ".join(player_mentions)}')
            else:
                logger.debug(
                    f'Ping triggered in private channel {ctx.channel.id}')
                await game.update_squad_channels(self.bot.guilds,
                                                 game.guild_id,
                                                 message=f'{full_message}',
                                                 suppress_errors=False,
                                                 include_message_mentions=True)
                if ctx.channel.id not in game_channels:
                    await ctx.send(
                        f'Sending ping to game channels:\n{full_message}')
        except exceptions.CheckFailedError as e:
            channel_tags = [f'<#{chan_id}>' for chan_id in permitted_channels]
            return await ctx.send(
                f'{e}\nTry sending `{ctx.prefix}ping` from a public channel that all members can view: {" ".join(channel_tags)}'
            )
Exemplo n.º 7
0
    async def pingall(self, ctx, *, message: str = None):
        """ Ping everyone in all of your incomplete games

        Not useable by all players.
        You can use `pingsteam` to only ping players in your Steam platform games,
        or `pingmobile` to only ping players in your Mobile games.

         **Examples**
        `[p]pingall My phone died and I will make all turns tomorrow`
        Send a message to everyone in all of your incomplete games
        `[p]pingall @Glouc3stershire Glouc is in Tahiti and will play again tomorrow`
        *Staff:* Send a message to everyone in another player's games
        `[p]pingmobile My phone died but I'm still taking turns on Steam!`

        """
        if not message:
            return await ctx.send('Message is required.')

        m = utilities.string_to_user_id(message.split()[0])

        if m:
            logger.debug(f'Third party use of {ctx.invoked_with}')
            # Staff member using command on third party
            if settings.get_user_level(ctx.author) <= 3:
                logger.debug('insufficient user level')
                return await ctx.send(
                    'You do not have permission to use this command on another player\'s games.'
                )
            message = ' '.join(
                message.split()[1:])  # remove @Mention first word of message
            target = str(m)
            log_message = f'{models.GameLog.member_string(ctx.author)} used pingall on behalf of player ID `{target}` with message: '
        else:
            logger.debug('first party usage of pingall')
            # Play using command on their own games
            if settings.get_user_level(ctx.author) <= 2:
                logger.debug('insufficient user level')
                return await ctx.send(
                    'You do not have permission to use this command. You can ask a server staff member to use this command on your games for you.'
                )
            target = str(ctx.author.id)
            log_message = f'{models.GameLog.member_string(ctx.author)} used {ctx.invoked_with} with message: '

        try:
            player_match = models.Player.get_or_except(player_string=target,
                                                       guild_id=ctx.guild.id)
        except exceptions.NoSingleMatch:
            return await ctx.send(
                f'User <@{target}> is not a registered ELO player.')

        if ctx.invoked_with == 'pingall':
            platform_filter = 2
            title_str = 'Message to all players in unfinished games'
        elif ctx.invoked_with == 'pingmobile':
            platform_filter = 1
            title_str = 'Message to players in unfinished mobile games'
        elif ctx.invoked_with == 'pingsteam':
            platform_filter = 0
            title_str = 'Message to players in unfinished Steam games'
        else:
            raise ValueError(
                f'{ctx.invoked_with} is not a handled alias for this command')

        game_list = models.Game.search(player_filter=[player_match],
                                       status_filter=2,
                                       guild_id=ctx.guild.id,
                                       platform_filter=platform_filter)
        logger.debug(f'{len(game_list)} incomplete games for target')

        list_of_players = []
        for g in game_list:
            list_of_players += g.mentions()

        list_of_players = list(set(list_of_players))
        logger.debug(f'{len(list_of_players)} unique opponents for target')
        clean_message = utilities.escape_role_mentions(message)
        if len(list_of_players) > 100:
            await ctx.send(
                '*Warning:* More than 100 unique players are addressed. Only the first 100 will be mentioned.'
            )
        await ctx.send(
            f'{title_str} for <@{target}> ({player_match.name}): *{clean_message}*'
        )

        recipient_message = f'Message recipients: {" ".join(list_of_players[:100])}'
        await ctx.send(recipient_message[:2000])

        for game in game_list:
            logger.debug(
                f'Sending message to game channels for game {game.id} from {ctx.invoked_with}'
            )
            models.GameLog.write(
                game_id=game,
                guild_id=ctx.guild.id,
                message=
                f'{log_message} *{discord.utils.escape_markdown(clean_message)}*'
            )
            await game.update_squad_channels(
                self.bot.guilds,
                game.guild_id,
                message=
                f'{title_str} for **{player_match.name}**: *{clean_message}*')