aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRapptz <[email protected]>2021-06-29 04:17:20 -0400
committerRapptz <[email protected]>2021-06-29 04:19:12 -0400
commit7386a971f85bfc66f31132e8f0c98e9b4879ee3b (patch)
tree9ae83ea0258f56b2601a6563b4c05cedd0a9d07f
parentType hint channel.py (diff)
downloaddiscord.py-7386a971f85bfc66f31132e8f0c98e9b4879ee3b.tar.xz
discord.py-7386a971f85bfc66f31132e8f0c98e9b4879ee3b.zip
Add examples for how to use views
-rw-r--r--examples/views/confirm.py57
-rw-r--r--examples/views/counter.py43
-rw-r--r--examples/views/ephemeral.py47
-rw-r--r--examples/views/persistent.py60
-rw-r--r--examples/views/tic_tac_toe.py139
5 files changed, 346 insertions, 0 deletions
diff --git a/examples/views/confirm.py b/examples/views/confirm.py
new file mode 100644
index 00000000..6ec81369
--- /dev/null
+++ b/examples/views/confirm.py
@@ -0,0 +1,57 @@
+from discord.ext import commands
+
+import discord
+
+
+class Bot(commands.Bot):
+ def __init__(self):
+ super().__init__(command_prefix=commands.when_mentioned_or('$'))
+
+ async def on_ready(self):
+ print(f'Logged in as {self.user} (ID: {self.user.id})')
+ print('------')
+
+
+# Define a simple View that gives us a confirmation menu
+class Confirm(discord.ui.View):
+ def __init__(self):
+ super().__init__()
+ self.value = None
+
+ # When the confirm button is pressed, set the inner value to `True` and
+ # stop the View from listening to more input.
+ # We also send the user an ephemeral message that we're confirming their choice.
+ @discord.ui.button(label='Confirm', style=discord.ButtonStyle.green)
+ async def confirm(self, button: discord.ui.Button, interaction: discord.Interaction):
+ await interaction.response.send_message('Confirming', ephemeral=True)
+ self.value = True
+ self.stop()
+
+ # This one is similar to the confirmation button except sets the inner value to `False`
+ @discord.ui.button(label='Cancel', style=discord.ButtonStyle.grey)
+ async def cancel(self, button: discord.ui.Button, interaction: discord.Interaction):
+ await interaction.response.send_message('Cancelling', ephemeral=True)
+ self.value = False
+ self.stop()
+
+
+bot = Bot()
+
+
+async def ask(ctx: commands.Context):
+ """Asks the user a question to confirm something."""
+ # We create the view and assign it to a variable so we can wait for it later.
+ view = Confirm()
+ await ctx.send('Do you want to continue?', view=view)
+ # Wait for the View to stop listening for input...
+ await view.wait()
+ if view.value is None:
+ print('Timed out...')
+ elif view.value:
+ print('Confirmed...')
+ else:
+ print('Cancelled...')
+
+
+bot.run('token')
diff --git a/examples/views/counter.py b/examples/views/counter.py
new file mode 100644
index 00000000..a1ab756a
--- /dev/null
+++ b/examples/views/counter.py
@@ -0,0 +1,43 @@
+from discord.ext import commands
+
+import discord
+
+
+class CounterBot(commands.Bot):
+ def __init__(self):
+ super().__init__(command_prefix=commands.when_mentioned_or('$'))
+
+ async def on_ready(self):
+ print(f'Logged in as {self.user} (ID: {self.user.id})')
+ print('------')
+
+
+# Define a simple View that gives us a counter button
+class Counter(discord.ui.View):
+
+ # Define the actual button
+ # When pressed, this increments the number displayed until it hits 5.
+ # When it hits 5, the counter button is disabled and it turns green.
+ # note: The name of the function does not matter to the library
+ @discord.ui.button(label='0', style=discord.ButtonStyle.red)
+ async def count(self, button: discord.ui.Button, interaction: discord.Interaction):
+ number = int(button.label) if button.label else 0
+ if number + 1 >= 5:
+ button.style = discord.ButtonStyle.green
+ button.disabled = True
+ button.label = str(number + 1)
+
+ # Make sure to update the message with our updated selves
+ await interaction.response.edit_message(view=self)
+
+
+bot = CounterBot()
+
+
+async def counter(ctx: commands.Context):
+ """Starts a counter for pressing."""
+ await ctx.send('Press!', view=Counter())
+
+
+bot.run('token')
diff --git a/examples/views/ephemeral.py b/examples/views/ephemeral.py
new file mode 100644
index 00000000..770d4b65
--- /dev/null
+++ b/examples/views/ephemeral.py
@@ -0,0 +1,47 @@
+from discord.ext import commands
+
+import discord
+
+class EphemeralCounterBot(commands.Bot):
+ def __init__(self):
+ super().__init__(command_prefix=commands.when_mentioned_or('$'))
+
+ async def on_ready(self):
+ print(f'Logged in as {self.user} (ID: {self.user.id})')
+ print('------')
+
+# Define a simple View that gives us a counter button
+class Counter(discord.ui.View):
+
+ # Define the actual button
+ # When pressed, this increments the number displayed until it hits 5.
+ # When it hits 5, the counter button is disabled and it turns green.
+ # note: The name of the function does not matter to the library
+ @discord.ui.button(label='0', style=discord.ButtonStyle.red)
+ async def count(self, button: discord.ui.Button, interaction: discord.Interaction):
+ number = int(button.label) if button.label else 0
+ if number + 1 >= 5:
+ button.style = discord.ButtonStyle.green
+ button.disabled = True
+ button.label = str(number + 1)
+
+ # Make sure to update the message with our updated selves
+ await interaction.response.edit_message(view=self)
+
+# Define a View that will give us our own personal counter button
+class EphemeralCounter(discord.ui.View):
+ # When this button is pressed, it will respond with a Counter view that will
+ # give the button presser their own personal button they can press 5 times.
+ @discord.ui.button(label='Click', style=discord.ButtonStyle.blurple)
+ async def receive(self, button: discord.ui.Button, interaction: discord.Interaction):
+ # ephemeral=True makes the message hidden from everyone except the button presser
+ await interaction.response.send_message('Enjoy!', view=Counter(), ephemeral=True)
+
+bot = EphemeralCounterBot()
+
+async def counter(ctx: commands.Context):
+ """Starts a counter for pressing."""
+ await ctx.send('Press!', view=EphemeralCounter())
+
+bot.run('token')
diff --git a/examples/views/persistent.py b/examples/views/persistent.py
new file mode 100644
index 00000000..140a7869
--- /dev/null
+++ b/examples/views/persistent.py
@@ -0,0 +1,60 @@
+from discord.ext import commands
+import discord
+
+
+class PersistentViewBot(commands.Bot):
+ def __init__(self):
+ super().__init__(command_prefix=commands.when_mentioned_or('$'))
+
+ async def on_ready(self):
+ print(f'Logged in as {self.user} (ID: {self.user.id})')
+ print('------')
+
+
+# Define a simple View that persists between bot restarts
+# In order a view to persist between restarts it needs to meet the following conditions:
+# 1) The timeout of the View has to be set to None
+# 2) Every item in the View has to have a custom_id set
+# It is recommended that the custom_id be sufficiently unique to
+# prevent conflicts with other buttons the bot sends.
+# For this example the custom_id is prefixed with the name of the bot.
+# Note that custom_ids can only be up to 100 characters long.
+class PersistentView(discord.ui.View):
+ def __init__(self):
+ super().__init__(timeout=None)
+
+ @discord.ui.button(label='Green', style=discord.ButtonStyle.green, custom_id='persistent_view:green')
+ async def green(self, button: discord.ui.Button, interaction: discord.Interaction):
+ await interaction.response.send_message('This is green.', ephemeral=True)
+
+ @discord.ui.button(label='Red', style=discord.ButtonStyle.red, custom_id='persistent_view:red')
+ async def red(self, button: discord.ui.Button, interaction: discord.Interaction):
+ await interaction.response.send_message('This is red.', ephemeral=True)
+
+ @discord.ui.button(label='Grey', style=discord.ButtonStyle.grey, custom_id='persistent_view:grey')
+ async def grey(self, button: discord.ui.Button, interaction: discord.Interaction):
+ await interaction.response.send_message('This is grey.', ephemeral=True)
+
+
+bot = PersistentViewBot()
+
+# Register the persistent view for listening
+# Note that this does not send the view to any message.
+# In order to do this you need to first send a message with the View, which is shown below.
+# If you have the message_id you can also pass it as a keyword argument, but for this example
+# we don't have one.
+bot.add_view(PersistentView())
+
+
+async def prepare(ctx: commands.Context):
+ """Starts a persistent view."""
+ # In order for a persistent view to be listened to, it needs to be sent to an actual message.
+ # Call this method once just to store it somewhere.
+ # In a more complicated program you might fetch the message_id from a database for use later.
+ # However this is outside of the scope of this simple example.
+ await ctx.send("What's your favourite colour?", view=PersistentView())
+
+
+bot.run('token')
diff --git a/examples/views/tic_tac_toe.py b/examples/views/tic_tac_toe.py
new file mode 100644
index 00000000..81632e26
--- /dev/null
+++ b/examples/views/tic_tac_toe.py
@@ -0,0 +1,139 @@
+from typing import List
+from discord.ext import commands
+import discord
+
+# Defines a custom button that contains the logic of the game.
+# The ['TicTacToe'] bit is for type hinting purposes to tell your IDE or linter
+# what the type of `self.view` is. It is not required.
+class TicTacToeButton(discord.ui.Button['TicTacToe']):
+ def __init__(self, x: int, y: int):
+ # A label is required, but we don't need one so a zero-width space is used
+ # The row parameter tells the View which row to place the button under.
+ # A View can only contain up to 5 rows -- each row can only have 5 buttons.
+ # Since a Tic Tac Toe grid is 3x3 that means we have 3 rows and 3 columns.
+ super().__init__(style=discord.ButtonStyle.secondary, label='\u200b', row=y)
+ self.x = x
+ self.y = y
+
+ # This function is called whenever this particular button is pressed
+ # This is part of the "meat" of the game logic
+ async def callback(self, interaction: discord.Interaction):
+ assert self.view is not None
+ view: TicTacToe = self.view
+ state = view.board[self.y][self.x]
+ if state in (view.X, view.O):
+ return
+
+ if view.current_player == view.X:
+ self.style = discord.ButtonStyle.danger
+ self.label = 'X'
+ self.disabled = True
+ view.board[self.y][self.x] = view.X
+ view.current_player = view.O
+ content = "It is now O's turn"
+ else:
+ self.style = discord.ButtonStyle.success
+ self.label = 'O'
+ self.disabled = True
+ view.board[self.y][self.x] = view.O
+ view.current_player = view.X
+ content = "It is now X's turn"
+
+ winner = view.check_board_winner()
+ if winner is not None:
+ if winner == view.X:
+ content = 'X won!'
+ elif winner == view.O:
+ content = 'O won!'
+ else:
+ content = "It's a tie!"
+
+ for child in view.children:
+ child.disabled = True
+
+ view.stop()
+
+ await interaction.response.edit_message(content=content, view=view)
+
+
+# This is our actual board View
+class TicTacToe(discord.ui.View):
+ # This tells the IDE or linter that all our children will be TicTacToeButtons
+ # This is not required
+ children: List[TicTacToeButton]
+ X = -1
+ O = 1
+ Tie = 2
+
+ def __init__(self):
+ super().__init__()
+ self.current_player = self.X
+ self.board = [
+ [0, 0, 0],
+ [0, 0, 0],
+ [0, 0, 0],
+ ]
+
+ # Our board is made up of 3 by 3 TicTacToeButtons
+ # The TicTacToeButton maintains the callbacks and helps steer
+ # the actual game.
+ for x in range(3):
+ for y in range(3):
+ self.add_item(TicTacToeButton(x, y))
+
+ # This method checks for the board winner -- it is used by the TicTacToeButton
+ def check_board_winner(self):
+ for across in self.board:
+ value = sum(across)
+ if value == 3:
+ return self.O
+ elif value == -3:
+ return self.X
+
+ # Check vertical
+ for line in range(3):
+ value = self.board[0][line] + self.board[1][line] + self.board[2][line]
+ if value == 3:
+ return self.O
+ elif value == -3:
+ return self.X
+
+ # Check diagonals
+ diag = self.board[0][2] + self.board[1][1] + self.board[2][0]
+ if diag == 3:
+ return self.O
+ elif diag == -3:
+ return self.X
+
+ diag = self.board[0][0] + self.board[1][1] + self.board[2][2]
+ if diag == 3:
+ return self.O
+ elif diag == -3:
+ return self.X
+
+ # If we're here, we need to check if a tie was made
+ if all(i != 0 for row in self.board for i in row):
+ return self.Tie
+
+ return None
+
+
+class TicTacToeBot(commands.Bot):
+ def __init__(self):
+ super().__init__(command_prefix=commands.when_mentioned_or('$'))
+
+ async def on_ready(self):
+ print(f'Logged in as {self.user} (ID: {self.user.id})')
+ print('------')
+
+
+bot = TicTacToeBot()
+
+
+async def tic(ctx: commands.Context):
+ """Starts a tic-tac-toe game with yourself."""
+ await ctx.send('Tic Tac Toe: X goes first', view=TicTacToe())
+
+
+bot.run('token')