Skip to content

Commit

Permalink
Merge branch 'master' into fix/localization-cog-copy
Browse files Browse the repository at this point in the history
  • Loading branch information
shiftinv authored Dec 8, 2023
2 parents eda1024 + 85cf393 commit 0c0b89d
Show file tree
Hide file tree
Showing 69 changed files with 769 additions and 343 deletions.
1 change: 1 addition & 0 deletions changelog/1091.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`Permissions.create_guild_expressions` and :attr:`Permissions.create_events`.
1 change: 1 addition & 0 deletions changelog/1094.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add inherited attributes to :class:`TeamMember`, and fix :attr:`TeamMember.avatar` documentation.
1 change: 1 addition & 0 deletions changelog/1094.feature.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add :attr:`TeamMember.role`.
1 change: 1 addition & 0 deletions changelog/1094.feature.1.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
|commands| Update :meth:`Bot.is_owner <ext.commands.Bot.is_owner>` to take team member roles into account.
1 change: 1 addition & 0 deletions changelog/1112.doc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document the :class:`.Option` attributes, the ``description`` and ``options`` properties for :class:`.ext.commands.InvokableSlashCommand` and the ``description`` and ``body`` properties for :class:`.ext.commands.SubCommand`.
1 change: 1 addition & 0 deletions changelog/1128.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
|commands| Support Python 3.12's ``type`` statement and :class:`py:typing.TypeAliasType` annotations in command signatures.
1 change: 1 addition & 0 deletions changelog/1136.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Update ``choices`` type in app commands to accept any :class:`~py:typing.Sequence` or :class:`~py:typing.Mapping`, instead of the more constrained :class:`list`/:class:`dict` types.
1 change: 1 addition & 0 deletions changelog/847.feature.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
|commands| Skip evaluating annotations of ``self`` (if present) and ``ctx`` parameters in prefix commands. These may now use stringified annotations with types that aren't available at runtime.
4 changes: 3 additions & 1 deletion disnake/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ async def _edit(
if p_id is not None and (parent := self.guild.get_channel(p_id)):
overwrites_payload = [c._asdict() for c in parent._overwrites]

if overwrites is not MISSING and overwrites is not None:
if overwrites not in (MISSING, None):
overwrites_payload = []
for target, perm in overwrites.items():
if not isinstance(perm, PermissionOverwrite):
Expand Down Expand Up @@ -853,7 +853,9 @@ async def set_permissions(
ban_members: Optional[bool] = ...,
change_nickname: Optional[bool] = ...,
connect: Optional[bool] = ...,
create_events: Optional[bool] = ...,
create_forum_threads: Optional[bool] = ...,
create_guild_expressions: Optional[bool] = ...,
create_instant_invite: Optional[bool] = ...,
create_private_threads: Optional[bool] = ...,
create_public_threads: Optional[bool] = ...,
Expand Down
2 changes: 1 addition & 1 deletion disnake/activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,7 +921,7 @@ def create_activity(
elif game_type is ActivityType.listening and "sync_id" in data and "session_id" in data:
activity = Spotify(**data)
else:
activity = Activity(**data)
activity = Activity(**data) # type: ignore

if isinstance(activity, (Activity, CustomActivity)) and activity.emoji and state:
activity.emoji._state = state
Expand Down
57 changes: 47 additions & 10 deletions disnake/app_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import math
import re
from abc import ABC
from typing import TYPE_CHECKING, ClassVar, Dict, List, Mapping, Optional, Tuple, Union
from typing import TYPE_CHECKING, ClassVar, List, Mapping, Optional, Sequence, Tuple, Union

from .enums import (
ApplicationCommandPermissionType,
Expand Down Expand Up @@ -37,10 +37,10 @@
)

Choices = Union[
List["OptionChoice"],
List[ApplicationCommandOptionChoiceValue],
Dict[str, ApplicationCommandOptionChoiceValue],
List[Localized[str]],
Sequence["OptionChoice"],
Sequence[ApplicationCommandOptionChoiceValue],
Mapping[str, ApplicationCommandOptionChoiceValue],
Sequence[Localized[str]],
]

APIApplicationCommand = Union["APIUserCommand", "APIMessageCommand", "APISlashCommand"]
Expand Down Expand Up @@ -179,8 +179,42 @@ class Option:
The option type, e.g. :class:`OptionType.user`.
required: :class:`bool`
Whether this option is required.
choices: Union[List[:class:`OptionChoice`], List[Union[:class:`str`, :class:`int`]], Dict[:class:`str`, Union[:class:`str`, :class:`int`]]]
The list of option choices.
choices: Union[Sequence[:class:`OptionChoice`], Sequence[Union[:class:`str`, :class:`int`, :class:`float`]], Mapping[:class:`str`, Union[:class:`str`, :class:`int`, :class:`float`]]]
The pre-defined choices for this option.
options: List[:class:`Option`]
The list of sub options. Normally you don't have to specify it directly,
instead consider using ``@main_cmd.sub_command`` or ``@main_cmd.sub_command_group`` decorators.
channel_types: List[:class:`ChannelType`]
The list of channel types that your option supports, if the type is :class:`OptionType.channel`.
By default, it supports all channel types.
autocomplete: :class:`bool`
Whether this option can be autocompleted.
min_value: Union[:class:`int`, :class:`float`]
The minimum value permitted.
max_value: Union[:class:`int`, :class:`float`]
The maximum value permitted.
min_length: :class:`int`
The minimum length for this option if this is a string option.
.. versionadded:: 2.6
max_length: :class:`int`
The maximum length for this option if this is a string option.
.. versionadded:: 2.6
Attributes
----------
name: :class:`str`
The option's name.
description: :class:`str`
The option's description.
type: :class:`OptionType`
The option type, e.g. :class:`OptionType.user`.
required: :class:`bool`
Whether this option is required.
choices: List[:class:`OptionChoice`]
The list of pre-defined choices.
options: List[:class:`Option`]
The list of sub options. Normally you don't have to specify it directly,
instead consider using ``@main_cmd.sub_command`` or ``@main_cmd.sub_command_group`` decorators.
Expand Down Expand Up @@ -270,6 +304,9 @@ def __init__(
if autocomplete:
raise TypeError("can not specify both choices and autocomplete args")

if isinstance(choices, str): # str matches `Sequence[str]`, but isn't meant to be used
raise TypeError("choices argument should be a list/sequence or dict, not str")

if isinstance(choices, Mapping):
self.choices = [OptionChoice(name, value) for name, value in choices.items()]
else:
Expand Down Expand Up @@ -336,7 +373,7 @@ def from_dict(cls, data: ApplicationCommandOptionPayload) -> Option:
def add_choice(
self,
name: LocalizedRequired,
value: Union[str, int],
value: ApplicationCommandOptionChoiceValue,
) -> None:
"""Adds an OptionChoice to the list of current choices,
parameters are the same as for :class:`OptionChoice`.
Expand All @@ -354,7 +391,7 @@ def add_option(
description: LocalizedOptional = None,
type: Optional[OptionType] = None,
required: bool = False,
choices: Optional[List[OptionChoice]] = None,
choices: Optional[Choices] = None,
options: Optional[list] = None,
channel_types: Optional[List[ChannelType]] = None,
autocomplete: bool = False,
Expand Down Expand Up @@ -850,7 +887,7 @@ def add_option(
description: LocalizedOptional = None,
type: Optional[OptionType] = None,
required: bool = False,
choices: Optional[List[OptionChoice]] = None,
choices: Optional[Choices] = None,
options: Optional[list] = None,
channel_types: Optional[List[ChannelType]] = None,
autocomplete: bool = False,
Expand Down
2 changes: 1 addition & 1 deletion disnake/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
ValidAssetFormatTypes = Literal["webp", "jpeg", "jpg", "png", "gif"]
AnyState = Union[ConnectionState, _WebhookState[BaseWebhook]]

AssetBytes = Union[bytes, "AssetMixin"]
AssetBytes = Union[utils._BytesLike, "AssetMixin"]

VALID_STATIC_FORMATS = frozenset({"jpeg", "jpg", "webp", "png"})
VALID_ASSET_FORMATS = VALID_STATIC_FORMATS | {"gif"}
Expand Down
2 changes: 1 addition & 1 deletion disnake/audit_logs.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def _transform_datetime(entry: AuditLogEntry, data: Optional[str]) -> Optional[d


def _transform_privacy_level(
entry: AuditLogEntry, data: int
entry: AuditLogEntry, data: Optional[int]
) -> Optional[Union[enums.StagePrivacyLevel, enums.GuildScheduledEventPrivacyLevel]]:
if data is None:
return None
Expand Down
13 changes: 7 additions & 6 deletions disnake/channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,7 @@ async def edit(
overwrites=overwrites,
flags=flags,
reason=reason,
**kwargs,
**kwargs, # type: ignore
)
if payload is not None:
# the payload will always be the proper channel payload
Expand Down Expand Up @@ -1207,6 +1207,7 @@ def permissions_for(
denied.update(
manage_channels=True,
manage_roles=True,
create_events=True,
manage_events=True,
manage_webhooks=True,
)
Expand Down Expand Up @@ -1628,7 +1629,7 @@ async def edit(
slowmode_delay=slowmode_delay,
flags=flags,
reason=reason,
**kwargs,
**kwargs, # type: ignore
)
if payload is not None:
# the payload will always be the proper channel payload
Expand Down Expand Up @@ -2453,7 +2454,7 @@ async def edit(
flags=flags,
slowmode_delay=slowmode_delay,
reason=reason,
**kwargs,
**kwargs, # type: ignore
)
if payload is not None:
# the payload will always be the proper channel payload
Expand Down Expand Up @@ -2946,7 +2947,7 @@ async def edit(
overwrites=overwrites,
flags=flags,
reason=reason,
**kwargs,
**kwargs, # type: ignore
)
if payload is not None:
# the payload will always be the proper channel payload
Expand Down Expand Up @@ -3619,7 +3620,7 @@ async def edit(
default_sort_order=default_sort_order,
default_layout=default_layout,
reason=reason,
**kwargs,
**kwargs, # type: ignore
)
if payload is not None:
# the payload will always be the proper channel payload
Expand Down Expand Up @@ -3994,7 +3995,7 @@ async def create_thread(
stickers=stickers,
)

if auto_archive_duration is not None:
if auto_archive_duration not in (MISSING, None):
auto_archive_duration = cast(
"ThreadArchiveDurationLiteral", try_enum_to_int(auto_archive_duration)
)
Expand Down
32 changes: 24 additions & 8 deletions disnake/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
Optional,
Sequence,
Tuple,
TypedDict,
TypeVar,
Union,
overload,
Expand Down Expand Up @@ -79,6 +80,8 @@
from .widget import Widget

if TYPE_CHECKING:
from typing_extensions import NotRequired

from .abc import GuildChannel, PrivateChannel, Snowflake, SnowflakeTime
from .app_commands import APIApplicationCommand
from .asset import AssetBytes
Expand Down Expand Up @@ -207,6 +210,17 @@ class GatewayParams(NamedTuple):
zlib: bool = True


# used for typing the ws parameter dict in the connect() loop
class _WebSocketParams(TypedDict):
initial: bool
shard_id: Optional[int]
gateway: Optional[str]

sequence: NotRequired[Optional[int]]
resume: NotRequired[bool]
session: NotRequired[Optional[str]]


class Client:
"""Represents a client connection that connects to Discord.
This class is used to interact with the Discord WebSocket and API.
Expand Down Expand Up @@ -1080,7 +1094,7 @@ async def connect(
if not ignore_session_start_limit and self.session_start_limit.remaining == 0:
raise SessionStartLimitReached(self.session_start_limit)

ws_params = {
ws_params: _WebSocketParams = {
"initial": True,
"shard_id": self.shard_id,
"gateway": initial_gateway,
Expand All @@ -1104,6 +1118,7 @@ async def connect(

while True:
await self.ws.poll_event()

except ReconnectWebSocket as e:
_log.info("Got a request to %s the websocket.", e.op)
self.dispatch("disconnect")
Expand All @@ -1116,6 +1131,7 @@ async def connect(
gateway=self.ws.resume_gateway if e.resume else initial_gateway,
)
continue

except (
OSError,
HTTPException,
Expand Down Expand Up @@ -1196,7 +1212,8 @@ async def close(self) -> None:
# if an error happens during disconnects, disregard it.
pass

if self.ws is not None and self.ws.open:
# can be None if not connected
if self.ws is not None and self.ws.open: # pyright: ignore[reportUnnecessaryComparison]
await self.ws.close(code=1000)

await self.http.close()
Expand Down Expand Up @@ -1874,16 +1891,15 @@ async def change_presence(

await self.ws.change_presence(activity=activity, status=status_str)

activities = () if activity is None else (activity,)
for guild in self._connection.guilds:
me = guild.me
if me is None:
if me is None: # pyright: ignore[reportUnnecessaryComparison]
# may happen if guild is unavailable
continue

if activity is not None:
me.activities = (activity,) # type: ignore
else:
me.activities = ()

# Member.activities is typehinted as Tuple[ActivityType, ...], we may be setting it as Tuple[BaseActivity, ...]
me.activities = activities # type: ignore
me.status = status

# Guild stuff
Expand Down
Loading

0 comments on commit 0c0b89d

Please sign in to comment.