or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

application-commands.mdautomod.mdchannels-messaging.mdclient-bot.mdcommand-framework.mderror-handling.mdevents-gateway.mdguild-management.mdindex.mdinteractions-ui.mdlocalization-i18n.mdpermissions-security.mdpolls.mdusers-members.mdvoice-audio.md

interactions-ui.mddocs/

0

# Interactions and UI Components

1

2

Interactive Discord UI elements including buttons, select menus, modals, and views for creating rich user interfaces. These components enable persistent interactions and complex user workflows within Discord.

3

4

## Capabilities

5

6

### Views

7

8

Container classes that manage collections of interactive UI components with persistent state and callback handling.

9

10

```python { .api }

11

class View:

12

def __init__(

13

self,

14

*,

15

timeout: Optional[float] = 180.0,

16

auto_defer: bool = True

17

):

18

"""

19

Create a view for managing UI components.

20

21

Parameters:

22

- timeout: View timeout in seconds (None for no timeout)

23

- auto_defer: Whether to auto-defer interactions

24

"""

25

26

timeout: Optional[float]

27

children: List[Item]

28

29

def add_item(self, item: Item) -> Self:

30

"""

31

Add a UI component to this view.

32

33

Parameters:

34

- item: Component to add

35

36

Returns:

37

Self for method chaining

38

"""

39

40

def remove_item(self, item: Item) -> Self:

41

"""

42

Remove a UI component from this view.

43

44

Parameters:

45

- item: Component to remove

46

47

Returns:

48

Self for method chaining

49

"""

50

51

def clear_items(self) -> Self:

52

"""

53

Remove all items from this view.

54

55

Returns:

56

Self for method chaining

57

"""

58

59

def get_item(self, custom_id: str) -> Optional[Item]:

60

"""

61

Get an item by custom_id.

62

63

Parameters:

64

- custom_id: Custom ID to search for

65

66

Returns:

67

Item if found

68

"""

69

70

async def interaction_check(self, interaction: Interaction) -> bool:

71

"""

72

Check if interaction should be processed by this view.

73

74

Parameters:

75

- interaction: Interaction to check

76

77

Returns:

78

True if interaction should be processed

79

"""

80

81

async def on_timeout(self) -> None:

82

"""Called when the view times out."""

83

84

async def on_error(self, interaction: Interaction, error: Exception, item: Item) -> None:

85

"""

86

Called when an error occurs in a component callback.

87

88

Parameters:

89

- interaction: Interaction that caused the error

90

- error: Exception that occurred

91

- item: Component that caused the error

92

"""

93

94

def stop(self) -> None:

95

"""Stop the view and prevent further interactions."""

96

97

def is_finished(self) -> bool:

98

"""Whether the view has finished (stopped or timed out)."""

99

100

def is_persistent(self) -> bool:

101

"""Whether the view is persistent (all items have custom_id)."""

102

103

@classmethod

104

def from_message(cls, message: Message, *, timeout: Optional[float] = 180.0) -> View:

105

"""

106

Create a view from existing message components.

107

108

Parameters:

109

- message: Message with components

110

- timeout: View timeout

111

112

Returns:

113

View instance with message components

114

"""

115

```

116

117

### Buttons

118

119

Clickable button components with various styles and callback handling.

120

121

```python { .api }

122

class Button(Item):

123

def __init__(

124

self,

125

*,

126

style: ButtonStyle = ButtonStyle.secondary,

127

label: Optional[str] = None,

128

disabled: bool = False,

129

custom_id: Optional[str] = None,

130

url: Optional[str] = None,

131

emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,

132

row: Optional[int] = None

133

):

134

"""

135

Create a button component.

136

137

Parameters:

138

- style: Button style (primary, secondary, success, danger, link)

139

- label: Button text label

140

- disabled: Whether button is disabled

141

- custom_id: Custom ID for persistent buttons

142

- url: URL for link buttons

143

- emoji: Button emoji

144

- row: Action row (0-4)

145

"""

146

147

style: ButtonStyle

148

label: Optional[str]

149

disabled: bool

150

custom_id: Optional[str]

151

url: Optional[str]

152

emoji: Optional[Union[str, Emoji, PartialEmoji]]

153

154

async def callback(self, interaction: MessageInteraction) -> None:

155

"""

156

Button click callback. Override this method.

157

158

Parameters:

159

- interaction: Button click interaction

160

"""

161

162

def button(

163

*,

164

label: Optional[str] = None,

165

custom_id: Optional[str] = None,

166

disabled: bool = False,

167

style: ButtonStyle = ButtonStyle.secondary,

168

emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,

169

row: Optional[int] = None

170

):

171

"""

172

Decorator for button callbacks in views.

173

174

Parameters:

175

- label: Button text label

176

- custom_id: Custom ID for persistent buttons

177

- disabled: Whether button is disabled

178

- style: Button style

179

- emoji: Button emoji

180

- row: Action row (0-4)

181

182

Returns:

183

Decorated method as a Button component

184

"""

185

```

