3434 AsyncIterator ,
3535 Callable ,
3636 Dict ,
37+ Generator ,
3738 Iterable ,
3839 List ,
40+ Literal ,
3941 Optional ,
4042 TYPE_CHECKING ,
4143 Protocol ,
6163from .sticker import GuildSticker , StickerItem
6264from . import utils
6365from .flags import InviteFlags
66+ import warnings
6467
6568__all__ = (
6669 'Snowflake' ,
114117 MessageableChannel = Union [PartialMessageableChannel , GroupChannel ]
115118 SnowflakeTime = Union ["Snowflake" , datetime ]
116119
120+ class PinnedMessage (Message ):
121+ pinned_at : datetime
122+ pinned : Literal [True ]
123+
124+
117125MISSING = utils .MISSING
118126
119127
@@ -125,6 +133,26 @@ def __repr__(self) -> str:
125133_undefined : Any = _Undefined ()
126134
127135
136+ class _PinsIterator :
137+ def __init__ (self , iterator : AsyncIterator [PinnedMessage ]) -> None :
138+ self .__iterator : AsyncIterator [PinnedMessage ] = iterator
139+
140+ def __await__ (self ) -> Generator [Any , None , List [PinnedMessage ]]:
141+ warnings .warn (
142+ "`await <channel>.pins()` is deprecated; use `async for message in <channel>.pins()` instead." ,
143+ DeprecationWarning ,
144+ stacklevel = 2 ,
145+ )
146+
147+ async def gather () -> List [PinnedMessage ]:
148+ return [msg async for msg in self .__iterator ]
149+
150+ return gather ().__await__ ()
151+
152+ def __aiter__ (self ) -> AsyncIterator [PinnedMessage ]:
153+ return self .__iterator
154+
155+
128156async def _single_delete_strategy (messages : Iterable [Message ], * , reason : Optional [str ] = None ):
129157 for m in messages :
130158 try :
@@ -1754,34 +1782,132 @@ async def fetch_message(self, id: int, /) -> Message:
17541782 data = await self ._state .http .get_message (channel .id , id )
17551783 return self ._state .create_message (channel = channel , data = data )
17561784
1757- async def pins (self ) -> List [Message ]:
1758- """|coro|
1785+ async def __pins (
1786+ self ,
1787+ * ,
1788+ limit : Optional [int ] = 50 ,
1789+ before : Optional [SnowflakeTime ] = None ,
1790+ oldest_first : bool = False ,
1791+ ) -> AsyncIterator [PinnedMessage ]:
1792+ channel = await self ._get_channel ()
1793+ state = self ._state
1794+ max_limit : int = 50
1795+
1796+ time : Optional [str ] = (
1797+ (before if isinstance (before , datetime ) else utils .snowflake_time (before .id )).isoformat ()
1798+ if before is not None
1799+ else None
1800+ )
1801+
1802+ while True :
1803+ retrieve = max_limit if limit is None else min (limit , max_limit )
1804+ if retrieve < 1 :
1805+ break
1806+
1807+ data = await self ._state .http .pins_from (
1808+ channel_id = channel .id ,
1809+ limit = retrieve ,
1810+ before = time ,
1811+ )
1812+
1813+ items = data and data ['items' ]
1814+ if items :
1815+ if limit is not None :
1816+ limit -= len (items )
1817+
1818+ time = items [- 1 ]['pinned_at' ]
1819+
1820+ # Terminate loop on next iteration; there's no data left after this
1821+ if len (items ) < max_limit or not data ['has_more' ]:
1822+ limit = 0
1823+
1824+ if oldest_first :
1825+ items = reversed (items )
1826+
1827+ count = 0
1828+ for count , m in enumerate (items , start = 1 ):
1829+ message : Message = state .create_message (channel = channel , data = m ['message' ])
1830+ message ._pinned_at = utils .parse_time (m ['pinned_at' ])
1831+ yield message # pyright: ignore[reportReturnType]
1832+
1833+ if count < max_limit :
1834+ break
1835+
1836+ def pins (
1837+ self ,
1838+ * ,
1839+ limit : Optional [int ] = 50 ,
1840+ before : Optional [SnowflakeTime ] = None ,
1841+ oldest_first : bool = False ,
1842+ ) -> _PinsIterator :
1843+ """Retrieves an :term:`asynchronous iterator` of the pinned messages in the channel.
17591844
1760- Retrieves all messages that are currently pinned in the channel.
1845+ You must have :attr:`~discord.Permissions.view_channel` and
1846+ :attr:`~discord.Permissions.read_message_history` in order to use this.
1847+
1848+ .. versionchanged:: 2.6
1849+
1850+ Due to a change in Discord's API, this now returns a paginated iterator instead of a list.
1851+
1852+ For backwards compatibility, you can still retrieve a list of pinned messages by
1853+ using ``await`` on the returned object. This is however deprecated.
17611854
17621855 .. note::
17631856
17641857 Due to a limitation with the Discord API, the :class:`.Message`
1765- objects returned by this method do not contain complete
1858+ object returned by this method does not contain complete
17661859 :attr:`.Message.reactions` data.
17671860
1861+ Examples
1862+ ---------
1863+
1864+ Usage ::
1865+
1866+ counter = 0
1867+ async for message in channel.pins(limit=250):
1868+ counter += 1
1869+
1870+ Flattening into a list: ::
1871+
1872+ messages = [message async for message in channel.pins(limit=50)]
1873+ # messages is now a list of Message...
1874+
1875+ All parameters are optional.
1876+
1877+ Parameters
1878+ -----------
1879+ limit: Optional[int]
1880+ The number of pinned messages to retrieve. If ``None``, it retrieves
1881+ every pinned message in the channel. Note, however, that this would
1882+ make it a slow operation.
1883+ Defaults to ``50``.
1884+
1885+ .. versionadded:: 2.6
1886+ before: Optional[Union[:class:`datetime.datetime`, :class:`.abc.Snowflake`]]
1887+ Retrieve pinned messages before this time or snowflake.
1888+ If a datetime is provided, it is recommended to use a UTC aware datetime.
1889+ If the datetime is naive, it is assumed to be local time.
1890+
1891+ .. versionadded:: 2.6
1892+ oldest_first: :class:`bool`
1893+ If set to ``True``, return messages in oldest pin->newest pin order.
1894+ Defaults to ``False``.
1895+
1896+ .. versionadded:: 2.6
1897+
17681898 Raises
17691899 -------
17701900 ~discord.Forbidden
17711901 You do not have the permission to retrieve pinned messages.
17721902 ~discord.HTTPException
17731903 Retrieving the pinned messages failed.
17741904
1775- Returns
1776- --------
1777- List[ :class:`~discord.Message`]
1778- The messages that are currently pinned .
1905+ Yields
1906+ -------
1907+ :class:`~discord.Message`
1908+ The pinned message with :attr:`.Message.pinned_at` set .
17791909 """
1780-
1781- channel = await self ._get_channel ()
1782- state = self ._state
1783- data = await state .http .pins_from (channel .id )
1784- return [state .create_message (channel = channel , data = m ) for m in data ]
1910+ return _PinsIterator (self .__pins (limit = limit , before = before , oldest_first = oldest_first ))
17851911
17861912 async def history (
17871913 self ,
0 commit comments