Using Asyncio with Quart

Quart is a Python web framework designed to have a similar API to Flask, but using asyncio.

from quart import Quart

app = Quart(__name__)

@app.route('/')
async def hello():
    return 'hello'

app.run()

Monkey Patching

Flask-Discord-Interactions supports using Quart. This can provide a more familiar development experience to those used to discord.py’s async commands.

Specifically, Quart supports monkey patching to allow Flask extensions to run with Quart. The specifics of this are in this tutorial.

Flask-Discord-Interactions will work with this monkey patching. Additionally, this library has some extra logic to handle mixing async and non-async commands, and to allow awaiting when handling followup messages.

from quart import Quart

import quart.flask_patch
from flask_discord_interactions import DiscordInteractions

app = Quart(__name__)
discord = DiscordInteractions(app)

discord.update_commands()

@discord.command()
async def ping(ctx):
    "Respond with a friendly 'pong'!"
    return "Pong!"

# Non-async functions still work
@discord.command()
def pong(ctx):
    return "Ping!"

Use an Async Route Handler

If you want to use async commands in Flask-Discord-Interactions, then the library needs to register an asynchronous handler for the interactions endpoint. This is what allows Flask-Discord-Interactions to await your function handler.

If you are using Flask-Discord-Interactions with Quart, you need to call DiscordInteractions.set_route_async() instead of DiscordInteractions.set_route().

discord.set_route_async("/interactions")

Context in Async Commands

A special AsyncContext class is exposed to asynchronous commands. This class makes AsyncContext.edit(), AsyncContext.send(), and AsyncContext.delete() awaitable.

@discord.command()
async def wait(ctx, seconds: int):

    async def do_followup():
        await asyncio.sleep(seconds)
        await ctx.edit("Done!")

    asyncio.create_task(do_followup())
    return Message(deferred=True)

# Normal followups use the normal Context
@discord.command()
def wait_sync(ctx, seconds: int):

    def do_followup():
        time.sleep(seconds)
        ctx.edit("Done!")

    threading.Thread(target=do_followup).start()
    return Message(deferred=True)

When creating command groups and subgroups, you will only get an AsyncContext if you provide the is_async=True flag.

toplevel = discord.command_group("toplevel", is_async=True)
secondlevel = toplevel.subgroup("secondlevel", is_async=True)

@secondlevel.command()
async def thirdlevel(ctx):
    async def do_followup():
        print(type(ctx))
        await asyncio.sleep(1)
        await ctx.edit(f"Hello, {ctx.author.display_name}!")

    asyncio.create_task(do_followup())
    return Message(deferred=True)

Full API

class flask_discord_interactions.AsyncContext(**kwargs)

Bases: Context

Represents the context in which an asynchronous Command is invoked. Also provides coroutine functions to handle followup messages.

Users should not need to instantiate this class manually.

async delete(message: str = '@original')

Delete an existing message.

Parameters:

message (str) – The ID of the message to delete. If omitted, deletes the original message.

async edit(updated: Union[str, Message], message: str = '@original')

Edit an existing message.

Parameters:
  • updated (Union[str, Message]) – The updated Message to edit the message to.

  • message (str) – The ID of the message to edit. If omitted, edits the original message.

async send(message: Union[Message, str])

Send a new followup message.

Parameters:

message (Union[Message, str]) – The Message object to send as a followup message.