Build a Dicord bot with ease and flexibility
pip install BuildABot==2.0.4
Build a Dicord bot with ease and flexibility
For editing module repo: Before replacing anything with modules from elsewhere, and I can't stress this enough, you MUST read the new and old files carefully to verify that you indeed have the latest version of the module. Check every file, every method, every line that you are not overriding things so that they are lost forever and you have to spend another 8 hours recreating it.
README.md
and put it alongside the module.json
__READMES__
with the same name as the module file (ie. ./my_module.py
=> ./__READMES__/my_module.md
)meta = {
...
} ## BuildABot -- end
this is so the automated bot maker can properly identify the module depencencies, but is not nessesary for non-public modulesdefaultConfig
is in the module metaTo start, you have two options; create a single file module or a folder. You should use a folder if you plan to have, commands, locale, many classes, or complex logic.
src/modules
(echo "" > src/modules/my_module.py
)from buildabot import module
)meta
and assign a dict, more info about module metas below (meta = {}
)
} ## BuildABot -- end
if it is a public modulemodule
(class Mymodule(module):
)mkdir src/modules/my_module
)cd src/modules/my_module
)module.json
and create an object inside, more info about module metas below (echo "{}" > module.json
)echo "" > my_module.py
)from buildabot import module
)module
(class Mymodule(module):
)Now that you have the basic setup done, it's time to setup your module meta
module Meta is something that tells BuildABot what your module does and how to get things rolling. The meta is an object (or dict), here is a list of all properties you can use, some are required
Key | Type | Default | Info |
---|---|---|---|
class | string | n/a | Starting class of the module, more info below |
name | string | n/a | Name of the module |
description | string | n/a | What the module does, be as short or complex as you need |
?depends | string[] | [] | module dependencies |
?softDepends | string[] | [] | Optional module dependencies |
?defaultConfig | object | {} | Default config for the module |
?requirements | string[] | [] | Pypi requirements for the module |
?threaded | boolean | false | module uses threading. modules that have this true can not be disabled |
?disable | boolean | false | modules that have this true will not be enabled |
?experimental | boolean | false | Experimental modules should be used causiously as they might not behave as expected. |
Example Meta:
{
"class": "my_module.Mymodule",
"name": "Mymodule",
"description": "My module is an awesome module that enables the bot to do some really awesome stuff!",
"depends": [],
"softDepends": [
"Commands"
],
"defaultConfig": {
"awesomeFactor": 1.42
},
"requirements": ["pytz"],
"threaded": false,
"disable": false,
"experimental": false
}
If for some reason your module needs to access your or another feautres meta you can do module#meta
class
as the name of the class that extends module
module
from the module folder (ie. settings.SnowflakeSettings
, in the file settings.py
with the class named SnowflakeSettings
)def on_load(self)
- called when module is loaded, don't attempt to access other modules as they may not be loaded yetasync def on_enable(self)
- called when the module is enabled, now is when you can garentee you dependencies will be loadedasync def on_disable(self)
- called when module is disabled, more offten than not this won't be called when the bot stopsEvents are listened using module#on_event(event_name, listener_func, [namespace="discord"], [priority=0], [ignore_canceled=False])
, event names can be found on the discord.py documentation, in addition here are the built-in events, the namespace is buildabot
:
on_ready
- Called when the module manager is setupon_done
- Called after enable process is finished (after on_all_enabled
)on_all_load
- Called after all modules are loaded, useful to add or modify functions in each moduleon_load
- Called once a module has been loaded, useful to add or modify a dependant moduleon_all_enabled
- Called when all modules have been enabledon_enabled
- Called once a module has been enabledon_all_disabled
- Called when all modules have been disabledon_disable
- Called once a module has been disabledImportant: All listener functions must be a coroutine. In order to turn a function into a coroutine they must be async def
functions
Each module may have their own events, you can read about them in the modules' README
To create an event, it's pretty simple, in your on_load
define the module with self.module_manager.define_event(event_name)
then to call the event run either await self.module_manager.emit_event(namespace, event_name, *args, **kwargs)
or self.module_manager.emit_event_sync(namespace, event_name, *args, **kwargs)
Using other modules is pretty easy, it's as simple as doing self.module_manager.get_module("SomeOthermodule")
. But before you use any module you should put the module in your dependiencies, this prevents any errors and garentees the module is there when your module is enabled. If you were to soft depend a module then you should always check if the module is enabled before using it, self.module_manager.is_enabled("SomeOthermodule")
Because commands are so common, I thought it'd be a good idea to tell you how to use the Commands module, but more detailed info will be in the Commands README. First of all, Commands requires EmbedUI and works best with SnowflakeSettings installed, and second your module should be in a folder to be the cleanest, the tutorial will be for foldered modules only.
cmds
if self.module_manager.is_enabled("Commands"):
from modules.commands.command_manager import CommandManager # import CommandManager for type hinting
cmd: CommandManager = self.module_manager.get_module("Commands") # Get the command manager
cmd.add_command_dir('my_module/cmds', ftr_dir=True) # Tell the command manager where your commands are
cmd.add_type("my", "My module", after='info') # More info in Commands README
This snippit will get the Commands command manager and tell it where to look for your commands, this isnt the only way to add commands, but it is the cleanest.
my_command.py
)from modules.commands.command import Command
from modules.commands.command_context import CommandContext
meta = {
'class': 'MyCommand',
'description': "A command that does cool stuff",
'usage': '',
'pack': 'my_module',
'type': "my",
'permissions': []
} # More info in Commands README
class MyCommand(Command):
async def on_command(self, context: CommandContext): # Called when the user runs the command, more info in Commands README
await context.ok("{}, my command is the coolest command!".format(context.author.mention)) # Replys to the channel using EmbedUI
This is a foldered module
src/modules/my_module/module.json
{
"class": "my_module.Mymodule",
"name": "Mymodule",
"description": "My module is an awesome module that enables the bot to do some really awesome stuff!",
"depends": [
"MongoDB"
],
"softDepends": [
"Commands"
],
"defaultConfig": {
"awesomeFactor": 1.42
},
}
src/modules/my_module/my_module.py
from buildabot import module
class Mymodule(module):
def __init__(self, fm, m):
super().__init__(fm, m)
self.collection = None
async def on_enable(self):
self.collection = self.module_manager.get_module("MongoDB").collection("my_module")
if self.module_manager.is_enabled("Commands"):
from modules.commands.command_manager import CommandManager
cmd: CommandManager = self.module_manager.get_module("Commands")
cmd.add_command_dir('my_module/cmds', ftr_dir=True)
cmd.add_type("my", "My module", after='info')
cmd.add_alias("add", "my")
cmd.add_alias("count", "my")
src/modules/my_module/cmds/my.py
import discord
from modules.commands.command import Command
from modules.commands.command_context import CommandContext
meta = {
'class': 'My', # Class of the command
'description': "A command that does cool stuff", # What the command does
'usage': '<number>', # Tells the user the command usage in the help command
'pack': 'my_module', # Used to disable specific module commands in the Commands config
'type': "my", # In the "My module" category in the help command
'permissions': ['administrator'] # WIll require the user to have the `administrator` permission
# Alias support in command meta not avaliable, for now add aliases in your `on_enable`
}
class My(Command):
async def on_command(self, context: CommandContext):
# Verify the correct amount of arguments
if len(context.args) < 1:
raise context.MissingArgsException(context)
# Test first argument for number
try:
add = int(context.args[0])
except:
# Make sure to tell users why a command failed
await context.error("That's not a number")
return
# Add to users counter, if not exist insert it with `upsert`
result = self.command_manager.get_module("Mymodule").collection.update_one(
{"user": context.author.id}, {"$add": {"count": add}}, {"upsert": True})
# Get count from document
count = result.raw_result["count"]
# Tell the user thier updated counter
await context.ok(f"Your counter is now at {count}")