PHP XMPP bot for SemanticScuttle

I'm storing bookmarks in a SemanticScuttle instance on my home server, to which I only have access via VPN. Since I don't want to run my bookmarking instance on a public server, bookmarking interesting things at work involved sending emails to myself and storing them later at home. ​

So my dream for years was to have a bot in my Jabber roster that I could paste URLs, and it would automatically bookmark them in SemanticScuttle. ​

Six years ago I even started to fix up the xmpphp library with the goal to some day write a bot with it. But it was never stable enough for me, and lacked MUC support. I abandoned it when that became obvious. ​

Fast forward to now, and the problem and its accompanying pain was still there. Unfortunately the XMPP client daemon idea never took foot on Linux (although the Pidgin developers had a ticket for it and even had a GSoC project), so I had to look for for XMPP bot implementations. ​

errbot​

The most promising software I found was errbot. It's written in Python, has many commits, is developed actively and has nice plugin writing documentation . ​

I had looked for a bot which also provides an init.d script, but errbot unfortunately does not provide one . (This isn't a problem, more about that later.) ​

errbot is still Python and not PHP, and I did not want to write my SemanticScuttle bot in Python. My plan was to write an errbot plugin that executes a program for every message sent to the bot, and sends its output back to the user. ​

Exec plugin​

errbot has a plugin template that you can use to kickstart your own plugin. Simply run errbot --new-plugin and you're done. ​

For development, errbot has a local test mode that does not connect to any server, which means quick bootup times. ​

While writing the plugin I learned about os.exec* and os.popen, and that I should rather use the subprocess module for starting programs in python. ​

By default, errbot plugins are configured through chat commands . I did not like this approach and implemented a way to configure the executable directly in errbot's config file. ​

In the end, the plugin consists of two files with 88 lines of code and does what I want: Execute the new bookmark-bot.php script in SemanticScuttle's scripts/ directory. ​

I can bookmark URLs via chat: ​

Screenshot ​

errbot configuration​

I'm running errbot on my home server, which I had to update from Debian 7 to Debian 8 because errbot failed with Debian 7's python 2.7.3, Debian's python 2.7.9 works fine though. ​

After SemanticScuttle was update to the latest git version, errbot itself gets installed via pip: ​

$ apt-get install python-pip
$ pip install errbot

Then I cloned the exec plugin into /usr/local/src: ​

$ cd /usr/local/src
$ git clone git://git.cweiske.de/errbot-exec.git

After that, errbot needs to be configured. Since configuration files are stored in /etc/, I created an errbot dir and put the config file in there: ​

$ mkdir /etc/errbot
$ emacs /etc/errbot/bookmarks.py

The configuration file is as minimal as possible: ​

import logging
BACKEND = 'XMPP'  # defaults to XMPP
BOT_DATA_DIR = '/var/lib/errbot/bookmarks'
 
BOT_EXTRA_PLUGIN_DIR = '/usr/local/src/errbot-exec/'
BOT_LOG_FILE = '/var/log/errbot-bookmarks.log'
 
CORE_PLUGINS = ('ACLs')
 
# DEBUG, INFO, WARN, ERROR.
BOT_LOG_LEVEL = logging.ERROR
BOT_ASYNC = True
 
BOT_IDENTITY = {
    # XMPP (Jabber) mode
    'username': 'dojo@cweiske.de/bookmarks',
    'password': 'yesiwilltellyouthis',
}
 
BOT_ADMINS = ('gbin@localhost','cweiske@cweiske.de')
ACCESS_CONTROLS_DEFAULT = {
    'allowusers': ('gbin@localhost','cweiske@cweiske.de'),
}
 
EXEC = {
    'command': '/usr/local/src/semanticscuttle/scripts/bookmark-bot.php'
}
 
# unfortunately required settings
BOT_LOG_SENTRY = False
SENTRY_DSN = ''
SENTRY_LOGLEVEL = BOT_LOG_LEVEL
 
CHATROOM_PRESENCE = ()
CHATROOM_FN = 'Err'
 
BOT_PREFIX = '!'
DIVERT_TO_PRIVATE = ()
CHATROOM_RELAY = {}
REVERSE_CHATROOM_RELAY = {}

Data and log directory need of course be created: ​

$ mkdir /var/lib/errbot/bookmark
$ touch /var/log/errbot-bookmarks.log

errbot shall run with as less privileges as possible, so I created an own user for it: ​

$ useradd --no-create-home --no-user-group -g nogroup -s /bin/false errbot
$ chown -R errbot /var/lib/errbot/
$ chown errbot /var/log/errbot-bookmarks.log

daemon / autostart​

Now I could already start it, but I wanted to run it whenever the server boots up. Debian 8 switched from SysV to systemd, and writing init scripts for it turned out to be easy. ​

Create /etc/systemd/system/errbot-bookmarks.service with the following contents: ​

[Unit]
Description=Jabber bot for bookmarking
After=network.target
 
[Service]
Type=forking
User=errbot
ExecStart=/usr/local/bin/errbot --daemon --config=/etc/errbot/bookmarks.py
ExecStop=/bin/kill -SIGINT $MAINPID
Restart=on-failure
 
[Install]
WantedBy=multi-user.target

Run man systemd.service to see the full list of allowed directives. ​

Do not use a symlink! systemd behaves strange if the service script is not a real file. ​

Now we make it start at boot and start it ourselves: ​

$ systemctl daemon-reload
$ systemctl enable errbot-bookmarks
$ systemctl start errbot-bookmarks
$ systemctl status errbot-bookmarks
● errbot-bookmarks.service - Jabber bot for bookmarking
   Loaded: loaded (/etc/systemd/system/errbot-bookmarks.service; enabled)
   Active: active (running) since Fr 2016-05-13 15:21:10 CEST; 4 days ago
 Main PID: 30673 (errbot)
   CGroup: /system.slice/errbot-bookmarks.service
           └─30673 /usr/bin/python /usr/local/bin/errbot --daemon --config=/etc/errbot/bookmarks.py

2016-05-13 15:21:10,210 INFO   errbot.err   Config check passed...
2016-05-13 15:21:10,210 INFO   errbot.err   Selected backend 'XMPP'.
2016-05-13 15:21:10,211 INFO   errbot.err   Checking for '/var/lib/errbot/bookmarks'...
2016-05-13 15:21:10,211 INFO   errbot.err   Daemonizing
Starting daemon.​

Voila, the bot is in action! ​

Written by Christian Weiske.

Comments? Please send an e-mail.