186

187

### Select Menus

188

189

Dropdown selection components with multiple variations for different data types.

190

191

```python { .api }

192

class BaseSelect(Item):

193

def __init__(

194

self,

195

*,

196

custom_id: Optional[str] = None,

197

placeholder: Optional[str] = None,

198

min_values: int = 1,

199

max_values: int = 1,

200

disabled: bool = False,

201

row: Optional[int] = None

202

):

203

"""

204

Base class for select menu components.

205

206

Parameters:

207

- custom_id: Custom ID for persistent components

208

- placeholder: Placeholder text

209

- min_values: Minimum selections required

210

- max_values: Maximum selections allowed

211

- disabled: Whether component is disabled

212

- row: Action row (0-4)

213

"""

214

215

custom_id: Optional[str]

216

placeholder: Optional[str]

217

min_values: int

218

max_values: int

219

disabled: bool

220

values: List[str]

221

222

async def callback(self, interaction: MessageInteraction) -> None:

223

"""

224

Selection callback. Override this method.

225

226

Parameters:

227

- interaction: Selection interaction

228

"""

229

230

class StringSelect(BaseSelect):

231

def __init__(

232

self,

233

*,

234

options: List[SelectOption],

235

custom_id: Optional[str] = None,

236

placeholder: Optional[str] = None,

237

min_values: int = 1,

238

max_values: int = 1,

239

disabled: bool = False,

240

row: Optional[int] = None

241

):

242

"""

243

String selection menu with predefined options.

244

245

Parameters:

246

- options: List of SelectOption objects

247

- custom_id: Custom ID for persistent components

248

- placeholder: Placeholder text

249

- min_values: Minimum selections required

250

- max_values: Maximum selections allowed

251

- disabled: Whether component is disabled

252

- row: Action row (0-4)

253

"""

254

255

options: List[SelectOption]

256

257

class UserSelect(BaseSelect):

258

def __init__(

259

self,

260

*,

261

custom_id: Optional[str] = None,

262

placeholder: Optional[str] = None,

263

min_values: int = 1,

264

max_values: int = 1,

265

disabled: bool = False,

266

row: Optional[int] = None,

267

default_values: Optional[List[SelectDefaultValue]] = None

268

):

269

"""

270

User selection menu.

271

272

Parameters:

273

- custom_id: Custom ID for persistent components

274

- placeholder: Placeholder text

275

- min_values: Minimum selections required

276

- max_values: Maximum selections allowed

277

- disabled: Whether component is disabled

278

- row: Action row (0-4)

279

- default_values: Default selected users

280

"""

281

282

default_values: Optional[List[SelectDefaultValue]]

283

284

class RoleSelect(BaseSelect):

285

def __init__(

286

self,

287

*,

288

custom_id: Optional[str] = None,

289

placeholder: Optional[str] = None,

290

min_values: int = 1,

291

max_values: int = 1,

292

disabled: bool = False,

293

row: Optional[int] = None,

294

default_values: Optional[List[SelectDefaultValue]] = None

295

):

296

"""

297

Role selection menu.

298

299

Parameters:

300

- custom_id: Custom ID for persistent components

301

- placeholder: Placeholder text

302

- min_values: Minimum selections required

303

- max_values: Maximum selections allowed

304

- disabled: Whether component is disabled

305

- row: Action row (0-4)

306

- default_values: Default selected roles

307

"""

308

309

default_values: Optional[List[SelectDefaultValue]]

310

311

class ChannelSelect(BaseSelect):

312

def __init__(

313

self,

314

*,

315

channel_types: Optional[List[ChannelType]] = None,

316

custom_id: Optional[str] = None,

317

placeholder: Optional[str] = None,

318

min_values: int = 1,

319

max_values: int = 1,

320

disabled: bool = False,

321

row: Optional[int] = None,

322

default_values: Optional[List[SelectDefaultValue]] = None

323

):

324

"""

325

Channel selection menu.

326

327

Parameters:

328

- channel_types: Allowed channel types

329

- custom_id: Custom ID for persistent components

330

- placeholder: Placeholder text

331

- min_values: Minimum selections required

332

- max_values: Maximum selections allowed

333

- disabled: Whether component is disabled

334

- row: Action row (0-4)

335

- default_values: Default selected channels

336

"""

337

338

channel_types: Optional[List[ChannelType]]

339

default_values: Optional[List[SelectDefaultValue]]

340

341

class MentionableSelect(BaseSelect):

342

def __init__(

343

self,

344

*,

345

custom_id: Optional[str] = None,

346

placeholder: Optional[str] = None,

347

min_values: int = 1,

348

max_values: int = 1,

349

disabled: bool = False,

350

row: Optional[int] = None,

351

default_values: Optional[List[SelectDefaultValue]] = None

352

):

353

"""

354

Mentionable entity (users and roles) selection menu.

355

356

Parameters:

357

- custom_id: Custom ID for persistent components

358

- placeholder: Placeholder text

359

- min_values: Minimum selections required

360

- max_values: Maximum selections allowed

361

- disabled: Whether component is disabled

362

- row: Action row (0-4)

363

- default_values: Default selected entities

364

"""

365

366

default_values: Optional[List[SelectDefaultValue]]

367

368

class SelectOption:

369

def __init__(

370

self,

371

*,

372

label: str,

373

value: str,

374

description: Optional[str] = None,

375

emoji: Optional[Union[str, Emoji, PartialEmoji]] = None,

376

default: bool = False

377

):

378

"""

379

Option for string select menus.

380

381

Parameters:

382

- label: Option display label

383

- value: Option value

384

- description: Option description

385

- emoji: Option emoji

386

- default: Whether option is selected by default

387

"""

388

389

label: str

390

value: str

391

description: Optional[str]

392

emoji: Optional[Union[str, Emoji, PartialEmoji]]

393

default: bool

394

395

class SelectDefaultValue:

396

def __init__(self, id: int, type: SelectDefaultValueType):

397

"""

398

Default value for entity select menus.

399

400

Parameters:

401

- id: Entity ID

402

- type: Entity type (user, role, channel)

403

"""

404

405

id: int

406

type: SelectDefaultValueType

407

```

