@@ -161,9 +161,7 @@ class ObjectConverter(IDConverter[discord.Object]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.Object:
|
async def convert(self, ctx: Context, argument: str) -> discord.Object:
|
||||||
match = self._get_id_match(argument) or re.match(
|
match = self._get_id_match(argument) or re.match(r"<(?:@(?:!|&)?|#)([0-9]{15,20})>$", argument)
|
||||||
r"<(?:@(?:!|&)?|#)([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
|
|
||||||
if match is None:
|
if match is None:
|
||||||
raise ObjectNotFound(argument)
|
raise ObjectNotFound(argument)
|
||||||
@@ -200,14 +198,10 @@ class MemberConverter(IDConverter[discord.Member]):
|
|||||||
if len(argument) > 5 and argument[-5] == "#":
|
if len(argument) > 5 and argument[-5] == "#":
|
||||||
username, _, discriminator = argument.rpartition("#")
|
username, _, discriminator = argument.rpartition("#")
|
||||||
members = await guild.query_members(username, limit=100, cache=cache)
|
members = await guild.query_members(username, limit=100, cache=cache)
|
||||||
return discord.utils.get(
|
return discord.utils.get(members, name=username, discriminator=discriminator)
|
||||||
members, name=username, discriminator=discriminator
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
members = await guild.query_members(argument, limit=100, cache=cache)
|
members = await guild.query_members(argument, limit=100, cache=cache)
|
||||||
return discord.utils.find(
|
return discord.utils.find(lambda m: m.name == argument or m.nick == argument, members)
|
||||||
lambda m: m.name == argument or m.nick == argument, members
|
|
||||||
)
|
|
||||||
|
|
||||||
async def query_member_by_id(self, bot, guild, user_id):
|
async def query_member_by_id(self, bot, guild, user_id):
|
||||||
ws = bot._get_websocket(shard_id=guild.shard_id)
|
ws = bot._get_websocket(shard_id=guild.shard_id)
|
||||||
@@ -232,9 +226,7 @@ class MemberConverter(IDConverter[discord.Member]):
|
|||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.Member:
|
async def convert(self, ctx: Context, argument: str) -> discord.Member:
|
||||||
bot = ctx.bot
|
bot = ctx.bot
|
||||||
match = self._get_id_match(argument) or re.match(
|
match = self._get_id_match(argument) or re.match(r"<@!?([0-9]{15,20})>$", argument)
|
||||||
r"<@!?([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
result = None
|
result = None
|
||||||
user_id = None
|
user_id = None
|
||||||
@@ -247,9 +239,7 @@ class MemberConverter(IDConverter[discord.Member]):
|
|||||||
else:
|
else:
|
||||||
user_id = int(match.group(1))
|
user_id = int(match.group(1))
|
||||||
if guild:
|
if guild:
|
||||||
result = guild.get_member(user_id) or _utils_get(
|
result = guild.get_member(user_id) or _utils_get(ctx.message.mentions, id=user_id)
|
||||||
ctx.message.mentions, id=user_id
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
result = _get_from_guilds(bot, "get_member", user_id)
|
result = _get_from_guilds(bot, "get_member", user_id)
|
||||||
|
|
||||||
@@ -289,17 +279,13 @@ class UserConverter(IDConverter[discord.User]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.User:
|
async def convert(self, ctx: Context, argument: str) -> discord.User:
|
||||||
match = self._get_id_match(argument) or re.match(
|
match = self._get_id_match(argument) or re.match(r"<@!?([0-9]{15,20})>$", argument)
|
||||||
r"<@!?([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
result = None
|
result = None
|
||||||
state = ctx._state
|
state = ctx._state
|
||||||
|
|
||||||
if match is not None:
|
if match is not None:
|
||||||
user_id = int(match.group(1))
|
user_id = int(match.group(1))
|
||||||
result = ctx.bot.get_user(user_id) or _utils_get(
|
result = ctx.bot.get_user(user_id) or _utils_get(ctx.message.mentions, id=user_id)
|
||||||
ctx.message.mentions, id=user_id
|
|
||||||
)
|
|
||||||
if result is None:
|
if result is None:
|
||||||
try:
|
try:
|
||||||
result = await ctx.bot.fetch_user(user_id)
|
result = await ctx.bot.fetch_user(user_id)
|
||||||
@@ -347,9 +333,7 @@ class PartialMessageConverter(Converter[discord.PartialMessage]):
|
|||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _get_id_matches(ctx, argument):
|
def _get_id_matches(ctx, argument):
|
||||||
id_regex = re.compile(
|
id_regex = re.compile(r"(?:(?P<channel_id>[0-9]{15,20})-)?(?P<message_id>[0-9]{15,20})$")
|
||||||
r"(?:(?P<channel_id>[0-9]{15,20})-)?(?P<message_id>[0-9]{15,20})$"
|
|
||||||
)
|
|
||||||
link_regex = re.compile(
|
link_regex = re.compile(
|
||||||
r"https?://(?:(ptb|canary|www)\.)?discord(?:app)?\.com/channels/"
|
r"https?://(?:(ptb|canary|www)\.)?discord(?:app)?\.com/channels/"
|
||||||
r"(?P<guild_id>[0-9]{15,20}|@me)"
|
r"(?P<guild_id>[0-9]{15,20}|@me)"
|
||||||
@@ -371,9 +355,7 @@ class PartialMessageConverter(Converter[discord.PartialMessage]):
|
|||||||
return guild_id, message_id, channel_id
|
return guild_id, message_id, channel_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _resolve_channel(
|
def _resolve_channel(ctx, guild_id, channel_id) -> Optional[PartialMessageableChannel]:
|
||||||
ctx, guild_id, channel_id
|
|
||||||
) -> Optional[PartialMessageableChannel]:
|
|
||||||
if guild_id is not None:
|
if guild_id is not None:
|
||||||
guild = ctx.bot.get_guild(guild_id)
|
guild = ctx.bot.get_guild(guild_id)
|
||||||
if guild is not None and channel_id is not None:
|
if guild is not None and channel_id is not None:
|
||||||
@@ -407,9 +389,7 @@ class MessageConverter(IDConverter[discord.Message]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.Message:
|
async def convert(self, ctx: Context, argument: str) -> discord.Message:
|
||||||
guild_id, message_id, channel_id = PartialMessageConverter._get_id_matches(
|
guild_id, message_id, channel_id = PartialMessageConverter._get_id_matches(ctx, argument)
|
||||||
ctx, argument
|
|
||||||
)
|
|
||||||
message = ctx.bot._connection._get_message(message_id)
|
message = ctx.bot._connection._get_message(message_id)
|
||||||
if message:
|
if message:
|
||||||
return message
|
return message
|
||||||
@@ -440,19 +420,13 @@ class GuildChannelConverter(IDConverter[discord.abc.GuildChannel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.abc.GuildChannel:
|
async def convert(self, ctx: Context, argument: str) -> discord.abc.GuildChannel:
|
||||||
return self._resolve_channel(
|
return self._resolve_channel(ctx, argument, "channels", discord.abc.GuildChannel)
|
||||||
ctx, argument, "channels", discord.abc.GuildChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _resolve_channel(
|
def _resolve_channel(ctx: Context, argument: str, attribute: str, type: Type[CT]) -> CT:
|
||||||
ctx: Context, argument: str, attribute: str, type: Type[CT]
|
|
||||||
) -> CT:
|
|
||||||
bot = ctx.bot
|
bot = ctx.bot
|
||||||
|
|
||||||
match = IDConverter._get_id_match(argument) or re.match(
|
match = IDConverter._get_id_match(argument) or re.match(r"<#([0-9]{15,20})>$", argument)
|
||||||
r"<#([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
result = None
|
result = None
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
@@ -480,14 +454,10 @@ class GuildChannelConverter(IDConverter[discord.abc.GuildChannel]):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _resolve_thread(
|
def _resolve_thread(ctx: Context, argument: str, attribute: str, type: Type[TT]) -> TT:
|
||||||
ctx: Context, argument: str, attribute: str, type: Type[TT]
|
|
||||||
) -> TT:
|
|
||||||
bot = ctx.bot
|
bot = ctx.bot
|
||||||
|
|
||||||
match = IDConverter._get_id_match(argument) or re.match(
|
match = IDConverter._get_id_match(argument) or re.match(r"<#([0-9]{15,20})>$", argument)
|
||||||
r"<#([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
result = None
|
result = None
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
|
|
||||||
@@ -524,9 +494,7 @@ class TextChannelConverter(IDConverter[discord.TextChannel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.TextChannel:
|
async def convert(self, ctx: Context, argument: str) -> discord.TextChannel:
|
||||||
return GuildChannelConverter._resolve_channel(
|
return GuildChannelConverter._resolve_channel(ctx, argument, "text_channels", discord.TextChannel)
|
||||||
ctx, argument, "text_channels", discord.TextChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class VoiceChannelConverter(IDConverter[discord.VoiceChannel]):
|
class VoiceChannelConverter(IDConverter[discord.VoiceChannel]):
|
||||||
@@ -546,9 +514,7 @@ class VoiceChannelConverter(IDConverter[discord.VoiceChannel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.VoiceChannel:
|
async def convert(self, ctx: Context, argument: str) -> discord.VoiceChannel:
|
||||||
return GuildChannelConverter._resolve_channel(
|
return GuildChannelConverter._resolve_channel(ctx, argument, "voice_channels", discord.VoiceChannel)
|
||||||
ctx, argument, "voice_channels", discord.VoiceChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class StageChannelConverter(IDConverter[discord.StageChannel]):
|
class StageChannelConverter(IDConverter[discord.StageChannel]):
|
||||||
@@ -567,9 +533,7 @@ class StageChannelConverter(IDConverter[discord.StageChannel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.StageChannel:
|
async def convert(self, ctx: Context, argument: str) -> discord.StageChannel:
|
||||||
return GuildChannelConverter._resolve_channel(
|
return GuildChannelConverter._resolve_channel(ctx, argument, "stage_channels", discord.StageChannel)
|
||||||
ctx, argument, "stage_channels", discord.StageChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class CategoryChannelConverter(IDConverter[discord.CategoryChannel]):
|
class CategoryChannelConverter(IDConverter[discord.CategoryChannel]):
|
||||||
@@ -589,9 +553,7 @@ class CategoryChannelConverter(IDConverter[discord.CategoryChannel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.CategoryChannel:
|
async def convert(self, ctx: Context, argument: str) -> discord.CategoryChannel:
|
||||||
return GuildChannelConverter._resolve_channel(
|
return GuildChannelConverter._resolve_channel(ctx, argument, "categories", discord.CategoryChannel)
|
||||||
ctx, argument, "categories", discord.CategoryChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class StoreChannelConverter(IDConverter[discord.StoreChannel]):
|
class StoreChannelConverter(IDConverter[discord.StoreChannel]):
|
||||||
@@ -610,9 +572,7 @@ class StoreChannelConverter(IDConverter[discord.StoreChannel]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.StoreChannel:
|
async def convert(self, ctx: Context, argument: str) -> discord.StoreChannel:
|
||||||
return GuildChannelConverter._resolve_channel(
|
return GuildChannelConverter._resolve_channel(ctx, argument, "channels", discord.StoreChannel)
|
||||||
ctx, argument, "channels", discord.StoreChannel
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ThreadConverter(IDConverter[discord.Thread]):
|
class ThreadConverter(IDConverter[discord.Thread]):
|
||||||
@@ -630,9 +590,7 @@ class ThreadConverter(IDConverter[discord.Thread]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.Thread:
|
async def convert(self, ctx: Context, argument: str) -> discord.Thread:
|
||||||
return GuildChannelConverter._resolve_thread(
|
return GuildChannelConverter._resolve_thread(ctx, argument, "threads", discord.Thread)
|
||||||
ctx, argument, "threads", discord.Thread
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class ColourConverter(Converter[discord.Colour]):
|
class ColourConverter(Converter[discord.Colour]):
|
||||||
@@ -661,9 +619,7 @@ class ColourConverter(Converter[discord.Colour]):
|
|||||||
Added support for ``rgb`` function and 3-digit hex shortcuts
|
Added support for ``rgb`` function and 3-digit hex shortcuts
|
||||||
"""
|
"""
|
||||||
|
|
||||||
RGB_REGEX = re.compile(
|
RGB_REGEX = re.compile(r"rgb\s*\((?P<r>[0-9]{1,3}%?)\s*,\s*(?P<g>[0-9]{1,3}%?)\s*,\s*(?P<b>[0-9]{1,3}%?)\s*\)")
|
||||||
r"rgb\s*\((?P<r>[0-9]{1,3}%?)\s*,\s*(?P<g>[0-9]{1,3}%?)\s*,\s*(?P<b>[0-9]{1,3}%?)\s*\)"
|
|
||||||
)
|
|
||||||
|
|
||||||
def parse_hex_number(self, argument):
|
def parse_hex_number(self, argument):
|
||||||
arg = "".join(i * 2 for i in argument) if len(argument) == 3 else argument
|
arg = "".join(i * 2 for i in argument) if len(argument) == 3 else argument
|
||||||
@@ -744,9 +700,7 @@ class RoleConverter(IDConverter[discord.Role]):
|
|||||||
if not guild:
|
if not guild:
|
||||||
raise NoPrivateMessage()
|
raise NoPrivateMessage()
|
||||||
|
|
||||||
match = self._get_id_match(argument) or re.match(
|
match = self._get_id_match(argument) or re.match(r"<@&([0-9]{15,20})>$", argument)
|
||||||
r"<@&([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
if match:
|
if match:
|
||||||
result = guild.get_role(int(match.group(1)))
|
result = guild.get_role(int(match.group(1)))
|
||||||
else:
|
else:
|
||||||
@@ -825,9 +779,7 @@ class EmojiConverter(IDConverter[discord.Emoji]):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
async def convert(self, ctx: Context, argument: str) -> discord.Emoji:
|
async def convert(self, ctx: Context, argument: str) -> discord.Emoji:
|
||||||
match = self._get_id_match(argument) or re.match(
|
match = self._get_id_match(argument) or re.match(r"<a?:[a-zA-Z0-9\_]{1,32}:([0-9]{15,20})>$", argument)
|
||||||
r"<a?:[a-zA-Z0-9\_]{1,32}:([0-9]{15,20})>$", argument
|
|
||||||
)
|
|
||||||
result = None
|
result = None
|
||||||
bot = ctx.bot
|
bot = ctx.bot
|
||||||
guild = ctx.guild
|
guild = ctx.guild
|
||||||
@@ -869,10 +821,7 @@ class PartialEmojiConverter(Converter[discord.PartialEmoji]):
|
|||||||
emoji_id = int(match.group(3))
|
emoji_id = int(match.group(3))
|
||||||
|
|
||||||
return discord.PartialEmoji.with_state(
|
return discord.PartialEmoji.with_state(
|
||||||
ctx.bot._connection,
|
ctx.bot._connection, animated=emoji_animated, name=emoji_name, id=emoji_id
|
||||||
animated=emoji_animated,
|
|
||||||
name=emoji_name,
|
|
||||||
id=emoji_id,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
raise PartialEmojiConversionFailure(argument)
|
raise PartialEmojiConversionFailure(argument)
|
||||||
@@ -957,11 +906,7 @@ class clean_content(Converter[str]):
|
|||||||
|
|
||||||
def resolve_member(id: int) -> str:
|
def resolve_member(id: int) -> str:
|
||||||
m = _utils_get(msg.mentions, id=id) or ctx.guild.get_member(id)
|
m = _utils_get(msg.mentions, id=id) or ctx.guild.get_member(id)
|
||||||
return (
|
return f"@{m.display_name if self.use_nicknames else m.name}" if m else "@deleted-user"
|
||||||
f"@{m.display_name if self.use_nicknames else m.name}"
|
|
||||||
if m
|
|
||||||
else "@deleted-user"
|
|
||||||
)
|
|
||||||
|
|
||||||
def resolve_role(id: int) -> str:
|
def resolve_role(id: int) -> str:
|
||||||
r = _utils_get(msg.role_mentions, id=id) or ctx.guild.get_role(id)
|
r = _utils_get(msg.role_mentions, id=id) or ctx.guild.get_role(id)
|
||||||
@@ -1051,11 +996,7 @@ class Greedy(List[T]):
|
|||||||
origin = getattr(converter, "__origin__", None)
|
origin = getattr(converter, "__origin__", None)
|
||||||
args = getattr(converter, "__args__", ())
|
args = getattr(converter, "__args__", ())
|
||||||
|
|
||||||
if not (
|
if not (callable(converter) or isinstance(converter, Converter) or origin is not None):
|
||||||
callable(converter)
|
|
||||||
or isinstance(converter, Converter)
|
|
||||||
or origin is not None
|
|
||||||
):
|
|
||||||
raise TypeError("Greedy[...] expects a type or a Converter instance.")
|
raise TypeError("Greedy[...] expects a type or a Converter instance.")
|
||||||
|
|
||||||
if converter in (str, type(None)) or origin is Greedy:
|
if converter in (str, type(None)) or origin is Greedy:
|
||||||
@@ -1103,13 +1044,7 @@ class Option(Generic[T, DT]): # type: ignore
|
|||||||
"name",
|
"name",
|
||||||
)
|
)
|
||||||
|
|
||||||
def __init__(
|
def __init__(self, default: T = inspect.Parameter.empty, *, description: DT, name: str = discord.utils.MISSING) -> None:
|
||||||
self,
|
|
||||||
default: T = inspect.Parameter.empty,
|
|
||||||
*,
|
|
||||||
description: DT,
|
|
||||||
name: str = discord.utils.MISSING,
|
|
||||||
) -> None:
|
|
||||||
self.description = description
|
self.description = description
|
||||||
self.default = default
|
self.default = default
|
||||||
self.name: str = name
|
self.name: str = name
|
||||||
@@ -1117,12 +1052,7 @@ class Option(Generic[T, DT]): # type: ignore
|
|||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
# Terrible workaround for type checking reasons
|
# Terrible workaround for type checking reasons
|
||||||
def Option(
|
def Option(default: T = inspect.Parameter.empty, *, description: str, name: str = discord.utils.MISSING) -> T:
|
||||||
default: T = inspect.Parameter.empty,
|
|
||||||
*,
|
|
||||||
description: str,
|
|
||||||
name: str = discord.utils.MISSING,
|
|
||||||
) -> T:
|
|
||||||
...
|
...
|
||||||
|
|
||||||
|
|
||||||
@@ -1177,9 +1107,7 @@ CONVERTER_MAPPING: Dict[Type[Any], Any] = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def _actual_conversion(
|
async def _actual_conversion(ctx: Context, converter, argument: str, param: inspect.Parameter):
|
||||||
ctx: Context, converter, argument: str, param: inspect.Parameter
|
|
||||||
):
|
|
||||||
if converter is bool:
|
if converter is bool:
|
||||||
return _convert_to_bool(argument)
|
return _convert_to_bool(argument)
|
||||||
|
|
||||||
@@ -1188,9 +1116,7 @@ async def _actual_conversion(
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if module is not None and (
|
if module is not None and (module.startswith("discord.") and not module.endswith("converter")):
|
||||||
module.startswith("discord.") and not module.endswith("converter")
|
|
||||||
):
|
|
||||||
converter = CONVERTER_MAPPING.get(converter, converter)
|
converter = CONVERTER_MAPPING.get(converter, converter)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -1216,14 +1142,10 @@ async def _actual_conversion(
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
name = converter.__class__.__name__
|
name = converter.__class__.__name__
|
||||||
|
|
||||||
raise BadArgument(
|
raise BadArgument(f'Converting to "{name}" failed for parameter "{param.name}".') from exc
|
||||||
f'Converting to "{name}" failed for parameter "{param.name}".'
|
|
||||||
) from exc
|
|
||||||
|
|
||||||
|
|
||||||
async def run_converters(
|
async def run_converters(ctx: Context, converter, argument: str, param: inspect.Parameter):
|
||||||
ctx: Context, converter, argument: str, param: inspect.Parameter
|
|
||||||
):
|
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Runs converters for a given converter, argument, and parameter.
|
Runs converters for a given converter, argument, and parameter.
|
||||||
|
@@ -53,13 +53,7 @@ from operator import itemgetter
|
|||||||
import discord
|
import discord
|
||||||
|
|
||||||
from .errors import *
|
from .errors import *
|
||||||
from .cooldowns import (
|
from .cooldowns import Cooldown, BucketType, CooldownMapping, MaxConcurrency, DynamicCooldownMapping
|
||||||
Cooldown,
|
|
||||||
BucketType,
|
|
||||||
CooldownMapping,
|
|
||||||
MaxConcurrency,
|
|
||||||
DynamicCooldownMapping,
|
|
||||||
)
|
|
||||||
from .converter import (
|
from .converter import (
|
||||||
CONVERTER_MAPPING,
|
CONVERTER_MAPPING,
|
||||||
Converter,
|
Converter,
|
||||||
@@ -80,10 +74,7 @@ if TYPE_CHECKING:
|
|||||||
from typing_extensions import Concatenate, ParamSpec, TypeGuard
|
from typing_extensions import Concatenate, ParamSpec, TypeGuard
|
||||||
|
|
||||||
from discord.message import Message
|
from discord.message import Message
|
||||||
from discord.types.interactions import (
|
from discord.types.interactions import EditApplicationCommand, ApplicationCommandInteractionDataOption
|
||||||
EditApplicationCommand,
|
|
||||||
ApplicationCommandInteractionDataOption,
|
|
||||||
)
|
|
||||||
|
|
||||||
from ._types import (
|
from ._types import (
|
||||||
Coro,
|
Coro,
|
||||||
@@ -403,9 +394,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
|
|
||||||
self.slash_command: Optional[bool] = kwargs.get("slash_command", None)
|
self.slash_command: Optional[bool] = kwargs.get("slash_command", None)
|
||||||
self.message_command: Optional[bool] = kwargs.get("message_command", None)
|
self.message_command: Optional[bool] = kwargs.get("message_command", None)
|
||||||
self.slash_command_guilds: Optional[Iterable[int]] = kwargs.get(
|
self.slash_command_guilds: Optional[Iterable[int]] = kwargs.get("slash_command_guilds", None)
|
||||||
"slash_command_guilds", None
|
|
||||||
)
|
|
||||||
|
|
||||||
help_doc = kwargs.get("help")
|
help_doc = kwargs.get("help")
|
||||||
if help_doc is not None:
|
if help_doc is not None:
|
||||||
@@ -424,9 +413,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
self.extras: Dict[str, Any] = kwargs.get("extras", {})
|
self.extras: Dict[str, Any] = kwargs.get("extras", {})
|
||||||
|
|
||||||
if not isinstance(self.aliases, (list, tuple)):
|
if not isinstance(self.aliases, (list, tuple)):
|
||||||
raise TypeError(
|
raise TypeError("Aliases of a command must be a list or a tuple of strings.")
|
||||||
"Aliases of a command must be a list or a tuple of strings."
|
|
||||||
)
|
|
||||||
|
|
||||||
self.description: str = inspect.cleandoc(kwargs.get("description", ""))
|
self.description: str = inspect.cleandoc(kwargs.get("description", ""))
|
||||||
self.hidden: bool = kwargs.get("hidden", False)
|
self.hidden: bool = kwargs.get("hidden", False)
|
||||||
@@ -452,9 +439,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
elif isinstance(cooldown, CooldownMapping):
|
elif isinstance(cooldown, CooldownMapping):
|
||||||
buckets = cooldown
|
buckets = cooldown
|
||||||
else:
|
else:
|
||||||
raise TypeError(
|
raise TypeError("Cooldown must be a an instance of CooldownMapping or None.")
|
||||||
"Cooldown must be a an instance of CooldownMapping or None."
|
|
||||||
)
|
|
||||||
|
|
||||||
self.checks: List[Check] = checks
|
self.checks: List[Check] = checks
|
||||||
self._buckets: CooldownMapping = buckets
|
self._buckets: CooldownMapping = buckets
|
||||||
@@ -495,10 +480,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
@property
|
@property
|
||||||
def callback(
|
def callback(
|
||||||
self,
|
self,
|
||||||
) -> Union[
|
) -> Union[Callable[Concatenate[CogT, Context, P], Coro[T]], Callable[Concatenate[Context, P], Coro[T]],]:
|
||||||
Callable[Concatenate[CogT, Context, P], Coro[T]],
|
|
||||||
Callable[Concatenate[Context, P], Coro[T]],
|
|
||||||
]:
|
|
||||||
return self._callback
|
return self._callback
|
||||||
|
|
||||||
@callback.setter
|
@callback.setter
|
||||||
@@ -518,9 +500,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
globalns = {}
|
globalns = {}
|
||||||
|
|
||||||
self.params, self.option_descriptions = get_signature_parameters(
|
self.params, self.option_descriptions = get_signature_parameters(function, globalns)
|
||||||
function, globalns
|
|
||||||
)
|
|
||||||
|
|
||||||
def _update_attrs(self, **command_attrs: Any):
|
def _update_attrs(self, **command_attrs: Any):
|
||||||
for key, value in command_attrs.items():
|
for key, value in command_attrs.items():
|
||||||
@@ -654,9 +634,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
|
|
||||||
required = param.default is param.empty
|
required = param.default is param.empty
|
||||||
converter = get_converter(param)
|
converter = get_converter(param)
|
||||||
consume_rest_is_special = (
|
consume_rest_is_special = param.kind == param.KEYWORD_ONLY and not self.rest_is_raw
|
||||||
param.kind == param.KEYWORD_ONLY and not self.rest_is_raw
|
|
||||||
)
|
|
||||||
view = ctx.view
|
view = ctx.view
|
||||||
view.skip_ws()
|
view.skip_ws()
|
||||||
|
|
||||||
@@ -664,13 +642,9 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
# it undos the view ready for the next parameter to use instead
|
# it undos the view ready for the next parameter to use instead
|
||||||
if isinstance(converter, Greedy):
|
if isinstance(converter, Greedy):
|
||||||
if param.kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY):
|
if param.kind in (param.POSITIONAL_OR_KEYWORD, param.POSITIONAL_ONLY):
|
||||||
return await self._transform_greedy_pos(
|
return await self._transform_greedy_pos(ctx, param, required, converter.converter)
|
||||||
ctx, param, required, converter.converter
|
|
||||||
)
|
|
||||||
elif param.kind == param.VAR_POSITIONAL:
|
elif param.kind == param.VAR_POSITIONAL:
|
||||||
return await self._transform_greedy_var_pos(
|
return await self._transform_greedy_var_pos(ctx, param, converter.converter)
|
||||||
ctx, param, converter.converter
|
|
||||||
)
|
|
||||||
else:
|
else:
|
||||||
# if we're here, then it's a KEYWORD_ONLY param type
|
# if we're here, then it's a KEYWORD_ONLY param type
|
||||||
# since this is mostly useless, we'll helpfully transform Greedy[X]
|
# since this is mostly useless, we'll helpfully transform Greedy[X]
|
||||||
@@ -683,10 +657,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
if required:
|
if required:
|
||||||
if self._is_typing_optional(param.annotation):
|
if self._is_typing_optional(param.annotation):
|
||||||
return None
|
return None
|
||||||
if (
|
if hasattr(converter, "__commands_is_flag__") and converter._can_be_constructible():
|
||||||
hasattr(converter, "__commands_is_flag__")
|
|
||||||
and converter._can_be_constructible()
|
|
||||||
):
|
|
||||||
return await converter._construct_default(ctx)
|
return await converter._construct_default(ctx)
|
||||||
raise MissingRequiredArgument(param)
|
raise MissingRequiredArgument(param)
|
||||||
return param.default
|
return param.default
|
||||||
@@ -731,9 +702,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
return param.default
|
return param.default
|
||||||
return result
|
return result
|
||||||
|
|
||||||
async def _transform_greedy_var_pos(
|
async def _transform_greedy_var_pos(self, ctx: Context, param: inspect.Parameter, converter: Any) -> Any:
|
||||||
self, ctx: Context, param: inspect.Parameter, converter: Any
|
|
||||||
) -> Any:
|
|
||||||
view = ctx.view
|
view = ctx.view
|
||||||
previous = view.index
|
previous = view.index
|
||||||
try:
|
try:
|
||||||
@@ -847,17 +816,13 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
try:
|
try:
|
||||||
next(iterator)
|
next(iterator)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise discord.ClientException(
|
raise discord.ClientException(f'Callback for {self.name} command is missing "self" parameter.')
|
||||||
f'Callback for {self.name} command is missing "self" parameter.'
|
|
||||||
)
|
|
||||||
|
|
||||||
# next we have the 'ctx' as the next parameter
|
# next we have the 'ctx' as the next parameter
|
||||||
try:
|
try:
|
||||||
next(iterator)
|
next(iterator)
|
||||||
except StopIteration:
|
except StopIteration:
|
||||||
raise discord.ClientException(
|
raise discord.ClientException(f'Callback for {self.name} command is missing "ctx" parameter.')
|
||||||
f'Callback for {self.name} command is missing "ctx" parameter.'
|
|
||||||
)
|
|
||||||
|
|
||||||
for name, param in iterator:
|
for name, param in iterator:
|
||||||
ctx.current_parameter = param
|
ctx.current_parameter = param
|
||||||
@@ -884,9 +849,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
break
|
break
|
||||||
|
|
||||||
if not self.ignore_extra and not view.eof:
|
if not self.ignore_extra and not view.eof:
|
||||||
raise TooManyArguments(
|
raise TooManyArguments("Too many arguments passed to " + self.qualified_name)
|
||||||
"Too many arguments passed to " + self.qualified_name
|
|
||||||
)
|
|
||||||
|
|
||||||
async def call_before_hooks(self, ctx: Context) -> None:
|
async def call_before_hooks(self, ctx: Context) -> None:
|
||||||
# now that we're done preparing we can call the pre-command hooks
|
# now that we're done preparing we can call the pre-command hooks
|
||||||
@@ -946,9 +909,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
ctx.command = self
|
ctx.command = self
|
||||||
|
|
||||||
if not await self.can_run(ctx):
|
if not await self.can_run(ctx):
|
||||||
raise CheckFailure(
|
raise CheckFailure(f"The check functions for command {self.qualified_name} failed.")
|
||||||
f"The check functions for command {self.qualified_name} failed."
|
|
||||||
)
|
|
||||||
|
|
||||||
if self._max_concurrency is not None:
|
if self._max_concurrency is not None:
|
||||||
# For this application, context can be duck-typed as a Message
|
# For this application, context can be duck-typed as a Message
|
||||||
@@ -1157,9 +1118,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
return self.help.split("\n", 1)[0]
|
return self.help.split("\n", 1)[0]
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
def _is_typing_optional(
|
def _is_typing_optional(self, annotation: Union[T, Optional[T]]) -> TypeGuard[Optional[T]]:
|
||||||
self, annotation: Union[T, Optional[T]]
|
|
||||||
) -> TypeGuard[Optional[T]]:
|
|
||||||
return getattr(annotation, "__origin__", None) is Union and type(None) in annotation.__args__ # type: ignore
|
return getattr(annotation, "__origin__", None) is Union and type(None) in annotation.__args__ # type: ignore
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -1190,24 +1149,13 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
origin = getattr(annotation, "__origin__", None)
|
origin = getattr(annotation, "__origin__", None)
|
||||||
|
|
||||||
if origin is Literal:
|
if origin is Literal:
|
||||||
name = "|".join(
|
name = "|".join(f'"{v}"' if isinstance(v, str) else str(v) for v in annotation.__args__)
|
||||||
f'"{v}"' if isinstance(v, str) else str(v)
|
|
||||||
for v in annotation.__args__
|
|
||||||
)
|
|
||||||
if param.default is not param.empty:
|
if param.default is not param.empty:
|
||||||
# We don't want None or '' to trigger the [name=value] case and instead it should
|
# We don't want None or '' to trigger the [name=value] case and instead it should
|
||||||
# do [name] since [name=None] or [name=] are not exactly useful for the user.
|
# do [name] since [name=None] or [name=] are not exactly useful for the user.
|
||||||
should_print = (
|
should_print = param.default if isinstance(param.default, str) else param.default is not None
|
||||||
param.default
|
|
||||||
if isinstance(param.default, str)
|
|
||||||
else param.default is not None
|
|
||||||
)
|
|
||||||
if should_print:
|
if should_print:
|
||||||
result.append(
|
result.append(f"[{name}={param.default}]" if not greedy else f"[{name}={param.default}]...")
|
||||||
f"[{name}={param.default}]"
|
|
||||||
if not greedy
|
|
||||||
else f"[{name}={param.default}]..."
|
|
||||||
)
|
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
result.append(f"[{name}]")
|
result.append(f"[{name}]")
|
||||||
@@ -1256,29 +1204,21 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
raise DisabledCommand(f"{self.name} command is disabled")
|
raise DisabledCommand(f"{self.name} command is disabled")
|
||||||
|
|
||||||
if ctx.interaction is None and (
|
if ctx.interaction is None and (
|
||||||
self.message_command is False
|
self.message_command is False or (self.message_command is None and not ctx.bot.message_commands)
|
||||||
or (self.message_command is None and not ctx.bot.message_commands)
|
|
||||||
):
|
):
|
||||||
raise DisabledCommand(
|
raise DisabledCommand(f"{self.name} command cannot be run as a message command")
|
||||||
f"{self.name} command cannot be run as a message command"
|
|
||||||
)
|
|
||||||
|
|
||||||
if ctx.interaction is not None and (
|
if ctx.interaction is not None and (
|
||||||
self.slash_command is False
|
self.slash_command is False or (self.slash_command is None and not ctx.bot.slash_commands)
|
||||||
or (self.slash_command is None and not ctx.bot.slash_commands)
|
|
||||||
):
|
):
|
||||||
raise DisabledCommand(
|
raise DisabledCommand(f"{self.name} command cannot be run as a slash command")
|
||||||
f"{self.name} command cannot be run as a slash command"
|
|
||||||
)
|
|
||||||
|
|
||||||
original = ctx.command
|
original = ctx.command
|
||||||
ctx.command = self
|
ctx.command = self
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if not await ctx.bot.can_run(ctx):
|
if not await ctx.bot.can_run(ctx):
|
||||||
raise CheckFailure(
|
raise CheckFailure(f"The global check functions for command {self.qualified_name} failed.")
|
||||||
f"The global check functions for command {self.qualified_name} failed."
|
|
||||||
)
|
|
||||||
|
|
||||||
cog = self.cog
|
cog = self.cog
|
||||||
if cog is not None:
|
if cog is not None:
|
||||||
@@ -1306,10 +1246,7 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
param
|
param
|
||||||
for name, flag in annotation.get_flags().items()
|
for name, flag in annotation.get_flags().items()
|
||||||
for param in self._param_to_options(
|
for param in self._param_to_options(
|
||||||
name,
|
name, flag.annotation, required=flag.required, varadic=flag.annotation is tuple
|
||||||
flag.annotation,
|
|
||||||
required=flag.required,
|
|
||||||
varadic=flag.annotation is tuple,
|
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1342,27 +1279,16 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
option["type"] = discord_type
|
option["type"] = discord_type
|
||||||
# Set channel types
|
# Set channel types
|
||||||
if discord_type == 7:
|
if discord_type == 7:
|
||||||
option["channel_types"] = application_option_channel_types[
|
option["channel_types"] = application_option_channel_types[annotation]
|
||||||
annotation
|
|
||||||
]
|
|
||||||
break
|
break
|
||||||
|
|
||||||
elif origin is Union:
|
elif origin is Union:
|
||||||
if annotation in {
|
if annotation in {Union[discord.Member, discord.Role], Union[MemberConverter, RoleConverter]}:
|
||||||
Union[discord.Member, discord.Role],
|
|
||||||
Union[MemberConverter, RoleConverter],
|
|
||||||
}:
|
|
||||||
option["type"] = 9
|
option["type"] = 9
|
||||||
|
|
||||||
elif all(
|
elif all([arg in application_option_channel_types for arg in annotation.__args__]):
|
||||||
[arg in application_option_channel_types for arg in annotation.__args__]
|
|
||||||
):
|
|
||||||
option["type"] = 7
|
option["type"] = 7
|
||||||
option["channel_types"] = [
|
option["channel_types"] = [discord_value for arg in annotation.__args__ for discord_value in application_option_channel_types[arg]]
|
||||||
discord_value
|
|
||||||
for arg in annotation.__args__
|
|
||||||
for discord_value in application_option_channel_types[arg]
|
|
||||||
]
|
|
||||||
|
|
||||||
elif origin is Literal:
|
elif origin is Literal:
|
||||||
literal_values = annotation.__args__
|
literal_values = annotation.__args__
|
||||||
@@ -1374,27 +1300,18 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
|
|
||||||
option["type"] = application_option_type_lookup[python_type]
|
option["type"] = application_option_type_lookup[python_type]
|
||||||
option["choices"] = [
|
option["choices"] = [
|
||||||
{"name": literal_value, "value": literal_value}
|
{"name": literal_value, "value": literal_value} for literal_value in annotation.__args__
|
||||||
for literal_value in annotation.__args__
|
|
||||||
]
|
]
|
||||||
|
|
||||||
return [option] # type: ignore
|
return [option] # type: ignore
|
||||||
|
|
||||||
def to_application_command(
|
def to_application_command(self, nested: int = 0) -> Optional[EditApplicationCommand]:
|
||||||
self, nested: int = 0
|
|
||||||
) -> Optional[EditApplicationCommand]:
|
|
||||||
if self.slash_command is False:
|
if self.slash_command is False:
|
||||||
return
|
return
|
||||||
elif nested == 3:
|
elif nested == 3:
|
||||||
raise ApplicationCommandRegistrationError(
|
raise ApplicationCommandRegistrationError(self, f"{self.qualified_name} is too deeply nested!")
|
||||||
self, f"{self.qualified_name} is too deeply nested!"
|
|
||||||
)
|
|
||||||
|
|
||||||
payload = {
|
payload = {"name": self.name, "description": self.short_doc or "no description", "options": []}
|
||||||
"name": self.name,
|
|
||||||
"description": self.short_doc or "no description",
|
|
||||||
"options": [],
|
|
||||||
}
|
|
||||||
if nested != 0:
|
if nested != 0:
|
||||||
payload["type"] = 1
|
payload["type"] = 1
|
||||||
|
|
||||||
@@ -1402,23 +1319,15 @@ class Command(_BaseCommand, Generic[CogT, P, T]):
|
|||||||
options = self._param_to_options(
|
options = self._param_to_options(
|
||||||
name,
|
name,
|
||||||
param.annotation if param.annotation is not param.empty else str,
|
param.annotation if param.annotation is not param.empty else str,
|
||||||
varadic=param.kind == param.KEYWORD_ONLY
|
varadic=param.kind == param.KEYWORD_ONLY or isinstance(param.annotation, Greedy),
|
||||||
or isinstance(param.annotation, Greedy),
|
required=(param.default is param.empty and not self._is_typing_optional(param.annotation))
|
||||||
required=(
|
|
||||||
param.default is param.empty
|
|
||||||
and not self._is_typing_optional(param.annotation)
|
|
||||||
)
|
|
||||||
or param.kind == param.VAR_POSITIONAL,
|
or param.kind == param.VAR_POSITIONAL,
|
||||||
)
|
)
|
||||||
if options is not None:
|
if options is not None:
|
||||||
payload["options"].extend(
|
payload["options"].extend(option for option in options if option is not None)
|
||||||
option for option in options if option is not None
|
|
||||||
)
|
|
||||||
|
|
||||||
# Now we have all options, make sure required is before optional.
|
# Now we have all options, make sure required is before optional.
|
||||||
payload["options"] = sorted(
|
payload["options"] = sorted(payload["options"], key=itemgetter("required"), reverse=True)
|
||||||
payload["options"], key=itemgetter("required"), reverse=True
|
|
||||||
)
|
|
||||||
return payload # type: ignore
|
return payload # type: ignore
|
||||||
|
|
||||||
|
|
||||||
@@ -1437,9 +1346,7 @@ class GroupMixin(Generic[CogT]):
|
|||||||
|
|
||||||
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
||||||
case_insensitive = kwargs.get("case_insensitive", True)
|
case_insensitive = kwargs.get("case_insensitive", True)
|
||||||
self.all_commands: Dict[str, Command[CogT, Any, Any]] = (
|
self.all_commands: Dict[str, Command[CogT, Any, Any]] = _CaseInsensitiveDict() if case_insensitive else {}
|
||||||
_CaseInsensitiveDict() if case_insensitive else {}
|
|
||||||
)
|
|
||||||
self.case_insensitive: bool = case_insensitive
|
self.case_insensitive: bool = case_insensitive
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
@@ -1645,12 +1552,7 @@ class GroupMixin(Generic[CogT]):
|
|||||||
*args: Any,
|
*args: Any,
|
||||||
**kwargs: Any,
|
**kwargs: Any,
|
||||||
) -> Callable[
|
) -> Callable[
|
||||||
[
|
[Union[Callable[Concatenate[CogT, ContextT, P], Coro[T]], Callable[Concatenate[ContextT, P], Coro[T]]]],
|
||||||
Union[
|
|
||||||
Callable[Concatenate[CogT, ContextT, P], Coro[T]],
|
|
||||||
Callable[Concatenate[ContextT, P], Coro[T]],
|
|
||||||
]
|
|
||||||
],
|
|
||||||
Group[CogT, P, T],
|
Group[CogT, P, T],
|
||||||
]:
|
]:
|
||||||
...
|
...
|
||||||
@@ -1801,23 +1703,18 @@ class Group(GroupMixin[CogT], Command[CogT, P, T]):
|
|||||||
view.previous = previous
|
view.previous = previous
|
||||||
await super().reinvoke(ctx, call_hooks=call_hooks)
|
await super().reinvoke(ctx, call_hooks=call_hooks)
|
||||||
|
|
||||||
def to_application_command(
|
def to_application_command(self, nested: int = 0) -> Optional[EditApplicationCommand]:
|
||||||
self, nested: int = 0
|
|
||||||
) -> Optional[EditApplicationCommand]:
|
|
||||||
if self.slash_command is False:
|
if self.slash_command is False:
|
||||||
return
|
return
|
||||||
elif nested == 2:
|
elif nested == 2:
|
||||||
raise ApplicationCommandRegistrationError(
|
raise ApplicationCommandRegistrationError(self, f"{self.qualified_name} is too deeply nested!")
|
||||||
self, f"{self.qualified_name} is too deeply nested!"
|
|
||||||
)
|
|
||||||
|
|
||||||
return { # type: ignore
|
return { # type: ignore
|
||||||
"name": self.name,
|
"name": self.name,
|
||||||
"type": int(not (nested - 1)) + 1,
|
"type": int(not (nested - 1)) + 1,
|
||||||
"description": self.short_doc or "no description",
|
"description": self.short_doc or "no description",
|
||||||
"options": [
|
"options": [
|
||||||
cmd.to_application_command(nested=nested + 1)
|
cmd.to_application_command(nested=nested + 1) for cmd in sorted(self.commands, key=lambda x: x.name)
|
||||||
for cmd in sorted(self.commands, key=lambda x: x.name)
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2125,9 +2022,7 @@ def check_any(*checks: Check) -> Callable[[T], T]:
|
|||||||
try:
|
try:
|
||||||
pred = wrapped.predicate
|
pred = wrapped.predicate
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
raise TypeError(
|
raise TypeError(f"{wrapped!r} must be wrapped by commands.check decorator") from None
|
||||||
f"{wrapped!r} must be wrapped by commands.check decorator"
|
|
||||||
) from None
|
|
||||||
else:
|
else:
|
||||||
unwrapped.append(pred)
|
unwrapped.append(pred)
|
||||||
|
|
||||||
@@ -2229,10 +2124,7 @@ def has_any_role(*items: Union[int, str]) -> Callable[[T], T]:
|
|||||||
# ctx.guild is None doesn't narrow ctx.author to Member
|
# ctx.guild is None doesn't narrow ctx.author to Member
|
||||||
getter = functools.partial(discord.utils.get, ctx.author.roles) # type: ignore
|
getter = functools.partial(discord.utils.get, ctx.author.roles) # type: ignore
|
||||||
if any(
|
if any(
|
||||||
getter(id=item) is not None
|
getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items
|
||||||
if isinstance(item, int)
|
|
||||||
else getter(name=item) is not None
|
|
||||||
for item in items
|
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
raise MissingAnyRole(list(items))
|
raise MissingAnyRole(list(items))
|
||||||
@@ -2291,10 +2183,7 @@ def bot_has_any_role(*items: int) -> Callable[[T], T]:
|
|||||||
me = ctx.me
|
me = ctx.me
|
||||||
getter = functools.partial(discord.utils.get, me.roles)
|
getter = functools.partial(discord.utils.get, me.roles)
|
||||||
if any(
|
if any(
|
||||||
getter(id=item) is not None
|
getter(id=item) is not None if isinstance(item, int) else getter(name=item) is not None for item in items
|
||||||
if isinstance(item, int)
|
|
||||||
else getter(name=item) is not None
|
|
||||||
for item in items
|
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
raise BotMissingAnyRole(list(items))
|
raise BotMissingAnyRole(list(items))
|
||||||
@@ -2340,9 +2229,7 @@ def has_permissions(**perms: bool) -> Callable[[T], T]:
|
|||||||
ch = ctx.channel
|
ch = ctx.channel
|
||||||
permissions = ch.permissions_for(ctx.author) # type: ignore
|
permissions = ch.permissions_for(ctx.author) # type: ignore
|
||||||
|
|
||||||
missing = [
|
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value]
|
||||||
perm for perm, value in perms.items() if getattr(permissions, perm) != value
|
|
||||||
]
|
|
||||||
|
|
||||||
if not missing:
|
if not missing:
|
||||||
return True
|
return True
|
||||||
@@ -2369,9 +2256,7 @@ def bot_has_permissions(**perms: bool) -> Callable[[T], T]:
|
|||||||
me = guild.me if guild is not None else ctx.bot.user
|
me = guild.me if guild is not None else ctx.bot.user
|
||||||
permissions = ctx.channel.permissions_for(me) # type: ignore
|
permissions = ctx.channel.permissions_for(me) # type: ignore
|
||||||
|
|
||||||
missing = [
|
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value]
|
||||||
perm for perm, value in perms.items() if getattr(permissions, perm) != value
|
|
||||||
]
|
|
||||||
|
|
||||||
if not missing:
|
if not missing:
|
||||||
return True
|
return True
|
||||||
@@ -2400,9 +2285,7 @@ def has_guild_permissions(**perms: bool) -> Callable[[T], T]:
|
|||||||
raise NoPrivateMessage
|
raise NoPrivateMessage
|
||||||
|
|
||||||
permissions = ctx.author.guild_permissions # type: ignore
|
permissions = ctx.author.guild_permissions # type: ignore
|
||||||
missing = [
|
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value]
|
||||||
perm for perm, value in perms.items() if getattr(permissions, perm) != value
|
|
||||||
]
|
|
||||||
|
|
||||||
if not missing:
|
if not missing:
|
||||||
return True
|
return True
|
||||||
@@ -2428,9 +2311,7 @@ def bot_has_guild_permissions(**perms: bool) -> Callable[[T], T]:
|
|||||||
raise NoPrivateMessage
|
raise NoPrivateMessage
|
||||||
|
|
||||||
permissions = ctx.me.guild_permissions # type: ignore
|
permissions = ctx.me.guild_permissions # type: ignore
|
||||||
missing = [
|
missing = [perm for perm, value in perms.items() if getattr(permissions, perm) != value]
|
||||||
perm for perm, value in perms.items() if getattr(permissions, perm) != value
|
|
||||||
]
|
|
||||||
|
|
||||||
if not missing:
|
if not missing:
|
||||||
return True
|
return True
|
||||||
@@ -2508,9 +2389,7 @@ def is_nsfw() -> Callable[[T], T]:
|
|||||||
|
|
||||||
def pred(ctx: Context) -> bool:
|
def pred(ctx: Context) -> bool:
|
||||||
ch = ctx.channel
|
ch = ctx.channel
|
||||||
if ctx.guild is None or (
|
if ctx.guild is None or (isinstance(ch, (discord.TextChannel, discord.Thread)) and ch.is_nsfw()):
|
||||||
isinstance(ch, (discord.TextChannel, discord.Thread)) and ch.is_nsfw()
|
|
||||||
):
|
|
||||||
return True
|
return True
|
||||||
raise NSFWChannelRequired(ch) # type: ignore
|
raise NSFWChannelRequired(ch) # type: ignore
|
||||||
|
|
||||||
@@ -2518,9 +2397,7 @@ def is_nsfw() -> Callable[[T], T]:
|
|||||||
|
|
||||||
|
|
||||||
def cooldown(
|
def cooldown(
|
||||||
rate: int,
|
rate: int, per: float, type: Union[BucketType, Callable[[Message], Any]] = BucketType.default
|
||||||
per: float,
|
|
||||||
type: Union[BucketType, Callable[[Message], Any]] = BucketType.default,
|
|
||||||
) -> Callable[[T], T]:
|
) -> Callable[[T], T]:
|
||||||
"""A decorator that adds a cooldown to a :class:`.Command`
|
"""A decorator that adds a cooldown to a :class:`.Command`
|
||||||
|
|
||||||
@@ -2555,17 +2432,14 @@ def cooldown(
|
|||||||
if not hasattr(func, "__command_attrs__"):
|
if not hasattr(func, "__command_attrs__"):
|
||||||
func.__command_attrs__ = {}
|
func.__command_attrs__ = {}
|
||||||
|
|
||||||
func.__command_attrs__["cooldown"] = CooldownMapping(
|
func.__command_attrs__["cooldown"] = CooldownMapping(Cooldown(rate, per), type)
|
||||||
Cooldown(rate, per), type
|
|
||||||
)
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
return decorator # type: ignore
|
return decorator # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def dynamic_cooldown(
|
def dynamic_cooldown(
|
||||||
cooldown: Union[BucketType, Callable[[Message], Any]],
|
cooldown: Union[BucketType, Callable[[Message], Any]], type: BucketType = BucketType.default
|
||||||
type: BucketType = BucketType.default,
|
|
||||||
) -> Callable[[T], T]:
|
) -> Callable[[T], T]:
|
||||||
"""A decorator that adds a dynamic cooldown to a :class:`.Command`
|
"""A decorator that adds a dynamic cooldown to a :class:`.Command`
|
||||||
|
|
||||||
@@ -2611,9 +2485,7 @@ def dynamic_cooldown(
|
|||||||
return decorator # type: ignore
|
return decorator # type: ignore
|
||||||
|
|
||||||
|
|
||||||
def max_concurrency(
|
def max_concurrency(number: int, per: BucketType = BucketType.default, *, wait: bool = False) -> Callable[[T], T]:
|
||||||
number: int, per: BucketType = BucketType.default, *, wait: bool = False
|
|
||||||
) -> Callable[[T], T]:
|
|
||||||
"""A decorator that adds a maximum concurrency to a :class:`.Command` or its subclasses.
|
"""A decorator that adds a maximum concurrency to a :class:`.Command` or its subclasses.
|
||||||
|
|
||||||
This enables you to only allow a certain number of command invocations at the same time,
|
This enables you to only allow a certain number of command invocations at the same time,
|
||||||
|
Reference in New Issue
Block a user