A wrapper for the Discord Interactions API that does not rely on websockets and can therefore be used in a stateless webhook environment.
Furthermore, it allows for strict separation between your commands' structure and and the data that is received when triggering it.
Requires Python 3.8+
- Latest release from PyPI using pip:
pip install discord-interactions.py
- Latest commit from GitHub using pip and git:
pip install git+https://github.com/LiBa001/discord-interactions.py
Note
Installing directly from GitHub requires you to have Git installed on your computer.
- If this doesn't work, you might try:
python -m pip install ...
- Or if you are on windows:
py -m pip install ...
This library is specifically designed to work seamlessly with the Flask microframework.
The most API-like example with the flask extension is this:
from discord_interactions.flask_ext import Interactions
from discord_interactions import (
ApplicationCommand,
ApplicationCommandOption,
ApplicationCommandOptionType,
Interaction,
InteractionResponse,
InteractionResponseType,
InteractionApplicationCommandCallbackData,
)
from flask import Flask
import os
app = Flask(__name__)
interactions = Interactions(app, os.getenv("CLIENT_PUBLIC_KEY"))
echo_cmd = ApplicationCommand("echo", "what goes around comes around")
echo_cmd.add_option(
ApplicationCommandOption(
type=ApplicationCommandOptionType.STRING,
name="message",
description="This will be echoed.",
required=True,
)
)
@interactions.command(echo_cmd)
def _echo(interaction: Interaction):
msg = interaction.data.options[0].value # "message" option content
return InteractionResponse(
type=InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE,
data=InteractionApplicationCommandCallbackData(content=msg),
)
Here, we use the rudimentary ApplicationCommand
, Interaction
and
InteractionResponse
classes, which are in their structure basically
exact counterparts of the original API models.
Let's make it a bit simpler:
@interactions.command(echo_cmd)
def _echo(interaction: Interaction):
# different way of getting an option
msg = interaction.data.get_option("message").value
return msg
Now, we don't need to deal with InteractionResponse
anymore, but instead just
return the response content as a string. The response type then defaults to
InteractionResponseType.CHANNEL_MESSAGE_WITH_SOURCE
. You could also just return
None, if you don't want to send a response. You can also simply return a boolean as a
second value, indicating whether or not the command call should be displayed in Discord
(i.e. the _WITH_SOURCE
part of the response type).
Also we get the option via the get_option
helper method.
This library provides another abstraction layer, though.
Inspired by the concept of database ORMs, it has an Object-Command Mapper (OCM)
that lets you define a class for each command which will then serve as both
a generic structural description of the command (like ApplicationCommand
)
and a container for the actual data that is received
when the command is called (like Interaction
).
So, the simplest possible example looks like this:
from discord_interactions.flask_ext import Interactions
from discord_interactions.ocm import Command, Option
from flask import Flask
import os
app = Flask(__name__)
interactions = Interactions(app, os.getenv("CLIENT_PUBLIC_KEY"))
class _Echo(Command):
""" what goes around comes around """
message: str = Option("This will be echoed.", required=True)
@interactions.command
def _echo(cmd: _Echo):
return cmd.message
If you want to send messages after the initial response, you need to create followup
messages. For this purpose you can use the after_command
decorator, that registers
a function to be called after the actual command function has returned. The function
needs to take exactly one parameter, the AfterCommandContext
, which contains the
several things, like the Interaction
and initial InteractionResponse
.
interactions = Interactions(app, PUBLIC_KEY)
@interactions.command("delay")
def delay(_: Interaction):
return "starting countdown", True # this message is ephemeral
@delay.after_command
def after_delay(ctx: AfterCommandContext):
delay_time = ctx.interaction.data.options[0].value
time.sleep(delay_time)
ctx.send(f"{delay_time} seconds have passed")
You can also register callbacks for message components, such as buttons.
Components are registered and identified by their custom_id
.
@interactions.component("my_button")
def my_button_handler(ctx: ComponentContext):
return f"{ctx.interaction.user.username} clicked the button"
For more examples of the different features take a look at examples.
If you want to know how to make your Discord bot work with Slash Commands and how to set everything up, take a look at this example project. It hosts the program in a serverless environment via Google Cloud Run and also provides a demo bot, so you can try out Slash Commands in your Discord server. Check it out to learn more!