408

409

### Modals

410

411

Dialog forms for collecting structured user input with multiple text fields.

412

413

```python { .api }

414

class Modal:

415

def __init__(

416

self,

417

*,

418

title: str,

419

custom_id: Optional[str] = None,

420

timeout: Optional[float] = None

421

):

422

"""

423

Create a modal dialog form.

424

425

Parameters:

426

- title: Modal title

427

- custom_id: Custom ID for persistent modals

428

- timeout: Modal timeout in seconds

429

"""

430

431

title: str

432

custom_id: Optional[str]

433

timeout: Optional[float]

434

children: List[TextInput]

435

436

def add_item(self, item: TextInput) -> Self:

437

"""

438

Add a text input to this modal.

439

440

Parameters:

441

- item: TextInput to add

442

443

Returns:

444

Self for method chaining

445

"""

446

447

def remove_item(self, item: TextInput) -> Self:

448

"""

449

Remove a text input from this modal.

450

451

Parameters:

452

- item: TextInput to remove

453

454

Returns:

455

Self for method chaining

456

"""

457

458

async def on_submit(self, interaction: ModalInteraction) -> None:

459

"""

460

Called when modal is submitted. Override this method.

461

462

Parameters:

463

- interaction: Modal submission interaction

464

"""

465

466

async def on_error(self, interaction: ModalInteraction, error: Exception) -> None:

467

"""

468

Called when an error occurs during modal submission.

469

470

Parameters:

471

- interaction: Modal interaction

472

- error: Exception that occurred

473

"""

474

475

async def on_timeout(self) -> None:

476

"""Called when the modal times out."""

477

478

def stop(self) -> None:

479

"""Stop the modal and prevent further interactions."""

480

481

class TextInput(Item):

482

def __init__(

483

self,

484

*,

485

label: str,

486

custom_id: Optional[str] = None,

487

style: TextInputStyle = TextInputStyle.short,

488

placeholder: Optional[str] = None,

489

value: Optional[str] = None,

490

required: bool = True,

491

min_length: Optional[int] = None,

492

max_length: Optional[int] = None,

493

row: Optional[int] = None

494

):

495

"""

496

Text input field for modals.

497

498

Parameters:

499

- label: Input field label

500

- custom_id: Custom ID for the input

501

- style: Input style (short or paragraph)

502

- placeholder: Placeholder text

503

- value: Pre-filled value

504

- required: Whether input is required

505

- min_length: Minimum text length

506

- max_length: Maximum text length

507

- row: Action row (0-4)

508

"""

509

510

label: str

511

custom_id: Optional[str]

512

style: TextInputStyle

513

placeholder: Optional[str]

514

value: Optional[str]

515

required: bool

516

min_length: Optional[int]

517

max_length: Optional[int]

518

519

def text_input(

520

*,

521

label: str,

522

custom_id: Optional[str] = None,

523

style: TextInputStyle = TextInputStyle.short,

524

placeholder: Optional[str] = None,

525

default: Optional[str] = None,

526

required: bool = True,

527

min_length: Optional[int] = None,

528

max_length: Optional[int] = None,

529

row: Optional[int] = None

530

):

531

"""

532

Decorator for text input fields in modals.

533

534

Parameters:

535

- label: Input field label

536

- custom_id: Custom ID for the input

537

- style: Input style (short or paragraph)

538

- placeholder: Placeholder text

539

- default: Pre-filled value

540

- required: Whether input is required

541

- min_length: Minimum text length

542

- max_length: Maximum text length

543

- row: Action row (0-4)

544

545

Returns:

546

Decorated attribute as a TextInput component

547

"""

548

```

