@@ -53,7 +53,7 @@ class Button(Item[V_co]):
5353 The style of the button.
5454 custom_id: Optional[:class:`str`]
5555 The ID of the button that gets received during an interaction.
56- If this button is for a URL, it does not have a custom ID.
56+ If this button is for a URL or an SKU , it does not have a custom ID.
5757 url: Optional[:class:`str`]
5858 The URL this button sends you to.
5959 disabled: :class:`bool`
@@ -62,6 +62,11 @@ class Button(Item[V_co]):
6262 The label of the button, if any.
6363 emoji: Optional[Union[:class:`.PartialEmoji`, :class:`.Emoji`, :class:`str`]]
6464 The emoji of the button, if available.
65+ sku_id: Optional[:class:`int`]
66+ The ID of a purchasable SKU, for premium buttons.
67+ Premium buttons additionally cannot have a ``label``, ``url``, or ``emoji``.
68+
69+ .. versionadded:: 2.11
6570 row: Optional[:class:`int`]
6671 The relative row this button belongs to. A Discord component can only have 5
6772 rows. By default, items are arranged automatically into those 5 rows. If you'd
@@ -76,6 +81,7 @@ class Button(Item[V_co]):
7681 "disabled" ,
7782 "label" ,
7883 "emoji" ,
84+ "sku_id" ,
7985 "row" ,
8086 )
8187 # We have to set this to MISSING in order to overwrite the abstract property from WrappedComponent
@@ -91,6 +97,7 @@ def __init__(
9197 custom_id : Optional [str ] = None ,
9298 url : Optional [str ] = None ,
9399 emoji : Optional [Union [str , Emoji , PartialEmoji ]] = None ,
100+ sku_id : Optional [int ] = None ,
94101 row : Optional [int ] = None ,
95102 ) -> None : ...
96103
@@ -104,6 +111,7 @@ def __init__(
104111 custom_id : Optional [str ] = None ,
105112 url : Optional [str ] = None ,
106113 emoji : Optional [Union [str , Emoji , PartialEmoji ]] = None ,
114+ sku_id : Optional [int ] = None ,
107115 row : Optional [int ] = None ,
108116 ) -> None : ...
109117
@@ -116,18 +124,23 @@ def __init__(
116124 custom_id : Optional [str ] = None ,
117125 url : Optional [str ] = None ,
118126 emoji : Optional [Union [str , Emoji , PartialEmoji ]] = None ,
127+ sku_id : Optional [int ] = None ,
119128 row : Optional [int ] = None ,
120129 ) -> None :
121130 super ().__init__ ()
122- if custom_id is not None and url is not None :
123- raise TypeError ("cannot mix both url and custom_id with Button" )
124131
125132 self ._provided_custom_id = custom_id is not None
126- if url is None and custom_id is None :
133+ mutually_exclusive = 3 - (custom_id , url , sku_id ).count (None )
134+
135+ if mutually_exclusive == 0 :
127136 custom_id = os .urandom (16 ).hex ()
137+ elif mutually_exclusive != 1 :
138+ raise TypeError ("cannot mix url, sku_id and custom_id with Button" )
128139
129140 if url is not None :
130141 style = ButtonStyle .link
142+ if sku_id is not None :
143+ style = ButtonStyle .premium
131144
132145 if emoji is not None :
133146 if isinstance (emoji , str ):
@@ -147,6 +160,7 @@ def __init__(
147160 label = label ,
148161 style = style ,
149162 emoji = emoji ,
163+ sku_id = sku_id ,
150164 )
151165 self .row = row
152166
@@ -167,7 +181,7 @@ def style(self, value: ButtonStyle) -> None:
167181 def custom_id (self ) -> Optional [str ]:
168182 """Optional[:class:`str`]: The ID of the button that gets received during an interaction.
169183
170- If this button is for a URL, it does not have a custom ID.
184+ If this button is for a URL or an SKU , it does not have a custom ID.
171185 """
172186 return self ._underlying .custom_id
173187
@@ -226,6 +240,20 @@ def emoji(self, value: Optional[Union[str, Emoji, PartialEmoji]]) -> None:
226240 else :
227241 self ._underlying .emoji = None
228242
243+ @property
244+ def sku_id (self ) -> Optional [int ]:
245+ """Optional[:class:`int`]: The ID of a purchasable SKU, for premium buttons.
246+
247+ .. versionadded:: 2.11
248+ """
249+ return self ._underlying .sku_id
250+
251+ @sku_id .setter
252+ def sku_id (self , value : Optional [int ]) -> None :
253+ if value is not None and not isinstance (value , int ):
254+ raise TypeError ("sku_id must be None or int" )
255+ self ._underlying .sku_id = value
256+
229257 @classmethod
230258 def from_component (cls , button : ButtonComponent ) -> Self :
231259 return cls (
@@ -235,6 +263,7 @@ def from_component(cls, button: ButtonComponent) -> Self:
235263 custom_id = button .custom_id ,
236264 url = button .url ,
237265 emoji = button .emoji ,
266+ sku_id = button .sku_id ,
238267 row = None ,
239268 )
240269
@@ -244,6 +273,8 @@ def is_dispatchable(self) -> bool:
244273 def is_persistent (self ) -> bool :
245274 if self .style is ButtonStyle .link :
246275 return self .url is not None
276+ elif self .style is ButtonStyle .premium :
277+ return self .sku_id is not None
247278 return super ().is_persistent ()
248279
249280 def refresh_component (self , button : ButtonComponent ) -> None :
@@ -279,11 +310,10 @@ def button(
279310
280311 .. note::
281312
282- Buttons with a URL cannot be created with this function.
283- Consider creating a :class:`Button` manually instead.
284- This is because buttons with a URL do not have a callback
285- associated with them since Discord does not do any processing
286- with it.
313+ Link/Premium buttons cannot be created with this function,
314+ since these buttons do not have a callback associated with them.
315+ Consider creating a :class:`Button` manually instead, and adding it
316+ using :meth:`View.add_item`.
287317
288318 Parameters
289319 ----------
0 commit comments