# This example builds on the concepts of the app_commands/basic.py example # It's suggested to look at that one to understand certain concepts first. from typing import Literal, Union, NamedTuple from enum import Enum import discord from discord import app_commands MY_GUILD = discord.Object(id=0) # replace with your guild id class MyClient(discord.Client): def __init__(self): super().__init__(intents=discord.Intents.default()) self.tree = app_commands.CommandTree(self) async def setup_hook(self): self.tree.copy_global_to(guild=MY_GUILD) await self.tree.sync(guild=MY_GUILD) client = MyClient() @client.event async def on_ready(): print(f'Logged in as {client.user} (ID: {client.user.id})') print('------') # A transformer is a class that specifies how a parameter in your code # should behave both when used on Discord and when you receive it from Discord. # There are a few built-in transformers, this example will show these along with # creating your own for maximum flexibility. # The first built-in transformer is app_commands.Range # It works on `str`, `int`, and `float` options and tells you # the maximum and minimum values (or length in the case of `str`) allowed @client.tree.command() @app_commands.describe(first='The first number to add', second='The second number to add') async def add( interaction: discord.Interaction, # This makes it so the first parameter can only be between 0 to 100. first: app_commands.Range[int, 0, 100], # This makes it so the second parameter must be over 0, with no maximum limit. second: app_commands.Range[int, 0, None], ): """Adds two numbers together""" await interaction.response.send_message(f'{first} + {second} = {first + second}', ephemeral=True) # Other transformers include regular type hints that are supported by Discord # Examples of these include int, str, float, bool, User, Member, Role, and any channel type. # Since there are a lot of these, for brevity only a channel example will be included. # This command shows how to only show text and voice channels to a user using the Union type hint # combined with the VoiceChannel and TextChannel types. @client.tree.command(name='channel-info') @app_commands.describe(channel='The channel to get info of') async def channel_info(interaction: discord.Interaction, channel: Union[discord.VoiceChannel, discord.TextChannel]): """Shows basic channel info for a text or voice channel.""" embed = discord.Embed(title='Channel Info') embed.add_field(name='Name', value=channel.name, inline=True) embed.add_field(name='ID', value=channel.id, inline=True) embed.add_field( name='Type', value='Voice' if isinstance(channel, discord.VoiceChannel) else 'Text', inline=True, ) embed.set_footer(text='Created').timestamp = channel.created_at await interaction.response.send_message(embed=embed) # In order to support choices, the library has a few ways of doing this. # The first one is using a typing.Literal for basic choices. # On Discord, this will show up as two choices, Buy and Sell. # In the code, you will receive either 'Buy' or 'Sell' as a string. @client.tree.command() @app_commands.describe(action='The action to do in the shop', item='The target item') async def shop(interaction: discord.Interaction, action: Literal['Buy', 'Sell'], item: str): """Interact with the shop""" await interaction.response.send_message(f'Action: {action}\nItem: {item}') # The second way to do choices is via an Enum from the standard library # On Discord, this will show up as four choices: apple, banana, cherry, and dragonfruit # In the code, you will receive the appropriate enum value. class Fruits(Enum): apple = 0 banana = 1 cherry = 2 dragonfruit = 3 @client.tree.command() @app_commands.describe(fruit='The fruit to choose') async def fruit(interaction: discord.Interaction, fruit: Fruits): """Choose a fruit!""" await interaction.response.send_message(repr(fruit)) # You can also make your own transformer by inheriting from app_commands.Transformer class Point(NamedTuple): x: int y: int # The default transformer takes in a string option and you can transform # it into any value you'd like. # # Transformers also support various other settings such as overriding # properties like `choices`, `max_value`, `min_value`, `type`, or `channel_types`. # However, this is outside of the scope of this example so check the documentation # for more information. class PointTransformer(app_commands.Transformer): async def transform(self, interaction: discord.Interaction, value: str) -> Point: (x, _, y) = value.partition(',') return Point(x=int(x.strip()), y=int(y.strip())) @client.tree.command() async def graph( interaction: discord.Interaction, # In order to use the transformer, you should use Transform to tell the # library to use it. point: app_commands.Transform[Point, PointTransformer], ): await interaction.response.send_message(str(point)) # For more basic transformers for your own types without too much repetition, # a concept known as "inline transformers" is supported. This allows you to use # a classmethod to have a string based transformer. It's only useful # if you only care about transforming a string to a class and nothing else. class Point3D(NamedTuple): x: int y: int z: int # This is the same as the above transformer except inline @classmethod async def transform(cls, interaction: discord.Interaction, value: str): x, y, z = value.split(',') return cls(x=int(x.strip()), y=int(y.strip()), z=int(z.strip())) @client.tree.command() async def graph3d(interaction: discord.Interaction, point: Point3D): await interaction.response.send_message(str(point)) client.run('token')