549

550

### Message Interactions

551

552

Interaction objects for UI component interactions like button clicks and select menu selections.

553

554

```python { .api }

555

class MessageInteraction:

556

def __init__(self): ...

557

558

id: int

559

type: InteractionType

560

data: MessageInteractionData

561

guild: Optional[Guild]

562

guild_id: Optional[int]

563

channel: Optional[Union[GuildChannel, PartialMessageable]]

564

channel_id: Optional[int]

565

user: Union[User, Member]

566

token: str

567

version: int

568

message: Message

569

locale: Optional[Locale]

570

guild_locale: Optional[Locale]

571

created_at: datetime

572

expires_at: datetime

573

574

@property

575

def response(self) -> InteractionResponse:

576

"""Interaction response handler."""

577

578

@property

579

def followup(self) -> Webhook:

580

"""Webhook for followup messages."""

581

582

@property

583

def component(self) -> Component:

584

"""The component that triggered this interaction."""

585

586

class ModalInteraction:

587

def __init__(self): ...

588

589

id: int

590

type: InteractionType

591

data: ModalInteractionData

592

guild: Optional[Guild]

593

guild_id: Optional[int]

594

channel: Optional[Union[GuildChannel, PartialMessageable]]

595

channel_id: Optional[int]

596

user: Union[User, Member]

597

token: str

598

version: int

599

message: Optional[Message]

600

locale: Optional[Locale]

601

guild_locale: Optional[Locale]

602

created_at: datetime

603

expires_at: datetime

604

605

@property

606

def response(self) -> InteractionResponse:

607

"""Interaction response handler."""

608

609

@property

610

def followup(self) -> Webhook:

611

"""Webhook for followup messages."""

612

613

@property

614

def text_values(self) -> Dict[str, str]:

615

"""Dictionary mapping text input custom_ids to their values."""

616

```

617

618

## Usage Examples

619

620

### Basic Button View

621

622

