mirror of
https://github.com/Rapptz/discord.py.git
synced 2025-06-06 11:57:17 +00:00
Add support for PartialMessageable instances
This allows library users to send messages to channels without fetching it first.
This commit is contained in:
parent
4fca699810
commit
1279510194
@ -78,7 +78,7 @@ if TYPE_CHECKING:
|
|||||||
from .channel import CategoryChannel
|
from .channel import CategoryChannel
|
||||||
from .embeds import Embed
|
from .embeds import Embed
|
||||||
from .message import Message, MessageReference, PartialMessage
|
from .message import Message, MessageReference, PartialMessage
|
||||||
from .channel import TextChannel, DMChannel, GroupChannel
|
from .channel import TextChannel, DMChannel, GroupChannel, PartialMessageable
|
||||||
from .threads import Thread
|
from .threads import Thread
|
||||||
from .enums import InviteTarget
|
from .enums import InviteTarget
|
||||||
from .ui.view import View
|
from .ui.view import View
|
||||||
@ -88,7 +88,7 @@ if TYPE_CHECKING:
|
|||||||
OverwriteType,
|
OverwriteType,
|
||||||
)
|
)
|
||||||
|
|
||||||
PartialMessageableChannel = Union[TextChannel, Thread, DMChannel]
|
PartialMessageableChannel = Union[TextChannel, Thread, DMChannel, PartialMessageable]
|
||||||
MessageableChannel = Union[PartialMessageableChannel, GroupChannel]
|
MessageableChannel = Union[PartialMessageableChannel, GroupChannel]
|
||||||
SnowflakeTime = Union["Snowflake", datetime]
|
SnowflakeTime = Union["Snowflake", datetime]
|
||||||
|
|
||||||
|
@ -26,13 +26,28 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
import asyncio
|
import asyncio
|
||||||
from typing import Any, Callable, Dict, Iterable, List, Mapping, Optional, TYPE_CHECKING, Tuple, Type, TypeVar, Union, overload
|
from typing import (
|
||||||
|
Any,
|
||||||
|
Callable,
|
||||||
|
Dict,
|
||||||
|
Iterable,
|
||||||
|
List,
|
||||||
|
Mapping,
|
||||||
|
Optional,
|
||||||
|
TYPE_CHECKING,
|
||||||
|
Tuple,
|
||||||
|
Type,
|
||||||
|
TypeVar,
|
||||||
|
Union,
|
||||||
|
overload,
|
||||||
|
)
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
import discord.abc
|
import discord.abc
|
||||||
from .permissions import PermissionOverwrite, Permissions
|
from .permissions import PermissionOverwrite, Permissions
|
||||||
from .enums import ChannelType, StagePrivacyLevel, try_enum, VoiceRegion, VideoQualityMode
|
from .enums import ChannelType, StagePrivacyLevel, try_enum, VoiceRegion, VideoQualityMode
|
||||||
from .mixins import Hashable
|
from .mixins import Hashable
|
||||||
|
from .object import Object
|
||||||
from . import utils
|
from . import utils
|
||||||
from .utils import MISSING
|
from .utils import MISSING
|
||||||
from .asset import Asset
|
from .asset import Asset
|
||||||
@ -49,6 +64,7 @@ __all__ = (
|
|||||||
'CategoryChannel',
|
'CategoryChannel',
|
||||||
'StoreChannel',
|
'StoreChannel',
|
||||||
'GroupChannel',
|
'GroupChannel',
|
||||||
|
'PartialMessageable',
|
||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -648,7 +664,7 @@ class TextChannel(discord.abc.Messageable, discord.abc.GuildChannel, Hashable):
|
|||||||
message: Optional[Snowflake] = None,
|
message: Optional[Snowflake] = None,
|
||||||
auto_archive_duration: ThreadArchiveDuration = 1440,
|
auto_archive_duration: ThreadArchiveDuration = 1440,
|
||||||
type: Optional[ChannelType] = None,
|
type: Optional[ChannelType] = None,
|
||||||
reason: Optional[str] = None
|
reason: Optional[str] = None,
|
||||||
) -> Thread:
|
) -> Thread:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
@ -1147,7 +1163,9 @@ class StageChannel(VocalGuildChannel):
|
|||||||
"""
|
"""
|
||||||
return utils.get(self.guild.stage_instances, channel_id=self.id)
|
return utils.get(self.guild.stage_instances, channel_id=self.id)
|
||||||
|
|
||||||
async def create_instance(self, *, topic: str, privacy_level: StagePrivacyLevel = MISSING, reason: Optional[str] = None) -> StageInstance:
|
async def create_instance(
|
||||||
|
self, *, topic: str, privacy_level: StagePrivacyLevel = MISSING, reason: Optional[str] = None
|
||||||
|
) -> StageInstance:
|
||||||
"""|coro|
|
"""|coro|
|
||||||
|
|
||||||
Create a stage instance.
|
Create a stage instance.
|
||||||
@ -1651,9 +1669,6 @@ class StoreChannel(discord.abc.GuildChannel, Hashable):
|
|||||||
await self._edit(options, reason=reason)
|
await self._edit(options, reason=reason)
|
||||||
|
|
||||||
|
|
||||||
DMC = TypeVar('DMC', bound='DMChannel')
|
|
||||||
|
|
||||||
|
|
||||||
class DMChannel(discord.abc.Messageable, Hashable):
|
class DMChannel(discord.abc.Messageable, Hashable):
|
||||||
"""Represents a Discord direct message channel.
|
"""Represents a Discord direct message channel.
|
||||||
|
|
||||||
@ -1677,10 +1692,8 @@ class DMChannel(discord.abc.Messageable, Hashable):
|
|||||||
|
|
||||||
Attributes
|
Attributes
|
||||||
----------
|
----------
|
||||||
recipient: Optional[:class:`User`]
|
recipient: :class:`User`
|
||||||
The user you are participating with in the direct message channel.
|
The user you are participating with in the direct message channel.
|
||||||
If this channel is received through the gateway, the recipient information
|
|
||||||
may not be always available.
|
|
||||||
me: :class:`ClientUser`
|
me: :class:`ClientUser`
|
||||||
The user presenting yourself.
|
The user presenting yourself.
|
||||||
id: :class:`int`
|
id: :class:`int`
|
||||||
@ -1691,7 +1704,7 @@ class DMChannel(discord.abc.Messageable, Hashable):
|
|||||||
|
|
||||||
def __init__(self, *, me: ClientUser, state: ConnectionState, data: DMChannelPayload):
|
def __init__(self, *, me: ClientUser, state: ConnectionState, data: DMChannelPayload):
|
||||||
self._state: ConnectionState = state
|
self._state: ConnectionState = state
|
||||||
self.recipient: Optional[User] = state.store_user(data['recipients'][0])
|
self.recipient: User = state.store_user(data['recipients'][0])
|
||||||
self.me: ClientUser = me
|
self.me: ClientUser = me
|
||||||
self.id: int = int(data['id'])
|
self.id: int = int(data['id'])
|
||||||
|
|
||||||
@ -1699,22 +1712,11 @@ class DMChannel(discord.abc.Messageable, Hashable):
|
|||||||
return self
|
return self
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
if self.recipient:
|
|
||||||
return f'Direct Message with {self.recipient}'
|
return f'Direct Message with {self.recipient}'
|
||||||
return 'Direct Message with Unknown User'
|
|
||||||
|
|
||||||
def __repr__(self) -> str:
|
def __repr__(self) -> str:
|
||||||
return f'<DMChannel id={self.id} recipient={self.recipient!r}>'
|
return f'<DMChannel id={self.id} recipient={self.recipient!r}>'
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def _from_message(cls: Type[DMC], state: ConnectionState, channel_id: int) -> DMC:
|
|
||||||
self: DMC = cls.__new__(cls)
|
|
||||||
self._state = state
|
|
||||||
self.id = channel_id
|
|
||||||
self.recipient = None
|
|
||||||
self.me = state.user # type: ignore
|
|
||||||
return self
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> ChannelType:
|
def type(self) -> ChannelType:
|
||||||
""":class:`ChannelType`: The channel's Discord type."""
|
""":class:`ChannelType`: The channel's Discord type."""
|
||||||
@ -1922,6 +1924,69 @@ class GroupChannel(discord.abc.Messageable, Hashable):
|
|||||||
await self._state.http.leave_group(self.id)
|
await self._state.http.leave_group(self.id)
|
||||||
|
|
||||||
|
|
||||||
|
class PartialMessageable(discord.abc.Messageable, Hashable):
|
||||||
|
"""Represents a partial messageable to aid with working messageable channels when
|
||||||
|
only a channel ID are present.
|
||||||
|
|
||||||
|
The only way to construct this class is through :meth:`Client.get_partial_messageable`.
|
||||||
|
|
||||||
|
Note that this class is trimmed down and has no rich attributes.
|
||||||
|
|
||||||
|
.. versionadded:: 2.0
|
||||||
|
|
||||||
|
.. container:: operations
|
||||||
|
|
||||||
|
.. describe:: x == y
|
||||||
|
|
||||||
|
Checks if two partial messageables are equal.
|
||||||
|
|
||||||
|
.. describe:: x != y
|
||||||
|
|
||||||
|
Checks if two partial messageables are not equal.
|
||||||
|
|
||||||
|
.. describe:: hash(x)
|
||||||
|
|
||||||
|
Returns the partial messageable's hash.
|
||||||
|
|
||||||
|
Attributes
|
||||||
|
-----------
|
||||||
|
id: :class:`int`
|
||||||
|
The channel ID associated with this partial messageable.
|
||||||
|
type: Optional[:class:`ChannelType`]
|
||||||
|
The channel type associated with this partial messageable, if given.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, state: ConnectionState, id: int, type: Optional[ChannelType] = None):
|
||||||
|
self._state: ConnectionState = state
|
||||||
|
self._channel: Object = Object(id=id)
|
||||||
|
self.id: int = id
|
||||||
|
self.type: Optional[ChannelType] = type
|
||||||
|
|
||||||
|
async def _get_channel(self) -> Object:
|
||||||
|
return self._channel
|
||||||
|
|
||||||
|
def get_partial_message(self, message_id: int, /) -> PartialMessage:
|
||||||
|
"""Creates a :class:`PartialMessage` from the message ID.
|
||||||
|
|
||||||
|
This is useful if you want to work with a message and only have its ID without
|
||||||
|
doing an unnecessary API call.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
------------
|
||||||
|
message_id: :class:`int`
|
||||||
|
The message ID to create a partial message for.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
---------
|
||||||
|
:class:`PartialMessage`
|
||||||
|
The partial message.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .message import PartialMessage
|
||||||
|
|
||||||
|
return PartialMessage(channel=self, id=message_id)
|
||||||
|
|
||||||
|
|
||||||
def _guild_channel_factory(channel_type: int):
|
def _guild_channel_factory(channel_type: int):
|
||||||
value = try_enum(ChannelType, channel_type)
|
value = try_enum(ChannelType, channel_type)
|
||||||
if value is ChannelType.text:
|
if value is ChannelType.text:
|
||||||
@ -1949,6 +2014,7 @@ def _channel_factory(channel_type: int):
|
|||||||
else:
|
else:
|
||||||
return cls, value
|
return cls, value
|
||||||
|
|
||||||
|
|
||||||
def _threaded_channel_factory(channel_type: int):
|
def _threaded_channel_factory(channel_type: int):
|
||||||
cls, value = _channel_factory(channel_type)
|
cls, value = _channel_factory(channel_type)
|
||||||
if value in (ChannelType.private_thread, ChannelType.public_thread, ChannelType.news_thread):
|
if value in (ChannelType.private_thread, ChannelType.public_thread, ChannelType.news_thread):
|
||||||
|
@ -39,7 +39,7 @@ from .template import Template
|
|||||||
from .widget import Widget
|
from .widget import Widget
|
||||||
from .guild import Guild
|
from .guild import Guild
|
||||||
from .emoji import Emoji
|
from .emoji import Emoji
|
||||||
from .channel import _threaded_channel_factory
|
from .channel import _threaded_channel_factory, PartialMessageable
|
||||||
from .enums import ChannelType
|
from .enums import ChannelType
|
||||||
from .mentions import AllowedMentions
|
from .mentions import AllowedMentions
|
||||||
from .errors import *
|
from .errors import *
|
||||||
@ -729,6 +729,26 @@ class Client:
|
|||||||
"""
|
"""
|
||||||
return self._connection.get_channel(id)
|
return self._connection.get_channel(id)
|
||||||
|
|
||||||
|
def get_partial_messageable(self, id: int, *, type: Optional[ChannelType] = None) -> PartialMessageable:
|
||||||
|
"""Returns a partial messageable with the given channel ID.
|
||||||
|
|
||||||
|
This is useful if you have a channel_id but don't want to do an API call
|
||||||
|
to send messages to it.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
-----------
|
||||||
|
id: :class:`int`
|
||||||
|
The channel ID to create a partial messageable for.
|
||||||
|
type: Optional[:class:`ChannelType`]
|
||||||
|
The underlying channel type for the partial messageable.
|
||||||
|
|
||||||
|
Returns
|
||||||
|
--------
|
||||||
|
:class:`PartialMessageable`
|
||||||
|
The partial messageable
|
||||||
|
"""
|
||||||
|
return PartialMessageable(state=self._connection, id=id, type=type)
|
||||||
|
|
||||||
def get_stage_instance(self, id) -> Optional[StageInstance]:
|
def get_stage_instance(self, id) -> Optional[StageInstance]:
|
||||||
"""Returns a stage instance with the given stage channel ID.
|
"""Returns a stage instance with the given stage channel ID.
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ if TYPE_CHECKING:
|
|||||||
from .abc import GuildChannel, PartialMessageableChannel, MessageableChannel
|
from .abc import GuildChannel, PartialMessageableChannel, MessageableChannel
|
||||||
from .components import Component
|
from .components import Component
|
||||||
from .state import ConnectionState
|
from .state import ConnectionState
|
||||||
from .channel import TextChannel, GroupChannel, DMChannel
|
from .channel import TextChannel, GroupChannel, DMChannel, PartialMessageable
|
||||||
from .mentions import AllowedMentions
|
from .mentions import AllowedMentions
|
||||||
from .user import User
|
from .user import User
|
||||||
from .role import Role
|
from .role import Role
|
||||||
@ -520,7 +520,7 @@ class Message(Hashable):
|
|||||||
This is not stored long term within Discord's servers and is only used ephemerally.
|
This is not stored long term within Discord's servers and is only used ephemerally.
|
||||||
embeds: List[:class:`Embed`]
|
embeds: List[:class:`Embed`]
|
||||||
A list of embeds the message has.
|
A list of embeds the message has.
|
||||||
channel: Union[:class:`TextChannel`, :class:`Thread`, :class:`DMChannel`, :class:`GroupChannel`]
|
channel: Union[:class:`TextChannel`, :class:`Thread`, :class:`DMChannel`, :class:`GroupChannel`, :class:`PartialMessageable`]
|
||||||
The :class:`TextChannel` or :class:`Thread` that the message was sent from.
|
The :class:`TextChannel` or :class:`Thread` that the message was sent from.
|
||||||
Could be a :class:`DMChannel` or :class:`GroupChannel` if it's a private message.
|
Could be a :class:`DMChannel` or :class:`GroupChannel` if it's a private message.
|
||||||
reference: Optional[:class:`~discord.MessageReference`]
|
reference: Optional[:class:`~discord.MessageReference`]
|
||||||
@ -646,7 +646,7 @@ class Message(Hashable):
|
|||||||
self,
|
self,
|
||||||
*,
|
*,
|
||||||
state: ConnectionState,
|
state: ConnectionState,
|
||||||
channel: Union[TextChannel, Thread, DMChannel, GroupChannel],
|
channel: Union[TextChannel, Thread, DMChannel, GroupChannel, PartialMessageable],
|
||||||
data: MessagePayload,
|
data: MessagePayload,
|
||||||
):
|
):
|
||||||
self._state: ConnectionState = state
|
self._state: ConnectionState = state
|
||||||
|
@ -405,12 +405,12 @@ class ConnectionState:
|
|||||||
try:
|
try:
|
||||||
guild = self._get_guild(int(data['guild_id']))
|
guild = self._get_guild(int(data['guild_id']))
|
||||||
except KeyError:
|
except KeyError:
|
||||||
channel = DMChannel._from_message(self, channel_id)
|
channel = PartialMessageable(state=self, id=channel_id, type=ChannelType.private)
|
||||||
guild = None
|
guild = None
|
||||||
else:
|
else:
|
||||||
channel = guild and guild._resolve_channel(channel_id)
|
channel = guild and guild._resolve_channel(channel_id)
|
||||||
|
|
||||||
return channel or Object(id=channel_id), guild
|
return channel or PartialMessageable(state=self, id=channel_id), guild
|
||||||
|
|
||||||
async def chunker(self, guild_id, query='', limit=0, presences=False, *, nonce=None):
|
async def chunker(self, guild_id, query='', limit=0, presences=False, *, nonce=None):
|
||||||
ws = self._get_websocket(guild_id) # This is ignored upstream
|
ws = self._get_websocket(guild_id) # This is ignored upstream
|
||||||
|
@ -3574,6 +3574,15 @@ RoleTags
|
|||||||
.. autoclass:: RoleTags()
|
.. autoclass:: RoleTags()
|
||||||
:members:
|
:members:
|
||||||
|
|
||||||
|
PartialMessageable
|
||||||
|
~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
.. attributetable:: PartialMessageable
|
||||||
|
|
||||||
|
.. autoclass:: PartialMessageable()
|
||||||
|
:members:
|
||||||
|
:inherited-members:
|
||||||
|
|
||||||
TextChannel
|
TextChannel
|
||||||
~~~~~~~~~~~~
|
~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user