async def set_guild(self, ctx: MyContext, role: discord.Role,
                        permission: str, value: bool):
        """
        Set a permission for a role in a guild
        """
        _ = await ctx.get_translate_function()

        if role.guild.id != ctx.guild.id:
            await ctx.send(
                _("❌ Can't set permissions for a role that does not exist in this guild."
                  ))
            return False

        db_guild = await get_from_db(ctx.guild)

        current_permissions = db_guild.permissions.get(str(role.id), {})
        current_permissions[permission] = value
        db_guild.permissions[str(role.id)] = current_permissions

        await db_guild.save(update_fields=['permissions'])
        await ctx.send(
            _("👌 Permission {permission} for role {role_name} [`{role_id}`] has been set to {value} in this guild.",
              permission=escape_everything(permission),
              role_name=escape_everything(role.name),
              role_id=role.id,
              value=value))
    async def set_channel(self, ctx: MyContext, channel: discord.TextChannel, role: discord.Role, permission: str,
                          value: bool):
        """
        Set a permission for a role in a channel
        """
        _ = await ctx.get_translate_function()

        if channel not in ctx.guild.channels:
            await ctx.send(_("❌ Can't set permissions in a channel that is not in this guild."))
            return False

        if role.guild.id != ctx.guild.id:
            await ctx.send(_("❌ Can't set permissions for a role that does not exist in this guild."))
            return False

        db_channel = await get_from_db(channel)
        current_permissions = db_channel.permissions.get(str(role.id), {})
        current_permissions[permission] = value

        db_channel.permissions[str(role.id)] = current_permissions

        await db_channel.save(update_fields=['permissions'])
        await ctx.send(
            _("👌 Permission {0} for role {1} [`{2}`] has been set to {3} in this channel.",
              escape_everything(permission),
              escape_everything(role.name),
              role.id,
              value))
    async def view_channel(self, ctx: MyContext, channel: discord.TextChannel = None):
        """
        Display all permissions set for this channel
        """
        _ = await ctx.get_translate_function()

        said_something = False
        guild = ctx.guild
        if not channel or channel.guild.id != guild.id:
            channel = ctx.channel

        db_channel = await get_from_db(channel)
        permissions_by_role = db_channel.permissions

        for role_id, role_permissions in permissions_by_role.items():
            role = guild.get_role(int(role_id))
            if role and role_permissions:
                message = [_("**{0} permissions**", escape_everything(role.name)), "```diff"]

                for permission, value in role_permissions.items():
                    sign = "+" if value else "-"
                    message.append(f"{sign} {permission}")
                message.append("```")
                said_something = True
                await ctx.send("\n".join(message))

        if not said_something:
            await ctx.send(_("There are no specific permissions set in this channel."))
    async def view_guild(self, ctx: MyContext):
        """
        Display all permissions set for this guild
        """
        _ = await ctx.get_translate_function()

        said_something = False
        guild = ctx.guild
        db_guild = await get_from_db(guild)
        permissions_by_role = db_guild.permissions

        for role_id, role_permissions in permissions_by_role.items():
            role = guild.get_role(int(role_id))
            if role and len(role_permissions):
                message = [
                    _("**{role} permissions**",
                      role=escape_everything(role.name)), "```diff"
                ]

                for permission, value in role_permissions.items():
                    sign = "+" if value else "-"
                    message.append(f"{sign} {permission}")
                message.append("```")
                said_something = True
                await ctx.send("\n".join(message))

        if not said_something:
            await ctx.send(
                _("There are no specific permissions set in this guild."))
    async def set_member(self, ctx: MyContext, member: discord.Member, permission: str, value: bool):
        """
        Set a permission for a member in a guild
        """
        _ = await ctx.get_translate_function()

        db_user = await get_from_db(member, as_user=False)
        db_user.permissions[permission] = value
        await db_user.save(update_fields=['permissions'])
        await ctx.send(_(
            "👌 Permission {0} for member {1} has been set to {2} globally.",
            escape_everything(permission),
            member,
            value))
    async def set_user(self, ctx: MyContext, user: discord.User, permission: str, value: bool):
        """
        Set a permission for a specific user, globally.
        """
        _ = await ctx.get_translate_function()

        db_user = await get_from_db(user, as_user=True)
        db_user.permissions[permission] = value
        await db_user.save(update_fields=['permissions'])

        await ctx.send(
            _("👌 Permission {0} for user {1} has been set to {2} globally.",
              escape_everything(permission),
              str(user),
              value))
