diff options
author | 2018-04-21 19:17:42 +0200 | |
---|---|---|
committer | 2018-04-21 18:17:42 +0100 | |
commit | db177136d2fb777c3da1009edc404df908da8847 (patch) | |
tree | 97d23fa27560a9333ebbdf8f14593f7ac36f305a | |
parent | [#1eeu1] Hiphopify (#54) (diff) |
Use converters for tag command argument validation. (#56)
* Use converters for tag command argument validation.
* Remove now unused imports.
* Show red embed with an error message.
This also adds a `CogBadArgument` class, which is intended to be
used with command error handlers in cogs in order to handle their
own `BadArgument` exceptions without having `on_command_error` do so.
* Placate the flake8
-rw-r--r-- | bot/cogs/events.py | 5 | ||||
-rw-r--r-- | bot/cogs/tags.py | 151 | ||||
-rw-r--r-- | bot/exceptions.py | 10 |
3 files changed, 88 insertions, 78 deletions
diff --git a/bot/cogs/events.py b/bot/cogs/events.py index 6f7a1423d..975b39ed9 100644 --- a/bot/cogs/events.py +++ b/bot/cogs/events.py @@ -9,6 +9,7 @@ from discord.ext.commands import ( ) from bot.constants import DEVLOG_CHANNEL, PYTHON_GUILD, SITE_API_KEY, SITE_API_USER_URL +from bot.exceptions import CogBadArgument from bot.utils import chunks log = logging.getLogger(__name__) @@ -49,7 +50,9 @@ class Events: else: help_command = (self.bot.get_command("help"),) - if isinstance(e, BadArgument): + if isinstance(e, CogBadArgument): + log.debug(f"Command {command} raised `CogBadArgument`, ignoring.") + elif isinstance(e, BadArgument): await ctx.send(f"Bad argument: {e}\n") await ctx.invoke(*help_command) elif isinstance(e, UserInputError): diff --git a/bot/cogs/tags.py b/bot/cogs/tags.py index ca48f09cb..24ace01c7 100644 --- a/bot/cogs/tags.py +++ b/bot/cogs/tags.py @@ -1,21 +1,77 @@ import logging import random import time -from typing import Optional -from discord import Colour, Embed, User -from discord.ext.commands import AutoShardedBot, Context, command +from discord import Colour, Embed +from discord.ext.commands import ( + AutoShardedBot, Context, + Converter, command +) from bot.constants import ( - ADMIN_ROLE, ERROR_REPLIES, MODERATOR_ROLE, OWNER_ROLE, + ADMIN_ROLE, MODERATOR_ROLE, OWNER_ROLE, SITE_API_KEY, SITE_API_TAGS_URL, TAG_COOLDOWN, ) from bot.decorators import with_role +from bot.exceptions import CogBadArgument from bot.pagination import LinePaginator log = logging.getLogger(__name__) +class TagNameConverter(Converter): + @staticmethod + async def convert(ctx: Context, tag_name: str): + def is_number(value): + try: + float(value) + except ValueError: + return False + return True + + tag_name = tag_name.lower().strip() + + # The tag name has at least one invalid character. + if ascii(tag_name)[1:-1] != tag_name: + log.warning(f"{ctx.author} tried to put an invalid character in a tag name. " + "Rejecting the request.") + raise CogBadArgument("Don't be ridiculous, you can't use that character!") + + # The tag name is either empty, or consists of nothing but whitespace. + elif not tag_name: + log.warning(f"{ctx.author} tried to create a tag with a name consisting only of whitespace. " + "Rejecting the request.") + raise CogBadArgument("Tag names should not be empty, or filled with whitespace.") + + # The tag name is a number of some kind, we don't allow that. + elif is_number(tag_name): + log.warning(f"{ctx.author} tried to create a tag with a digit as its name. " + "Rejecting the request.") + raise CogBadArgument("Tag names can't be numbers.") + + # The tag name is longer than 127 characters. + elif len(tag_name) > 127: + log.warning(f"{ctx.author} tried to request a tag name with over 127 characters. " + "Rejecting the request.") + raise CogBadArgument("Are you insane? That's way too long!") + + return tag_name + + +class TagContentConverter(Converter): + @staticmethod + async def convert(ctx: Context, tag_content: str): + tag_content = tag_content.strip() + + # The tag contents should not be empty, or filled with whitespace. + if not tag_content: + log.warning(f"{ctx.author} tried to create a tag containing only whitespace. " + "Rejecting the request.") + raise CogBadArgument("Tag contents should not be empty, or filled with whitespace.") + + return tag_content + + class Tags: """ Save new tags and fetch existing tags. @@ -89,59 +145,6 @@ class Tags: return tag_data - @staticmethod - async def validate(author: User, tag_name: str, tag_content: str = None) -> Optional[Embed]: - """ - Create an embed based on the validity of a tag's name and content - - :param author: The user that called the command - :param tag_name: The name of the tag to validate. - :param tag_content: The tag's content, if any. - :return: A validation embed if invalid, otherwise None - """ - - def is_number(value): - try: - float(value) - except ValueError: - return False - else: - return True - - embed = Embed() - embed.colour = Colour.red() - - # 'tag_name' has at least one invalid character. - if ascii(tag_name)[1:-1] != tag_name: - log.warning(f"{author} tried to put an invalid character in a tag name. " - "Rejecting the request.") - embed.description = "Don't be ridiculous, you can't use that character!" - - # 'tag_content' or 'tag_name' are either empty, or consist of nothing but whitespace - elif (tag_content is not None and not tag_content) or not tag_name: - log.warning(f"{author} tried to create a tag with a name consisting only of whitespace. " - "Rejecting the request.") - embed.description = "Tags should not be empty, or filled with whitespace." - - # 'tag_name' is a number of some kind, we don't allow that. - elif is_number(tag_name): - log.error("inside the is_number") - log.warning(f"{author} tried to create a tag with a digit as its name. " - "Rejecting the request.") - embed.description = "Tag names can't be numbers." - - # 'tag_name' is longer than 127 characters - elif len(tag_name) > 127: - log.warning(f"{author} tried to request a tag name with over 127 characters. " - "Rejecting the request.") - embed.description = "Are you insane? That's way too long!" - - else: - return None - - embed.title = random.choice(ERROR_REPLIES) - return embed - @command(name="tags()", aliases=["tags"], hidden=True) async def info_command(self, ctx: Context): """ @@ -154,7 +157,7 @@ class Tags: return await ctx.invoke(self.bot.get_command("help"), "Tags") @command(name="tags.get()", aliases=["tags.get", "tags.show()", "tags.show", "get_tag"]) - async def get_command(self, ctx: Context, tag_name: str=None): + async def get_command(self, ctx: Context, tag_name: TagNameConverter=None): """ Get a list of all tags or a specified tag. @@ -195,13 +198,6 @@ class Tags: tags = [] - if tag_name is not None: - tag_name = tag_name.lower().strip() - validation = await self.validate(ctx.author, tag_name) - - if validation is not None: - return await ctx.send(embed=validation) - embed = Embed() embed.colour = Colour.red() tag_data = await self.get_tag_data(tag_name) @@ -259,7 +255,7 @@ class Tags: @with_role(ADMIN_ROLE, OWNER_ROLE, MODERATOR_ROLE) @command(name="tags.set()", aliases=["tags.set", "tags.add", "tags.add()", "tags.edit", "tags.edit()", "add_tag"]) - async def set_command(self, ctx: Context, tag_name: str, tag_content: str): + async def set_command(self, ctx: Context, tag_name: TagNameConverter, tag_content: TagContentConverter): """ Create a new tag or edit an existing one. @@ -268,11 +264,6 @@ class Tags: :param tag_content: The content of the tag. """ - validation = await self.validate(ctx.author, tag_name, tag_content) - - if validation is not None: - return await ctx.send(embed=validation) - tag_name = tag_name.lower().strip() tag_content = tag_content.strip() @@ -300,7 +291,7 @@ class Tags: @with_role(ADMIN_ROLE, OWNER_ROLE) @command(name="tags.delete()", aliases=["tags.delete", "tags.remove", "tags.remove()", "remove_tag"]) - async def delete_command(self, ctx: Context, tag_name: str): + async def delete_command(self, ctx: Context, tag_name: TagNameConverter): """ Remove a tag from the database. @@ -308,11 +299,6 @@ class Tags: :param tag_name: The name of the tag to delete. """ - validation = await self.validate(ctx.author, tag_name) - - if validation is not None: - return await ctx.send(embed=validation) - tag_name = tag_name.lower().strip() embed = Embed() embed.colour = Colour.red() @@ -340,6 +326,17 @@ class Tags: return await ctx.send(embed=embed) + @get_command.error + @set_command.error + @delete_command.error + async def command_error(self, ctx, error): + if isinstance(error, CogBadArgument): + embed = Embed() + embed.colour = Colour.red() + embed.description = str(error) + embed.title = random.choice(self.FAIL_TITLES) + await ctx.send(embed=embed) + @command(name="tags.keys()") async def keys_command(self, ctx: Context): """ diff --git a/bot/exceptions.py b/bot/exceptions.py new file mode 100644 index 000000000..8f5da91e9 --- /dev/null +++ b/bot/exceptions.py @@ -0,0 +1,10 @@ +from discord.ext.commands import BadArgument + + +class CogBadArgument(BadArgument): + """ + A custom `BadArgument` subclass that can be used for + setting up custom error handlers on a per-command + basis in cogs. The standard `on_command_error` handler + ignores any exceptions of this type. + """ |