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)
    channels: [foo, bar]
    private: [demo-x, demo-y]
    named: [demo-y]


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/repo1/module1
  - /path/to/repo2/module2


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
      (): __main__.LocalFilter
      format: "[%(asctime)s %(levelname)-5.5s %(name)s] %(message)s"
      datefmt: "%d/%m/%Y %H:%M:%S"
      class: logging.StreamHandler
      filters: [local]
      formatter: simple
    level: DEBUG
    handlers: [console]

This will output IMMP’s debug log messages (by filtering with __main__.LocalFilter, which captures __main__ and immp namespaces) to stdout.