A plugin-based, extensible bot for Discord
The bot configuration is read from a yaml
file (specified with --config-path
)
You can copy the existing config.example.yaml
file and fill in the values yourself
You must at least provide a valid token
for your bot under the top level bot
configuration key
There must also be a top level web
key as well as a top level db
A minimalistic configuration file could look like this:
bot:
# A valid discord application token
token: 'MyaWes0mebOtT0k3n'
# Plugins to enable
plugins:
some_plugin: {}
some_other_plugin_with_conf:
conf_key: 'conf value'
# Ids of people able to use "master commands"
masters:
- 'Some snowflake id for a discord account'
web:
port: 80
db:
host: db
user: postgres
If you want to use the webserver:
Place/generate your SSL/TLS certificate in ./web/server/fullchain.pem
Place the associated private key in ./web/server/privkey.pem
Then make sure docker >= 1.10
and docker-compose >= 1.6
are installed and run:
./dev run --rm web-builder npm install # Fetch node dependencies
./dev run --rm web-builder webpack -p # Bundle the website
./prod up -d db # Boots up the db
./dev run --rm db flyway migrate # Populate the db
./prod up -d # Boots up the bot and the webserver
./prod logs -f bot # To access the bot's stdout logs
Globibot comes with a minimalistic set of plugins
Plugins are python modules loaded from the directory specified by --plugin-path
To enable a plugin, you can just add its name in the configuration file under the plugins
key
For globibot to be able to load a plugin, your plugin module must export a symbol called plugin_cls
which would point to a class that inherits Plugin
from bot.lib.plugin
Let's create a simple plugin called foo
In ./bot/plugins/foo/__init__.py
:
from .foo_plugin import Foo
plugin_cls = Foo
In ./bot/plugins/foo/foo_plugin.py
:
from globibot.lib.plugin import Plugin
class Foo(Plugin):
pass
In ./bot/config.yaml
let's add our plugin with a dummy config:
bot:
# ...
plugins:
foo:
bar: 1337
# ...
Now if you start/restart the bot:
./script/dev up -d bot # To start the bot
# Or
./script/dev restart -t 0 bot # To restart the bot
You should see in the logs that Globibot loaded your plugin:
From this moment on, any changes to your foo
modules will be reloaded automatically when module files change
Let's add a simple command to the plugin:
In ./bot/src/bot/plugins/foo/foo_plugin.py
:
from globibot.lib.plugin import Plugin
from globibot.lib.decorators import command # Command declaration helper
from globibot.lib.helpers import parsing as p # Parser combinator tools
from globibot.lib.helpers.hooks import master_only # Hook to only allow master users
class Foo(Plugin):
def load(self):
'''
Called on plugin load/reload
'''
# Loads the value of the `bar` configuration key with a default value
# if the key were to be absent
self.bar = self.config.get('bar', 42)
@command(p.string('!foo') + p.bind(p.integer, 'number'))
async def foo_command(self, message, number):
'''
Called on inputs starting with the string '!foo' (case insensitive)
followed by an integer whose value will be bound to the `number`
variable
'''
await self.send_message(
message.channel, # Sends a message in the same channel as the input
'{} * {} = {}'.format(number, self.bar, number * self.bar)
)
@command(p.string('!id') + p.bind(p.mention, 'user_id'), master_only)
async def id_command(self, message, user_id):
'''
Called on inputs starting with the string '!id' (case insensitive)
followed by a discord user mention (usually displayed '@SomeUser' on
discord's client)
We have access to SomeUser's discord's ID in the `user_id` variable
The command is marked as `master_only` and will execute only if the
input was sent by someone who was registered as 'master' in the
configuration file
'''
await self.send_message(
message.channel,
'{}: the id is {}'
.format(message.author.mention, user_id),
delete_after = 30 # Message deletes itself after 30 seconds
)
Upon saving this file, you should see this:
And then you can simply test the results in discord directly: