Runner & config#

The easiest way to get a host up is by running IMMP using the built-in runner:

$ immp [-w] <config file>

The expected configuration method is via a config file. Most file formats are understood, as long as the relevant parser libraries are installed (e.g. PyYAML for YAML files).

Pass -w to have the runner write the current config back to the file on exit. This is useful for plugs and hooks that change their state at runtime (e.g. commands to manage their settings). Note that any comments in the file will be lost – it will be regenerated from scratch.

IMMP is config-driven: plugs and hooks are all created with a given configuration. This allows for a richer specification to suit each instance, and means all instances can be created in the same way with the same basic arguments.

The examples below assume a YAML config file for succinctness.


Plug directives go under a root plugs section of the config. Each plug instance has a name, which can be referenced elsewhere in the config, and needs a path attribute to the corresponding Python module and class. A config subkey is generally needed to provide plug-specific options.

A sample plug config may look like this:

    path: demo.DemoPlug
      api-key: xyzzy


Most networks will provide multiple “rooms” or “conversations” where messages come from, which are all handled by a single plug. A channel is used to specify which rooms are monitored.

Channels are used to name and pair room identifiers with the plug they belong to:

    plug: demo
    source: 12345
    plug: demo
    source: 98765


A group defines a set of plugs, or a subset of their channels, and can be referenced by hooks to limit the scope of their actions, for example to provide features only in private or named channels.

Each group can contain channels, a list of individual channels to be included, or lists of certain categories of plugs:

  • private: all private channels from this plug

  • shared: all non-private channels from this plug

  • named: all channels from this plug declared in the channels section above

  • anywhere: all channels provided by this plug (i.e. private + shared)

You can also declare exclude alongside categories, to remove named channels individually.

    channels: [foo, bar]
    private: [demo-x, demo-y]
    named: [demo-z]


Hooks are configured in essentially the same way as plugs. A common idiom for hooks is to accept a list of group or channel names where the hook should apply:

    path: test.TestHook
    priority: 1
      groups: [secure]
      args: [123, 456]

Hooks accept an optional top-level property: priority. If specified as an integer, it defines the order in which hooks receive each new message, lowest first. This will incur a performance penalty as prioritised hooks run in serial, so only apply to those that need it – hooks without a priority will all run against each message in parallel.

Search path#

During development, or when working with a third-party module, you may want to include plugs or hooks from Python modules outside the usual search path. You can do this by setting path to a list of additional paths, which will be added to sys.path.

  - /path/to/repo/a
  - /path/to/repo/b

If you have a hook YourHook defined in /path/to/repo/a/module/, you would use module.file.YourHook as its path.


By default, only startup and shutdown messages are displayed. To see what’s going on under the hood, you can provide your own logging options, suitable for Python’s logging.config:

  version: 1
      class: logging.StreamHandler
      level: INFO
      class: logging.FileHandler
      filename: debug.log
      level: DEBUG
      level: DEBUG
      level: ERROR
    level: WARNING
    handlers: [console, file]

This example defines behaviour for both stdout (at info level) and a log file (debug level), with IMMP’s full output and warnings/errors for external modules included.