Discord Amethyst
An opinionated extension for discord.py. Amethyst adds a handful of features that I found myself re-implementing or wanting for many of my Discord bots such as automatic app command synchronisation and job scheduling.
Widgets
Command
The amethyst command decorator is just a wrapper around discord.py's app_commands.command
decorator. Please refer to the discord.py documentation for usage.
Event
Amethyst's event decorator is a type hinted wrapper around discord.py's normal event decorator. The primary difference is that you must specify which event you wish to subscribe to. The property amethyst.events
is a module containing all of the default discord.py events.
import amethyst
@amethyst.event(amethyst.events.on_ready)
async def on_ready():
print("Bot is ready!")
Schedule
Amethyst implements a cron-like asynchronous scheduler for calling functions on a set schedule, powered by croniter.
The following is an example of a schedule that will run every day at 8 am.
import amethyst
@amethyst.schedule("0 8 * * *")
async def every_morning():
print("Good morning!")
Dynamic Module Import
Amethyst can dynamically load python modules, powered by dynamicpy. When run, the client will automatically import and register any widgets found in the .command
, .commands
, .plugin
and .plugins
submodules. The submodules which are searched can be configured in the AmethystClient
's constructor.
Lets say you have the following project structure:
my-bot/
├── __init__.py
├── main.py
└── commands/
├── __init__.py
├── foo.py
└── bar.py
If your instance of AmethystClient
is instantiated in main.py
then the commands/
package will be recursively searched for widgets to register.
The searched modules can also be top-level, the only requirement is that they are at the same level as the module inside which the client was instantiated.
my-bot.py
plugins.py
commands/
├── __init__.py
├── foo.py
└── bar.py
In this example, the commands.py
module and the commands/
package will be searched.
Plugin System
Amethyst has a plugin system, powered by dynamicpy and inspired by discord.py Cogs. You can create a plugin by simply defining a class that extends AmethystPlugin
. If this is found by the Dynamic Module Importer then it will be automatically registered to the client, otherwise you will have to use the AmethystClient.register_plugin
method.
An example plugin may look like the following:
import amethyst
class ExamplePlugin(amethyst.AmethystPlugin):
@amethyst.event(amethyst.events.on_ready)
async def on_ready(self):
channel = self.client.get_channel(000000000000000000)
await channel.send("Bot is ready!")
Plugin Dependency Injection
Amethyst plugins support dynamicpy dependency injection for their constructors. You can add dependencies to the client using the AmethystClient.add_dependency
method, which will then be injected into constructor parameters when the plugin is registered.
import mysql.connector
import amethyst
client = amethyst.AmethystClient(...)
database: mysql.connector.MySQLConnection = mysql.connector.connect(...)
client.add_dependency(database)
class ExamplePlugin(amethyst.AmethystPlugin):
def __init__(self, database: mysql.connector.MySQLConnection) -> None:
self.database = database
Evironment Variables
Amethyst uses python-dotenv to load .env
files found at the project root. This can be used to configure certain aspects of the library.
Name | Default | Description |
---|---|---|
AMETHYST-BOT-TOKEN | None | If present, token can be omitted from the AmethystClient.run and this will be used instead. |
AMETHYST-AUTO-SYNC | true | If true , the client will synchronise app_commands if they are out of date with Discord. |
AMETHYST-SYNC-GUILD | None | If present, app_commands will only be synchronised to specified guild id. |
Roadmap
- Context Menus
- Hybrid Commands
- Plugins enable/disable after initialisation
- Debug mode featuring automatic reload