Esempio n. 7
0
    async def on_command_error(self, ctx: MyContext,
                               exception: Exception) -> None:
        _ = await ctx.get_translate_function()

        # This prevents any commands with local handlers being handled here in on_command_error.
        if hasattr(ctx.command, 'on_error'):
            return

        ignored = (commands.CommandNotFound, )

        # Allows us to check for original exceptions raised and sent to CommandInvokeError.
        # If nothing is found. We keep the exception passed to on_command_error.

        # Anything in ignored will return and prevent anything happening.
        if isinstance(exception, ignored):
            return

        DELETE_ERROR_MESSAGE_AFTER = 60
        command_invoke_help = f"{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}"

        ctx.logger.debug(
            f"Error during processing: {exception} ({repr(exception)})")

        # https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.CommandError
        if isinstance(exception, commands.CommandError):
            if isinstance(exception, commands.ConversionError):
                original = exception.original
                message = _(
                    "There was an error converting one of your arguments with {exception.converter}. The "
                    "correct syntax would be `{command_invoke_help}`. The converter returned the following error"
                    ": {original}",
                    command_invoke_help=command_invoke_help,
                    original=escape_everything(str(original)))
            elif isinstance(exception, commands.UserInputError):
                if isinstance(exception,
                              commands.errors.MissingRequiredArgument):
                    message = _(
                        "This command is missing an argument. The correct syntax would be `{command_invoke_help}`.",
                        command_invoke_help=command_invoke_help)
                elif isinstance(exception,
                                commands.errors.ArgumentParsingError):
                    if isinstance(exception, commands.UnexpectedQuoteError):
                        message = _(
                            "Too many quotes were provided in your message: don't forget to escape your "
                            "quotes like this `\\{exception.quote}`. The correct syntax for the command is "
                            "`{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help,
                            exception=exception)
                    elif isinstance(exception,
                                    commands.InvalidEndOfQuotedStringError):
                        message = _(
                            "A space was expected after a closing quote, but I found {exception.char}. "
                            "Please check that you are using the correct syntax: `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help,
                            exception=exception)
                    elif isinstance(exception,
                                    commands.ExpectedClosingQuoteError):
                        message = _(
                            "A closing quote was expected, but wasn't found. Don't forget to close your "
                            "quotes with `{exception.close_quote}` at the end of your argument. Please check "
                            "that you are using the correct syntax: `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help,
                            exception=exception)
                    elif isinstance(exception, commands.TooManyArguments):
                        message = _(
                            "Too many arguments were passed in this command. "
                            "Please check that you are using the correct syntax: `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help)
                    else:  # Should not trigger, just in case some more errors are added.
                        message = _(
                            "The way you are invoking this command is confusing me. The correct syntax would "
                            "be `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help)

                elif isinstance(exception, commands.BadArgument):
                    message = _(
                        "An argument passed was incorrect. `{exception}`."
                        "Please check that you are using the correct syntax: `{command_invoke_help}`.",
                        command_invoke_help=command_invoke_help,
                        exception=exception)
                elif isinstance(exception, commands.BadUnionArgument):
                    message = _(
                        "{exception} Please check that you are using the correct syntax: `{command_invoke_help}`.",
                        command_invoke_help=command_invoke_help,
                        exception=str(exception))
                else:
                    message = f"{str(exception)} ({type(exception).__name__})"
                    ctx.logger.error("".join(
                        traceback.format_exception(type(exception), exception,
                                                   exception.__traceback__)))

            elif isinstance(exception, commands.CheckFailure):
                if isinstance(exception, commands.PrivateMessageOnly):
                    message = _(
                        "This command can only be used in a private message.")
                elif isinstance(exception, commands.NoPrivateMessage):
                    message = _(
                        "This command cannot be used in a private message.")
                elif isinstance(exception, commands.CheckAnyFailure):
                    message = _(
                        "Multiple errors were encountered when running your command : {exception.errors}",
                        exception=exception)
                elif isinstance(exception, commands.NotOwner):
                    message = _(
                        "You need to be the owner of the bot to run that.")
                # We could edit and change the message here, but the lib messages are fine and specify exactly what permissions are missing
                elif isinstance(exception, commands.MissingPermissions):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.BotMissingPermissions):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.MissingRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.BotMissingRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.MissingAnyRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.BotMissingAnyRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.NSFWChannelRequired):
                    message = _(
                        "You need to be in a NSFW channel to run that.")

                # Custom checks errors
                elif isinstance(exception, checks.NotInServer):
                    correct_guild = self.bot.get_guild(
                        exception.must_be_in_guild_id)
                    if correct_guild:
                        message = _(
                            "You need to be in the {correct_guild.name} server (`{exception.must_be_in_guild_id}`).",
                            correct_guild=correct_guild,
                            exception=exception)
                    else:
                        message = _(
                            "You need to be in a server with ID {exception.must_be_in_guild_id}.",
                            exception=exception)
                elif isinstance(exception, checks.HavingPermission):
                    message = _(
                        "You have the `{exception.permission}` permission.",
                        exception=exception)
                elif isinstance(exception, checks.MissingPermission):
                    message = _(
                        "You need the `{exception.permission}` permission.",
                        exception=exception)
                elif isinstance(exception, checks.HavingPermissions):
                    message = _(
                        "You have {exception.required} or more of the following permissions : `{exception.permissions}`.",
                        exception=exception)
                elif isinstance(exception, checks.MissingPermissions):
                    message = _(
                        "You need {exception.required} or more of the following permissions : `{exception.permissions}`.",
                        exception=exception)
                elif isinstance(exception, checks.BotIgnore):
                    return
                else:
                    message = f"Check error running this command : {str(exception)} ({type(exception).__name__})"
                    ctx.logger.error("".join(
                        traceback.format_exception(type(exception), exception,
                                                   exception.__traceback__)))
            elif isinstance(exception, commands.CommandNotFound):
                # This should be disabled.
                message = _("The provided command was not found.")
            elif isinstance(exception, commands.errors.DisabledCommand):
                message = _("That command has been disabled.")
            elif isinstance(exception, commands.CommandInvokeError):
                message = _(
                    "There was an error running the specified command. Contact the bot admins."
                )
                ctx.logger.error("".join(
                    traceback.format_exception(type(exception), exception,
                                               exception.__traceback__)))
            elif isinstance(exception, commands.errors.CommandOnCooldown):
                if await self.bot.is_owner(
                        ctx.author
                ) or checks.has_permission("bot.bypass_cooldowns"):
                    await ctx.reinvoke()
                    return
                else:
                    delta = datetime.timedelta(
                        seconds=min(round(exception.retry_after, 1), 1))
                    # NOTE : This message uses a formatted, direction date in some_time. Formatted, it'll give something like :
                    # "This command is overused. Please try again *in 4 seconds*"
                    message = _(
                        "This command is overused. Please try again {some_time}.",
                        some_time=dates.format_timedelta(
                            delta,
                            add_direction=True,
                            locale=await ctx.get_language_code()))
            elif isinstance(exception, commands.errors.MaxConcurrencyReached):
                message = f"{str(exception)}"  # The message from the lib is great.
            else:
                message = f"{str(exception)} ({type(exception).__name__})"
                ctx.logger.error("".join(
                    traceback.format_exception(type(exception), exception,
                                               exception.__traceback__)))
        else:
            message = _(
                "This should not have happened. A command raised an error that does not comes from CommandError. Please inform the owner."
            )
            ctx.logger.error("".join(
                traceback.format_exception(type(exception), exception,
                                           exception.__traceback__)))

        if message:
            await ctx.send("❌ " + message,
                           delete_after=DELETE_ERROR_MESSAGE_AFTER)