```python

623

import disnake

624

625

class BasicView(disnake.ui.View):

626

def __init__(self):

627

super().__init__(timeout=60)

628

self.value = None

629

630

@disnake.ui.button(label="Yes", style=disnake.ButtonStyle.green)

631

async def yes_button(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):

632

self.value = True

633

self.stop()

634

await interaction.response.send_message("You clicked Yes!", ephemeral=True)

635

636

@disnake.ui.button(label="No", style=disnake.ButtonStyle.red)

637

async def no_button(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):

638

self.value = False

639

self.stop()

640

await interaction.response.send_message("You clicked No!", ephemeral=True)

641

642

# Usage

643

@bot.slash_command(description="Ask a yes/no question")

644

async def question(inter: disnake.ApplicationCommandInteraction):

645

view = BasicView()

646

await inter.response.send_message("Do you like pizza?", view=view)

647

648

await view.wait()

649

if view.value is None:

650

await inter.followup.send("You didn't respond in time!")

651

elif view.value:

652

await inter.followup.send("Great choice!")

653

else:

654

await inter.followup.send("That's okay!")

655

```

656

657

### Select Menu Example

658

659

```python

660

class GameSelect(disnake.ui.View):

661

@disnake.ui.string_select(

662

placeholder="Choose your favorite game...",

663

min_values=1,

664

max_values=3,

665

options=[

666

disnake.SelectOption(

667

label="Minecraft",

668

value="minecraft",

669

description="Block building game",

670

emoji="🟫"

671

),

672

disnake.SelectOption(

673

label="Among Us",

674

value="among_us",

675

description="Social deduction game",

676

emoji="πŸ”΄"

677

),

678

disnake.SelectOption(

679

label="Discord",

680

value="discord",

681

description="Chat application",

682

emoji="πŸ’¬"

683

),

684

]

685

)

686

async def select_callback(self, select: disnake.ui.StringSelect, interaction: disnake.MessageInteraction):

687

games = ", ".join(select.values)

688

await interaction.response.send_message(f"You selected: {games}")

689

690

@bot.slash_command(description="Choose your favorite games")

691

async def games(inter: disnake.ApplicationCommandInteraction):

692

view = GameSelect()

693

await inter.response.send_message("Select your favorites:", view=view)

694

```

695

696

### User/Role Select Menus

697

698

```python

699

class ModeratorPanel(disnake.ui.View):

700

@disnake.ui.user_select(placeholder="Select users to moderate...")

701

async def select_users(self, select: disnake.ui.UserSelect, interaction: disnake.MessageInteraction):

702

users = [f"{user.mention}" for user in select.values]

703

await interaction.response.send_message(

704

f"Selected users: {', '.join(users)}",

705

ephemeral=True

706

)

707

708

@disnake.ui.role_select(placeholder="Select roles to manage...")

709

async def select_roles(self, select: disnake.ui.RoleSelect, interaction: disnake.MessageInteraction):

710

roles = [f"{role.mention}" for role in select.values]

711

await interaction.response.send_message(

712

f"Selected roles: {', '.join(roles)}",

713

ephemeral=True

714

)

715

716

@bot.slash_command(description="Moderator panel")

717

async def mod_panel(inter: disnake.ApplicationCommandInteraction):

718

view = ModeratorPanel()

719

await inter.response.send_message("Moderator Controls:", view=view)

720

```

721

722

### Modal Forms

723

724

```python

725

class FeedbackModal(disnake.ui.Modal):

726

def __init__(self):

727

super().__init__(title="Submit Feedback", timeout=300)

728

729

name = disnake.ui.TextInput(

730

label="Your Name",

731

placeholder="Enter your name here...",

732

required=True,

733

max_length=100

734

)

735

736

feedback = disnake.ui.TextInput(

737

label="Feedback",

738

style=disnake.TextInputStyle.paragraph,

739

placeholder="Tell us what you think...",

740

required=True,

741

min_length=10,

742

max_length=1000

743

)

744

745

async def on_submit(self, interaction: disnake.ModalInteraction):

746

embed = disnake.Embed(

747

title="New Feedback",

748

color=disnake.Color.blue()

749

)

750

embed.add_field(name="From", value=self.name.value, inline=True)

751

embed.add_field(name="User", value=interaction.author.mention, inline=True)

752

embed.add_field(name="Feedback", value=self.feedback.value, inline=False)

753

754

# Send to feedback channel

755

feedback_channel = bot.get_channel(FEEDBACK_CHANNEL_ID)

756

await feedback_channel.send(embed=embed)

757

758

await interaction.response.send_message(

759

"Thank you for your feedback!",

760

ephemeral=True

761

)

762

763

@bot.slash_command(description="Submit feedback")

764

async def feedback(inter: disnake.ApplicationCommandInteraction):

765

modal = FeedbackModal()

766

await inter.response.send_modal(modal)

767

```

