discord.py 2.0 update (3d914e08->2.0.1) (#5709)

This commit is contained in:
Jakub Kuczys
2022-10-03 16:07:15 +02:00
committed by GitHub
parent d7d6ab46f4
commit f02528378f
44 changed files with 454 additions and 317 deletions

View File

@@ -38,7 +38,7 @@ class IssueDiagnoserBase:
self,
bot: Red,
original_ctx: commands.Context,
channel: Union[discord.TextChannel, discord.Thread],
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
author: discord.Member,
command: commands.Command,
) -> None:

View File

@@ -220,8 +220,6 @@ class Red(
self._main_dir = bot_dir
self._cog_mgr = CogManager()
self._use_team_features = cli_flags.use_team_features
# to prevent multiple calls to app info during startup
self._app_info = None
super().__init__(*args, help_command=None, **kwargs)
# Do not manually use the help formatter attribute here, see `send_help_for`,
# for a documented API. The internals of this object are still subject to change.
@@ -815,7 +813,7 @@ class Red(
return False
if guild:
assert isinstance(channel, (discord.abc.GuildChannel, discord.Thread))
assert isinstance(channel, (discord.TextChannel, discord.VoiceChannel, discord.Thread))
if not can_user_send_messages_in(guild.me, channel):
return False
if not (await self.ignored_channel_or_guild(message)):
@@ -1207,16 +1205,12 @@ class Red(
if self.rpc_enabled:
await self.rpc.initialize(self.rpc_port)
async def _pre_fetch_owners(self) -> None:
app_info = await self.application_info()
if app_info.team:
def _setup_owners(self) -> None:
if self.application.team:
if self._use_team_features:
self.owner_ids.update(m.id for m in app_info.team.members)
self.owner_ids.update(m.id for m in self.application.team.members)
elif self._owner_id_overwrite is None:
self.owner_ids.add(app_info.owner.id)
self._app_info = app_info
self.owner_ids.add(self.application.owner.id)
if not self.owner_ids:
raise _NoOwnerSet("Bot doesn't have any owner set!")
@@ -1229,7 +1223,7 @@ class Red(
await self.connect()
async def setup_hook(self) -> None:
await self._pre_fetch_owners()
self._setup_owners()
await self._pre_connect()
async def send_help_for(
@@ -1249,7 +1243,12 @@ class Red(
async def embed_requested(
self,
channel: Union[
discord.TextChannel, commands.Context, discord.User, discord.Member, discord.Thread
discord.TextChannel,
discord.VoiceChannel,
commands.Context,
discord.User,
discord.Member,
discord.Thread,
],
*,
command: Optional[commands.Command] = None,
@@ -1260,7 +1259,7 @@ class Red(
Arguments
---------
channel : Union[`discord.TextChannel`, `commands.Context`, `discord.User`, `discord.Member`, `discord.Thread`]
channel : Union[`discord.TextChannel`, `discord.VoiceChannel`, `commands.Context`, `discord.User`, `discord.Member`, `discord.Thread`]
The target messageable object to check embed settings for.
Keyword Arguments
@@ -1307,7 +1306,7 @@ class Red(
"You cannot pass a GroupChannel, DMChannel, or PartialMessageable to this method."
)
if isinstance(channel, (discord.TextChannel, discord.Thread)):
if isinstance(channel, (discord.TextChannel, discord.VoiceChannel, discord.Thread)):
channel_id = channel.parent_id if isinstance(channel, discord.Thread) else channel.id
if check_permissions and not channel.permissions_for(channel.guild.me).embed_links:
@@ -1371,7 +1370,7 @@ class Red(
scopes = ("bot", "applications.commands") if commands_scope else ("bot",)
perms_int = data["invite_perm"]
permissions = discord.Permissions(perms_int)
return discord.utils.oauth_url(self._app_info.id, permissions=permissions, scopes=scopes)
return discord.utils.oauth_url(self.application_id, permissions=permissions, scopes=scopes)
async def is_invite_url_public(self) -> bool:
"""
@@ -1613,7 +1612,6 @@ class Red(
cogname: str,
/,
*,
# DEP-WARN: MISSING is implementation detail
guild: Optional[discord.abc.Snowflake] = discord.utils.MISSING,
guilds: List[discord.abc.Snowflake] = discord.utils.MISSING,
) -> Optional[commands.Cog]:
@@ -1725,7 +1723,6 @@ class Red(
/,
*,
override: bool = False,
# DEP-WARN: MISSING is implementation detail
guild: Optional[discord.abc.Snowflake] = discord.utils.MISSING,
guilds: List[discord.abc.Snowflake] = discord.utils.MISSING,
) -> None:
@@ -1880,7 +1877,7 @@ class Red(
async def get_owner_notification_destinations(
self,
) -> List[Union[discord.TextChannel, discord.User]]:
) -> List[Union[discord.TextChannel, discord.VoiceChannel, discord.User]]:
"""
Gets the users and channels to send to
"""

View File

@@ -45,7 +45,6 @@ from .help import (
)
from .requires import (
CheckPredicate as CheckPredicate,
DM_PERMS as DM_PERMS,
GlobalPermissionModel as GlobalPermissionModel,
GuildPermissionModel as GuildPermissionModel,
PermissionModel as PermissionModel,
@@ -189,4 +188,14 @@ from discord.ext.commands import (
bot_has_any_role as bot_has_any_role,
before_invoke as before_invoke,
after_invoke as after_invoke,
CurrentChannel as CurrentChannel,
Author as Author,
param as param,
MissingRequiredAttachment as MissingRequiredAttachment,
Parameter as Parameter,
ForumChannelConverter as ForumChannelConverter,
CurrentGuild as CurrentGuild,
Range as Range,
RangeError as RangeError,
parameter as parameter,
)

View File

@@ -339,7 +339,7 @@ if TYPE_CHECKING or os.getenv("BUILDING_DOCS", False):
...
@property
def channel(self) -> Union[discord.TextChannel, discord.Thread]:
def channel(self) -> Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]:
...
@property

View File

@@ -842,12 +842,12 @@ class RedHelpFormatter(HelpFormatterABC):
if (
not use_DMs # we're not in DMs
and delete_delay > 0 # delete delay is enabled
and ctx.channel.permissions_for(ctx.me).manage_messages # we can manage messages
and ctx.bot_permissions.manage_messages # we can manage messages
):
# We need to wrap this in a task to not block after-sending-help interactions.
# The channel has to be TextChannel or Thread as we can't bulk-delete from DMs
async def _delete_delay_help(
channel: Union[discord.TextChannel, discord.Thread],
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
messages: List[discord.Message],
delay: int,
):

View File

@@ -40,7 +40,6 @@ if TYPE_CHECKING:
__all__ = [
"CheckPredicate",
"DM_PERMS",
"GlobalPermissionModel",
"GuildPermissionModel",
"PermissionModel",
@@ -75,6 +74,7 @@ GlobalPermissionModel = Union[
discord.User,
discord.VoiceChannel,
discord.TextChannel,
discord.ForumChannel,
discord.CategoryChannel,
discord.Role,
discord.Guild,
@@ -83,6 +83,7 @@ GuildPermissionModel = Union[
discord.Member,
discord.VoiceChannel,
discord.TextChannel,
discord.ForumChannel,
discord.CategoryChannel,
discord.Role,
discord.Guild,
@@ -90,22 +91,6 @@ GuildPermissionModel = Union[
PermissionModel = Union[GlobalPermissionModel, GuildPermissionModel]
CheckPredicate = Callable[["Context"], Union[Optional[bool], Awaitable[Optional[bool]]]]
# Here we are trying to model DM permissions as closely as possible. The only
# discrepancy I've found is that users can pin messages, but they cannot delete them.
# This means manage_messages is only half True, so it's left as False.
# This is also the same as the permissions returned when `permissions_for` is used in DM.
DM_PERMS = discord.Permissions.none()
DM_PERMS.update(
add_reactions=True,
attach_files=True,
embed_links=True,
external_emojis=True,
mention_everyone=True,
read_message_history=True,
read_messages=True,
send_messages=True,
)
class PrivilegeLevel(enum.IntEnum):
"""Enumeration for special privileges."""
@@ -520,15 +505,11 @@ class Requires:
return await self._transition_state(ctx)
async def _verify_bot(self, ctx: "Context") -> None:
if ctx.guild is None:
bot_user = ctx.bot.user
else:
bot_user = ctx.guild.me
cog = ctx.cog
if cog and await ctx.bot.cog_disabled_in_guild(cog, ctx.guild):
raise discord.ext.commands.DisabledCommand()
cog = ctx.cog
if ctx.guild is not None and cog and await ctx.bot.cog_disabled_in_guild(cog, ctx.guild):
raise discord.ext.commands.DisabledCommand()
bot_perms = ctx.channel.permissions_for(bot_user)
bot_perms = ctx.bot_permissions
if not (bot_perms.administrator or bot_perms >= self.bot_perms):
raise BotMissingPermissions(missing=self._missing_perms(self.bot_perms, bot_perms))
@@ -574,7 +555,7 @@ class Requires:
return False
if self.user_perms is not None:
user_perms = ctx.channel.permissions_for(ctx.author)
user_perms = ctx.permissions
if user_perms.administrator or user_perms >= self.user_perms:
return True
@@ -633,17 +614,6 @@ class Requires:
return True
return await discord.utils.async_all(check(ctx) for check in self.checks)
@staticmethod
def _get_perms_for(ctx: "Context", user: discord.abc.User) -> discord.Permissions:
if ctx.guild is None:
return DM_PERMS
else:
return ctx.channel.permissions_for(user)
@classmethod
def _get_bot_perms(cls, ctx: "Context") -> discord.Permissions:
return cls._get_perms_for(ctx, ctx.guild.me if ctx.guild else ctx.bot.user)
@staticmethod
def _missing_perms(
required: discord.Permissions, actual: discord.Permissions
@@ -656,13 +626,6 @@ class Requires:
relative_complement = required.value & ~actual.value
return discord.Permissions(relative_complement)
@staticmethod
def _member_as_user(member: discord.abc.User) -> discord.User:
if isinstance(member, discord.Member):
# noinspection PyProtectedMember
return member._user
return member
def __repr__(self) -> str:
return (
f"<Requires privilege_level={self.privilege_level!r} user_perms={self.user_perms!r} "

View File

@@ -591,7 +591,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
"""
# Can't check this as a command check, and want to prompt DMs as an option.
if not ctx.channel.permissions_for(ctx.me).attach_files:
if not ctx.bot_permissions.attach_files:
ctx.command.reset_cooldown(ctx)
return await ctx.send(_("I need to be able to attach files (try in DMs?)."))
@@ -1349,7 +1349,12 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@embedset.command(name="channel")
@checks.guildowner_or_permissions(administrator=True)
@commands.guild_only()
async def embedset_channel(self, ctx: commands.Context, enabled: bool = None):
async def embedset_channel(
self,
ctx: commands.Context,
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel],
enabled: bool = None,
):
"""
Set's a channel's embed setting.
@@ -1361,27 +1366,20 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
To see full evaluation order of embed settings, run `[p]help embedset`.
**Examples:**
- `[p]embedset channel False` - Disables embeds in this channel.
- `[p]embedset channel` - Resets value to use guild default.
- `[p]embedset channel #text-channel False` - Disables embeds in the #text-channel.
- `[p]embedset channel #forum-channel disable` - Disables embeds in the #forum-channel.
- `[p]embedset channel #text-channel` - Resets value to use guild default in the #text-channel .
**Arguments:**
- `<channel>` - The text, voice, or forum channel to set embed setting for.
- `[enabled]` - Whether to use embeds in this channel. Leave blank to reset to default.
"""
if isinstance(ctx.channel, discord.Thread):
await ctx.send(
_(
"This setting cannot be set for threads. If you want to set this for"
" the parent channel, send the command in that channel."
)
)
return
if enabled is None:
await self.bot._config.channel(ctx.channel).embeds.clear()
await self.bot._config.channel(channel).embeds.clear()
await ctx.send(_("Embeds will now fall back to the global setting."))
return
await self.bot._config.channel(ctx.channel).embeds.set(enabled)
await self.bot._config.channel(channel).embeds.set(enabled)
await ctx.send(
_("Embeds are now {} for this channel.").format(
_("enabled") if enabled else _("disabled")
@@ -2247,7 +2245,11 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@modlogset.command(aliases=["channel"], name="modlog")
@commands.guild_only()
async def modlogset_modlog(self, ctx: commands.Context, channel: discord.TextChannel = None):
async def modlogset_modlog(
self,
ctx: commands.Context,
channel: Union[discord.TextChannel, discord.VoiceChannel] = None,
):
"""Set a channel as the modlog.
Omit `[channel]` to disable the modlog.
@@ -3131,7 +3133,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
view=SetApiView(default_service=service),
)
else:
if ctx.channel.permissions_for(ctx.me).manage_messages:
if ctx.bot_permissions.manage_messages:
await ctx.message.delete()
await ctx.bot.set_shared_api_tokens(service, **tokens)
await ctx.send(_("`{service}` API tokens have been set.").format(service=service))
@@ -3241,7 +3243,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@_set_ownernotifications.command(name="adddestination")
async def _set_ownernotifications_adddestination(
self, ctx: commands.Context, *, channel: discord.TextChannel
self, ctx: commands.Context, *, channel: Union[discord.TextChannel, discord.VoiceChannel]
):
"""
Adds a destination text channel to receive owner notifications.
@@ -3263,7 +3265,10 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
name="removedestination", aliases=["remdestination", "deletedestination", "deldestination"]
)
async def _set_ownernotifications_removedestination(
self, ctx: commands.Context, *, channel: Union[discord.TextChannel, int]
self,
ctx: commands.Context,
*,
channel: Union[discord.TextChannel, discord.VoiceChannel, int],
):
"""
Removes a destination text channel from receiving owner notifications.
@@ -4136,8 +4141,11 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
async def diagnoseissues(
self,
ctx: commands.Context,
channel: Optional[Union[discord.TextChannel, discord.Thread]],
member: Union[discord.Member, discord.User],
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]
] = commands.CurrentChannel,
# avoid non-default argument following default argument by using empty param()
member: Union[discord.Member, discord.User] = commands.param(),
*,
command_name: str,
) -> None:
@@ -4155,16 +4163,14 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
- `<member>` - The member that should be considered as the command caller.
- `<command_name>` - The name of the command to test.
"""
if channel is None:
channel = ctx.channel
if not isinstance(channel, (discord.TextChannel, discord.Thread)):
await ctx.send(
_(
"The text channel or thread needs to be passed"
" when using this command in DMs."
)
if ctx.guild is None:
await ctx.send(
_(
"A text channel, voice channel, or thread needs to be passed"
" when using this command in DMs."
)
return
)
return
command = self.bot.get_command(command_name)
if command is None:
@@ -5124,9 +5130,13 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
async def ignore_channel(
self,
ctx: commands.Context,
channel: Optional[
Union[discord.TextChannel, discord.CategoryChannel, discord.Thread]
] = None,
channel: Union[
discord.TextChannel,
discord.VoiceChannel,
discord.ForumChannel,
discord.CategoryChannel,
discord.Thread,
] = commands.CurrentChannel,
):
"""
Ignore commands in the channel, thread, or category.
@@ -5144,8 +5154,6 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `<channel>` - The channel to ignore. This can also be a thread or category channel.
"""
if not channel:
channel = ctx.channel
if not await self.bot._ignored_cache.get_ignored_channel(channel):
await self.bot._ignored_cache.set_ignored_channel(channel, True)
await ctx.send(_("Channel added to ignore list."))
@@ -5180,9 +5188,13 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
async def unignore_channel(
self,
ctx: commands.Context,
channel: Optional[
Union[discord.TextChannel, discord.CategoryChannel, discord.Thread]
] = None,
channel: Union[
discord.TextChannel,
discord.VoiceChannel,
discord.ForumChannel,
discord.CategoryChannel,
discord.Thread,
] = commands.CurrentChannel,
):
"""
Remove a channel, thread, or category from the ignore list.
@@ -5198,9 +5210,6 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
**Arguments:**
- `<channel>` - The channel to unignore. This can also be a thread or category channel.
"""
if not channel:
channel = ctx.channel
if await self.bot._ignored_cache.get_ignored_channel(channel):
await self.bot._ignored_cache.set_ignored_channel(channel, False)
await ctx.send(_("Channel removed from ignore list."))
@@ -5225,7 +5234,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
async def count_ignored(self, ctx: commands.Context):
category_channels: List[discord.CategoryChannel] = []
text_channels: List[discord.TextChannel] = []
channels: List[Union[discord.TextChannel, discord.VoiceChannel, discord.ForumChannel]] = []
threads: List[discord.Thread] = []
if await self.bot._ignored_cache.get_ignored_guild(ctx.guild):
return _("This server is currently being ignored.")
@@ -5234,7 +5243,19 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
if await self.bot._ignored_cache.get_ignored_channel(channel.category):
category_channels.append(channel.category)
if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False):
text_channels.append(channel)
channels.append(channel)
for channel in ctx.guild.voice_channels:
if channel.category and channel.category not in category_channels:
if await self.bot._ignored_cache.get_ignored_channel(channel.category):
category_channels.append(channel.category)
if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False):
channels.append(channel)
for channel in ctx.guild.forum_channels:
if channel.category and channel.category not in category_channels:
if await self.bot._ignored_cache.get_ignored_channel(channel.category):
category_channels.append(channel.category)
if await self.bot._ignored_cache.get_ignored_channel(channel, check_category=False):
channels.append(channel)
for thread in ctx.guild.threads:
if await self.bot._ignored_cache.get_ignored_channel(thread, check_category=False):
threads.append(thread)
@@ -5242,9 +5263,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
cat_str = (
humanize_list([c.name for c in category_channels]) if category_channels else _("None")
)
chan_str = (
humanize_list([c.mention for c in text_channels]) if text_channels else _("None")
)
chan_str = humanize_list([c.mention for c in channels]) if channels else _("None")
thread_str = humanize_list([c.mention for c in threads]) if threads else _("None")
msg = _(
"Currently ignored categories: {categories}\n"
@@ -5255,7 +5274,7 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
# Removing this command from forks is a violation of the GPLv3 under which it is licensed.
# Otherwise interfering with the ability for this command to be accessible is also a violation.
@commands.cooldown(1, 180, lambda msg: (msg.channel.id, msg.author.id))
@commands.cooldown(1, 180, lambda ctx: (ctx.message.channel.id, ctx.message.author.id))
@commands.command(
cls=commands.commands._AlwaysAvailableCommand,
name="licenseinfo",

View File

@@ -30,7 +30,6 @@ from .utils._internal_utils import (
expected_version,
fetch_latest_red_version_info,
send_to_owners_with_prefix_replaced,
get_converter,
)
from .utils.chat_formatting import inline, bordered, format_perms_list, humanize_timedelta
@@ -71,7 +70,7 @@ def init_events(bot, cli_flags):
guilds = len(bot.guilds)
users = len(set([m for m in bot.get_all_members()]))
invite_url = discord.utils.oauth_url(bot._app_info.id, scopes=("bot",))
invite_url = discord.utils.oauth_url(bot.application_id, scopes=("bot",))
prefixes = cli_flags.prefix or (await bot._config.prefix())
lang = await bot._config.locale()
@@ -222,7 +221,7 @@ def init_events(bot, cli_flags):
await ctx.send_help()
elif isinstance(error, commands.BadArgument):
if isinstance(error.__cause__, ValueError):
converter = get_converter(ctx.current_parameter)
converter = ctx.current_parameter.converter
argument = ctx.current_argument
if converter is int:
await ctx.send(_('"{argument}" is not an integer.').format(argument=argument))

View File

@@ -349,12 +349,12 @@ class Case:
self.message = message
@property
def parent_channel(self) -> Optional[discord.TextChannel]:
def parent_channel(self) -> Optional[Union[discord.TextChannel, discord.ForumChannel]]:
"""
The parent text channel of the thread in `channel`.
The parent text/forum channel of the thread in `channel`.
This will be `None` if `channel` is not a thread
and when the parent text channel is not in cache (probably due to removal).
and when the parent text/forum channel is not in cache (probably due to removal).
"""
if self.parent_channel_id is None:
return None
@@ -645,13 +645,18 @@ class Case:
@classmethod
async def from_json(
cls, mod_channel: discord.TextChannel, bot: Red, case_number: int, data: dict, **kwargs
cls,
mod_channel: Union[discord.TextChannel, discord.VoiceChannel],
bot: Red,
case_number: int,
data: dict,
**kwargs,
):
"""Get a Case object from the provided information
Parameters
----------
mod_channel: discord.TextChannel
mod_channel: `discord.TextChannel` or `discord.VoiceChannel`
The mod log channel for the guild
bot: Red
The bot's instance. Needed to get the target user
@@ -1228,7 +1233,9 @@ async def register_casetypes(new_types: List[dict]) -> List[CaseType]:
return type_list
async def get_modlog_channel(guild: discord.Guild) -> discord.TextChannel:
async def get_modlog_channel(
guild: discord.Guild,
) -> Union[discord.TextChannel, discord.VoiceChannel]:
"""
Get the current modlog channel.
@@ -1239,7 +1246,7 @@ async def get_modlog_channel(guild: discord.Guild) -> discord.TextChannel:
Returns
-------
`discord.TextChannel`
`discord.TextChannel` or `discord.VoiceChannel`
The channel object representing the modlog channel.
Raises
@@ -1259,7 +1266,7 @@ async def get_modlog_channel(guild: discord.Guild) -> discord.TextChannel:
async def set_modlog_channel(
guild: discord.Guild, channel: Union[discord.TextChannel, None]
guild: discord.Guild, channel: Union[discord.TextChannel, discord.VoiceChannel, None]
) -> bool:
"""
Changes the modlog channel
@@ -1268,7 +1275,7 @@ async def set_modlog_channel(
----------
guild: `discord.Guild`
The guild to set a mod log channel for
channel: `discord.TextChannel` or `None`
channel: `discord.TextChannel`, `discord.VoiceChannel`, or `None`
The channel to be set as modlog channel
Returns

View File

@@ -154,7 +154,11 @@ class IgnoreManager:
self._cached_guilds: Dict[int, bool] = {}
async def get_ignored_channel(
self, channel: Union[discord.TextChannel, discord.Thread], check_category: bool = True
self,
channel: Union[
discord.TextChannel, discord.VoiceChannel, discord.ForumChannel, discord.Thread
],
check_category: bool = True,
) -> bool:
ret: bool
@@ -181,7 +185,13 @@ class IgnoreManager:
async def set_ignored_channel(
self,
channel: Union[discord.TextChannel, discord.Thread, discord.CategoryChannel],
channel: Union[
discord.TextChannel,
discord.VoiceChannel,
discord.Thread,
discord.ForumChannel,
discord.CategoryChannel,
],
set_to: bool,
):
cid: int = channel.id

View File

@@ -34,7 +34,9 @@ from discord.utils import maybe_coroutine
from redbot.core import commands
if TYPE_CHECKING:
GuildMessageable = Union[commands.GuildContext, discord.abc.GuildChannel, discord.Thread]
GuildMessageable = Union[
commands.GuildContext, discord.TextChannel, discord.VoiceChannel, discord.Thread
]
DMMessageable = Union[commands.DMContext, discord.Member, discord.User, discord.DMChannel]
__all__ = (

View File

@@ -32,7 +32,6 @@ from typing import (
import aiohttp
import discord
import pkg_resources
from discord.ext.commands.converter import get_converter # DEP-WARN
from fuzzywuzzy import fuzz, process
from rich.progress import ProgressColumn
from rich.progress_bar import ProgressBar
@@ -60,7 +59,6 @@ __all__ = (
"deprecated_removed",
"RichIndefiniteBarColumn",
"cli_level_to_log_level",
"get_converter",
)
_T = TypeVar("_T")

View File

@@ -10,7 +10,8 @@ if TYPE_CHECKING:
async def mass_purge(
messages: List[discord.Message], channel: Union[discord.TextChannel, discord.Thread]
messages: List[discord.Message],
channel: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
):
"""Bulk delete messages from a channel.
@@ -26,7 +27,7 @@ async def mass_purge(
----------
messages : `list` of `discord.Message`
The messages to bulk delete.
channel : `discord.TextChannel` or `discord.Thread`
channel : `discord.TextChannel`, `discord.VoiceChannel`, or `discord.Thread`
The channel to delete messages from.
Raises
@@ -247,7 +248,7 @@ async def check_permissions(ctx: "Context", perms: Dict[str, bool]) -> bool:
return True
elif not perms:
return False
resolved = ctx.channel.permissions_for(ctx.author)
resolved = ctx.permissions
return resolved.administrator or all(
getattr(resolved, name, None) == value for name, value in perms.items()

View File

@@ -67,7 +67,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def same_context(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the message fits the described context.
@@ -76,7 +78,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
The current invocation context.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
The channel we expect a message in. If unspecified,
defaults to ``ctx.channel``. If ``ctx`` is unspecified
too, the message's channel will be ignored.
@@ -104,7 +106,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def cancelled(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the message is ``[p]cancel``.
@@ -113,7 +117,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -133,7 +137,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def yes_or_no(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the message is "yes"/"y" or "no"/"n".
@@ -145,7 +151,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -176,7 +182,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def valid_int(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is an integer.
@@ -187,7 +195,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -216,7 +224,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def valid_float(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is a float.
@@ -227,7 +237,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -256,7 +266,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def positive(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is a positive number.
@@ -267,7 +279,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -300,7 +312,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def valid_role(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response refers to a role in the current guild.
@@ -313,7 +325,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -344,7 +356,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def valid_member(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response refers to a member in the current guild.
@@ -357,7 +369,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -392,7 +404,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def valid_text_channel(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response refers to a text channel in the current guild.
@@ -405,7 +417,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -440,7 +452,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
def has_role(
cls,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread]] = None,
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response refers to a role which the author has.
@@ -454,7 +466,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
----------
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -479,7 +491,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
return False
role = self._find_role(guild, m.content)
if role is None or role not in user.roles:
if role is None or user.get_role(role.id) is None:
return False
self.result = role
@@ -492,7 +504,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
value: str,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is equal to the specified value.
@@ -503,7 +517,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The value to compare the response with.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -522,7 +536,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
value: str,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response *as lowercase* is equal to the specified value.
@@ -533,7 +549,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The value to compare the response with.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -552,7 +568,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
value: Union[int, float],
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is less than the specified value.
@@ -563,7 +581,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The value to compare the response with.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -583,7 +601,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
value: Union[int, float],
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is greater than the specified value.
@@ -594,7 +614,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The value to compare the response with.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -614,7 +634,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
length: int,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response's length is less than the specified length.
@@ -625,7 +647,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The value to compare the response's length with.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -644,7 +666,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
length: int,
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response's length is greater than the specified length.
@@ -655,7 +679,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The value to compare the response's length with.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -674,7 +698,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
collection: Sequence[str],
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response is contained in the specified collection.
@@ -688,7 +714,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The collection containing valid responses.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -718,7 +744,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
collection: Sequence[str],
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Same as :meth:`contained_in`, but the response is set to lowercase before matching.
@@ -729,7 +757,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The collection containing valid lowercase responses.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -759,7 +787,9 @@ class MessagePredicate(Callable[[discord.Message], bool]):
cls,
pattern: Union[Pattern[str], str],
ctx: Optional[commands.Context] = None,
channel: Optional[Union[discord.TextChannel, discord.Thread, discord.DMChannel]] = None,
channel: Optional[
Union[discord.TextChannel, discord.VoiceChannel, discord.Thread, discord.DMChannel]
] = None,
user: Optional[discord.abc.User] = None,
) -> "MessagePredicate":
"""Match if the response matches the specified regex pattern.
@@ -774,7 +804,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
The pattern to search for in the response.
ctx : Optional[Context]
Same as ``ctx`` in :meth:`same_context`.
channel : Optional[Union[`discord.TextChannel`, `discord.Thread`, `discord.DMChannel`]]
channel : Optional[Union[`discord.TextChannel`, `discord.VoiceChannel`, `discord.Thread`, `discord.DMChannel`]]
Same as ``channel`` in :meth:`same_context`.
user : Optional[discord.abc.User]
Same as ``user`` in :meth:`same_context`.
@@ -816,7 +846,7 @@ class MessagePredicate(Callable[[discord.Message], bool]):
@staticmethod
def _get_guild(
ctx: Optional[commands.Context],
channel: Optional[Union[discord.TextChannel, discord.Thread]],
channel: Optional[Union[discord.TextChannel, discord.VoiceChannel, discord.Thread]],
user: Optional[discord.Member],
) -> discord.Guild:
if ctx is not None:

View File

@@ -57,7 +57,7 @@ class Tunnel(metaclass=TunnelMeta):
----------
sender: `discord.Member`
The person who opened the tunnel
origin: `discord.TextChannel` or `discord.Thread`
origin: `discord.TextChannel`, `discord.VoiceChannel`, or `discord.Thread`
The channel in which it was opened
recipient: `discord.User`
The user on the other end of the tunnel
@@ -67,7 +67,7 @@ class Tunnel(metaclass=TunnelMeta):
self,
*,
sender: discord.Member,
origin: Union[discord.TextChannel, discord.Thread],
origin: Union[discord.TextChannel, discord.VoiceChannel, discord.Thread],
recipient: discord.User,
):
self.sender = sender
@@ -164,14 +164,14 @@ class Tunnel(metaclass=TunnelMeta):
if images_only and a.height is None:
# if this is None, it's not an image
continue
_fp = io.BytesIO()
try:
await a.save(_fp, use_cached=use_cached)
file = await a.to_file()
except discord.HTTPException as e:
# this is required, because animated webp files aren't cached
if not (e.status == 415 and images_only and use_cached):
raise
files.append(discord.File(_fp, filename=a.filename))
else:
files.append(file)
return files
# Backwards-compatible typo fix (GH-2496)