=head1 NAME

App::Wubot::Guide::MultipleBots - guide for running multiple communicating bots

=head1 DESCRIPTION

This document describes how to set up bots on multiple servers and
forward data between them.

=head2 overview

wubot is designed to be able to run on multiple hosts, and to be able
to send messages between bots.

It is not necessary to run every component of wubot (monitor, reactor,
webui) on every host.  In most multi-host configurations, it's likely
that the reactor and/or the web ui may only run on a single host.
Every host being monitored would run a wubot-monitor process, and all
messages would be forwarded to one or more hosts for processing.
Complex networks can be created by forwarding messages through a
series of reactors.

It is also possible to run a local wubot-reactor and to have the
wubot-reactor forward some or all of the messages on to other hosts
for further processing.

It is expected that a user would not want to run more than one
monitor, reactor, or webui on a given host.  If you want to run more
than one wubot on a single host, either run each as a different user,
or else run them in virtual hosts.

To give an example, I have wubot-monitor running on several servers at
home, and they all forward their messages to an old server running the
reactor and webui.  But I also run a wubot-monitor and wubot-reactor
on my laptop, and sometimes my laptop goes off of the network.  When I
connect back up to the XMPP server, I get any notifications that
occurred when I was offline forwarded to me by the wubot server.  Also
any monitoring data such as load and idle time get forward back to the
wubot server.  I have duplicated a few rule trees between the hosts so
that I can get to my load and idle graphs locally even while I'm
offline, but then I can also see those stats from another workstation
on the wubot server.

=head2 XMPP

The mechanism for sending messages between wubot instances on separate
hosts is XMPP.  This is not provided with wubot, nor is any
information about how to set up a server.  I can highly recommend
ejabberd.

You will definitely want to use an XMPP server that supports offline
message queueing.  That way, the server will queue messages for any
wubot instance that is offline if other wubots are trying to send it
messages.  Without offline messaging, messages that are sent while a
target wubot instance is down will be lost.

If a host goes offline and is unable to connect to the XMPP server, it
will queue up messages until the connection can been re-established.
When the connection comes back up, the host will transmit all messages
in the queue in order.  Reactor plugins use the 'lastupdate' time on
the message to determine when the message was originally generated.



=head2 wubot-monitor

XMPP messages is implemented as a plugin for wubot-monitor, not as
part of the reactor.  So every host that wishes to participate in
inter-host messaging must run a wubot-monitor process with the XMPP
monitor enabled.

XMPP messaging is implemented as a queue, and works exactly like the
message queue for the reactor (i.e. it is a App::Wubot::LocalMessageQueue).
So if you aren't running a wubot-reactor process on a host, you could
just point the XMPP monitor at the default reactor queue, and it would
forward the reactor messages to a remote host for processing.

The monitor config could live here:

  ~/wubot/config/plugins/XMPP/myhost.yaml

Here is an example config for a host that is not running the reactor:

  ---
  account: wubot-hostname@myserver
  password: mypassword
  host: 192.168.1.2
  port: 5222
  directory: /home/wu/wubot/reactor
  user: wubot-reactor@hostname/myserver
  delay: 5s

The 'account' and 'password' fields should contain the information
used to log into the XMPP server.  I use a separate account for each
host, and the hostname is included in that host's account.  That way I
know which host each message is coming from by simply looking at the
sender's username.

The 'host' and 'port' are obviously the host on which the XMPP server
is running.  If you forward the XMPP traffic over ssh, then 'host'
would be 'localhost', and 'port' would be the local port you are
forwarding.

The 'directory' is the path to the wubot queue.  By default, the
wubot-monitor process saves its data to ~/wubot/reactor, so if you are
not running a wubot-reactor process on the host, point the XMPP
monitor there.  For any host running the wubot-reactor process, then
don't point them at the same place, or they will both by trying to
pick up messages from the same queue.

=head2 wubot-reactor

The reactor cannot send a message over XMPP directly as there is no
XMPP plugin for the reactor.  Instead, the reactor can save messages
to a queue where the wubot-monitor process will pick them up and
forward them.

The reactor can save a message to the queue using the MessageQueue
plugin.  Here is an example reactor rule that can be used to store all
messages in a separate queue to be forwarded:

  - Name: forward
    plugin: MessageQueue
    config:
      directory: /home/wu/wubot/forward

Then configure the 'directory' param in the XMPP monitor to point to
that same directory.


=head2 Avoiding infinite loops

When deciding which hosts forward messages to other hosts, care must
be taken not to create an infinite routing loop.  For example, if host
A is set up to forward its messages to host B and host B is set up to
forward its messages back to host A, then every message generated
would get continually routed back and forth between the two hosts.
This is obvious when there are two hosts, but may be less obvious when
you have multiple routes involving several hosts.  For example, hosts
A, B, C, and D forward to host E, and host E forwards to host F.  Then
some day you realize that you would like to forward some of the
messages from host F to host A and then BLAM, you have created a
routing loop.

To help prevent this from happening, the XMPP monitor sets a field
named 'noforward' any time it sends or receives a message.  When
picking up a message from the queue, if the message contains a
'noforward' field, it will not be forwarded.  To improve performance,
use a condition to prevent the messages from being added to the queue
if it contains the 'noforward' flag, e.g.:

  - name: forward
    condition: noforward is false
    plugin: MessageQueue
    config:
      directory: /home/wu/wubot/forward

This allows you to point two bots at each other without creating a
loop.  If you want for a bot to forward messages it received from
other bots, simply add a rule to delete the 'noforward' field before
your MessageQueue rule is reached.  I usually make my MessageQueue
forward rule be the very last rule in my list.


=head2 managing config files

It may be a pain to manage your config files on each host separately.
I prefer to manage all my wubot config files on my laptop.  You can
keep the wubot config files for multiple hosts in the same directory
tree by using the short hostname on the end of the file.  For example,
the following config file would be loaded on a host named
'somehostname', but will be ignored by all other hosts.

  ~/wubot/config/plugins/RSS/perl.org.yaml.somehostname

If the filename does not end in a hostname, that config file will get
loaded on all hosts.

A simple rsync of the config tree out to each of the remote hosts that
run wubot will do the trick.

Config files may contain usernames, passwords, or other private
information.  So you may want to go a step further and ensure that you
only push out config files for each host that are relevant to that
host.  An rsync command such as this will make that easy, just replace
{remotehost} with the remote hostname:

  rsync -rav --delete --exclude='*.git' --include="*.{remotehost}" --exclude='*.yaml.*' --exclude='*.yaml' --delete-excluded ~/wubot/config/ {remotehost}:wubot/config/

You will also probably want to sync the sql schema files which live in
~/wubot/schemas.

