Skip to content

Commit bddf256

Browse files
authored
Merge branch 'master' into fix/client-run
Signed-off-by: Paillat <[email protected]>
2 parents 2d0b61a + 3b8b79f commit bddf256

File tree

19 files changed

+796
-97
lines changed

19 files changed

+796
-97
lines changed

.github/ISSUE_TEMPLATE/config.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
blank_issues_enabled: false
1+
blank_issues_enabled: true
22
contact_links:
33
- name: Ask a question
44
about: Ask questions and discuss with other users of the library.

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,17 @@ These changes are available on the `master` branch, but have not yet been releas
1717
([#2711](https://github.com/Pycord-Development/pycord/pull/2711))
1818
- Added `RawMessageUpdateEvent.new_message` - message update events now contain full
1919
message objects ([#2780](https://github.com/Pycord-Development/pycord/pull/2780))
20+
- Added support for setting guild-specific `avatar`, `banner`, and `bio` for the bot
21+
user through `Member.edit`.
22+
([#2908](https://github.com/Pycord-Development/pycord/pull/2908))
23+
- Added support for select default values.
24+
([#2899](https://github.com/Pycord-Development/pycord/pull/2899))
25+
- Adds a new generic parameter to selects to type `ui.Select.values` return type.
26+
- Adds `SelectDefaultValue` object to create select default values.
27+
- Adds `SelectDefaultValueType` enum.
28+
- Adds pre-typed and pre-constructed with select_type `ui.Select` aliases for the
29+
different select types: `ui.StringSelect`, `ui.UserSelect`, `ui.RoleSelect`,
30+
`ui.MentionableSelect`, and `ui.ChannelSelect`.
2031

2132
### Changed
2233

@@ -30,6 +41,10 @@ These changes are available on the `master` branch, but have not yet been releas
3041
([#2915](https://github.com/Pycord-Development/pycord/pull/2915))
3142
- Fixed Async I/O errors that could be raised when using `Client.run`.
3243
([#2645](https://github.com/Pycord-Development/pycord/pull/2645))
44+
- `View.message` being `None` when it had not been interacted with yet.
45+
([#2916](https://github.com/Pycord-Development/pycord/pull/2916))
46+
- Fixed a crash when processing message edit events while message cache was disabled.
47+
([#2924](https://github.com/Pycord-Development/pycord/pull/2924))
3348

3449
### Removed
3550

discord/abc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ class User(Snowflake, Protocol):
235235
name: str
236236
discriminator: str
237237
global_name: str | None
238-
avatar: Asset
238+
avatar: Asset | None
239239
bot: bool
240240

241241
@property

discord/components.py

Lines changed: 186 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525

2626
from __future__ import annotations
2727

28-
from typing import TYPE_CHECKING, Any, ClassVar, Iterator, TypeVar
28+
from typing import TYPE_CHECKING, Any, ClassVar, Iterator, TypeVar, overload
2929

3030
from .asset import AssetMixin
3131
from .colour import Colour
@@ -34,6 +34,7 @@
3434
ChannelType,
3535
ComponentType,
3636
InputTextStyle,
37+
SelectDefaultValueType,
3738
SeparatorSpacingSize,
3839
try_enum,
3940
)
@@ -42,9 +43,9 @@
4243
from .utils import MISSING, find, get_slots
4344

4445
if TYPE_CHECKING:
46+
from . import abc
4547
from .emoji import AppEmoji, GuildEmoji
4648
from .types.components import ActionRow as ActionRowPayload
47-
from .types.components import BaseComponent as BaseComponentPayload
4849
from .types.components import ButtonComponent as ButtonComponentPayload
4950
from .types.components import Component as ComponentPayload
5051
from .types.components import ContainerComponent as ContainerComponentPayload
@@ -54,6 +55,7 @@
5455
from .types.components import MediaGalleryComponent as MediaGalleryComponentPayload
5556
from .types.components import MediaGalleryItem as MediaGalleryItemPayload
5657
from .types.components import SectionComponent as SectionComponentPayload
58+
from .types.components import SelectDefaultValue as SelectDefaultValuePayload
5759
from .types.components import SelectMenu as SelectMenuPayload
5860
from .types.components import SelectOption as SelectOptionPayload
5961
from .types.components import SeparatorComponent as SeparatorComponentPayload
@@ -78,6 +80,7 @@
7880
"Separator",
7981
"Container",
8082
"Label",
83+
"SelectDefaultValue",
8184
)
8285

8386
C = TypeVar("C", bound="Component")
@@ -437,6 +440,7 @@ class SelectMenu(Component):
437440
"channel_types",
438441
"disabled",
439442
"required",
443+
"default_values",
440444
)
441445

442446
__repr_info__: ClassVar[tuple[str, ...]] = __slots__
@@ -457,6 +461,9 @@ def __init__(self, data: SelectMenuPayload):
457461
try_enum(ChannelType, ct) for ct in data.get("channel_types", [])
458462
]
459463
self.required: bool | None = data.get("required")
464+
self.default_values: list[SelectDefaultValue] = SelectDefaultValue._from_data(
465+
data.get("default_values")
466+
)
460467

461468
def to_dict(self) -> SelectMenuPayload:
462469
payload: SelectMenuPayload = {
@@ -476,10 +483,187 @@ def to_dict(self) -> SelectMenuPayload:
476483
payload["placeholder"] = self.placeholder
477484
if self.required is not None:
478485
payload["required"] = self.required
486+
if self.type is not ComponentType.string_select:
487+
payload["default_values"] = [dv.to_dict() for dv in self.default_values]
479488

480489
return payload
481490

482491

492+
class SelectDefaultValue:
493+
r"""Represents a :class:`discord.SelectMenu`\s default value.
494+
495+
This is only applicable to selects of type other than :attr:`ComponentType.string_select`.
496+
497+
.. versionadded:: 2.7
498+
499+
Parameters
500+
----------
501+
object: :class:`abc.Snowflake`
502+
The model type this select default value is based of.
503+
504+
Below, is a table defining the model instance type and the default value type it will be mapped:
505+
506+
+-----------------------------------+--------------------------------------------------------------------------+
507+
| Model Type | Default Value Type |
508+
+-----------------------------------+--------------------------------------------------------------------------+
509+
| :class:`discord.User` | :attr:`discord.SelectDefaultValueType.user` |
510+
+-----------------------------------+--------------------------------------------------------------------------+
511+
| :class:`discord.Member` | :attr:`discord.SelectDefaultValueType.user` |
512+
+-----------------------------------+--------------------------------------------------------------------------+
513+
| :class:`discord.Role` | :attr:`discord.SelectDefaultValueType.role` |
514+
+-----------------------------------+--------------------------------------------------------------------------+
515+
| :class:`discord.abc.GuildChannel` | :attr:`discord.SelectDefaultValueType.channel` |
516+
+-----------------------------------+--------------------------------------------------------------------------+
517+
| :class:`discord.Object` | depending on :attr:`discord.Object.type`, it will be mapped to any above |
518+
+-----------------------------------+--------------------------------------------------------------------------+
519+
520+
If you pass a model that is not defined in the table, ``TypeError`` will be raised.
521+
522+
.. note::
523+
524+
The :class:`discord.abc.GuildChannel` protocol includes :class:`discord.TextChannel`, :class:`discord.VoiceChannel`, :class:`discord.StageChannel`,
525+
:class:`discord.ForumChannel`, :class:`discord.Thread`, :class:`discord.MediaChannel`. This list is not exhaustive, and is bound to change
526+
based of the new channel types Discord adds.
527+
528+
id: :class:`int`
529+
The ID of the default value. This cannot be used with ``object``.
530+
type: :class:`SelectDefaultValueType`
531+
The default value type. This cannot be used with ``object``.
532+
533+
Raises
534+
------
535+
TypeError
536+
You did not provide any parameter, you provided all parameters, or you provided ``id`` but not ``type``.
537+
"""
538+
539+
__slots__ = ("id", "type")
540+
541+
@overload
542+
def __init__(
543+
self,
544+
object: abc.Snowflake,
545+
/,
546+
) -> None: ...
547+
548+
@overload
549+
def __init__(
550+
self,
551+
/,
552+
*,
553+
id: int,
554+
type: SelectDefaultValueType,
555+
) -> None: ...
556+
557+
def __init__(
558+
self,
559+
object: abc.Snowflake = MISSING,
560+
/,
561+
*,
562+
id: int = MISSING,
563+
type: SelectDefaultValueType = MISSING,
564+
) -> None:
565+
self.id: int = id
566+
self.type: SelectDefaultValueType = type
567+
if object is not MISSING:
568+
if any(p is not MISSING for p in (id, type)):
569+
raise TypeError("you cannot pass id or type when passing object")
570+
self._handle_model(object, inst=self)
571+
elif id is not MISSING and type is not MISSING:
572+
self.id = id
573+
self.type = type
574+
else:
575+
raise TypeError("you must provide an object model, or an id and type")
576+
577+
def __repr__(self) -> str:
578+
return f"<SelectDefaultValue id={self.id} type={self.type}>"
579+
580+
@classmethod
581+
def _from_data(
582+
cls, default_values: list[SelectDefaultValuePayload] | None
583+
) -> list[SelectDefaultValue]:
584+
if not default_values:
585+
return []
586+
return [
587+
cls(id=int(d["id"]), type=try_enum(SelectDefaultValueType, d["type"]))
588+
for d in default_values
589+
]
590+
591+
@classmethod
592+
def _handle_model(
593+
cls,
594+
model: abc.Snowflake,
595+
select_type: ComponentType | None = None,
596+
inst: SelectDefaultValue | None = None,
597+
) -> SelectDefaultValue:
598+
# preventing >circular imports<
599+
from discord import Member, Object, Role, User, abc
600+
from discord.user import _UserTag
601+
602+
instances_mapping: dict[
603+
type, tuple[tuple[ComponentType, ...], SelectDefaultValueType]
604+
] = {
605+
Role: (
606+
(ComponentType.role_select, ComponentType.mentionable_select),
607+
SelectDefaultValueType.role,
608+
),
609+
User: (
610+
(ComponentType.user_select, ComponentType.mentionable_select),
611+
SelectDefaultValueType.user,
612+
),
613+
Member: (
614+
(ComponentType.user_select, ComponentType.mentionable_select),
615+
SelectDefaultValueType.user,
616+
),
617+
_UserTag: (
618+
(ComponentType.user_select, ComponentType.mentionable_select),
619+
SelectDefaultValueType.user,
620+
),
621+
abc.GuildChannel: (
622+
(ComponentType.channel_select,),
623+
SelectDefaultValueType.channel,
624+
),
625+
}
626+
627+
obj_id = model.id
628+
obj_type = model.__class__
629+
630+
if isinstance(model, Object):
631+
obj_type = model.type
632+
633+
sel_types = None
634+
def_type = None
635+
636+
for typ, (st, dt) in instances_mapping.items():
637+
if issubclass(obj_type, typ):
638+
sel_types = st
639+
def_type = dt
640+
break
641+
642+
if sel_types is None or def_type is None:
643+
raise TypeError(
644+
f"{obj_type.__name__} is not a valid instance for a select default value"
645+
)
646+
647+
# we can't actually check select types when not in a select context
648+
if select_type is not None and select_type not in sel_types:
649+
raise TypeError(
650+
f"{model.__class__.__name__} objects can not be set as a default value for {select_type.value} selects",
651+
)
652+
653+
if inst is None:
654+
return cls(id=obj_id, type=def_type)
655+
else:
656+
inst.id = obj_id
657+
inst.type = def_type
658+
return inst
659+
660+
def to_dict(self) -> SelectDefaultValuePayload:
661+
return {
662+
"id": self.id,
663+
"type": self.type.value,
664+
}
665+
666+
483667
class SelectOption:
484668
"""Represents a :class:`discord.SelectMenu`'s option.
485669

discord/enums.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
"ThreadArchiveDuration",
8484
"SubscriptionStatus",
8585
"SeparatorSpacingSize",
86+
"SelectDefaultValueType",
8687
)
8788

8889

@@ -1120,6 +1121,14 @@ def __int__(self):
11201121
return self.value
11211122

11221123

1124+
class SelectDefaultValueType(Enum):
1125+
"""Represents the default value type of a select menu."""
1126+
1127+
channel = "channel"
1128+
role = "role"
1129+
user = "user"
1130+
1131+
11231132
T = TypeVar("T")
11241133

11251134

discord/http.py

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,38 +1018,6 @@ def guild_voice_state(
10181018
def edit_profile(self, payload: dict[str, Any]) -> Response[user.User]:
10191019
return self.request(Route("PATCH", "/users/@me"), json=payload)
10201020

1021-
def change_my_nickname(
1022-
self,
1023-
guild_id: Snowflake,
1024-
nickname: str,
1025-
*,
1026-
reason: str | None = None,
1027-
) -> Response[member.Nickname]:
1028-
r = Route("PATCH", "/guilds/{guild_id}/members/@me", guild_id=guild_id)
1029-
payload = {
1030-
"nick": nickname,
1031-
}
1032-
return self.request(r, json=payload, reason=reason)
1033-
1034-
def change_nickname(
1035-
self,
1036-
guild_id: Snowflake,
1037-
user_id: Snowflake,
1038-
nickname: str,
1039-
*,
1040-
reason: str | None = None,
1041-
) -> Response[member.Member]:
1042-
r = Route(
1043-
"PATCH",
1044-
"/guilds/{guild_id}/members/{user_id}",
1045-
guild_id=guild_id,
1046-
user_id=user_id,
1047-
)
1048-
payload = {
1049-
"nick": nickname,
1050-
}
1051-
return self.request(r, json=payload, reason=reason)
1052-
10531021
def edit_my_voice_state(
10541022
self, guild_id: Snowflake, payload: dict[str, Any]
10551023
) -> Response[None]:

0 commit comments

Comments
 (0)