Writing plugs

A Plug class allows a single external network to be represented in a standard form. Such a class provides message objects from interactions in the network.

Plugs are expected to implement the following core methods, if needed by the network:

  • Plug.start(): Make any initial connections, subscribe to events.
  • Plug.stop(): Close all connections, perform any general cleanup.
  • Plug.put(): Push new messages into the network, and return their identifiers.

They are also expected to start their own tasks to collect messages from the network, see below.

Hooks and external code may wish to interact deeper with the network – the following utility methods should also be implemented if possible:

Persistent identifiers

Each plug should provide a constant identifier Plug.network_id that uniquely defines access to the underlying network. Typically this will include a public identifier for the user account being used to connect. For disconnected instances within a single network, an identifier for the instance should also be included to avoid ID clashes.

Similarly, each channel yielded by the network needs a Channel.source, a unique string wrapper for underlying channel IDs (e.g. if your network uses integers, you’ll need to handle the boxing and unboxing of these before exposing them to the rest of the system). If a network has no concept of channels, use a constant-source channel for all messages.

Message queue

Messages enter the system from a queue on the plug instance, the input end of which is accessible via Plug.queue(). If the network of a plug is backed by an asyncio client, it may be possible to register an event handler that just calls the queue method on new messages.

For manual message pickup, for example via websockets or long-polling, you’ll need to create your own background task during Plug.start() (it must not block the startup – see asyncio.ensure_future()), and cancel it during Plug.stop().

Locks during sending

In order to ensure correctness of message processing by hooks, calls to Plug.send() must return with a message ID before the new message is yielded to the system. This is typically needed if sending or receiving requires multiple API calls (e.g. to upload/download images), as an asynchronous call to transfer an image would block one method, potentially returning to the other. An internal lock applies to Plug.put() and queue retrieval in order to achieve this.