Exemple #1
0
    async def delete(self, ctx, game: SpiesGame = None):
        """Deletes a game

        You can delete a game if you are the winner and the win was claimed in the last 24 hours.
        Staff can delete any completed games.

        ELO changes will be reversed and the ELO changes for any games that had been claimed subsequent to the deleted game will be recalculated.

        **Example:**
        `[p]delete 25`
        """
        if not game:
            return await ctx.send(
                f'Game ID not provided. Usage: __`{ctx.prefix}delete GAME_ID`__'
            )

        if settings.is_staff(ctx) or (game.winning_player.discord_id
                                      == ctx.author.id
                                      and game.win_claimed_ts > yesterday):
            pass
        else:
            return await ctx.send(
                f'To delete a game you must be server staff, or be the winner of a game claimed in the last 24 hours.'
            )

        gid = game.id
        async with ctx.typing():
            await self.bot.loop.run_in_executor(None, game.delete_game)
            # Allows bot to remain responsive while this large operation is running.
            await ctx.send(
                f'Game with ID {gid} has been deleted and team/player ELO changes have been reverted, if applicable.'
            )
    async def cog_check(self, ctx):

        if settings.is_staff(ctx):
            return True
        else:
            if ctx.invoked_with == 'help' and ctx.command.name != 'help':
                return False
            else:
                await ctx.send(
                    'You do not have permission to use this command.')
                return False
    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)}')
Exemple #4
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)}')
Exemple #5
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)}'
            )