|
26 | 26 | from __future__ import annotations
|
27 | 27 |
|
28 | 28 | import asyncio
|
29 |
| -import types |
30 | 29 | import functools
|
31 | 30 | import inspect
|
| 31 | +import re |
| 32 | +import types |
32 | 33 | from collections import OrderedDict
|
33 | 34 | from typing import Any, Callable, Dict, List, Optional, Union, TYPE_CHECKING
|
34 | 35 |
|
| 36 | +from .context import ApplicationContext, AutocompleteContext |
| 37 | +from .errors import ApplicationCommandError, CheckFailure, ApplicationCommandInvokeError |
| 38 | +from .permissions import Permission |
35 | 39 | from ..enums import SlashCommandOptionType, ChannelType
|
| 40 | +from ..errors import ValidationError, ClientException |
36 | 41 | from ..member import Member
|
37 |
| -from ..user import User |
38 | 42 | from ..message import Message
|
39 |
| -from .context import ApplicationContext, AutocompleteContext |
| 43 | +from ..user import User |
40 | 44 | from ..utils import find, get_or_fetch, async_all
|
41 |
| -from ..errors import ValidationError, ClientException |
42 |
| -from .errors import ApplicationCommandError, CheckFailure, ApplicationCommandInvokeError |
43 |
| -from .permissions import Permission |
44 | 45 |
|
45 | 46 | __all__ = (
|
46 | 47 | "_BaseCommand",
|
@@ -390,7 +391,7 @@ def __init__(self, func: Callable, *args, **kwargs) -> None:
|
390 | 391 | self.cog = None
|
391 | 392 |
|
392 | 393 | params = self._get_signature_parameters()
|
393 |
| - self.options: List[Option] = self._parse_options(params) |
| 394 | + self.options: List[Option] = kwargs.get('options') or self._parse_options(params) |
394 | 395 |
|
395 | 396 | try:
|
396 | 397 | checks = func.__commands_checks__
|
@@ -459,6 +460,9 @@ def _parse_options(self, params) -> List[Option]:
|
459 | 460 | option.name = p_name
|
460 | 461 | option._parameter_name = p_name
|
461 | 462 |
|
| 463 | + validate_chat_input_name(option.name) |
| 464 | + validate_chat_input_description(option.description) |
| 465 | + |
462 | 466 | final_options.append(option)
|
463 | 467 |
|
464 | 468 | return final_options
|
@@ -645,7 +649,7 @@ def __init__(
|
645 | 649 | minmax_types = (int, float, type(None))
|
646 | 650 | else:
|
647 | 651 | minmax_types = (type(None),)
|
648 |
| - minmax_typehint = Optional[Union[minmax_types]] # type: ignore |
| 652 | + minmax_typehint = Optional[Union[minmax_types]] # type: ignore |
649 | 653 |
|
650 | 654 | self.min_value: minmax_typehint = kwargs.pop("min_value", None)
|
651 | 655 | self.max_value: minmax_typehint = kwargs.pop("max_value", None)
|
@@ -1128,24 +1132,33 @@ def command(**kwargs):
|
1128 | 1132 | """
|
1129 | 1133 | return application_command(**kwargs)
|
1130 | 1134 |
|
| 1135 | + |
| 1136 | +docs = "https://discord.com/developers/docs" |
| 1137 | + |
| 1138 | + |
1131 | 1139 | # Validation
|
1132 | 1140 | def validate_chat_input_name(name: Any):
|
| 1141 | + # Must meet the regex ^[\w-]{1,32}$ |
1133 | 1142 | if not isinstance(name, str):
|
1134 |
| - raise TypeError("Name of a command must be a string.") |
1135 |
| - if " " in name: |
1136 |
| - raise ValidationError("Name of a chat input command cannot have spaces.") |
1137 |
| - if not name.islower(): |
1138 |
| - raise ValidationError("Name of a chat input command must be lowercase.") |
1139 |
| - if len(name) > 32 or len(name) < 1: |
| 1143 | + raise TypeError(f"Chat input command names and options must be of type str. Received {name}") |
| 1144 | + if not re.match(r"^[\w-]{1,32}$", name): |
| 1145 | + raise ValidationError( |
| 1146 | + r'Chat input command names and options must follow the regex "^[\w-]{1,32}$". For more information, see ' |
| 1147 | + f"{docs}/interactions/application-commands#application-command-object-application-command-naming. Received " |
| 1148 | + f"{name}" |
| 1149 | + ) |
| 1150 | + if not 1 <= len(name) <= 32: |
1140 | 1151 | raise ValidationError(
|
1141 |
| - "Name of a chat input command must be less than 32 characters and non empty." |
| 1152 | + f"Chat input command names and options must be 1-32 characters long. Received {name}" |
1142 | 1153 | )
|
| 1154 | + if not name.lower() == name: # Can't use islower() as it fails if none of the chars can be lower. See #512. |
| 1155 | + raise ValidationError(f"Chat input command names and options must be lowercase. Received {name}") |
1143 | 1156 |
|
1144 | 1157 |
|
1145 | 1158 | def validate_chat_input_description(description: Any):
|
1146 | 1159 | if not isinstance(description, str):
|
1147 |
| - raise TypeError("Description of a command must be a string.") |
1148 |
| - if len(description) > 100 or len(description) < 1: |
| 1160 | + raise TypeError(f"Command description must be of type str. Received {description}") |
| 1161 | + if not 1 <= len(description) <= 100: |
1149 | 1162 | raise ValidationError(
|
1150 |
| - "Description of a chat input command must be less than 100 characters and non empty." |
| 1163 | + f"Command description must be 1-100 characters long. Received {description}" |
1151 | 1164 | )
|
0 commit comments