Interactions

Pincer makes it extremely easy to create an interaction. All you need to do is create a method with the @command decorator.

Note

Although purely functional bots are possible, it’s recommended to inherit from the Client class as seen in the example below.

from pincer import Client
from pincer.commands import command

class Bot(Client):
  @command
  async def some_command(self):
    ...

bot = Bot("TOKEN")
bot.run()

This registers a command called “some_command”. It’s pretty useless right now, so let’s take a closer look at what else you can do.

Note

If you’re not seeing your command register, it’s likely because it takes up to one hour for a global slash command to do so. Specify the guild in the command decorator to have application commands register instantly.

To register your command to a guild do:

@command(guild=MY_GUILD_ID)
async def some_command(self):
    ...

Sending messages to the user is extremely easy. What you want to return is inferred by the object’s return type. A str can be returned to send a text message. Here’s a simple ping command.

@command
async def ping(self):
    return "pong"

If you need access to more information, you can pass in the ctx object.

Note

ctx and self should be those exact names or the correct value will not be passed in.

from pincer.objects import MessageContext
...

@command
async def hello(self, ctx: MessageContext):
    # Returns the name of the user that initiated the interaction
    return f"hello {ctx.author}"

Application Command Types

Pincer provides an API for all three interaction command types. The only thing that varies is the function signature.

from pincer.commands import command, user_command, message_command
from pincer.objects import MessageContext, UserMessage, User
...

@command
# Can have any amount of inputs
async def ping(self, ctx: MessageContext, arg1: str, arg2: str):
    return "pong"

# Must have a parameter for users. User can be a GuildMember. All User
# methods are available to GuildMember because GuildMember inherits from
# User.
@user_command
async def user_ping(self, ctx: MessageContext, user: User):
    return "pong"

# Must have a parameter for messages.
@message_command
async def message_ping(self, ctx: MessageContext, message: UserMessage):
    return "pong"

Interaction Timeout

Interactions time out after 3 seconds. To extend the timeout to 15 minutes you can run ack() from MessageContext. InteractionFlags is available in this method.

Arguments

Every parameter besides ctx and self is inferred to be a slash command argument. Notice how word is typehinted as string. Pincer uses type hints to infer the argument type that you want.

@command
async def say(self, word: str):
    return word

The list of possible type hints is as follows:

Class

Command Argument Type

str

String

int

Integer

bool

Boolean

float

Number

pincer.objects.User

User

pincer.objects.Channel

Channel

pincer.objects.Role

Role

pincer.objects.Mentionable

Mentionable

You might want to specify more information for your arguments. If you want a description for your command, you will have to use the Description type. Modifier types like this need to be inside of the CommandArg type.

from pincer.commands import CommandArg, Description
from pincer.objects import MessageContext

@command
async def say(
    self,
    ctx: MessageContext,
    word: CommandArg[
      str,
      # This will likely be marked as incorrect by your linter but it is
      # valid Python. Simply append # type: ignore for most linters and
      # noqa: F722 if you are using Flake8.
      Description["A word that the bot will say."]  # type: ignore # noqa: F722
    ]
):
    # Returns the name of the user that initiated the interaction
    return word

Parameters will be an optional slash command arguments if they have a default value in Python.

@command
async def say(
    self,
    word: str = "apple"  # Word is not optional
):
    return word

These are the available modifiers:

Modifier

What it does

Locked to types

Description

Description of a command option.

Choices

Application command choices.

str, int, float

ChannelTypes

A group of channel types that a user can pick from.

Channel

MaxValue

The maximum value for a number.

int, float

MinValue

The minimum value for a number.

int, float

Return Types

str isn’t the only thing you can return. For a more complex message, you can return a Message object. The message object allows you to return embeds and attachments. InteractionFlags are only available in the response if you return a Message object.

from pincer import Client, command, Embed
from pincer.objects import Message, File
...

@command
async def a_complex_message(self):
  return Message(
    "This is the message's content"
    embeds=[
      Embed(
        title="Pincer",
        description=(
          "🚀 An asynchronous python API wrapper meant to replace"
          " discord.py\n> Snappy discord api wrapper written"
          " with aiohttp & websockets"
        ).set_image(
          url="attachments://some_image.png"
        )
      )
    ],
    attachments=[
      File.from_file("path/to/a/file.png", filename="new_name.png"),
      "path/to/another/file.png" # A string is inferred to be a filepath here!
    ]
  )

Attachments can also be Pillow images if Pillow is installed.

from PIL import Image
...

attachments=[
  Image.new("RGBA", (500, 500), (255, 0, 0)), # Will automatically be named `image0.png`
  Image.new("RGB", (500, 500)), # Will automatically be named `image1.png`
  File.from_pillow_image(some_pillow_image, "this_is_the_image_name.png") # You can also do this to specify the name
]

Additionally, Pillow Images, Files, and Embeds can be returned directly without wrapping them in a Message object.

...
@command
async def a_complex_message(self):
  return Embed(
    title="Pincer",
    description=(
      "🚀 An asynchronous python API wrapper meant to replace"
      " discord.py\n> Snappy discord api wrapper written"
      " with aiohttp & websockets"
    )
  )
Possible Return Types

Return Type

Discord Message

str

text only message

Embed

Discord embed

File

file attachment

PIL.Image.Image

single image attachment

Sending Messages Without Return

The MessageContext object provides methods to send a response to an interaction.

from pincer.objects import MessageContext, Message

@command
async def some_command(self, ctx: MessageContext):
    await ctx.send("Hello world!") # Sends hello world as the response to the interaction
    return # No response will be sent now that the interaction has been completed

@command
async def some_other_command(self, ctx: MessageContext):
    await ctx.channel.send("Hello world!") # Sends a message in the channel
    return "Hello world 2" # This is sent because the interaction was not "used up"