2525
2626from __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
3030from .asset import AssetMixin
3131from .colour import Colour
3434 ChannelType ,
3535 ComponentType ,
3636 InputTextStyle ,
37+ SelectDefaultValueType ,
3738 SeparatorSpacingSize ,
3839 try_enum ,
3940)
4243from .utils import MISSING , find , get_slots
4344
4445if 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
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
7880 "Separator" ,
7981 "Container" ,
8082 "Label" ,
83+ "SelectDefaultValue" ,
8184)
8285
8386C = 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+
483667class SelectOption :
484668 """Represents a :class:`discord.SelectMenu`'s option.
485669
0 commit comments