Compare commits
23 Commits
paris-ci/i
...
paris-ci/p
Author | SHA1 | Date | |
---|---|---|---|
5a51f9dd23 | |||
c34612dd71 | |||
6f5614373a | |||
2e12746c70 | |||
5ef72e4f70 | |||
7e18d30820 | |||
923a6a885d | |||
b28893aa36 | |||
6e41bd2219 | |||
773ad6f5bf | |||
de0e8ef108 | |||
64ee792391 | |||
22de755059 | |||
fa7f8efc8e | |||
9d1df65af3 | |||
3ce86f6cde | |||
31e3e99c2b | |||
cc90d312f5 | |||
75f052b8c9 | |||
c8cdb275c5 | |||
406f0ffe04 | |||
a4acbd2e08 | |||
e4750c7105 |
@ -313,10 +313,11 @@ class Asset(AssetMixin):
|
||||
if self._animated:
|
||||
if format not in VALID_ASSET_FORMATS:
|
||||
raise InvalidArgument(f'format must be one of {VALID_ASSET_FORMATS}')
|
||||
else:
|
||||
url = url.with_path(f'{path}.{format}')
|
||||
elif static_format is MISSING:
|
||||
if format not in VALID_STATIC_FORMATS:
|
||||
raise InvalidArgument(f'format must be one of {VALID_STATIC_FORMATS}')
|
||||
url = url.with_path(f'{path}.{format}')
|
||||
url = url.with_path(f'{path}.{format}')
|
||||
|
||||
if static_format is not MISSING and not self._animated:
|
||||
if static_format not in VALID_STATIC_FORMATS:
|
||||
|
@ -330,6 +330,10 @@ class AuditLogEntry(Hashable):
|
||||
|
||||
Returns the entry's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the entry's ID.
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
Audit log entries are now comparable and hashable.
|
||||
|
||||
|
@ -115,6 +115,10 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
||||
|
||||
Returns the channel's name.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the channel's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
@ -1334,6 +1338,10 @@ class CategoryChannel(discord.abc.GuildChannel, Hashable):
|
||||
|
||||
Returns the category's name.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the category's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
@ -1556,6 +1564,10 @@ class StoreChannel(discord.abc.GuildChannel, Hashable):
|
||||
|
||||
Returns the channel's name.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the channel's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
@ -1728,6 +1740,10 @@ class DMChannel(discord.abc.Messageable, Hashable):
|
||||
|
||||
Returns a string representation of the channel
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the channel's ID.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
recipient: Optional[:class:`User`]
|
||||
@ -1854,6 +1870,10 @@ class GroupChannel(discord.abc.Messageable, Hashable):
|
||||
|
||||
Returns a string representation of the channel
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the channel's ID.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
recipients: List[:class:`User`]
|
||||
@ -2000,6 +2020,10 @@ class PartialMessageable(discord.abc.Messageable, Hashable):
|
||||
|
||||
Returns the partial messageable's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the messageable's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
id: :class:`int`
|
||||
|
@ -142,7 +142,6 @@ class Client:
|
||||
intents: :class:`Intents`
|
||||
The intents that you want to enable for the session. This is a way of
|
||||
disabling and enabling certain gateway events from triggering and being sent.
|
||||
If not given, defaults to a regularly constructed :class:`Intents` class.
|
||||
|
||||
.. versionadded:: 1.5
|
||||
member_cache_flags: :class:`MemberCacheFlags`
|
||||
@ -203,9 +202,12 @@ class Client:
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
intents: Intents,
|
||||
loop: Optional[asyncio.AbstractEventLoop] = None,
|
||||
**options: Any,
|
||||
):
|
||||
options["intents"] = intents
|
||||
|
||||
# self.ws is set in the connect method
|
||||
self.ws: DiscordWebSocket = None # type: ignore
|
||||
self.loop: asyncio.AbstractEventLoop = asyncio.get_event_loop() if loop is None else loop
|
||||
|
@ -324,6 +324,15 @@ class Colour:
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return cls(0xFEE75C)
|
||||
|
||||
@classmethod
|
||||
def dark_blurple(cls: Type[CT]) -> CT:
|
||||
"""A factory method that returns a :class:`Colour` with a value of ``0x4E5D94``.
|
||||
This is the original Dark Blurple branding.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
"""
|
||||
return cls(0x4E5D94)
|
||||
|
||||
|
||||
Color = Colour
|
||||
|
@ -72,30 +72,36 @@ if TYPE_CHECKING:
|
||||
T = TypeVar('T')
|
||||
MaybeEmpty = Union[T, _EmptyEmbed]
|
||||
|
||||
|
||||
class _EmbedFooterProxy(Protocol):
|
||||
text: MaybeEmpty[str]
|
||||
icon_url: MaybeEmpty[str]
|
||||
|
||||
|
||||
class _EmbedFieldProxy(Protocol):
|
||||
name: MaybeEmpty[str]
|
||||
value: MaybeEmpty[str]
|
||||
inline: bool
|
||||
|
||||
|
||||
class _EmbedMediaProxy(Protocol):
|
||||
url: MaybeEmpty[str]
|
||||
proxy_url: MaybeEmpty[str]
|
||||
height: MaybeEmpty[int]
|
||||
width: MaybeEmpty[int]
|
||||
|
||||
|
||||
class _EmbedVideoProxy(Protocol):
|
||||
url: MaybeEmpty[str]
|
||||
height: MaybeEmpty[int]
|
||||
width: MaybeEmpty[int]
|
||||
|
||||
|
||||
class _EmbedProviderProxy(Protocol):
|
||||
name: MaybeEmpty[str]
|
||||
url: MaybeEmpty[str]
|
||||
|
||||
|
||||
class _EmbedAuthorProxy(Protocol):
|
||||
name: MaybeEmpty[str]
|
||||
url: MaybeEmpty[str]
|
||||
@ -175,15 +181,15 @@ class Embed:
|
||||
Empty: Final = EmptyEmbed
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
*,
|
||||
colour: Union[int, Colour, _EmptyEmbed] = EmptyEmbed,
|
||||
color: Union[int, Colour, _EmptyEmbed] = EmptyEmbed,
|
||||
title: MaybeEmpty[Any] = EmptyEmbed,
|
||||
type: EmbedType = 'rich',
|
||||
url: MaybeEmpty[Any] = EmptyEmbed,
|
||||
description: MaybeEmpty[Any] = EmptyEmbed,
|
||||
timestamp: datetime.datetime = None,
|
||||
self,
|
||||
*,
|
||||
colour: Union[int, Colour, _EmptyEmbed] = EmptyEmbed,
|
||||
color: Union[int, Colour, _EmptyEmbed] = EmptyEmbed,
|
||||
title: MaybeEmpty[Any] = EmptyEmbed,
|
||||
type: EmbedType = 'rich',
|
||||
url: MaybeEmpty[Any] = EmptyEmbed,
|
||||
description: MaybeEmpty[Any] = EmptyEmbed,
|
||||
timestamp: datetime.datetime = None,
|
||||
):
|
||||
|
||||
self.colour = colour if colour is not EmptyEmbed else color
|
||||
@ -366,7 +372,7 @@ class Embed:
|
||||
self._footer['icon_url'] = str(icon_url)
|
||||
|
||||
return self
|
||||
|
||||
|
||||
def remove_footer(self: E) -> E:
|
||||
"""Clears embed's footer information.
|
||||
|
||||
@ -381,7 +387,7 @@ class Embed:
|
||||
pass
|
||||
|
||||
return self
|
||||
|
||||
|
||||
@property
|
||||
def image(self) -> _EmbedMediaProxy:
|
||||
"""Returns an ``EmbedProxy`` denoting the image contents.
|
||||
@ -397,6 +403,19 @@ class Embed:
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_image', {})) # type: ignore
|
||||
|
||||
@image.setter
|
||||
def image(self: E, *, url: Any):
|
||||
self._image = {
|
||||
'url': str(url),
|
||||
}
|
||||
|
||||
@image.deleter
|
||||
def image(self: E):
|
||||
try:
|
||||
del self._image
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def set_image(self: E, *, url: MaybeEmpty[Any]) -> E:
|
||||
"""Sets the image for the embed content.
|
||||
|
||||
@ -413,14 +432,9 @@ class Embed:
|
||||
"""
|
||||
|
||||
if url is EmptyEmbed:
|
||||
try:
|
||||
del self._image
|
||||
except AttributeError:
|
||||
pass
|
||||
del self.image
|
||||
else:
|
||||
self._image = {
|
||||
'url': str(url),
|
||||
}
|
||||
self.image = url
|
||||
|
||||
return self
|
||||
|
||||
@ -439,7 +453,25 @@ class Embed:
|
||||
"""
|
||||
return EmbedProxy(getattr(self, '_thumbnail', {})) # type: ignore
|
||||
|
||||
def set_thumbnail(self: E, *, url: MaybeEmpty[Any]) -> E:
|
||||
@thumbnail.setter
|
||||
def thumbnail(self: E, *, url: Any):
|
||||
"""Sets the thumbnail for the embed content.
|
||||
"""
|
||||
|
||||
self._thumbnail = {
|
||||
'url': str(url),
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@thumbnail.deleter
|
||||
def thumbnail(self):
|
||||
try:
|
||||
del self.thumbnail
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def set_thumbnail(self: E, *, url: MaybeEmpty[Any]):
|
||||
"""Sets the thumbnail for the embed content.
|
||||
|
||||
This function returns the class instance to allow for fluent-style
|
||||
@ -453,16 +485,10 @@ class Embed:
|
||||
url: :class:`str`
|
||||
The source URL for the thumbnail. Only HTTP(S) is supported.
|
||||
"""
|
||||
|
||||
if url is EmptyEmbed:
|
||||
try:
|
||||
del self._thumbnail
|
||||
except AttributeError:
|
||||
pass
|
||||
del self.thumbnail
|
||||
else:
|
||||
self._thumbnail = {
|
||||
'url': str(url),
|
||||
}
|
||||
self.thumbnail = url
|
||||
|
||||
return self
|
||||
|
||||
|
@ -72,6 +72,10 @@ class Emoji(_EmojiTag, AssetMixin):
|
||||
|
||||
Returns the emoji rendered for discord.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the emoji ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
@ -137,6 +141,9 @@ class Emoji(_EmojiTag, AssetMixin):
|
||||
return f'<a:{self.name}:{self.id}>'
|
||||
return f'<:{self.name}:{self.id}>'
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.id
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Emoji id={self.id} name={self.name!r} animated={self.animated} managed={self.managed}>'
|
||||
|
||||
|
@ -120,8 +120,8 @@ class _DefaultRepr:
|
||||
_default = _DefaultRepr()
|
||||
|
||||
class BotBase(GroupMixin):
|
||||
def __init__(self, command_prefix, help_command=_default, description=None, **options):
|
||||
super().__init__(**options)
|
||||
def __init__(self, command_prefix, help_command=_default, description=None, *, intents: discord.Intents, **options):
|
||||
super().__init__(**options, intents=intents)
|
||||
self.command_prefix = command_prefix
|
||||
self.extra_events: Dict[str, List[CoroFunc]] = {}
|
||||
self.__cogs: Dict[str, Cog] = {}
|
||||
|
@ -480,16 +480,6 @@ class Intents(BaseFlags):
|
||||
self.value = self.DEFAULT_VALUE
|
||||
return self
|
||||
|
||||
@classmethod
|
||||
def default(cls: Type[Intents]) -> Intents:
|
||||
"""A factory method that creates a :class:`Intents` with everything enabled
|
||||
except :attr:`presences` and :attr:`members`.
|
||||
"""
|
||||
self = cls.all()
|
||||
self.presences = False
|
||||
self.members = False
|
||||
return self
|
||||
|
||||
@flag_value
|
||||
def guilds(self):
|
||||
""":class:`bool`: Whether guild related events are enabled.
|
||||
|
@ -140,6 +140,10 @@ class Guild(Hashable):
|
||||
|
||||
Returns the guild's name.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the guild's ID.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
name: :class:`str`
|
||||
@ -738,12 +742,16 @@ class Guild(Hashable):
|
||||
|
||||
@property
|
||||
def humans(self) -> List[Member]:
|
||||
"""List[:class:`Member`]: A list of human members that belong to this guild."""
|
||||
"""List[:class:`Member`]: A list of human members that belong to this guild.
|
||||
|
||||
.. versionadded:: 2.0 """
|
||||
return [member for member in self.members if not member.bot]
|
||||
|
||||
@property
|
||||
def bots(self) -> List[Member]:
|
||||
"""List[:class:`Member`]: A list of bots that belong to this guild."""
|
||||
"""List[:class:`Member`]: A list of bots that belong to this guild.
|
||||
|
||||
.. versionadded:: 2.0 """
|
||||
return [member for member in self.members if member.bot]
|
||||
|
||||
def get_member(self, user_id: int, /) -> Optional[Member]:
|
||||
|
@ -230,6 +230,7 @@ class Invite(Hashable):
|
||||
|
||||
Returns the invite URL.
|
||||
|
||||
|
||||
The following table illustrates what methods will obtain the attributes:
|
||||
|
||||
+------------------------------------+------------------------------------------------------------+
|
||||
@ -433,6 +434,9 @@ class Invite(Hashable):
|
||||
def __str__(self) -> str:
|
||||
return self.url
|
||||
|
||||
def __int__(self) -> int:
|
||||
return 0 # To keep the object compatible with the hashable abc.
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f'<Invite code={self.code!r} guild={self.guild!r} '
|
||||
|
@ -226,6 +226,10 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
|
||||
Returns the member's name with the discriminator.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the user's ID.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
joined_at: Optional[:class:`datetime.datetime`]
|
||||
@ -300,6 +304,9 @@ class Member(discord.abc.Messageable, _UserTag):
|
||||
def __str__(self) -> str:
|
||||
return str(self._user)
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.id
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f'<Member id={self._user.id} name={self._user.name!r} discriminator={self._user.discriminator!r}'
|
||||
|
@ -125,6 +125,10 @@ class Attachment(Hashable):
|
||||
|
||||
Returns the hash of the attachment.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the attachment's ID.
|
||||
|
||||
.. versionchanged:: 1.7
|
||||
Attachment can now be casted to :class:`str` and is hashable.
|
||||
|
||||
@ -503,6 +507,14 @@ class Message(Hashable):
|
||||
|
||||
Returns the message's hash.
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the message's content.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the message's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
tts: :class:`bool`
|
||||
@ -712,6 +724,10 @@ class Message(Hashable):
|
||||
f'<{name} id={self.id} channel={self.channel!r} type={self.type!r} author={self.author!r} flags={self.flags!r}>'
|
||||
)
|
||||
|
||||
|
||||
def __str__(self) -> Optional[str]:
|
||||
return self.content
|
||||
|
||||
def _try_patch(self, data, key, transform=None) -> None:
|
||||
try:
|
||||
value = data[key]
|
||||
@ -1106,7 +1122,7 @@ class Message(Hashable):
|
||||
if self.type is MessageType.guild_invite_reminder:
|
||||
return 'Wondering who to invite?\nStart by inviting anyone who can help you build the server!'
|
||||
|
||||
async def delete(self, *, delay: Optional[float] = None) -> None:
|
||||
async def delete(self, *, delay: Optional[float] = None, silent: bool = False) -> None:
|
||||
"""|coro|
|
||||
|
||||
Deletes the message.
|
||||
@ -1117,12 +1133,17 @@ class Message(Hashable):
|
||||
|
||||
.. versionchanged:: 1.1
|
||||
Added the new ``delay`` keyword-only parameter.
|
||||
.. versionchanged:: 2.0
|
||||
Added the new ``silent`` keyword-only parameter.
|
||||
|
||||
Parameters
|
||||
-----------
|
||||
delay: Optional[:class:`float`]
|
||||
If provided, the number of seconds to wait in the background
|
||||
before deleting the message. If the deletion fails then it is silently ignored.
|
||||
silent: :class:`bool`
|
||||
If silent is set to ``True``, the error will not be raised, it will be ignored.
|
||||
This defaults to ``False``
|
||||
|
||||
Raises
|
||||
------
|
||||
@ -1144,7 +1165,11 @@ class Message(Hashable):
|
||||
|
||||
asyncio.create_task(delete(delay))
|
||||
else:
|
||||
await self._state.http.delete_message(self.channel.id, self.id)
|
||||
try:
|
||||
await self._state.http.delete_message(self.channel.id, self.id)
|
||||
except Exception:
|
||||
if not silent:
|
||||
raise
|
||||
|
||||
@overload
|
||||
async def edit(
|
||||
@ -1625,6 +1650,10 @@ class PartialMessage(Hashable):
|
||||
|
||||
Returns the partial message's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the partial message's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
channel: Union[:class:`TextChannel`, :class:`Thread`, :class:`DMChannel`]
|
||||
|
@ -43,5 +43,8 @@ class EqualityComparable:
|
||||
class Hashable(EqualityComparable):
|
||||
__slots__ = ()
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.id
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return self.id >> 22
|
||||
|
@ -69,6 +69,10 @@ class Object(Hashable):
|
||||
|
||||
Returns the object's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the object's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
id: :class:`int`
|
||||
|
@ -39,8 +39,11 @@ if TYPE_CHECKING:
|
||||
from .message import Message
|
||||
from .partial_emoji import PartialEmoji
|
||||
from .member import Member
|
||||
from .threads import Thread
|
||||
|
||||
|
||||
from .enums import ChannelType, try_enum
|
||||
|
||||
__all__ = (
|
||||
'RawMessageDeleteEvent',
|
||||
'RawBulkMessageDeleteEvent',
|
||||
@ -49,6 +52,7 @@ __all__ = (
|
||||
'RawReactionClearEvent',
|
||||
'RawReactionClearEmojiEvent',
|
||||
'RawIntegrationDeleteEvent',
|
||||
'RawThreadDeleteEvent',
|
||||
)
|
||||
|
||||
|
||||
@ -276,3 +280,31 @@ class RawIntegrationDeleteEvent(_RawReprMixin):
|
||||
self.application_id: Optional[int] = int(data['application_id'])
|
||||
except KeyError:
|
||||
self.application_id: Optional[int] = None
|
||||
|
||||
class RawThreadDeleteEvent(_RawReprMixin):
|
||||
"""Represents the payload for a :func:`on_raw_thread_delete` event.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
Attributes
|
||||
----------
|
||||
thread_id: :class:`int`
|
||||
The ID of the thread that was deleted.
|
||||
thread_type: :class:`discord.ChannelType`
|
||||
The channel type of the deleted thread.
|
||||
guild_id: :class:`int`
|
||||
The ID of the guild the thread was deleted in.
|
||||
parent_id: :class:`int`
|
||||
The ID of the channel the thread was belonged to.
|
||||
thread: Optional[:class:`discord.Thread`]
|
||||
The thread, if it could be found in the internal cache.
|
||||
"""
|
||||
|
||||
__slots__ = ('thread_id', 'thread_type', 'parent_id', 'guild_id', 'thread')
|
||||
|
||||
def __init__(self, data) -> None:
|
||||
self.thread_id: int = int(data['id'])
|
||||
self.thread_type: ChannelType = try_enum(ChannelType, data['type'])
|
||||
self.guild_id: int = int(data['guild_id'])
|
||||
self.parent_id: int = int(data['parent_id'])
|
||||
self.thread: Optional[Thread] = None
|
||||
|
@ -141,6 +141,14 @@ class Role(Hashable):
|
||||
|
||||
Returns the role's name.
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the role's ID.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the role's ID.
|
||||
|
||||
Attributes
|
||||
----------
|
||||
id: :class:`int`
|
||||
@ -195,6 +203,9 @@ class Role(Hashable):
|
||||
def __str__(self) -> str:
|
||||
return self.name
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.id
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'<Role id={self.id} name={self.name!r}>'
|
||||
|
||||
|
@ -61,6 +61,10 @@ class StageInstance(Hashable):
|
||||
|
||||
Returns the stage instance's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the stage instance's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
id: :class:`int`
|
||||
|
@ -152,6 +152,7 @@ class ConnectionState:
|
||||
handlers: Dict[str, Callable],
|
||||
hooks: Dict[str, Callable],
|
||||
http: HTTPClient,
|
||||
intents: Intents,
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
**options: Any,
|
||||
) -> None:
|
||||
@ -194,12 +195,8 @@ class ConnectionState:
|
||||
else:
|
||||
status = str(status)
|
||||
|
||||
intents = options.get('intents', None)
|
||||
if intents is not None:
|
||||
if not isinstance(intents, Intents):
|
||||
raise TypeError(f'intents parameter must be Intent not {type(intents)!r}')
|
||||
else:
|
||||
intents = Intents.default()
|
||||
if not isinstance(intents, Intents):
|
||||
raise TypeError(f'intents parameter must be Intent not {type(intents)!r}')
|
||||
|
||||
if not intents.guilds:
|
||||
_log.warning('Guilds intent seems to be disabled. This may cause state related issues.')
|
||||
@ -854,8 +851,10 @@ class ConnectionState:
|
||||
_log.debug('THREAD_DELETE referencing an unknown guild ID: %s. Discarding', guild_id)
|
||||
return
|
||||
|
||||
thread_id = int(data['id'])
|
||||
thread = guild.get_thread(thread_id)
|
||||
raw = RawThreadDeleteEvent(data)
|
||||
raw.thread = thread = guild.get_thread(raw.thread_id)
|
||||
self.dispatch('raw_thread_delete', raw)
|
||||
|
||||
if thread is not None:
|
||||
guild._remove_thread(thread) # type: ignore
|
||||
self.dispatch('thread_delete', thread)
|
||||
|
@ -67,6 +67,14 @@ class StickerPack(Hashable):
|
||||
|
||||
Returns the name of the sticker pack.
|
||||
|
||||
.. describe:: hash(x)
|
||||
|
||||
Returns the hash of the sticker pack.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the ID of the sticker pack.
|
||||
|
||||
.. describe:: x == y
|
||||
|
||||
Checks if the sticker pack is equal to another sticker pack.
|
||||
|
@ -74,6 +74,10 @@ class Thread(Messageable, Hashable):
|
||||
|
||||
Returns the thread's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the thread's ID.
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the thread's name.
|
||||
@ -748,6 +752,10 @@ class ThreadMember(Hashable):
|
||||
|
||||
Returns the thread member's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the thread member's ID.
|
||||
|
||||
.. describe:: str(x)
|
||||
|
||||
Returns the thread member's name.
|
||||
|
@ -96,6 +96,9 @@ class BaseUser(_UserTag):
|
||||
def __str__(self) -> str:
|
||||
return f'{self.name}#{self.discriminator}'
|
||||
|
||||
def __int__(self) -> int:
|
||||
return self.id
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
return isinstance(other, _UserTag) and other.id == self.id
|
||||
|
||||
@ -415,6 +418,10 @@ class User(BaseUser, discord.abc.Messageable):
|
||||
|
||||
Returns the user's name with discriminator.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the user's ID.
|
||||
|
||||
Attributes
|
||||
-----------
|
||||
name: :class:`str`
|
||||
|
@ -886,6 +886,10 @@ class Webhook(BaseWebhook):
|
||||
|
||||
Returns the webhooks's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the webhooks's ID.
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
Webhooks are now comparable and hashable.
|
||||
|
||||
|
@ -475,6 +475,10 @@ class SyncWebhook(BaseWebhook):
|
||||
|
||||
Returns the webhooks's hash.
|
||||
|
||||
.. describe:: int(x)
|
||||
|
||||
Returns the webhooks's ID.
|
||||
|
||||
.. versionchanged:: 1.4
|
||||
Webhooks are now comparable and hashable.
|
||||
|
||||
|
26
docs/api.rst
26
docs/api.rst
@ -718,7 +718,11 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||
|
||||
.. function:: on_thread_delete(thread)
|
||||
|
||||
Called whenever a thread is deleted.
|
||||
Called whenever a thread is deleted. If the thread could
|
||||
not be found in the internal cache this event will not be called.
|
||||
Threads will not be in the cache if they are archived.
|
||||
|
||||
If you need this information use :func:`on_raw_thread_delete` instead.
|
||||
|
||||
Note that you can get the guild from :attr:`Thread.guild`.
|
||||
|
||||
@ -729,6 +733,18 @@ to handle it, which defaults to print a traceback and ignoring the exception.
|
||||
:param thread: The thread that got deleted.
|
||||
:type thread: :class:`Thread`
|
||||
|
||||
.. function:: on_raw_thread_delete(payload)
|
||||
|
||||
Called whenever a thread is deleted. Unlike :func:`on_thread_delete` this
|
||||
is called regardless of the thread being in the internal thread cache or not.
|
||||
|
||||
This requires :attr:`Intents.guilds` to be enabled.
|
||||
|
||||
.. versionadded:: 2.0
|
||||
|
||||
:param payload: The raw event payload data.
|
||||
:type payload: :class:`RawThreadDeleteEvent`
|
||||
|
||||
.. function:: on_thread_member_join(member)
|
||||
on_thread_member_remove(member)
|
||||
|
||||
@ -3902,6 +3918,14 @@ RawIntegrationDeleteEvent
|
||||
.. autoclass:: RawIntegrationDeleteEvent()
|
||||
:members:
|
||||
|
||||
RawThreadDeleteEvent
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. attributetable:: RawThreadDeleteEvent
|
||||
|
||||
.. autoclass:: RawThreadDeleteEvent()
|
||||
:members:
|
||||
|
||||
PartialWebhookGuild
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -11,6 +11,134 @@ Changelog
|
||||
This page keeps a detailed human friendly rendering of what's new and changed
|
||||
in specific versions.
|
||||
|
||||
.. _vp2p0p0:
|
||||
|
||||
v2.0.0
|
||||
--------
|
||||
|
||||
This version was partly developed by Danny, and partly by the enhanced-discord.py contributors.
|
||||
The library has been updated with breaking changes, and as such the major version was changed.
|
||||
|
||||
|
||||
- Performance of the library has improved significantly (all times with 1 process and 1 AutoShardedBot):
|
||||
- 735 guilds boot up time (with chunking): 57s/1.7 GiB RAM -> 42s/1.4 GiB RAM
|
||||
- 27k guilds boot up time (with chunking): 477s/8 GiB RAM -> 303s/7.2 GiB RAM
|
||||
- 48k guilds boot up time (without chunking): 109s -> 67s
|
||||
- 106k guilds boot up time (without chunking): 3300s -> 3090s
|
||||
- The entire public API of the library is now completely type hinted.
|
||||
- There may still be bugs however.
|
||||
- For best type hinting experience consider using Pyright.
|
||||
- Almost all edit methods now return their updated counterpart rather than doing an in-place edit.
|
||||
- Japanese docs were removed, as we are no longer able to keep them in sync.
|
||||
|
||||
|
||||
Breaking Changes
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
- :meth:`Asset.replace` now only accepts keyword arguments
|
||||
- ``Asset.with_`` functions now only accept positional only arguments
|
||||
- :meth:`TextChannel.get_partial_message` is now pos-only
|
||||
- :meth:`TextChannel.get_thread` is now pos-only
|
||||
- ``permissions_for`` is now pos-only
|
||||
- :attr:`GroupChannel.owner` is now Optional
|
||||
- ``edit`` methods now only accept None if it actually means something (e.g. clearing it)
|
||||
- ``timeout`` parameter for ``ui.View.__init__`` is now keyword only
|
||||
- When an interaction has already been responded and another one is sent, :exc:`InteractionResponded`is now raised.
|
||||
- Discord's API only allows a single :attr:`interaction.response`.
|
||||
- Separate :func:`on_member_update` and :func:`on_presence_update`
|
||||
- The new event :func:`on_presence_update` is now called when status/activity is changed.
|
||||
- :func:`on_member_update` will now no longer have status/activity changes.
|
||||
- afk parameter in :meth:`Client.change_presence` is removed
|
||||
- The undocumented private :func:`on_socket_response` event got removed.
|
||||
- Consider using the newer documented :func:`on_socket_event_type` event instead.
|
||||
- Using :func:`on_socket_raw_receive` and :func:`on_socket_raw_send` are now opt-in via :attr:`enable_debug_events` toggle.
|
||||
- :func:`on_socket_raw_receive` is now only dispatched after decompressing the payload.
|
||||
- Persistent View dispatch mechanism now uses the ``message_id`` key if provided.
|
||||
- :meth:`Message.start_thread` was renamed to :meth:`Message.create_thread`
|
||||
- :meth:`TextChannel.start_thread` was renamed to :meth:`TextChannel.create_thread`
|
||||
- All ``get_`` lookup functions now use positional-only parameters for the id parameter.
|
||||
- Remove :meth:`TextChannel.active_threads` due to the endpoint being deprecated and slated for removal.
|
||||
- Use :meth:`Guild.active_threads` instead.
|
||||
- :attr:`User.avatar` now returns None if the user did not upload an avatar.
|
||||
- Use :attr:`User.display_avatar` to get the avatar and fallback to the default avatar to go back to the old behaviour.
|
||||
|
||||
New Features
|
||||
~~~~~~~~~~~~~~
|
||||
|
||||
- Channel types are now typed
|
||||
- Member is now typed
|
||||
- Client is now typed
|
||||
- Permissions are now typed
|
||||
- Core library errors are now typed
|
||||
- Add various examples showing how to use views. There are more to come.
|
||||
- :attr:`GroupChannel.owner_id` now gets the owner ID
|
||||
- ``edit`` methods now don't rely on previous state
|
||||
- :meth:`View.from_message` converts a Message.components to a View
|
||||
- :attr:`Thread.type` to get the thread channel type
|
||||
- :attr:`ButtonStyle.url` alias for :attr:`ButtonStyle.link`
|
||||
- Add default style for :class:`ui.Button` constructor
|
||||
- This makes it so creating a URL button is as simple as ``ui.Button(url='...', label='...')``
|
||||
- :attr:`Thread.mention` to get the mention string for a thread
|
||||
- :meth:`Thread.is_nsfw` to check whether the parent channel of the thread is NSFW
|
||||
- Add support for fetching the original interaction response message.
|
||||
- :meth:`Interaction.original_message` will retrieve it and returns an InteractionMessage
|
||||
- :meth:`InteractionMessage.edit` or :meth:`Interaction.edit_original_message` will edit it
|
||||
- :meth:`InteractionMessage.delete` or :meth:`Interaction.delete_original_message` will delete it
|
||||
- :attr:`MessageFlags.ephemeral` to get whether a message is ephemeral
|
||||
- :meth:`Client.fetch_channel` now fetches threads as well
|
||||
- :class:`SelectOption` now has a __str__ that matches the client representation.
|
||||
- This might change in the future to remove the description from it.
|
||||
- Add a converter for :class:`discord.Thread`
|
||||
- Allow ``clean_content`` converter to work stand-alone
|
||||
- Add :meth:`User.banner` to get a user's banner and :meth:`User.accent_colour` to get the set banner colour.
|
||||
- Due to an API limitation this requires using :meth:`Client.fetch_user`.
|
||||
- Add ``reason`` keyword argument to more methods
|
||||
- Add audit log events for threads
|
||||
- Allow public threads to be created without a starter message
|
||||
- Add :meth:`Guild.get_channel_or_thread` helper method
|
||||
- Add full support for the new sticker API
|
||||
- Add :func:`on_socket_event_type` event to get the event type of an event
|
||||
- Add :attr:`TextChannel.default_auto_archive_duration`
|
||||
- Add :class:`PartialMessageable` type to allow for sending messages to a channel using only its ``channel_id``.
|
||||
- This is constructed with :meth:`Client.get_partial_messageable`.
|
||||
- Add :meth:`Guild.active_threads` to get all of a guild's active threads.
|
||||
- Add :attr:`Thread.members` to get all cached :class:`ThreadMember` instances of a thread.
|
||||
- Add :meth:`Thread.fetch_members` to fetch all :class:`ThreadMember` instances of a thread.
|
||||
- These two require :attr:`Intents.members` to be useful.
|
||||
- Add support for guild avatars for members under :attr:`Member.guild_avatar`
|
||||
- Add :attr:`User.display_avatar` and :attr:`Member.display_avatar` to get the user's displayed avatar.
|
||||
- Add :attr:`Colour.brand_green` and :attr:`Colour.brand_red`
|
||||
- |commands| :attr:`CommandOnCooldown.type` to get back the type of the cooldown since it was removed from :class:`Cooldown`
|
||||
- Add :attr:`Guild.bots` and :attr:`Guild.humans`
|
||||
|
||||
|
||||
Bug Fixes
|
||||
~~~~~~~~~~~
|
||||
|
||||
- :class:`Channel` converters now work in DMs again
|
||||
- Fix :attr:`Interaction.channel` being None in threads
|
||||
- Change timeouts in :class:`ui.View` to work as documented
|
||||
- :attr:`Message.__repr__` now shows the proper type, e.g. :class:`WebhookMessage` and :class:`InteractionMessage`
|
||||
- Change :class:`Cooldown` handling to not reset token window when the number of tokens reaches 0
|
||||
- Fix audit log permission construction breaking due to unexpected type errors.
|
||||
- Fix :func:`on_thread_join` not dispatching when a thread is unarchived
|
||||
- Fix :attr:`Message.guild` being None when a thread is unarchived due to a new message
|
||||
- :class:`MessageConverter` now works with threads
|
||||
- Retry requests when a 504 is hit
|
||||
- Fix :attr:`Thread.slowmode_delay` not updating on edit
|
||||
- Fix ``permissions_for`` for roles
|
||||
- Update :attr:`Message.system_content` for newer message types
|
||||
- Fix :class:`PartialMessage` not working with threads
|
||||
- Fix crash with stage instances not having the documented ``discoverable_enabled`` key
|
||||
- Fix some built-in checks not working with threads
|
||||
- Fix :class:`SyncWebhook` not working in a multi-threaded context
|
||||
- Fix :func:`on_thread_member_remove` not dispatching properly
|
||||
- Fix :func:`on_typing` not dispatching for threads
|
||||
- Update :attr:`Message.is_system` to work with newer message types
|
||||
- Fix some enums like :class:`VerificationLevel` not being comparable.
|
||||
- Fix ``io.BytesIO`` sources not working with ffmpeg players
|
||||
- Fix :meth:`Client.fetch_channel` and :meth:`Guild.fetch_channel` not returning threads
|
||||
|
||||
.. _vp1p7p3:
|
||||
|
||||
v1.7.3
|
||||
|
@ -26,5 +26,5 @@ class MyClient(discord.Client):
|
||||
async def before_my_task(self):
|
||||
await self.wait_until_ready() # wait until the bot logs in
|
||||
|
||||
client = MyClient()
|
||||
client = MyClient(intents=discord.Intents(guilds=True))
|
||||
client.run('token')
|
||||
|
@ -22,5 +22,5 @@ class MyClient(discord.Client):
|
||||
await asyncio.sleep(60) # task runs every 60 seconds
|
||||
|
||||
|
||||
client = MyClient()
|
||||
client = MyClient(intents=discord.Intents(guilds=True))
|
||||
client.run('token')
|
||||
|
@ -9,10 +9,8 @@ module.
|
||||
|
||||
There are a number of utility commands being showcased here.'''
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.members = True
|
||||
|
||||
bot = commands.Bot(command_prefix='?', description=description, intents=intents)
|
||||
intents = discord.Intents(guilds=True, messages=True, members=True)
|
||||
bot = commands.Bot(command_prefix='t-', description=description, intents=intents)
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
|
@ -123,8 +123,11 @@ class Music(commands.Cog):
|
||||
elif ctx.voice_client.is_playing():
|
||||
ctx.voice_client.stop()
|
||||
|
||||
bot = commands.Bot(command_prefix=commands.when_mentioned_or("!"),
|
||||
description='Relatively simple music bot example')
|
||||
bot = commands.Bot(
|
||||
command_prefix=commands.when_mentioned_or("!"),
|
||||
description='Relatively simple music bot example',
|
||||
intents=discord.Intents(guilds=True, guild_messages=True, voice_states=True)
|
||||
)
|
||||
|
||||
@bot.event
|
||||
async def on_ready():
|
||||
|
@ -5,9 +5,8 @@ import typing
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.members = True
|
||||
|
||||
intents = discord.Intents(guilds=True, messages=True, members=True)
|
||||
bot = commands.Bot('!', intents=intents)
|
||||
|
||||
|
||||
|
@ -29,7 +29,7 @@ class MyBot(commands.Bot):
|
||||
return await super().get_context(message, cls=cls)
|
||||
|
||||
|
||||
bot = MyBot(command_prefix='!')
|
||||
bot = MyBot(command_prefix='!', intents=discord.Intents(guilds=True, messages=True))
|
||||
|
||||
@bot.command()
|
||||
async def guess(ctx, number: int):
|
||||
|
@ -17,5 +17,5 @@ class MyClient(discord.Client):
|
||||
msg = f'{message.author} has deleted the message: {message.content}'
|
||||
await message.channel.send(msg)
|
||||
|
||||
client = MyClient()
|
||||
client = MyClient(intents=discord.Intents(guilds=True, messages=True))
|
||||
client.run('token')
|
||||
|
@ -16,5 +16,5 @@ class MyClient(discord.Client):
|
||||
msg = f'**{before.author}** edited their message:\n{before.content} -> {after.content}'
|
||||
await before.channel.send(msg)
|
||||
|
||||
client = MyClient()
|
||||
client = MyClient(intents=discord.Intents(guilds=True, messages=True))
|
||||
client.run('token')
|
||||
|
@ -30,5 +30,5 @@ class MyClient(discord.Client):
|
||||
else:
|
||||
await message.channel.send(f'Oops. It is actually {answer}.')
|
||||
|
||||
client = MyClient()
|
||||
client = MyClient(intents=discord.Intents(guilds=True, messages=True))
|
||||
client.run('token')
|
||||
|
@ -14,8 +14,5 @@ class MyClient(discord.Client):
|
||||
await guild.system_channel.send(to_send)
|
||||
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.members = True
|
||||
|
||||
client = MyClient(intents=intents)
|
||||
client = MyClient(intents=discord.Intents(guilds=True, members=True))
|
||||
client.run('token')
|
||||
|
@ -78,8 +78,6 @@ class MyClient(discord.Client):
|
||||
# If we want to do something in case of errors we'd do it here.
|
||||
pass
|
||||
|
||||
intents = discord.Intents.default()
|
||||
intents.members = True
|
||||
|
||||
intents = discord.Intents(guilds=True, members=True, guild_reactions=True)
|
||||
client = MyClient(intents=intents)
|
||||
client.run('token')
|
||||
|
@ -13,5 +13,5 @@ class MyClient(discord.Client):
|
||||
if message.content.startswith('!hello'):
|
||||
await message.reply('Hello!', mention_author=True)
|
||||
|
||||
client = MyClient()
|
||||
client = MyClient(intents=discord.Intents(guilds=True, messages=True))
|
||||
client.run('token')
|
||||
|
@ -3,7 +3,11 @@ import typing
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
bot = commands.Bot(command_prefix=commands.when_mentioned, description="Nothing to see here!")
|
||||
bot = commands.Bot(
|
||||
command_prefix=commands.when_mentioned,
|
||||
description="Nothing to see here!",
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
# the `hidden` keyword argument hides it from the help command.
|
||||
@bot.group(hidden=True)
|
||||
|
@ -5,7 +5,10 @@ import discord
|
||||
|
||||
class Bot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Logged in as {self.user} (ID: {self.user.id})')
|
||||
|
@ -5,7 +5,10 @@ import discord
|
||||
|
||||
class CounterBot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Logged in as {self.user} (ID: {self.user.id})')
|
||||
|
@ -1,5 +1,3 @@
|
||||
import typing
|
||||
|
||||
import discord
|
||||
from discord.ext import commands
|
||||
|
||||
@ -39,7 +37,10 @@ class DropdownView(discord.ui.View):
|
||||
|
||||
class Bot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Logged in as {self.user} (ID: {self.user.id})')
|
||||
|
@ -4,7 +4,10 @@ import discord
|
||||
|
||||
class EphemeralCounterBot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Logged in as {self.user} (ID: {self.user.id})')
|
||||
|
@ -5,7 +5,10 @@ from urllib.parse import quote_plus
|
||||
|
||||
class GoogleBot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Logged in as {self.user} (ID: {self.user.id})')
|
||||
@ -36,4 +39,4 @@ async def google(ctx: commands.Context, *, query: str):
|
||||
await ctx.send(f'Google Result for: `{query}`', view=Google(query))
|
||||
|
||||
|
||||
bot.run('token')
|
||||
bot.run()
|
||||
|
@ -29,7 +29,11 @@ class PersistentView(discord.ui.View):
|
||||
|
||||
class PersistentViewBot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
self.persistent_views_added = False
|
||||
|
||||
async def on_ready(self):
|
||||
|
@ -120,7 +120,10 @@ class TicTacToe(discord.ui.View):
|
||||
|
||||
class TicTacToeBot(commands.Bot):
|
||||
def __init__(self):
|
||||
super().__init__(command_prefix=commands.when_mentioned_or('$'))
|
||||
super().__init__(
|
||||
command_prefix=commands.when_mentioned_or('$'),
|
||||
intents=discord.Intents(guilds=True, messages=True)
|
||||
)
|
||||
|
||||
async def on_ready(self):
|
||||
print(f'Logged in as {self.user} (ID: {self.user.id})')
|
||||
|
Reference in New Issue
Block a user