@@ -53,7 +53,7 @@ class Button(Item[V_co]):
53
53
The style of the button.
54
54
custom_id: Optional[:class:`str`]
55
55
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.
57
57
url: Optional[:class:`str`]
58
58
The URL this button sends you to.
59
59
disabled: :class:`bool`
@@ -62,6 +62,11 @@ class Button(Item[V_co]):
62
62
The label of the button, if any.
63
63
emoji: Optional[Union[:class:`.PartialEmoji`, :class:`.Emoji`, :class:`str`]]
64
64
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
65
70
row: Optional[:class:`int`]
66
71
The relative row this button belongs to. A Discord component can only have 5
67
72
rows. By default, items are arranged automatically into those 5 rows. If you'd
@@ -76,6 +81,7 @@ class Button(Item[V_co]):
76
81
"disabled" ,
77
82
"label" ,
78
83
"emoji" ,
84
+ "sku_id" ,
79
85
"row" ,
80
86
)
81
87
# We have to set this to MISSING in order to overwrite the abstract property from WrappedComponent
@@ -91,6 +97,7 @@ def __init__(
91
97
custom_id : Optional [str ] = None ,
92
98
url : Optional [str ] = None ,
93
99
emoji : Optional [Union [str , Emoji , PartialEmoji ]] = None ,
100
+ sku_id : Optional [int ] = None ,
94
101
row : Optional [int ] = None ,
95
102
) -> None : ...
96
103
@@ -104,6 +111,7 @@ def __init__(
104
111
custom_id : Optional [str ] = None ,
105
112
url : Optional [str ] = None ,
106
113
emoji : Optional [Union [str , Emoji , PartialEmoji ]] = None ,
114
+ sku_id : Optional [int ] = None ,
107
115
row : Optional [int ] = None ,
108
116
) -> None : ...
109
117
@@ -116,18 +124,23 @@ def __init__(
116
124
custom_id : Optional [str ] = None ,
117
125
url : Optional [str ] = None ,
118
126
emoji : Optional [Union [str , Emoji , PartialEmoji ]] = None ,
127
+ sku_id : Optional [int ] = None ,
119
128
row : Optional [int ] = None ,
120
129
) -> None :
121
130
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" )
124
131
125
132
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 :
127
136
custom_id = os .urandom (16 ).hex ()
137
+ elif mutually_exclusive != 1 :
138
+ raise TypeError ("cannot mix url, sku_id and custom_id with Button" )
128
139
129
140
if url is not None :
130
141
style = ButtonStyle .link
142
+ if sku_id is not None :
143
+ style = ButtonStyle .premium
131
144
132
145
if emoji is not None :
133
146
if isinstance (emoji , str ):
@@ -147,6 +160,7 @@ def __init__(
147
160
label = label ,
148
161
style = style ,
149
162
emoji = emoji ,
163
+ sku_id = sku_id ,
150
164
)
151
165
self .row = row
152
166
@@ -167,7 +181,7 @@ def style(self, value: ButtonStyle) -> None:
167
181
def custom_id (self ) -> Optional [str ]:
168
182
"""Optional[:class:`str`]: The ID of the button that gets received during an interaction.
169
183
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.
171
185
"""
172
186
return self ._underlying .custom_id
173
187
@@ -226,6 +240,20 @@ def emoji(self, value: Optional[Union[str, Emoji, PartialEmoji]]) -> None:
226
240
else :
227
241
self ._underlying .emoji = None
228
242
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
+
229
257
@classmethod
230
258
def from_component (cls , button : ButtonComponent ) -> Self :
231
259
return cls (
@@ -235,6 +263,7 @@ def from_component(cls, button: ButtonComponent) -> Self:
235
263
custom_id = button .custom_id ,
236
264
url = button .url ,
237
265
emoji = button .emoji ,
266
+ sku_id = button .sku_id ,
238
267
row = None ,
239
268
)
240
269
@@ -244,6 +273,8 @@ def is_dispatchable(self) -> bool:
244
273
def is_persistent (self ) -> bool :
245
274
if self .style is ButtonStyle .link :
246
275
return self .url is not None
276
+ elif self .style is ButtonStyle .premium :
277
+ return self .sku_id is not None
247
278
return super ().is_persistent ()
248
279
249
280
def refresh_component (self , button : ButtonComponent ) -> None :
@@ -279,11 +310,10 @@ def button(
279
310
280
311
.. note::
281
312
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`.
287
317
288
318
Parameters
289
319
----------
0 commit comments