Pincer Commands Module#
Commands#
command#
- @command(func=None, *, name=None, description='Description not set', enable_default=True, guild=None, cooldown=0, cooldown_scale=60.0, cooldown_scope=ThrottleScope.USER, parent=None)#
A decorator to create a slash command to register and respond to with the discord API from a function.
str - String int - Integer bool - Boolean float - Number pincer.objects.User - User pincer.objects.Channel - Channel pincer.objects.Role - Role pincer.objects.Mentionable - Mentionable
class Bot(Client): @command( name="test", description="placeholder" ) async def test_command( self, ctx: MessageContext, amount: int, name: CommandArg[ str, Description("Do something cool"), Choices(Choice("first value", 1), 5) ], optional_int: CommandArg[ int, MinValue(10), MaxValue(100), ] = 50 ): return Message( f"You chose {amount}, {name}, {letter}", flags=InteractionFlags.EPHEMERAL )
- References from above:
Client
,Message
,MessageContext
,InteractionFlags
,Choices
,Choice
,typing_extensions.Annotated
(Python 3.8),typing.Annotated
(Python 3.9+),CommandArg
,Description
,MinValue
,MaxValue
- Parameters
name (Optional[
str
]) – The name of the commandDefault:None
description (Optional[
str
]) – The description of the commandDefault:Description not set
enable_default (Optional[
bool
]) – Whether the command is enabled by defaultDefault:True
guild (Optional[Union[
Snowflake
,int
,str
]]) – What guild to add it to (don’t specify for global)Default:None
cooldown (Optional[
int
]) – The amount of times in the cooldown_scale the command can be invokedDefault:0
cooldown_scale (Optional[
float
]) – The ‘checking time’ of the cooldownDefault:60
cooldown_scope (
ThrottleScope
) – What type of cooldown strategy to useDefault:ThrottleScope.USER
- Raises
CommandIsNotCoroutine – If the command function is not a coro
InvalidCommandName – If the command name does not follow the regex
^[\w-]{1,32}$
InvalidCommandGuild – If the guild id is invalid
CommandDescriptionTooLong – Descriptions max 100 characters If the annotation on an argument is too long (also max 100)
CommandAlreadyRegistered – If the command already exists
TooManyArguments – Max 25 arguments to pass for commands
InvalidArgumentAnnotation – Annotation amount is max 25, Not a valid argument type, Annotations must consist of name and value
- @message_command(func=None, *, name=None, enable_default=True, guild=None, cooldown=0, cooldown_scale=60, cooldown_scope=ThrottleScope.USER)#
A decorator to create a user command to register and respond to the Discord API from a function.
class Bot(Client): @user_command async def test_message_command( self, ctx: MessageContext, message: UserMessage, ): return message.content
- References from above:
Client
,MessageContext
,UserMessage
,User
,GuildMember
,
- Parameters
name (Optional[
str
]) – The name of the commandDefault:None
enable_default (Optional[
bool
]) – Whether the command is enabled by defaultDefault:True
guild (Optional[Union[
Snowflake
,int
,str
]]) – What guild to add it to (don’t specify for global)Default:None
cooldown (Optional[
int
]) – The amount of times in the cooldown_scale the command can be invokedDefault:0
cooldown_scale (Optional[
float
]) – The ‘checking time’ of the cooldownDefault:60
cooldown_scope (
ThrottleScope
) – What type of cooldown strategy to useDefault:ThrottleScope.USER
- Raises
CommandIsNotCoroutine – If the command function is not a coro
InvalidCommandName – If the command name does not follow the regex
^[\w-]{1,32}$
InvalidCommandGuild – If the guild id is invalid
CommandDescriptionTooLong – Descriptions max 100 characters If the annotation on an argument is too long (also max 100)
CommandAlreadyRegistered – If the command already exists
InvalidArgumentAnnotation – Annotation amount is max 25, Not a valid argument type, Annotations must consist of name and value
- @user_command(func=None, *, name=None, enable_default=True, guild=None, cooldown=0, cooldown_scale=60, cooldown_scope=ThrottleScope.USER)#
A decorator to create a user command registering and responding to the Discord API from a function.
class Bot(Client): @user_command async def test_user_command( self, ctx: MessageContext, user: User, member: GuildMember ): if not member: # member is missing if this is a DM # This bot doesn't like being DMed, so it won't respond return return f"Hello {user.name}, this is a Guild."
- References from above:
Client
,MessageContext
,User
,GuildMember
,
- Parameters
name (Optional[
str
]) – The name of the commandDefault:None
enable_default (Optional[
bool
]) – Whether the command is enabled by defaultDefault:True
guild (Optional[Union[
Snowflake
,int
,str
]]) – What guild to add it to (don’t specify for global)Default:None
cooldown (Optional[
int
]) – The amount of times in the cooldown_scale the command can be invokedDefault:0
cooldown_scale (Optional[
float
]) – The ‘checking time’ of the cooldownDefault:60
cooldown_scope (
ThrottleScope
) – What type of cooldown strategy to useDefault:ThrottleScope.USER
- Raises
CommandIsNotCoroutine – If the command function is not a coro
InvalidCommandName – If the command name does not follow the regex
^[\w-]{1,32}$
InvalidCommandGuild – If the guild id is invalid
CommandDescriptionTooLong – Descriptions max 100 characters If the annotation on an argument is too long (also max 100)
CommandAlreadyRegistered – If the command already exists
InvalidArgumentAnnotation – Annotation amount is max 25, Not a valid argument type, Annotations must consist of name and value
Command Types#
- class Modifier#
Bases:
object
Modifies a CommandArg by being added to
CommandArg
’s args.Modifiers go inside an
typing.Annotated
type hint.Annotated[ # This is the type of command. # Supported types are str, int, bool, float, User, Channel, and Role int, # The modifiers to the command go here Description("Pick a number 1-10"), MinValue(1), MaxValue(10) ]
- class Description#
Bases:
Modifier
Represents the description of an application command option
# Creates an int argument with the description "example description" Annotated[ int, Description("example description") ]
- Parameters
desc (str) – The description for the command.
- class Choices#
Bases:
Modifier
Represents a group of application command choices that a user can pick from
Annotated[ int, Choices( Choice("First Number", 10), 20, 50 ) ]
- class Choice#
Bases:
Modifier
Represents a choice that the user can pick from
Choices( Choice("First Number", 10), Choice("Second Number", 20) )
- class MaxValue#
Bases:
Modifier
Represents the max value for a number
Annotated[ int, # The user can't pick a number above 10 MaxValue(10) ]
- class MinValue#
Bases:
Modifier
Represents the minimum value for a number
Annotated[ int, # The user can't pick a number below 10 MinValue(10) ]
- class ChannelTypes#
Bases:
Modifier
Represents a group of channel types that a user can pick from
Annotated[ Channel, # The user will only be able to choice between GUILD_TEXT and GUILD_TEXT channels. ChannelTypes( ChannelType.GUILD_TEXT, ChannelType.GUILD_VOICE ) ]
- Parameters
*types (
ChannelType
) – A list of channel types that the user can pick from.
- class CommandArg#
Bases:
object
Holds the parameters of an application command option
Note
Deprecated.
typing.Annotated
ortyping_extensions.Annotated
should be used instead. See https://docs.pincer.dev/en/stable/interactions.html#arguments for more information.Annotated[ # This is the type of command. # Supported types are str, int, bool, float, User, Channel, and Role int, # The modifiers to the command go here Description("Pick a number 1-10"), MinValue(1), MaxValue(10) ]
- Parameters
command_type (T) – The type of the command
*args (
Modifier
) –
ChatCommandHandler#
AttributesMethods- asyncadd_command
- asyncadd_commands
- asyncget_commands
- asyncinitialize
- asyncremove_command
- class ChatCommandHandler#
Bases:
object
Singleton containing methods used to handle various commands
The register and built_register#
I found the way Discord expects commands to be registered to be very different than how you want to think about command registration. i.e. Discord wants nesting but we don’t want any nesting. Nesting makes it hard to think about commands and also will increase lookup time. The way this problem is avoided is by storing a version of the commands that we can deal with as library developers and a version of the command that Discord thinks we should provide. That is where the register and the built_register help simplify the design of the library. The register is simply where the “Pincer version” of commands gets saved to memory. The built_register is where the version of commands that Discord requires is saved. The register allows for O(1) lookups by storing commands in a Python dictionary. It does cost some memory to save two copies in the current iteration of the system but we should be able to drop the built_register in runtime if we want to. I don’t feel that lost maintainability from this is optimal. We can index by in O(1) by checking the register but can still use the built_register if we need to do a nested lookup.
- client#
The client object
- Type
Client
- built_register#
Dictionary of
InteractableStructure
where the commands are converted to the format that Discord expects for sub commands and sub command groups.- Type
Dict[
str
,AppCommand
]]
- await add_command(cmd)#
This function is a coroutine.
Add an app command
- Parameters
cmd (
AppCommand
) – Command to add
- await add_commands(commands)#
This function is a coroutine.
Add a list of app commands
- Parameters
commands (List[
AppCommand
]) – List of command objects to add
- await get_commands()#
This function is a coroutine.
Get a list of app commands from Discord
- Returns
List of commands.
- Return type
List[
AppCommand
]
- await initialize()#
This function is a coroutine.
Call methods of this class to refresh all app commands
- await remove_command(cmd)#
This function is a coroutine.
Remove a specific command
- Parameters
cmd (
AppCommand
) – What command to delete
Message Components#
- class ActionRow#
Bases:
APIObject
Represents an Action Row
- Parameters
*components (
MessageComponent
) –MessageComponent
,Button
, orSelectMenu
- to_dict()#
Transform the current object to a dictionary representation. Parameters that start with an underscore are not serialized.
- class Button#
Bases:
_Component
Represents a Discord Button object. Buttons are interactive components that render on messages.
They can be clicked by users, and send an interaction to your app when clicked.
- style#
One of button styles. Use
LinkButton
if you need theLINK
style.- Type
- disabled#
Whether the button is disabled
Default:False
- Type
APINullable[
bool
]
- classmethod bind_client(client)#
Links the object to the client.
- Parameters
client (Client) – The client to link to.
- classmethod from_dict(data)#
Parse an API object from a dictionary.
- to_dict()#
Transform the current object to a dictionary representation. Parameters that start with an underscore are not serialized.
- with_attrs(**kwargs)#
Modifies attributes are returns an object with these attributes.
some_button.with_attrs(label="A new label")
- **kwargs
Attributes to modify and their values.
- class LinkButton#
Bases:
_Component
Represents Button message component with a link.
- class ButtonStyle#
Bases:
IntEnum
Buttons come in a variety of styles to convey different types of actions. These styles also define what fields are valid for a button.
- Primary#
color: blurple
required_field: custom_id
- Secondary#
color: gray
required_field: custom_id
- Success#
color: green
required_field: custom_id
- Danger#
color: red
required_field: custom_id
- Link#
color: gray, navigates to a URL
required_field: url
- class SelectMenu#
Bases:
_Component
Represents a Discord Select Menu
- options#
The choices in the select, max
25
- Type
List[
SelectOption
]
- placeholder#
Custom placeholder text if nothing is selected, max 100 characters
- Type
APINullable[
str
]
- min_values#
The minimum number of items that must be chosen; min
0
, max25
Default:1
- Type
APINullable[
int
]
- max_values#
The maximum number of items that can be chosen; max 25
Default:1
- Type
APINullable[
int
]
- disabled#
Disable the selects
Default: False- Type
APINullable[
bool
]
- classmethod bind_client(client)#
Links the object to the client.
- Parameters
client (Client) – The client to link to.
- classmethod from_dict(data)#
Parse an API object from a dictionary.
- to_dict()#
Transform the current object to a dictionary representation. Parameters that start with an underscore are not serialized.
- with_appended_options(*options)#
Append *options to the
options
parameter and returns a newSelectMenu
.- *optionsSelectOption
List of options to append
- with_attrs(**kwargs)#
Modifies attributes are returns an object with these attributes.
some_button.with_attrs(label="A new label")
- **kwargs
Attributes to modify and their values.
- with_options(*options)#
Sets the
options
parameter to *options and returns a newSelectMenu
.- *optionsSelectOption
List of options to set
- class _Component#
Bases:
APIObject
Represents a message component with attributes that can be modified inline
- with_attrs(**kwargs)#
Modifies attributes are returns an object with these attributes.
some_button.with_attrs(label="A new label")
- **kwargs
Attributes to modify and their values.
- @button(label, style, emoji=None, url=None, disabled=None, custom_id=None)#
Turn a function into handler for a
Button
. SeeButton
for information on parameters.The function will still be callable.
from pincer.commands import ActionRow, Button class Bot(Client): @command async def send_a_button(self): return Message( content="Click a button", components=[ ActionRow( self.button_one ) ] ) @button(label="Click me!", style=ButtonStyle.PRIMARY) async def button_one(): return "Button one pressed"
Turn a function into handler for a
SelectMenu
. SeeSelectMenu
for information on parameters.The function will still be callable.
from pincer.commands import button, ActionRow, ButtonStyle class Bot(Client): @command async def send_a_select_menu(self): return Message( content="Choose an option", components=[ ActionRow( self.select_menu ) ] ) @select_menu(options=[ SelectOption(label="Option 1"), SelectOption(label="Option 2", value="value different than label") ]) async def select_menu(values: List[str]): return f"{values[0]} selected"
- @component(custom_id)#
Generic handler for Message Components. Can be used with manually constructed
Button
andSelectMenu
objects.- Parameters
custom_id (str) – The ID of the message component to handle.
Command Groups#
- class Group#
Bases:
object
The group object represents a group that commands can be in. This is always a top level command.
class Bot: group = Group("cool_commands") @command(parent=group) async def a_very_cool_command(): pass
This code creates a command called
cool_commands
with the subcommanda_very_cool_command
- class Subgroup#
Bases:
object
A subgroup of commands. This allows you to create subcommands inside a subcommand-group.
class Bot: group = Group("cool_commands") sub_group = Subgroup("group_of_cool_commands") @command(parent=sub_group) async def a_very_cool_command(): pass
This code creates a command called
cool_commands
with the subcommand-groupgroup_of_cool_commands
that has the subcommanda_very_cool_command
.
Interactable Objects#
Methods- defunassign
- class Interactable#
Bases:
object
Class that can register
PartialInteractable
objects. Any class that subclasses this class can register Application Commands and Message Components. PartialInteractable objects are registered by running the register function and setting an attribute of the client to the result.- unassign()#
Removes this objects loaded commands from ChatCommandHandler and ComponentHandler and removes loaded events from the client.