Esempio n. 8
0
    async def on_command_error(self, ctx: MyContext,
                               exception: Exception) -> None:
        _ = await ctx.get_translate_function()

        # This prevents any commands with local handlers being handled here in on_command_error.
        if hasattr(ctx.command, 'on_error'):
            return

        ignored = (commands.CommandNotFound, )

        # Allows us to check for original exceptions raised and sent to CommandInvokeError.
        # If nothing is found. We keep the exception passed to on_command_error.

        # Anything in ignored will return and prevent anything happening.
        if isinstance(exception, ignored):
            return

        delete_error_message_after = 60
        command_invoke_help = f"{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}"

        ctx.logger.warning(
            f"Error during processing: {exception} ({repr(exception)})")

        # https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.CommandError
        if isinstance(exception, commands.CommandError):
            if isinstance(exception, commands.ConversionError):
                original = exception.original
                message = _(
                    "There was an error converting one of your arguments with {0}. The "
                    "correct syntax would be `{1}`. The converter returned the following "
                    "error: {2}", exception.converter, command_invoke_help,
                    escape_everything(str(original)))
            elif isinstance(exception, commands.UserInputError):
                if isinstance(exception,
                              commands.errors.MissingRequiredArgument):
                    message = _(
                        "This command is missing an argument. The correct syntax would be "
                        "`{0}`.", command_invoke_help)
                elif isinstance(exception,
                                commands.errors.ArgumentParsingError):
                    if isinstance(exception, commands.UnexpectedQuoteError):
                        message = _(
                            "Too many quotes were provided in your message: don't forget to escape your "
                            "quotes like this `\\{0}`. The correct syntax for the command is "
                            "`{1}`.", exception.quote, command_invoke_help)
                    elif isinstance(exception,
                                    commands.InvalidEndOfQuotedStringError):
                        message = _(
                            "A space was expected after a closing quote, but I found {0}. "
                            "Please check that you are using the correct syntax: `{1}`.",
                            exception.char, command_invoke_help)
                    elif isinstance(exception,
                                    commands.ExpectedClosingQuoteError):
                        message = _(
                            "A closing quote was expected, but wasn't found. Don't forget to close your "
                            "quotes with `{0}` at the end of your argument. Please check "
                            "that you are using the correct syntax: `{1}`.",
                            exception.close_quote, command_invoke_help)
                    elif isinstance(exception, commands.TooManyArguments):
                        message = _(
                            "Too many arguments were passed in this command. "
                            "Please check that you are using the correct syntax: `{0}`.",
                            command_invoke_help)
                    else:  # Should not trigger, just in case some more errors are added.
                        message = _(
                            "The way you are invoking this command is confusing me. The correct syntax would "
                            "be `{0}`.", command_invoke_help)

                elif isinstance(exception, commands.BadArgument):
                    message = _(
                        "An argument passed was incorrect. `{0}`."
                        "Please check that you are using the correct syntax: `{1}`.",
                        str(exception), command_invoke_help)
                elif isinstance(exception, commands.BadUnionArgument):
                    message = _(
                        "{0} Please check that you are using the correct syntax: `{1}`.",
                        str(exception), command_invoke_help)
                else:
                    message = "{str(exception)} ({type(exception).__name__})"
                    ctx.logger.error("".join(
                        traceback.format_exception(type(exception), exception,
                                                   exception.__traceback__)))

            elif isinstance(exception, commands.CheckFailure):
                if isinstance(exception, commands.PrivateMessageOnly):
                    message = _(
                        "This command can only be used in a private message.")
                elif isinstance(exception, commands.NoPrivateMessage):
                    message = _(
                        "This command cannot be used in a private message.")
                elif isinstance(exception, commands.CheckAnyFailure):
                    message = _(
                        "Multiple errors were encountered when running your command: {0}",
                        exception.errors)
                elif isinstance(exception, commands.NotOwner):
                    message = _(
                        "You need to be the owner of the bot to run that.")
                # We could edit and change the message here, but the lib messages are fine and specify exactly what
                # permissions are missing
                elif isinstance(exception, commands.MissingPermissions):
                    message = _(
                        "You are missing permissions to run this command. {0}",
                        " ".join(exception.missing_perms))
                elif isinstance(exception, commands.BotMissingPermissions):
                    message = _(
                        "I am missing permissions to run this command. {0}",
                        " ".join(exception.missing_perms))
                elif isinstance(exception, commands.MissingRole):
                    message = _("You are missing the following role: {0}",
                                exception.missing_role)
                elif isinstance(exception, commands.BotMissingRole):
                    message = _("I am missing the following role: {0}",
                                exception.missing_role)
                elif isinstance(exception, commands.MissingAnyRole):
                    message = _(
                        "You are missing one of the following roles: {0}",
                        " ".join(exception.missing_roles))
                elif isinstance(exception, commands.BotMissingAnyRole):
                    message = _("I am missing one of the following roles: {0}",
                                " ".join(exception.missing_roles))
                elif isinstance(exception, commands.NSFWChannelRequired):
                    message = _(
                        "You need to be in a NSFW channel to run that.")

                # Custom checks errors
                elif isinstance(exception, checks.NotInServer):
                    correct_guild = self.bot.get_guild(
                        exception.must_be_in_guild_id)
                    if correct_guild:
                        message = _(
                            "You need to be in the {0} server "
                            "(`{1}`).", correct_guild.name,
                            exception.must_be_in_guild_id)
                    else:
                        message = _("You need to be in a server with ID {0}.",
                                    exception.must_be_in_guild_id)
                elif isinstance(exception, checks.HavingPermission):
                    message = _("You have the `{0}` permission.",
                                exception.permission)
                elif isinstance(exception, checks.MissingPermission):
                    message = _("You need the `{0}` permission.",
                                exception.permission)
                elif isinstance(exception, checks.HavingPermissions):
                    message = _(
                        "You have {0} or more of the following permissions: "
                        "`{1}`.", exception.required, exception.permissions)
                elif isinstance(exception, checks.MissingPermissions):
                    message = _(
                        "You need {0} or more of the following permissions: "
                        "`{1}`.", exception.required, exception.permissions)
                elif isinstance(exception, checks.BotIgnore):
                    return
                else:
                    message = "Check error running this command : {str(exception)} ({type(exception).__name__})"
                    ctx.logger.error("".join(
                        traceback.format_exception(type(exception), exception,
                                                   exception.__traceback__)))
            elif isinstance(exception, commands.CommandNotFound):
                # This should be disabled.
                message = _("The provided command was not found.")
            elif isinstance(exception, commands.errors.DisabledCommand):
                message = _("That command has been disabled.")
            elif isinstance(exception, commands.CommandInvokeError):
                if isinstance(exception.original, discord.errors.Forbidden):
                    await ctx.author.send(
                        _("I don't have permissions to send messages there! Try again somewhere I do "
                          "have permissions to send messages!"))
                    return
                elif isinstance(exception.original, RuntimeError) and exception.original.args[0] == \
                        "The bot hasn't been set up yet! Ensure bot.async_setup is called ASAP!":
                    message = _(
                        "The bot's still setting up, please wait a few minutes and try again!"
                    )
                elif isinstance(exception.original, discord.errors.NotFound):
                    message = _(
                        "I can't find your original message, Discord may be having issues! Try again."
                    )
                elif isinstance(exception.original, api.NoDataAvailable):
                    message = _(
                        "No data is available at the moment. Wait a few moments. **Spamming this command will "
                        "result in a temporary ban!**")
                elif isinstance(exception.original, InvalidKeyError):
                    message = _(
                        "A invalid key was found. If you want to use `{` and `}`, make sure you escape them by "
                        "using two brackets instead of one. If you don't, make sure the values are correct."
                        "{0}", exception.original.__str__())
                else:
                    message = _(
                        "There was an error running the specified command‽ This error has been logged."
                    )
                    # we want the original instead of the CommandError one
                    await submit_error_message(exception.original,
                                               "unknown thing", self.bot, ctx)
                    # ctx.logger.error("".join(traceback.format_exception(type(exception),
                    # exception, exception.__traceback__)))
            elif isinstance(exception, commands.errors.CommandOnCooldown):
                if await self.bot.is_owner(ctx.author):
                    try:
                        await ctx.reinvoke()
                    except Exception as e:
                        await self.on_command_error(ctx, e)
                    return
                else:
                    delta = datetime.timedelta(seconds=exception.retry_after)
                    # NOTE : This message uses a formatted, direction date in some_time. Formatted, it'll give something
                    # like: "This command is overused. Please try again *in 4 seconds*"
                    message = _(
                        "You are being ratelimited. Please try again {0}.",
                        dates.format_timedelta(delta,
                                               add_direction=True,
                                               locale=await
                                               ctx.get_language_code()))
            elif isinstance(exception, commands.errors.MaxConcurrencyReached):
                bucket_types = {
                    commands.BucketType.default: _("globally"),
                    commands.BucketType.user: _("per user"),
                    commands.BucketType.guild: _("per guild"),
                    commands.BucketType.channel: _("per channel"),
                    commands.BucketType.member: _("per member"),
                    commands.BucketType.category: _("per category"),
                    commands.BucketType.role: _("per role")
                }
                message = _(
                    "Too many users are using this command. Only {0} users can use it at the same time {1}.",
                    exception.number, bucket_types[exception.per])
            else:
                message = "{str(exception)} ({type(exception).__name__})"
                ctx.logger.error("".join(
                    traceback.format_exception(type(exception), exception,
                                               exception.__traceback__)))
                await submit_error_message(exception, "unknown thing", ctx.bot,
                                           ctx)
        else:
            message = _(
                "This should not have happened. A command raised an error that does not comes from "
                "CommandError. Please inform the owner.")
            ctx.logger.error("".join(
                traceback.format_exception(type(exception), exception,
                                           exception.__traceback__)))

        if message:
            await ctx.send("❌ " + message + _(
                "\nFor help, join the bot's support server at {0}\n"
                "Check <https://discordstatus.com/> for more details on Discord issues.",
                self.bot.support_server_invite),
                           delete_after=delete_error_message_after)
        else:
            await ctx.send(
                "❌ " +
                _("No message was defined. This error has been logged."))
    async def view_me(self, ctx: MyContext, permission: str, negate: bool = False, administrator: bool = True,
                      show_none: bool = False):
        """
        Check a permission for yourself
        """
        _ = await ctx.get_translate_function()

        value = await permissions.has_permission(ctx, permission, negate=negate, administrator=administrator)
        if value:
            await ctx.send(_("😃 You have that permission."))
        else:
            await ctx.send(_("☹️ You don't have that permission."))

        if ctx.guild:
            message = [_("Details of permissions hierarchy"), "```diff"]
            parsed_permission = permission.split(".")

            default_permissions = ctx.bot.config['permissions']['default']

            value, by = _recursive_permission_check(parsed_permission, default_permissions)
            message.append(_("{0} Default (from {0}{1})", get_sign(value), by))

            db_member: DiscordMember = await get_from_db(ctx.author)
            db_channel: DiscordChannel = await get_from_db(ctx.channel)
            db_user: DiscordUser = db_member.user
            db_guild: DiscordGuild = db_member.guild
            guild_permissions = db_guild.permissions

            for role in ctx.author.roles:
                role_permissions = guild_permissions.get(str(role.id), {})
                if role_permissions:
                    value, by = _recursive_permission_check(parsed_permission, role_permissions)
                    if value in [True, False] or show_none:
                        message.append(_("{0} Guild role {1} (from {0}{2})",
                                         get_sign(value),
                                         escape_everything(role.name),
                                         by))

            channel_permissions = db_channel.permissions
            for role in ctx.author.roles:
                role_permissions = channel_permissions.get(str(role.id), {})
                if role_permissions:
                    value, by = _recursive_permission_check(parsed_permission, role_permissions)
                    if value in [True, False] or show_none:
                        message.append(_("{0} Channel role {1} (from {0}{2})",
                                         get_sign(value), escape_everything(role.name),
                                         by))

            member_permissions = db_member.permissions
            value, by = _recursive_permission_check(parsed_permission, member_permissions)
            if value in [True, False] or show_none:
                message.append(_("{0} Member (from {0}{1})",
                                 get_sign(value), by))

            fixed_permissions = ctx.bot.config['permissions']['fixed']
            value, by = _recursive_permission_check(parsed_permission, fixed_permissions)
            if value in [True, False] or show_none:
                message.append(_("{0} Fixed (from {0}{1})",
                                 get_sign(value),
                                 by))

            user_permissions = db_user.permissions
            value, by = _recursive_permission_check(parsed_permission, user_permissions)
            if value in [True, False] or show_none:
                message.append(_("{0} User (from {0}{1})",
                                 get_sign(value),
                                 by))

            message.append("```")

            await ctx.send("\n".join(message))
