Browse Source

Improve generic duck type programming with PartialMessageable

This adds jump_url, permissions_for, and created_at. Luckily, most
cases of this type being constructed already have the guild_id at
creation time.
pull/10109/head
Rapptz 3 years ago
committed by dolfies
parent
commit
18ae88b791
  1. 39
      discord/channel.py
  2. 11
      discord/client.py
  3. 2
      discord/state.py
  4. 4
      discord/webhook/async_.py
  5. 2
      discord/webhook/sync.py

39
discord/channel.py

@ -3031,13 +3031,16 @@ class PartialMessageable(discord.abc.Messageable, Hashable):
-----------
id: :class:`int`
The channel ID associated with this partial messageable.
guild_id: Optional[:class:`int`]
The guild 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):
def __init__(self, state: ConnectionState, id: int, guild_id: Optional[int] = None, type: Optional[ChannelType] = None):
self._state: ConnectionState = state
self.id: int = id
self.guild_id: Optional[int] = guild_id
self.type: Optional[ChannelType] = type
self.last_message_id: Optional[int] = None
@ -3047,6 +3050,40 @@ class PartialMessageable(discord.abc.Messageable, Hashable):
async def _get_channel(self) -> PartialMessageable:
return self
@property
def jump_url(self) -> str:
""":class:`str`: Returns a URL that allows the client to jump to the channel."""
if self.guild_id is None:
return f'https://discord.com/channels/@me/{self.id}'
return f'https://discord.com/channels/{self.guild_id}/{self.id}'
@property
def created_at(self) -> datetime.datetime:
""":class:`datetime.datetime`: Returns the direct message channel's creation time in UTC."""
return utils.snowflake_time(self.id)
def permissions_for(self, obj: Any = None, /) -> Permissions:
"""Handles permission resolution for a :class:`User`.
This function is there for compatibility with other channel types.
Since partial messageables cannot reasonably have the concept of
permissions, this will always return :meth:`Permissions.none`.
Parameters
-----------
obj: :class:`User`
The user to check permissions for. This parameter is ignored
but kept for compatibility with other ``permissions_for`` methods.
Returns
--------
:class:`Permissions`
The resolved permissions.
"""
return Permissions.none()
def get_partial_message(self, message_id: int, /) -> PartialMessage:
"""Creates a :class:`PartialMessage` from the message ID.

11
discord/client.py

@ -1039,7 +1039,9 @@ class Client:
"""
return self._connection.get_channel(id) # type: ignore # The cache contains all channel types
def get_partial_messageable(self, id: int, *, type: Optional[ChannelType] = None) -> PartialMessageable:
def get_partial_messageable(
self, id: int, *, guild_id: Optional[int] = None, 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
@ -1051,6 +1053,11 @@ class Client:
-----------
id: :class:`int`
The channel ID to create a partial messageable for.
guild_id: Optional[:class:`int`]
The optional guild ID to create a partial messageable for.
This is not required to actually send messages, but it does allow the
:meth:`PartialMessageable.jump_url` property to form a well formed URL.
type: Optional[:class:`.ChannelType`]
The underlying channel type for the partial messageable.
@ -1059,7 +1066,7 @@ class Client:
:class:`.PartialMessageable`
The partial messageable
"""
return PartialMessageable(state=self._connection, id=id, type=type)
return PartialMessageable(state=self._connection, id=id, guild_id=guild_id, type=type)
def get_stage_instance(self, id: int, /) -> Optional[StageInstance]:
"""Returns a stage instance with the given stage channel ID.

2
discord/state.py

@ -873,7 +873,7 @@ class ConnectionState:
else:
channel = guild and guild._resolve_channel(channel_id)
return channel or PartialMessageable(state=self, id=channel_id), guild
return channel or PartialMessageable(state=self, guild_id=guild_id, id=channel_id), guild
async def _delete_messages(self, channel_id, messages, reason: Optional[str] = None) -> None:
delete_message = self.http.delete_message

4
discord/webhook/async_.py

@ -1157,14 +1157,14 @@ class Webhook(BaseWebhook):
state = _WebhookState(self, parent=self._state, thread=thread)
# state may be artificial (unlikely at this point...)
if thread is MISSING:
channel = self.channel or PartialMessageable(state=self._state, id=int(data['channel_id'])) # type: ignore
channel = self.channel or PartialMessageable(state=self._state, guild_id=self.guild_id, id=int(data['channel_id'])) # type: ignore
else:
channel = self.channel
if isinstance(channel, TextChannel):
channel = channel.get_thread(thread.id)
if channel is None:
channel = PartialMessageable(state=self._state, id=int(data['channel_id'])) # type: ignore
channel = PartialMessageable(state=self._state, guild_id=self.guild_id, id=int(data['channel_id'])) # type: ignore
# state is artificial
return WebhookMessage(data=data, state=state, channel=channel) # type: ignore

2
discord/webhook/sync.py

@ -842,7 +842,7 @@ class SyncWebhook(BaseWebhook):
def _create_message(self, data: MessagePayload, *, thread: Snowflake = MISSING) -> SyncWebhookMessage:
state = _WebhookState(self, parent=self._state, thread=thread)
# state may be artificial (unlikely at this point...)
channel = self.channel or PartialMessageable(state=self._state, id=int(data['channel_id'])) # type: ignore
channel = self.channel or PartialMessageable(state=self._state, guild_id=self.guild_id, id=int(data['channel_id'])) # type: ignore
# state is artificial
return SyncWebhookMessage(data=data, state=state, channel=channel) # type: ignore

Loading…
Cancel
Save