768

769

### Persistent Views

770

771

```python

772

class PersistentView(disnake.ui.View):

773

def __init__(self):

774

super().__init__(timeout=None) # No timeout for persistent views

775

776

@disnake.ui.button(

777

label="Get Support",

778

style=disnake.ButtonStyle.primary,

779

custom_id="support_button" # Custom ID for persistence

780

)

781

async def support_button(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):

782

# This button will work even after bot restarts

783

await interaction.response.send_message(

784

"Support ticket created! A moderator will be with you shortly.",

785

ephemeral=True

786

)

787

788

# Add persistent view on bot startup

789

@bot.event

790

async def on_ready():

791

bot.add_view(PersistentView())

792

print(f'{bot.user} is ready!')

793

794

@bot.slash_command(description="Send support panel")

795

async def support_panel(inter: disnake.ApplicationCommandInteraction):

796

embed = disnake.Embed(

797

title="Support Center",

798

description="Click the button below to get help."

799

)

800

view = PersistentView()

801

await inter.response.send_message(embed=embed, view=view)

802

```

803

804

### Complex Interactive Workflow

805

806

```python

807

class ConfigurationWizard(disnake.ui.View):

808

def __init__(self):

809

super().__init__(timeout=300)

810

self.config = {}

811

812

@disnake.ui.button(label="Step 1: Choose Channel", style=disnake.ButtonStyle.primary)

813

async def step1(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):

814

view = ChannelSelectView(self)

815

await interaction.response.edit_message(

816

content="Step 1: Select a channel for notifications:",

817

view=view

818

)

819

820

@disnake.ui.button(label="Step 2: Set Permissions", style=disnake.ButtonStyle.secondary, disabled=True)

821

async def step2(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):

822

view = RoleSelectView(self)

823

await interaction.response.edit_message(

824

content="Step 2: Select roles that can use this feature:",

825

view=view

826

)

827

828

@disnake.ui.button(label="Finish", style=disnake.ButtonStyle.green, disabled=True)

829

async def finish(self, button: disnake.ui.Button, interaction: disnake.MessageInteraction):

830

embed = disnake.Embed(title="Configuration Complete!")

831

embed.add_field(name="Channel", value=self.config.get('channel', 'None'))

832

embed.add_field(name="Roles", value=', '.join(self.config.get('roles', [])))

833

834

await interaction.response.edit_message(

835

content="Configuration saved!",

836

embed=embed,

837

view=None

838

)

839

840

class ChannelSelectView(disnake.ui.View):

841

def __init__(self, parent):

842

super().__init__(timeout=300)

843

self.parent = parent

844

845

@disnake.ui.channel_select(

846

placeholder="Select notification channel...",

847

channel_types=[disnake.ChannelType.text]

848

)

849

async def select_channel(self, select: disnake.ui.ChannelSelect, interaction: disnake.MessageInteraction):

850

self.parent.config['channel'] = select.values[0].mention

851

852

# Enable step 2

853

self.parent.children[1].disabled = False

854

855

await interaction.response.edit_message(

856

content="βœ… Channel selected! Now proceed to step 2:",

857

view=self.parent

858

)

859

860

class RoleSelectView(disnake.ui.View):

861

def __init__(self, parent):

862

super().__init__(timeout=300)

863

self.parent = parent

864

865

@disnake.ui.role_select(

866

placeholder="Select authorized roles...",

867

min_values=1,

868

max_values=5

869

)

870

async def select_roles(self, select: disnake.ui.RoleSelect, interaction: disnake.MessageInteraction):

871

self.parent.config['roles'] = [role.mention for role in select.values]

872

873

# Enable finish button

874

self.parent.children[2].disabled = False

875

876

await interaction.response.edit_message(

877

content="βœ… Roles selected! Click Finish to complete setup:",

878

view=self.parent

879

)

880

881

@bot.slash_command(description="Configure bot settings")

882

async def configure(inter: disnake.ApplicationCommandInteraction):

883

view = ConfigurationWizard()

884

await inter.response.send_message("Welcome to the configuration wizard!", view=view)

885

```