IMMP comes with a number of common idioms and features provided by built-in hooks, which can be used by your own classes to avoid reimplementing common bot tasks.
Data schema validation#
The included plugs make use of an internal schema validator to ensure all API responses contain the expected fields. These effectively act as assertions on the existence of all required fields, before starting to parse the object.
In the most basic case, prepare a
Schema representing the data structure, and call it on
each API response of that type before using it. For plug and hook configuration, attach it to the
schema attribute to have incoming config validated at creation.
Some hooks may need to store their own information. The hook config is read-only – any changes are not persisted because the config must be provided at startup. For data encountered during the app’s lifetime, a database may be required.
DatabaseHook resource provides generic database access using the Peewee ORM. Define
your models as subclasses of
BaseModel, then at startup, obtain the database connection
DatabaseHook.db and call
Database.create_tables() with a list of all your models.
From there, you can use Peewee’s model methods to query and manage records.
CommandHook provides an easy way to register commands that interact with a custom
hook. Each command method should be decorated using
command(), and should accept a
Message argument followed by any arguments expected from the user. Keyword arguments are
not supported, though optional (with default values) and variable (varargs) parameters may be used.
This replaces the class-level attribute with a
Command instance. This instance is
callable, allowing you to use the underlying method like any other, though introspection may
give surprising results if you rely on it being a traditional method.
You may need to make commands available based on config or state. For simple cases, you can make a
command conditional by setting the
test field to a method returning
True if the command
is currently valid.
For more complex hooks, you may instead need to generate new commands based on config. This is
where dynamic commands come into play. An unnamed command is effectively a template – it won’t
itself be included in command sets, but you can provide qualified instances of it inside
Identities and access#
If your hook interacts with a third-party service, with users from connected plugs mapping to
external users within the service, consider implementing
IdentityProvider to make this
information available to other hooks like Sync.
Incoming HTTP requests#
Some networks may only support listening for messages by subscribing to a webhook. You may also
need to provide some web-based UI for certain features. You can use the
resource hook to spin up a local
aiohttp.web server, and bind URL paths to it from your plug
Initial setup is abstracted by the
WebContext context – an instance of this can be
WebHook.context(). With this, you can configure new routes into your code
like a native
If a template name is given to
WebContext.route(), the request handler method will be
aiohttp_jinja2, meaning it should return a
dict to act as the template
WebContext.env for the variables provided by default.