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.

Plugs

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:

plugs:
  demo:
    path: demo.DemoPlug
    config:
      api-key: xyzzy

Channels

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:

channels:
  foo:
    plug: demo
    source: 12345
  bar:
    plug: demo
    source: 98765

Groups

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

Hooks

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:

hooks:
  test:
    path: test.TestHook
    priority: 1
    config:
      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:
  - /path/to/repo1/module1
  - /path/to/repo2/module2

Logging

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:

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