Esempio n. 10
0
    async def on_command_error(self, ctx: MyContext,
                               exception: Exception) -> None:
        _ = await ctx.get_translate_function()

        # This prevents any commands with local handlers being handled here in on_command_error.
        if hasattr(ctx.command, 'on_error'):
            return

        ignored = (commands.CommandNotFound, )

        # Allows us to check for original exceptions raised and sent to CommandInvokeError.
        # If nothing is found. We keep the exception passed to on_command_error.

        # Anything in ignored will return and prevent anything happening.
        if isinstance(exception, ignored):
            return

        command_invoke_help = f"{ctx.prefix}{ctx.command.qualified_name} {ctx.command.signature}"

        ctx.logger.debug(
            f"Error during processing: {exception} ({repr(exception)})")

        if hasattr(exception, "original"):
            exception = exception.original

        # https://discordpy.readthedocs.io/en/latest/ext/commands/api.html#discord.ext.commands.CommandError
        if isinstance(exception, commands.CommandError):
            if isinstance(exception, commands.ConversionError):
                original = exception.original
                message = _(
                    "There was an error converting one of your arguments with {exception.converter}. The "
                    "correct syntax would be `{command_invoke_help}`. The converter returned the following "
                    "error: {original}",
                    command_invoke_help=command_invoke_help,
                    original=escape_everything(str(original)))
            elif isinstance(exception, commands.UserInputError):
                if isinstance(exception,
                              commands.errors.MissingRequiredArgument):
                    message = _(
                        "This command is missing an argument. The correct syntax would be `{command_invoke_help}`.",
                        command_invoke_help=command_invoke_help)
                elif isinstance(exception,
                                commands.errors.ArgumentParsingError):
                    if isinstance(exception, commands.UnexpectedQuoteError):
                        message = _(
                            "Too many quotes were provided in your message: don't forget to escape your "
                            "quotes like this `\\{exception.quote}`. The correct syntax for the command is "
                            "`{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help,
                            exception=exception)
                    elif isinstance(exception,
                                    commands.InvalidEndOfQuotedStringError):
                        message = _(
                            "A space was expected after a closing quote, but I found {exception.char}. "
                            "Please check that you are using the correct syntax: `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help,
                            exception=exception)
                    elif isinstance(exception,
                                    commands.ExpectedClosingQuoteError):
                        message = _(
                            "A closing quote was expected, but wasn't found. Don't forget to close your "
                            "quotes with `{exception.close_quote}` at the end of your argument. Please check "
                            "that you are using the correct syntax: `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help,
                            exception=exception)
                    elif isinstance(exception, commands.TooManyArguments):
                        message = _(
                            "Too many arguments were passed in this command. "
                            "Please check that you are using the correct syntax: `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help)
                    else:  # Should not trigger, just in case some more errors are added.
                        message = _(
                            "The way you are invoking this command is confusing me. The correct syntax would "
                            "be `{command_invoke_help}`.",
                            command_invoke_help=command_invoke_help)

                elif isinstance(exception, commands.BadArgument):
                    message = _(
                        "An argument passed was incorrect. `{exception}`. "
                        "Please check that you are using the correct syntax: `{command_invoke_help}`.",
                        command_invoke_help=command_invoke_help,
                        exception=exception)
                elif isinstance(exception, commands.BadUnionArgument):
                    message = _(
                        "{exception} Please check that you are using the correct syntax: `{command_invoke_help}`.",
                        command_invoke_help=command_invoke_help,
                        exception=str(exception))
                else:
                    message = f"{str(exception)} ({type(exception).__name__})"
                    ctx.logger.error("".join(
                        traceback.format_exception(type(exception), exception,
                                                   exception.__traceback__)))

            elif isinstance(exception, commands.CheckFailure):
                if isinstance(exception, commands.PrivateMessageOnly):
                    message = _(
                        "This command can only be used in a private message.")
                elif isinstance(exception, commands.NoPrivateMessage):
                    message = _(
                        "This command cannot be used in a private message.")
                elif isinstance(exception, commands.CheckAnyFailure):
                    message = _(
                        "Multiple errors were encountered when running your command: {exception.errors}",
                        exception=exception)
                elif isinstance(exception, commands.NotOwner):
                    message = _(
                        "You need to be the owner of the bot to run that.")
                # We could edit and change the message here, but the lib messages are fine and specify exactly what
                # permissions are missing
                elif isinstance(exception, commands.MissingPermissions):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.BotMissingPermissions):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.MissingRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.BotMissingRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.MissingAnyRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.BotMissingAnyRole):
                    message = f"{str(exception)}"
                elif isinstance(exception, commands.NSFWChannelRequired):
                    message = _(
                        "You need to be in a NSFW channel to run that.")

                # Custom checks errors
                elif isinstance(exception, checks.NotInServer):
                    correct_guild = self.bot.get_guild(
                        exception.must_be_in_guild_id)
                    if correct_guild:
                        message = _(
                            "You need to be in the {correct_guild.name} server (`{exception.must_be_in_guild_id}`).",
                            correct_guild=correct_guild,
                            exception=exception)
                    else:
                        message = _(
                            "You need to be in a server with ID {exception.must_be_in_guild_id}.",
                            exception=exception)
                elif isinstance(exception, checks.NotInChannel):
                    correct_channel = self.bot.get_channel(
                        exception.must_be_in_channel_id)
                    if correct_channel:
                        message = _(
                            "You need to be in the {correct_channel.name} channel (`{exception.must_be_in_channel_id}`).",
                            correct_channel=correct_channel,
                            exception=exception)
                    else:
                        message = _(
                            "You need to be in a channel with ID {exception.must_be_in_channel_id}.",
                            exception=exception)

                elif type(exception).__name__ == NotEnoughExperience.__name__:
                    message = _(
                        "You don't have enough experience to enjoy this item. You'd need at least {exception.needed} exp, but you only have {exception.having}.",
                        exception=exception)

                elif isinstance(exception, checks.AccessTooLow):
                    message = _(
                        "Your access level is too low : you have an access level of {exception.current_access.name}, and you need at least {exception.required_access.name}.",
                        exception=exception)

                elif isinstance(exception, checks.ChannelDisabled):
                    db_guild = await get_from_db(ctx.guild)

                    if db_guild.channel_disabled_message:
                        message = _(
                            "The game isn't running on this channel. "
                            "Admins can disable this message by running `dh!settings channel_disabled_message False`, "
                            "or can enable the channel with `dh!settings enabled True`.",
                            exception=exception)
                    else:
                        return

                elif isinstance(exception, checks.BotIgnore):
                    return
                else:
                    message = _(
                        "Check error running this command: {err_data}",
                        err_data=
                        f"{str(exception)} ({type(exception).__name__})")
                    ctx.logger.error("".join(
                        traceback.format_exception(type(exception), exception,
                                                   exception.__traceback__)))
            elif isinstance(exception, commands.CommandNotFound):
                # This should be disabled.
                message = _("The provided command was not found.")
            elif isinstance(exception, commands.errors.DisabledCommand):
                message = _("That command has been disabled.")
            elif isinstance(exception, commands.CommandInvokeError):
                message = _(
                    "There was an error running the specified command. Contact the bot admins."
                )
                ctx.logger.error("".join(
                    traceback.format_exception(type(exception), exception,
                                               exception.__traceback__)))
            elif isinstance(exception, commands.errors.CommandOnCooldown):
                if await self.bot.is_owner(ctx.author):
                    await ctx.reinvoke()
                    return
                else:
                    delta = datetime.timedelta(
                        seconds=min(round(exception.retry_after, 1), 1))
                    # NOTE: This message uses a formatted, direction date in some_time. Formatted, it'll give
                    # something like:
                    # "This command is overused. Please try again *in 4 seconds*"
                    message = _(
                        "This command is overused. Please try again {some_time}.",
                        some_time=dates.format_timedelta(
                            delta,
                            add_direction=True,
                            locale=(await ctx.get_language_code())))
            elif isinstance(exception, commands.errors.MaxConcurrencyReached):
                message = f"{str(exception)}"  # The message from the lib is great.
            else:
                message = f"{str(exception)} ({type(exception).__name__})"
                ctx.logger.error("".join(
                    traceback.format_exception(type(exception), exception,
                                               exception.__traceback__)))
        else:
            message = _(
                "This should not have happened. A command raised an error that does not comes from CommandError. "
                "Please inform the owner.")
            ctx.logger.error("".join(
                traceback.format_exception(type(exception), exception,
                                           exception.__traceback__)))
            if isinstance(exception, tortoise.exceptions.OperationalError):
                message = _(
                    "You are above the limits of DuckHunt database. Try to reduce your expectations. Database message: `{exception}`",
                    exception=exception)
            elif isinstance(exception, babel.core.UnknownLocaleError):
                message = f"Unknown server language. Fix with `{ctx.prefix}set lang en`"
            elif isinstance(exception, discord.Forbidden):
                message = _(
                    "Missing permissions, please check I can embed links here. `{exception}`",
                    exception=exception)
            elif isinstance(exception, menus.CannotAddReactions):
                message = _(
                    "Missing permissions, please check I can add reactions here."
                )
            else:
                message = _(
                    "This should not have happened. A command raised an error that does not come from CommandError, and isn't handled by our error handler. "
                    "Please inform the owner.") + "\n```py\n" + "".join(
                        traceback.format_exception(
                            type(exception), exception,
                            exception.__traceback__)) + "\n```"
                ctx.logger.error("".join(
                    traceback.format_exception(type(exception), exception,
                                               exception.__traceback__)))

        if message:
            await ctx.send("❌ " + message,
                           delete_after=DELETE_ERROR_MESSAGE_AFTER)