Browse Source

Update to support new pin endpoints

pull/10262/head
Soheab 5 days ago
committed by GitHub
parent
commit
705eb2c2a5
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 152
      discord/abc.py
  2. 19
      discord/http.py
  3. 15
      discord/message.py
  4. 10
      discord/types/message.py

152
discord/abc.py

@ -34,8 +34,10 @@ from typing import (
AsyncIterator, AsyncIterator,
Callable, Callable,
Dict, Dict,
Generator,
Iterable, Iterable,
List, List,
Literal,
Optional, Optional,
TYPE_CHECKING, TYPE_CHECKING,
Protocol, Protocol,
@ -61,6 +63,7 @@ from .voice_client import VoiceClient, VoiceProtocol
from .sticker import GuildSticker, StickerItem from .sticker import GuildSticker, StickerItem
from . import utils from . import utils
from .flags import InviteFlags from .flags import InviteFlags
import warnings
__all__ = ( __all__ = (
'Snowflake', 'Snowflake',
@ -114,6 +117,11 @@ if TYPE_CHECKING:
MessageableChannel = Union[PartialMessageableChannel, GroupChannel] MessageableChannel = Union[PartialMessageableChannel, GroupChannel]
SnowflakeTime = Union["Snowflake", datetime] SnowflakeTime = Union["Snowflake", datetime]
class PinnedMessage(Message):
pinned_at: datetime
pinned: Literal[True]
MISSING = utils.MISSING MISSING = utils.MISSING
@ -125,6 +133,26 @@ class _Undefined:
_undefined: Any = _Undefined() _undefined: Any = _Undefined()
class _PinsIterator:
def __init__(self, iterator: AsyncIterator[PinnedMessage]) -> None:
self.__iterator: AsyncIterator[PinnedMessage] = iterator
def __await__(self) -> Generator[Any, None, List[PinnedMessage]]:
warnings.warn(
"`await <channel>.pins()` is deprecated; use `async for message in <channel>.pins()` instead.",
DeprecationWarning,
stacklevel=2,
)
async def gather() -> List[PinnedMessage]:
return [msg async for msg in self.__iterator]
return gather().__await__()
def __aiter__(self) -> AsyncIterator[PinnedMessage]:
return self.__iterator
async def _single_delete_strategy(messages: Iterable[Message], *, reason: Optional[str] = None): async def _single_delete_strategy(messages: Iterable[Message], *, reason: Optional[str] = None):
for m in messages: for m in messages:
try: try:
@ -1754,17 +1782,119 @@ class Messageable:
data = await self._state.http.get_message(channel.id, id) data = await self._state.http.get_message(channel.id, id)
return self._state.create_message(channel=channel, data=data) return self._state.create_message(channel=channel, data=data)
async def pins(self) -> List[Message]: async def __pins(
"""|coro| self,
*,
limit: Optional[int] = 50,
before: Optional[SnowflakeTime] = None,
oldest_first: bool = False,
) -> AsyncIterator[PinnedMessage]:
channel = await self._get_channel()
state = self._state
max_limit: int = 50
time: Optional[str] = (
(before if isinstance(before, datetime) else utils.snowflake_time(before.id)).isoformat()
if before is not None
else None
)
while True:
retrieve = max_limit if limit is None else min(limit, max_limit)
if retrieve < 1:
break
data = await self._state.http.pins_from(
channel_id=channel.id,
limit=retrieve,
before=time,
)
items = data and data['items']
if items:
if limit is not None:
limit -= len(items)
time = items[-1]['pinned_at']
# Terminate loop on next iteration; there's no data left after this
if len(items) < max_limit or not data['has_more']:
limit = 0
if oldest_first:
items = reversed(items)
count = 0
for count, m in enumerate(items, start=1):
message: Message = state.create_message(channel=channel, data=m['message'])
message._pinned_at = utils.parse_time(m['pinned_at'])
yield message # pyright: ignore[reportReturnType]
if count < max_limit:
break
def pins(
self,
*,
limit: Optional[int] = 50,
before: Optional[SnowflakeTime] = None,
oldest_first: bool = False,
) -> _PinsIterator:
"""Retrieves an :term:`asynchronous iterator` of the pinned messages in the channel.
Retrieves all messages that are currently pinned in the channel. You must have :attr:`~discord.Permissions.view_channel` and
:attr:`~discord.Permissions.read_message_history` in order to use this.
.. versionchanged:: 2.6
Due to a change in Discord's API, this now returns a paginated iterator instead of a list.
For backwards compatibility, you can still retrieve a list of pinned messages by
using ``await`` on the returned object. This is however deprecated.
.. note:: .. note::
Due to a limitation with the Discord API, the :class:`.Message` Due to a limitation with the Discord API, the :class:`.Message`
objects returned by this method do not contain complete object returned by this method does not contain complete
:attr:`.Message.reactions` data. :attr:`.Message.reactions` data.
Examples
---------
Usage ::
counter = 0
async for message in channel.pins(limit=250):
counter += 1
Flattening into a list: ::
messages = [message async for message in channel.pins(limit=50)]
# messages is now a list of Message...
All parameters are optional.
Parameters
-----------
limit: Optional[int]
The number of pinned messages to retrieve. If ``None``, it retrieves
every pinned message in the channel. Note, however, that this would
make it a slow operation.
Defaults to ``50``.
.. versionadded:: 2.6
before: Optional[Union[:class:`datetime.datetime`, :class:`.abc.Snowflake`]]
Retrieve pinned messages before this time or snowflake.
If a datetime is provided, it is recommended to use a UTC aware datetime.
If the datetime is naive, it is assumed to be local time.
.. versionadded:: 2.6
oldest_first: :class:`bool`
If set to ``True``, return messages in oldest pin->newest pin order.
Defaults to ``False``.
.. versionadded:: 2.6
Raises Raises
------- -------
~discord.Forbidden ~discord.Forbidden
@ -1772,16 +1902,12 @@ class Messageable:
~discord.HTTPException ~discord.HTTPException
Retrieving the pinned messages failed. Retrieving the pinned messages failed.
Returns Yields
-------- -------
List[:class:`~discord.Message`] :class:`~discord.Message`
The messages that are currently pinned. The pinned message with :attr:`.Message.pinned_at` set.
""" """
return _PinsIterator(self.__pins(limit=limit, before=before, oldest_first=oldest_first))
channel = await self._get_channel()
state = self._state
data = await state.http.pins_from(channel.id)
return [state.create_message(channel=channel, data=m) for m in data]
async def history( async def history(
self, self,

19
discord/http.py

@ -1047,7 +1047,7 @@ class HTTPClient:
def pin_message(self, channel_id: Snowflake, message_id: Snowflake, reason: Optional[str] = None) -> Response[None]: def pin_message(self, channel_id: Snowflake, message_id: Snowflake, reason: Optional[str] = None) -> Response[None]:
r = Route( r = Route(
'PUT', 'PUT',
'/channels/{channel_id}/pins/{message_id}', '/channels/{channel_id}/messages/pins/{message_id}',
channel_id=channel_id, channel_id=channel_id,
message_id=message_id, message_id=message_id,
) )
@ -1056,14 +1056,25 @@ class HTTPClient:
def unpin_message(self, channel_id: Snowflake, message_id: Snowflake, reason: Optional[str] = None) -> Response[None]: def unpin_message(self, channel_id: Snowflake, message_id: Snowflake, reason: Optional[str] = None) -> Response[None]:
r = Route( r = Route(
'DELETE', 'DELETE',
'/channels/{channel_id}/pins/{message_id}', '/channels/{channel_id}/messages/pins/{message_id}',
channel_id=channel_id, channel_id=channel_id,
message_id=message_id, message_id=message_id,
) )
return self.request(r, reason=reason) return self.request(r, reason=reason)
def pins_from(self, channel_id: Snowflake) -> Response[List[message.Message]]: def pins_from(
return self.request(Route('GET', '/channels/{channel_id}/pins', channel_id=channel_id)) self,
channel_id: Snowflake,
limit: Optional[int] = None,
before: Optional[str] = None,
) -> Response[message.ChannelPins]:
params = {}
if before is not None:
params['before'] = before
if limit is not None:
params['limit'] = limit
return self.request(Route('GET', '/channels/{channel_id}/messages/pins', channel_id=channel_id), params=params)
# Member management # Member management

15
discord/message.py

@ -2185,6 +2185,7 @@ class Message(PartialMessage, Hashable):
'call', 'call',
'purchase_notification', 'purchase_notification',
'message_snapshots', 'message_snapshots',
'_pinned_at',
) )
if TYPE_CHECKING: if TYPE_CHECKING:
@ -2224,6 +2225,8 @@ class Message(PartialMessage, Hashable):
self.application_id: Optional[int] = utils._get_as_snowflake(data, 'application_id') self.application_id: Optional[int] = utils._get_as_snowflake(data, 'application_id')
self.stickers: List[StickerItem] = [StickerItem(data=d, state=state) for d in data.get('sticker_items', [])] self.stickers: List[StickerItem] = [StickerItem(data=d, state=state) for d in data.get('sticker_items', [])]
self.message_snapshots: List[MessageSnapshot] = MessageSnapshot._from_value(state, data.get('message_snapshots')) self.message_snapshots: List[MessageSnapshot] = MessageSnapshot._from_value(state, data.get('message_snapshots'))
# Set by Messageable.pins
self._pinned_at: Optional[datetime.datetime] = None
self.poll: Optional[Poll] = None self.poll: Optional[Poll] = None
try: try:
@ -2644,6 +2647,18 @@ class Message(PartialMessage, Hashable):
# Fall back to guild threads in case one was created after the message # Fall back to guild threads in case one was created after the message
return self._thread or self.guild.get_thread(self.id) return self._thread or self.guild.get_thread(self.id)
@property
def pinned_at(self) -> Optional[datetime.datetime]:
"""Optional[:class:`datetime.datetime`]: An aware UTC datetime object containing the time
when the message was pinned.
.. note::
This is only set for messages that are returned by :meth:`abc.Messageable.pins`.
.. versionadded:: 2.6
"""
return self._pinned_at
@property @property
@deprecated('interaction_metadata') @deprecated('interaction_metadata')
def interaction(self) -> Optional[MessageInteraction]: def interaction(self) -> Optional[MessageInteraction]:

10
discord/types/message.py

@ -237,3 +237,13 @@ class AllowedMentions(TypedDict):
roles: SnowflakeList roles: SnowflakeList
users: SnowflakeList users: SnowflakeList
replied_user: bool replied_user: bool
class MessagePin(TypedDict):
pinned_at: str
message: Message
class ChannelPins(TypedDict):
items: List[MessagePin]
has_more: bool

Loading…
Cancel
Save