aboutsummaryrefslogtreecommitdiffstats
#  LocalWords: asyncio async plugin

* Setting up the bot
Warmachine is a no bullshit extensible IRC/Slack bot written for Python 3.5 using
asyncio.

** Install dependencies:

#+BEGIN_SRC bash
    pip install websockets
#+END_SRC

** Configure
Copy warmachine.conf.example to warmachine.conf and edit this file. You can
specify multiples of the same connection by prefixing the config section with
'slack:' or 'irc:' followed by a unique name for this connection.

** Running
Simply run the command:

#+BEGIN_SRC bash
    ./bin/warmachine -c /path/to/my/warmachine.conf
#+END_SRC

* Writing a Plugin
To write a new plugin you must inherit from
~warmachine.addons.base.WarMachinePlugin~. This class defines an interface you
must implement in order to interact with connections. If you override ~__init__~
you must call ~super()~.
** ~self._loop~
This is given to you access to the asyncio loop. This is useful for scheduling
tasks to be run later.
** ~self.config_dir~
The directory to store any configuration files and other junk in.
** ~self.log~
This is a logger that you should use when logging messages.
** ~self.recv_msg(channel, message)~
~recv_msg~ is the only required method for a plugin. It is called for every
plugin every time a connection receives a message. It takes the arguments
~connection~ and ~message~. ~connection~ is the Connection object which allows
you to interact with that chat server. ~message~ is a dictionary containing
information about the message received. It has the following format:

#+BEGIN_SRC python
{
    'sender': 'sender nickname',
    'channel': '#channel_name or None for private messages',
    'message': 'The message that was received',
}
#+END_SRC

You will parse ~message['message']~ for what you are interested in and take some
action based on what was found.

For example:

#+BEGIN_SRC python
async def recv_msg(connection, message):
    """
    A simple echo 'server'.
    """
    # if the message starts with !echo, and it was said in a channel (not a
    # private message)...
    if message['message'].startswith('!echo') and message['channel']:
        # Strip the command !echo from the message
        try:
            msg_to_repeat = ' '.join(message['message'].split(' ')[1:])
        except IndexError:
            # There was nothing to echo
            return
        # Repeat the remainder of the received message
        await connection.say(msg_to_repeat, message['channel'])
#+END_SRC
** ~self.on_connect(connection)~
This method is called when a connection successfully connects and takes the
single argument ~connection~. Use this method to do any start up initialization
you may want to do such as create configuration files that don't exist yet.
* Writing a Connection
To write a new connection protocol you must inherit from
~warmachine.connections.base.Connection~. This class defines an interface you
must implement to support the plugins. If you override ~__init__~ you must call
~super()~.
** ~__config_prefix__~
This global is used to decide which connection to use when it is found in the
config file. E.g. IRC uses ~'irc'~ and Slack uses ~'slack'~. It should be
defined somewhere near the top of your file.
** ~self.config_dir~
The directory to store any configuration files and other junk in.
** ~self.connect()~
Warmachine uses python 3's asyncio to manage multiple connections concurrently thus
you should use ~asyncio.open_connection~ to create your connection. Once you
have successfully connected you must set ~self.status~ to
~warmachine.connections.base.CONNECTED~. This indicates the connection is ready
to use.
** ~self.read()~
This method is constantly checked in a loop by the ~Bot~ class. When a message
is returned it is passed into the ~recv_msg~ method in all loaded plugins. This
return value should be formatted in the following format:

#+BEGIN_SRC python
{
    'sender': 'sender nickname',
    'channel': '#channel_name or None for private messages',
    'message': 'The message that was received',
}
#+END_SRC
** ~self.say(message, destination)~
This method is used by plugins to send a message to a channel or user.
** ~self.id~
This should return a unique id used to identify this particular connection. This
is used by plugins when saving state. As an example, the IRC connection uses
something like this:

#+BEGIN_SRC python
@property
@warmachine.utils.decorators.memoize
def id(self):
    from hashlib import md5

    value = '{}-{}'.format(self.host, self.nick)
    return md5(value.encode()).hexdigest()
#+END_SRC
** ~self.get_users_by_channel(channel)~
This method should return a list of all users (including the bot) for the
connection.