_images/logo.svg

Détaché

Détaché is a framework for creating productive and efficient Discord bots, built off of discord.py.

With Détaché, you can easily create bots without sacrificing direct access to the API. Commands and similar features are split into groups called Plugins, allowing for better organization. It’s inspired by the simplicity of Click and Flask.

Détaché’s features include:

  • intuitive argument parsing with support for custom types
  • automatic documentation and help messages
  • support for per-guild bot prefix via a callback
  • support for sharding

Here’s a simple bot that does math:

import detache

bot = detache.Bot(default_prefix="!")

@bot.plugin("Math")
class MathPlugin(detache.Plugin):
    """
    Basic math commands.
    """

    @detache.command("add", "Adds two numbers.")
    @detache.argument("a", detache.Number, help="First addend")
    @detache.argument("b", detache.Number, help="Second addend")
    async def add(self, ctx, a, b):
        return a + b

    @detache.command("subtract")
    @detache.argument("a", detache.Number, help="Minuend")
    @detache.argument("b", detache.Number, help="Subtrahend")
    async def subtract(self, ctx, a, b):
        """Subtracts two numbers."""

        return a - b

Both commands take two arguments, “a” and “b”, which are specified as numbers. The commands return the sum or difference, which Détaché automatically replies with.

Commands can be documented with the command decorator, or by using docstrings.

_images/mathbot.png

If argument parsing fails, the generated documentation will be shown.

TODO: Plugin, command, and argument descriptions are shown in the automatically generated help message.

You can install the library from PyPI:

$ pip install detache

Documentation

Quickstart

You can install Détaché from PyPI:

$ pip install detache

Basics

Détaché uses decorators to convert functions to commands.

Commands are declared through detache.command(), and arguments are added with detache.argument().

@detache.command("hi", description="Says hi to someone")
@detache.argument("user", type=detache.User, help="User to say hi to")
async def say_hi(self, ctx, user):
    await ctx.send("Hi, " + user.mention + "!")

Command decorators are placed in front of the function and any arguments. The name passed will be a how a user calls the command, and an optional description can be passed as well.

Argument decorators are placed in the order they’ll be used.

Plugins

Plugins are used to organize commands into groups. Plugins are created by inheriting a class from detache.Plugin. The detache.Bot.plugin() decorator should be placed before it with the name of the plugin.

import detache

bot = detache.Bot()

@bot.plugin("Example")
class ExamplePlugin(detache.Plugin)
    """Example Description used when documenting plugins and commands"""

Plugins can also be split across multiple files. For the plugin to work, you have to import and register it to the bot.

import detache

from plugins.example import ExamplePlugin

bot = detache.Bot()

bot.register_plugin(ExamplePlugin, name="Example")

Background Tasks

Détaché also supports background tasks, which are run when the bot connects to Discord. If the bot loses connection, the task will be cancelled and restart when the bot reconnects.

Similar to commands, the detache.background_task() decorator is used to declare a background task.

@detache.background_task("say_hi")
async def hi(self):
    # Say hi every 5 seconds.

    while True:
        for guild in self.bot.guilds:
            for channel in guild.channels:
                await channel.send("Hi!")

        await asyncio.sleep(5)

Event Listeners

An important feature of Détaché is that a provides a high level API while still giving access to the underlying events. Using event listeners, you can add a callback function to any discord.py event through the detache.event_listener() decorator.

This event listener listens for any “on_message_delete” events.

@detache.event_listener("on_message_delete")
async def undelete(self, message):
    await message.channel.send(
        "{} said {!r} at {}".format(message.author.mention, message.content, message.created_at)
    )

Arguments

detache.argument(name, type=None, default=None, required=True, nargs=1, help=None)

Command argument.

Parameters:
  • name – Name of the argument. Should match one of the function’s arguments.
  • type – (Optional) Argument type. Leave as None to accept any type.
  • default – (Optional) Default value.
  • required – (Optional) Whether the argument is required. Defaults to True
  • nargs – (Optional) Number of times the argument can occur. Defaults to 1. -1 allows unlimited arguments.
  • help – (Optional) Argument description.

If nargs is anything other than 1, the parsed argument will be returned as a list.

Détaché supports several argument types:

Custom types can also be created by inheriting from detache.Any. This type takes a hexadecimal number and converts it to an int, for example:

class Hex(detache.Any):
    # arguments are parsed using regex patterns
    pattern = "(0x)?[0-9a-f]+"

    @classmethod  # <- must be a classmethod
    def convert(cls, ctx, raw):
        # a context object and the raw argument are passed for conversion

        # remove 0x
        if raw.startswith("0x"):
            raw = raw[2:]

        return int(raw, base=16)  # return the converted argument

Variadic Arguments

Variadic arguments allow an argument to be passed a specific or unlimited number of times. This is set with the nargs parameter. If this is set to -1, an unlimited number of arguments is accepted.

Variadic arguments are passed to the underlying function as a list.

Example:

@detache.command("add", "Variadic argument test that adds numbers.")
@detache.argument("addends", detache.Number, nargs=-1, help="Addends")
async def add_cmd(self, ctx, addends):
    return sum(addends)

If nargs is -1, then setting required=False will allow the argument to not be passed at all. Otherwise, the argument must be passed at least once.