Skip to main content

Extensions

Extensions are extra packages that add functionality to a maxbot application. For example, an extension might add support for new channel, custom state tracker or sending some special types of messages.

maxbot extensions are usually named "maxbot-foo". You can search PyPI for packages tagged with Framework :: MaxBot.

Using Extensions

Consult each extension’s documentation for installation, configuration, and usage instructions.

Installing

First, you need to install the extension

$ pip install maxbot-foo

If you are developing the maxbot project you need to add the extension package as a project dependency.

Next, there are two ways to add the extension to your bot. You can simply declare the required extensions in bot.yaml. If you need more control you can apply extensions in the source code.

Declaring in bot.yaml

An extension can be declared in the extensions section of your bot.yaml.

extensions:
foo: {}

Where foo is the name of the extension and value {} is the extension configuration dictionary. In our case configuration is empty. Generally, each extension has it's own configuration schema described in the extension's documentation. For example, an extension that installs a custom sql state_store for a bot could be configured as follows

extensinos:
my-state-tracker:
dsn: "mssql+pyodbc://user:password@DSNSTRING"

No programming skills are required to add extensions this way.

TODO: Need an example based on real life extension.

Applying in source code

If you are programmatically customizing your bot with a builder pattern note that the extensions declared in bot.yaml are loaded when your call the build method after all the customizations have been done. To apply your customizations to a loaded extension you must first load the extension manually.

from maxbot_foo import foo_extension
from maxbot import MaxBot

builder = MaxBot.builder()

# manualy load an extension
foo_extension(builder, config={})

# here you can customize your bot

# provide a way to load resources
builder.use_file_resources('bot.yaml')

# actually loads extensinos declared in the bot.yaml
bot = builder.build()

You do not need to declare extension in bot.yaml if you are loading it manually.

TODO: Do we need to configure such extensions in bot.yaml?

Developing Extensions

This section will show how to create maxbot extensions.

Naming

We give extensions string names so they can be declared in bot.yaml. Most of declarations in bot.yaml must be valid Python identifiers. For consistency, extension names follow the same requirement.

Typically, the extension name is prefixed with maxbot to make it a unique Python package name. A general Python packaging recommendation is that the install name from the package index and the name used in import statement should be related. The import name is lowercase, with words separated by underscores (_). The install name is lower case case, with words separated by dashes (-).

For example, extension named foo_bar installed as maxbot-foo-bar and imported as maxbot_foo_bar.

Customizing Bots

An extension is nothing more then a Python callable with two arguments: a bot builder and an optional configuration dictionary. Given these arguments an extension should customize a bot builder.

def foo_bar_extension(builder, config):
builder.before_turn(...)
...

There are many ways that an extension can customize the builder. Any methods that are available on a bot builder can be used during an extension’s call. See Bots guide to learn how to customize a bot.

It is important that the builder instance is not stored on the extension and the builder methods should not be called after the extension callable is complete.

Configuring

TODO: Not implemented yet. Needs to be refactored in that way.

The second argument to the extension callable is an optional configuration dictionay. If you want to pass a non-empty configuration when declaring an extension in bot.yaml, you must specify a schema for that configuration. The configuration dictionary schema is specified using the maxbot_extension annotation.

from marshmallow import Schema, fields
from maxbot import maxbot_extension

class ForBarConfig(Schema):
some_str = fields.String(required=True)
some_number = fields.Integer()

@maxbot_extension(config_schema=ForBarConfig)
def foo_bar_extension(builder, config):
builder.before_turn(...)

Then the extension can be configured in bot.yaml as follows

extensions:
foo_bar:
some_str: !ENV some value with ${SOME_VARIABLE}
some_number: 123

You can also use variable substitution (TODO: link) to pass environment variables to the extension's configuration.

A schema specification is not required when you apply an extension in source code.

Discovering

To be available for declaration in bot.yaml, an extension must be specified in the maxbot_extensions entry point. maxbot will automatically discover such extensions. Entry points are specified in setup.py

from setuptools import setup

setup(
name='maxbot-foo-bar',
...,
entry_points={
'maxbot_extensions': [
'foo_bar=maxbot_foo_bar:foo_bar_extension'
],
},
)

or pyproject.toml if you are using for example the poetry tool.

[tool.poetry.plugins.maxbot_extensions]
foo_bar = "maxbot_foo_bar:foo_bar_extension"

For other packaging tools refer to their documentation for support of defining entry points.

In-project Extensions

Let's say you have developed an in-project extension in your maxbot project and want to make it available for declaring in the bot.yaml file. To do this, pass the available_extensions argument to the builder factory as follows.

TODO: Why is it an argument and not a separate builder method.

from maxbot import MaxBot

from .bar import bar_extension

builder = MaxBot.builder(available_extensions={
'bar': bar_extension
})
# ...
builder.use_file_resources('bot.yaml')
bot = builder.build()

Then the extension can be declared in the bot.yaml file as usual.

extensions:
bar: {}

In this case it will be loaded when your call the build method.