Skip to content

Commit e6bffc7

Browse files
committed
Merge remote-tracking branch 'upstream/master' into akiko
2 parents 549b0b0 + 38c6407 commit e6bffc7

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2285
-771
lines changed

discord/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@
7373
from .soundboard import *
7474
from .subscription import *
7575
from .presences import *
76+
from .primary_guild import *
77+
from .onboarding import *
7678

7779

7880
class VersionInfo(NamedTuple):

discord/abc.py

Lines changed: 139 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,10 @@
3434
AsyncIterator,
3535
Callable,
3636
Dict,
37+
Generator,
3738
Iterable,
3839
List,
40+
Literal,
3941
Optional,
4042
TYPE_CHECKING,
4143
Protocol,
@@ -61,6 +63,7 @@
6163
from .sticker import GuildSticker, StickerItem
6264
from . import utils
6365
from .flags import InviteFlags
66+
import warnings
6467

6568
__all__ = (
6669
'Snowflake',
@@ -114,6 +117,11 @@
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+
117125
MISSING = 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+
128156
async def _single_delete_strategy(messages: Iterable[Message], *, reason: Optional[str] = None):
129157
for m in messages:
130158
try:
@@ -1756,34 +1784,132 @@ async def fetch_message(self, id: int, /) -> Message:
17561784
data = await self._state.http.get_message(channel.id, id)
17571785
return self._state.create_message(channel=channel, data=data)
17581786

1759-
async def pins(self) -> List[Message]:
1760-
"""|coro|
1787+
async def __pins(
1788+
self,
1789+
*,
1790+
limit: Optional[int] = 50,
1791+
before: Optional[SnowflakeTime] = None,
1792+
oldest_first: bool = False,
1793+
) -> AsyncIterator[PinnedMessage]:
1794+
channel = await self._get_channel()
1795+
state = self._state
1796+
max_limit: int = 50
1797+
1798+
time: Optional[str] = (
1799+
(before if isinstance(before, datetime) else utils.snowflake_time(before.id)).isoformat()
1800+
if before is not None
1801+
else None
1802+
)
1803+
1804+
while True:
1805+
retrieve = max_limit if limit is None else min(limit, max_limit)
1806+
if retrieve < 1:
1807+
break
1808+
1809+
data = await self._state.http.pins_from(
1810+
channel_id=channel.id,
1811+
limit=retrieve,
1812+
before=time,
1813+
)
1814+
1815+
items = data and data['items']
1816+
if items:
1817+
if limit is not None:
1818+
limit -= len(items)
1819+
1820+
time = items[-1]['pinned_at']
1821+
1822+
# Terminate loop on next iteration; there's no data left after this
1823+
if len(items) < max_limit or not data['has_more']:
1824+
limit = 0
1825+
1826+
if oldest_first:
1827+
items = reversed(items)
1828+
1829+
count = 0
1830+
for count, m in enumerate(items, start=1):
1831+
message: Message = state.create_message(channel=channel, data=m['message'])
1832+
message._pinned_at = utils.parse_time(m['pinned_at'])
1833+
yield message # pyright: ignore[reportReturnType]
1834+
1835+
if count < max_limit:
1836+
break
1837+
1838+
def pins(
1839+
self,
1840+
*,
1841+
limit: Optional[int] = 50,
1842+
before: Optional[SnowflakeTime] = None,
1843+
oldest_first: bool = False,
1844+
) -> _PinsIterator:
1845+
"""Retrieves an :term:`asynchronous iterator` of the pinned messages in the channel.
17611846
1762-
Retrieves all messages that are currently pinned in the channel.
1847+
You must have :attr:`~discord.Permissions.view_channel` and
1848+
:attr:`~discord.Permissions.read_message_history` in order to use this.
1849+
1850+
.. versionchanged:: 2.6
1851+
1852+
Due to a change in Discord's API, this now returns a paginated iterator instead of a list.
1853+
1854+
For backwards compatibility, you can still retrieve a list of pinned messages by
1855+
using ``await`` on the returned object. This is however deprecated.
17631856
17641857
.. note::
17651858
17661859
Due to a limitation with the Discord API, the :class:`.Message`
1767-
objects returned by this method do not contain complete
1860+
object returned by this method does not contain complete
17681861
:attr:`.Message.reactions` data.
17691862
1863+
Examples
1864+
---------
1865+
1866+
Usage ::
1867+
1868+
counter = 0
1869+
async for message in channel.pins(limit=250):
1870+
counter += 1
1871+
1872+
Flattening into a list: ::
1873+
1874+
messages = [message async for message in channel.pins(limit=50)]
1875+
# messages is now a list of Message...
1876+
1877+
All parameters are optional.
1878+
1879+
Parameters
1880+
-----------
1881+
limit: Optional[int]
1882+
The number of pinned messages to retrieve. If ``None``, it retrieves
1883+
every pinned message in the channel. Note, however, that this would
1884+
make it a slow operation.
1885+
Defaults to ``50``.
1886+
1887+
.. versionadded:: 2.6
1888+
before: Optional[Union[:class:`datetime.datetime`, :class:`.abc.Snowflake`]]
1889+
Retrieve pinned messages before this time or snowflake.
1890+
If a datetime is provided, it is recommended to use a UTC aware datetime.
1891+
If the datetime is naive, it is assumed to be local time.
1892+
1893+
.. versionadded:: 2.6
1894+
oldest_first: :class:`bool`
1895+
If set to ``True``, return messages in oldest pin->newest pin order.
1896+
Defaults to ``False``.
1897+
1898+
.. versionadded:: 2.6
1899+
17701900
Raises
17711901
-------
17721902
~discord.Forbidden
17731903
You do not have the permission to retrieve pinned messages.
17741904
~discord.HTTPException
17751905
Retrieving the pinned messages failed.
17761906
1777-
Returns
1778-
--------
1779-
List[:class:`~discord.Message`]
1780-
The messages that are currently pinned.
1907+
Yields
1908+
-------
1909+
:class:`~discord.Message`
1910+
The pinned message with :attr:`.Message.pinned_at` set.
17811911
"""
1782-
1783-
channel = await self._get_channel()
1784-
state = self._state
1785-
data = await state.http.pins_from(channel.id)
1786-
return [state.create_message(channel=channel, data=m) for m in data]
1912+
return _PinsIterator(self.__pins(limit=limit, before=before, oldest_first=oldest_first))
17871913

17881914
async def history(
17891915
self,

discord/activity.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from typing import Any, Dict, List, Optional, TYPE_CHECKING, Union, overload
2929

3030
from .asset import Asset
31-
from .enums import ActivityType, try_enum
31+
from .enums import ActivityType, StatusDisplayType, try_enum
3232
from .colour import Colour
3333
from .partial_emoji import PartialEmoji
3434
from .utils import _get_as_snowflake
@@ -180,8 +180,10 @@ class Activity(BaseActivity):
180180
181181
- ``large_image``: A string representing the ID for the large image asset.
182182
- ``large_text``: A string representing the text when hovering over the large image asset.
183+
- ``large_url``: A string representing the URL of the large image asset.
183184
- ``small_image``: A string representing the ID for the small image asset.
184185
- ``small_text``: A string representing the text when hovering over the small image asset.
186+
- ``small_url``: A string representing the URL of the small image asset.
185187
186188
party: :class:`dict`
187189
A dictionary representing the activity party. It contains the following optional keys:
@@ -195,6 +197,19 @@ class Activity(BaseActivity):
195197
196198
emoji: Optional[:class:`PartialEmoji`]
197199
The emoji that belongs to this activity.
200+
details_url: Optional[:class:`str`]
201+
A URL that is linked to when clicking on the details text of the activity.
202+
203+
.. versionadded:: 2.6
204+
state_url: Optional[:class:`str`]
205+
A URL that is linked to when clicking on the state text of the activity.
206+
207+
.. versionadded:: 2.6
208+
status_display_type: Optional[:class:`StatusDisplayType`]
209+
Determines which field from the user's status text is displayed
210+
in the members list.
211+
212+
.. versionadded:: 2.6
198213
"""
199214

200215
__slots__ = (
@@ -213,6 +228,9 @@ class Activity(BaseActivity):
213228
'application_id',
214229
'emoji',
215230
'buttons',
231+
'state_url',
232+
'details_url',
233+
'status_display_type',
216234
)
217235

218236
def __init__(self, **kwargs: Any) -> None:
@@ -239,6 +257,18 @@ def __init__(self, **kwargs: Any) -> None:
239257
emoji = kwargs.pop('emoji', None)
240258
self.emoji: Optional[PartialEmoji] = PartialEmoji.from_dict(emoji) if emoji is not None else None
241259

260+
self.state_url: Optional[str] = kwargs.pop('state_url', None)
261+
self.details_url: Optional[str] = kwargs.pop('details_url', None)
262+
263+
status_display_type = kwargs.pop('status_display_type', None)
264+
self.status_display_type: Optional[StatusDisplayType] = (
265+
status_display_type
266+
if isinstance(status_display_type, StatusDisplayType)
267+
else try_enum(StatusDisplayType, status_display_type)
268+
if status_display_type is not None
269+
else None
270+
)
271+
242272
def __repr__(self) -> str:
243273
attrs = (
244274
('type', self.type),
@@ -267,6 +297,8 @@ def to_dict(self) -> Dict[str, Any]:
267297
ret['type'] = int(self.type)
268298
if self.emoji:
269299
ret['emoji'] = self.emoji.to_dict()
300+
if self.status_display_type:
301+
ret['status_display_type'] = int(self.status_display_type.value)
270302
return ret
271303

272304
@property

discord/app_commands/commands.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2524,7 +2524,10 @@ def inner(f: T) -> T:
25242524
allowed_contexts = getattr(f, '__discord_app_commands_contexts__', None) or AppCommandContext()
25252525
f.__discord_app_commands_contexts__ = allowed_contexts # type: ignore # Runtime attribute assignment
25262526

2527-
allowed_contexts.guild = True
2527+
# Ensure that only Guild context is allowed
2528+
allowed_contexts.guild = True # Enable guild context
2529+
allowed_contexts.private_channel = False # Disable private channel context
2530+
allowed_contexts.dm_channel = False # Disable DM context
25282531

25292532
return f
25302533

@@ -2578,7 +2581,10 @@ def inner(f: T) -> T:
25782581
allowed_contexts = getattr(f, '__discord_app_commands_contexts__', None) or AppCommandContext()
25792582
f.__discord_app_commands_contexts__ = allowed_contexts # type: ignore # Runtime attribute assignment
25802583

2581-
allowed_contexts.private_channel = True
2584+
# Ensure that only Private Channel context is allowed
2585+
allowed_contexts.guild = False # Disable guild context
2586+
allowed_contexts.private_channel = True # Enable private channel context
2587+
allowed_contexts.dm_channel = False # Disable DM context
25822588

25832589
return f
25842590

@@ -2630,7 +2636,11 @@ def inner(f: T) -> T:
26302636
allowed_contexts = getattr(f, '__discord_app_commands_contexts__', None) or AppCommandContext()
26312637
f.__discord_app_commands_contexts__ = allowed_contexts # type: ignore # Runtime attribute assignment
26322638

2633-
allowed_contexts.dm_channel = True
2639+
# Ensure that only DM context is allowed
2640+
allowed_contexts.guild = False # Disable guild context
2641+
allowed_contexts.private_channel = False # Disable private channel context
2642+
allowed_contexts.dm_channel = True # Enable DM context
2643+
26342644
return f
26352645

26362646
# Check if called with parentheses or not
@@ -2724,6 +2734,7 @@ def inner(f: T) -> T:
27242734
f.__discord_app_commands_installation_types__ = allowed_installs # type: ignore # Runtime attribute assignment
27252735

27262736
allowed_installs.guild = True
2737+
allowed_installs.user = False
27272738

27282739
return f
27292740

@@ -2774,6 +2785,7 @@ def inner(f: T) -> T:
27742785
f.__discord_app_commands_installation_types__ = allowed_installs # type: ignore # Runtime attribute assignment
27752786

27762787
allowed_installs.user = True
2788+
allowed_installs.guild = False
27772789

27782790
return f
27792791

0 commit comments

Comments
 (0)