|
@ -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, |
|
|