2021-01-10 13:10:39 +00:00
|
|
|
# xbotlib
|
|
|
|
|
2021-01-10 18:23:21 +00:00
|
|
|
[![PyPI version](https://badge.fury.io/py/xbotlib.svg)](https://badge.fury.io/py/xbotlib)
|
2021-01-10 18:24:20 +00:00
|
|
|
[![Build Status](https://drone.autonomic.zone/api/badges/decentral1se/xbotlib/status.svg?ref=refs/heads/main)](https://drone.autonomic.zone/decentral1se/xbotlib)
|
2021-01-10 18:23:21 +00:00
|
|
|
|
2021-01-10 13:10:39 +00:00
|
|
|
## XMPP bots for humans
|
|
|
|
|
2021-01-10 18:17:10 +00:00
|
|
|
> status: experimental
|
2021-01-10 15:54:55 +00:00
|
|
|
|
2021-01-10 18:17:10 +00:00
|
|
|
A friendly lightweight wrapper around
|
|
|
|
[slixmpp](https://slixmpp.readthedocs.io/) for writing XMPP bots in Python. The
|
2021-01-10 18:35:39 +00:00
|
|
|
goal is to make writing and running XMPP bots easy and fun. `xbotlib` is a
|
|
|
|
[single file implementation](./xbotlib.py) which can easily be understood and
|
|
|
|
extended. It provides a small API surface which reflects the `slixmpp` way of
|
2021-01-16 21:26:22 +00:00
|
|
|
doing things. The `xbotlib` source code and ideas are largely
|
2021-01-15 17:21:41 +00:00
|
|
|
borrowed/stolen/adapted/reimagined from the XMPP bot experiments that have gone
|
2021-01-15 17:22:24 +00:00
|
|
|
on and are still going on in
|
|
|
|
[Varia](https://git.vvvvvvaria.org/explore/repos?tab=&sort=recentupdate&q=bots).
|
2021-01-15 17:21:41 +00:00
|
|
|
|
2021-01-16 21:26:22 +00:00
|
|
|
- [Install](#install)
|
|
|
|
- [Example](#example)
|
|
|
|
- [API Reference](#api-reference)
|
2021-01-17 17:26:42 +00:00
|
|
|
- [Bot.direct(message)](#bot-direct-message)
|
|
|
|
- [Bot.group(message)](#bot-group-message)
|
|
|
|
- [Bot.serve(request)](#bot-serve-request)
|
2021-01-16 21:32:02 +00:00
|
|
|
- [SimpleMessage](#simplemessage)
|
2021-01-19 07:24:51 +00:00
|
|
|
- [Bot](#bot)
|
2021-01-16 21:30:25 +00:00
|
|
|
- [Working with your bot](#working-with-your-bot)
|
2021-01-16 21:32:02 +00:00
|
|
|
- [Documentation](#documentation)
|
|
|
|
- [Commands](#commands)
|
|
|
|
- [Avatars](#avatars)
|
|
|
|
- [Configuration](#configuration)
|
|
|
|
- [Using the `.conf` configuration file](#using-the--conf--configuration-file)
|
|
|
|
- [Using the command-line interface](#using-the-command-line-interface)
|
|
|
|
- [Using the environment](#using-the-environment)
|
|
|
|
- [Persistent storage](#persistent-storage)
|
2021-01-17 19:20:56 +00:00
|
|
|
- [File system](#file-system)
|
2021-01-16 21:32:02 +00:00
|
|
|
- [Redis key/value storage](#redis-key-value-storage)
|
|
|
|
- [Loading Plugins](#loading-plugins)
|
2021-01-17 13:38:37 +00:00
|
|
|
- [Serving HTTP](#serving-http)
|
2021-01-16 21:26:22 +00:00
|
|
|
- [Deploy your bots](#deploy-your-bots)
|
|
|
|
- [Roadmap](#roadmap)
|
|
|
|
- [Changes](#changes)
|
2021-01-16 21:30:25 +00:00
|
|
|
- [License](#license)
|
2021-01-16 21:26:22 +00:00
|
|
|
|
2021-01-10 13:10:39 +00:00
|
|
|
## Install
|
|
|
|
|
|
|
|
```sh
|
|
|
|
$ pip install xbotlib
|
|
|
|
```
|
|
|
|
|
|
|
|
## Example
|
|
|
|
|
2021-01-17 13:42:45 +00:00
|
|
|
Put the following in a `echo.py` file. This bot is pretty simple: it echoes
|
|
|
|
back whatever message you send it. It is an easy way to get started.
|
2021-01-13 13:08:44 +00:00
|
|
|
|
|
|
|
```python
|
2021-01-16 21:26:22 +00:00
|
|
|
from xbotlib import Bot
|
|
|
|
|
2021-01-13 13:08:44 +00:00
|
|
|
class EchoBot(Bot):
|
2021-01-16 21:26:22 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
def direct(self, message):
|
2021-01-16 16:39:05 +00:00
|
|
|
self.reply(message.text, to=message.sender)
|
2021-01-16 21:26:22 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
def group(self, message):
|
2021-01-16 16:39:05 +00:00
|
|
|
self.reply(message.content, room=message.room)
|
2021-01-13 13:08:44 +00:00
|
|
|
```
|
|
|
|
|
2021-01-17 13:42:45 +00:00
|
|
|
And then `python echo.py`. You will be asked a few questions in order to load
|
|
|
|
the account details that your bot will be using. This will generate an
|
|
|
|
`echobot.conf` file in the same working directory for further use. See the
|
|
|
|
[configuration](#configure-your-bot) section for more.
|
2021-01-10 14:09:42 +00:00
|
|
|
|
2021-01-17 13:42:45 +00:00
|
|
|
Read more in the [API reference](#api-reference) for how to write your own bots.
|
2021-01-10 15:51:20 +00:00
|
|
|
|
2021-01-17 14:28:44 +00:00
|
|
|
See more examples on [git.vvvvvvaria.org](https://git.vvvvvvaria.org/explore/repos?q=xbotlib&topic=1).
|
|
|
|
|
2021-01-10 15:51:20 +00:00
|
|
|
## API Reference
|
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
When writing your own bot, you always sub-class the `Bot` class provided from
|
|
|
|
`xbotlib`. Then if you want to respond to a direct message, you write a
|
|
|
|
[direct](#botdirectmessage) function. If you want to respond to a group chat
|
2021-01-16 21:26:22 +00:00
|
|
|
message, you write a [group](#botgroupmessage) function. That's it for the
|
|
|
|
basics.
|
2021-01-10 15:51:20 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
### Bot.direct(message)
|
2021-01-10 16:00:38 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
Respond to direct messages.
|
2021-01-10 16:00:38 +00:00
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
- **message**: received message (see [SimpleMessage](#simplemessage) below for available attributes)
|
2021-01-10 16:00:38 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
### Bot.group(message)
|
2021-01-10 15:51:20 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
Respond to a message in a group chat.
|
2021-01-10 15:51:20 +00:00
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
- **message**: received message (see [SimpleMessage](#simplemessage) below for available attributes)
|
2021-01-10 15:51:20 +00:00
|
|
|
|
2021-01-17 17:25:34 +00:00
|
|
|
### Bot.serve(request)
|
2021-01-17 13:38:37 +00:00
|
|
|
|
|
|
|
Serve requests via the built-in web server.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
- **request**: the web request
|
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
### SimpleMessage
|
2021-01-10 16:00:38 +00:00
|
|
|
|
2021-01-13 20:56:45 +00:00
|
|
|
A simple message interface.
|
2021-01-10 16:00:38 +00:00
|
|
|
|
|
|
|
Attributes:
|
|
|
|
|
2021-01-16 16:39:18 +00:00
|
|
|
- **text**: the entire text of the message
|
|
|
|
- **content**: the text of the message after the nick
|
2021-01-13 13:51:23 +00:00
|
|
|
- **sender**: the user the message came from
|
|
|
|
- **room**: the room the message came from
|
2021-01-13 13:30:46 +00:00
|
|
|
- **receiver**: the receiver of the message
|
2021-01-16 16:39:18 +00:00
|
|
|
- **nick**: the nickname of the sender
|
|
|
|
- **type**: the type of message
|
2021-01-17 19:49:57 +00:00
|
|
|
- **url**: The URL of a sent file
|
2021-01-10 16:00:38 +00:00
|
|
|
|
2021-01-19 07:24:51 +00:00
|
|
|
### Bot
|
|
|
|
|
|
|
|
> Bot.reply(message, to=None, room=None)
|
|
|
|
|
|
|
|
Send a reply back.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
- **message**: the message that is sent
|
|
|
|
- **to**: the user to send the reply to
|
|
|
|
- **room**: the room to send the reply to
|
|
|
|
|
2021-01-19 07:29:56 +00:00
|
|
|
> Bot.respond(response, content_type="text/html")
|
|
|
|
|
|
|
|
Return a response via the web server.
|
|
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
- **response**: the text of the response
|
|
|
|
- **content_type**: the type of response
|
|
|
|
|
2021-01-19 07:24:51 +00:00
|
|
|
Other useful attributes on the `Bot` class are:
|
|
|
|
|
|
|
|
- **self.db**: The [Redis database](#redis-key-value-storage) if you're using it
|
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
## Working with your bot
|
|
|
|
|
|
|
|
### Documentation
|
2021-01-14 22:45:23 +00:00
|
|
|
|
|
|
|
Add a `help = "my help"` to your `Bot` class like so.
|
|
|
|
|
|
|
|
```python
|
|
|
|
class MyBot(Bot):
|
|
|
|
help = "My help"
|
|
|
|
```
|
|
|
|
|
2021-01-15 10:27:50 +00:00
|
|
|
See more in the [commands](#commands) section on how to use this.
|
2021-01-14 22:45:23 +00:00
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
### Commands
|
2021-01-15 10:27:50 +00:00
|
|
|
|
2021-01-16 21:26:22 +00:00
|
|
|
Using `@<command>` in direct messages and `<nick>, @<command>` (the `,` is
|
|
|
|
optional, anything will be accepted here and there doesn't seem to be a
|
|
|
|
consensus on what is most common way to "at" another user in XMPP) in group chats,
|
2021-01-15 10:27:50 +00:00
|
|
|
here are the supported commands.
|
|
|
|
|
2021-01-16 21:27:25 +00:00
|
|
|
- `@uptime`: how long the bot has been running
|
|
|
|
- `@help`: the help text for what the bot does
|
2021-01-14 22:45:23 +00:00
|
|
|
|
2021-01-15 17:24:07 +00:00
|
|
|
There are also more general status commands which all bots respond to.
|
|
|
|
|
2021-01-16 21:27:25 +00:00
|
|
|
- `@bots`: status check on who is a bot in the group chat
|
2021-01-15 17:24:07 +00:00
|
|
|
|
2021-01-18 19:48:01 +00:00
|
|
|
These commands will be detected in any part of the message sent to the bot. So
|
|
|
|
you can write `echobot, can we see your @uptime`, or `I'd love to know which @bots are here.`
|
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
### Avatars
|
2021-01-15 10:08:07 +00:00
|
|
|
|
2021-01-15 10:27:50 +00:00
|
|
|
By default, `xbotlib` will look for an `avatar.png` (so far tested with `.png`
|
|
|
|
but other file types may work) file alongside your Python script which contains
|
|
|
|
your bot implementation. You can also specify another path using the `--avatar`
|
|
|
|
option on the command-line interface. The images should ideally have a height
|
|
|
|
of `64` and a width of `64` pixels each.
|
2021-01-15 10:08:07 +00:00
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
## Configuration
|
2021-01-12 20:39:50 +00:00
|
|
|
|
2021-01-16 21:26:22 +00:00
|
|
|
All the ways you can pass configuration details to your bot. There are three
|
|
|
|
ways to configure your bot, the configuration file, command-line interface and
|
|
|
|
the environment. Use whichever one suits you best. The values are loaded in the
|
|
|
|
following order: command-line > configuration file > environment.
|
2021-01-14 21:26:13 +00:00
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
#### Using the `.conf` configuration file
|
2021-01-12 20:39:50 +00:00
|
|
|
|
2021-01-14 21:26:13 +00:00
|
|
|
If you run simply run your Python script which contains the bot then `xbotlib`
|
|
|
|
will generate a configuration for you by asking a few questions. This is the
|
|
|
|
simplest way to run your bot locally.
|
|
|
|
|
2021-01-16 21:08:59 +00:00
|
|
|
Here is an example of a working configuration.
|
|
|
|
|
|
|
|
```conf
|
|
|
|
[echobot]
|
|
|
|
account = echobot@vvvvvvaria.org
|
|
|
|
password = ...thepassword...
|
|
|
|
nick = echobot
|
|
|
|
rooms = test@muc.example.com
|
|
|
|
```
|
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
#### Using the command-line interface
|
2021-01-14 21:26:13 +00:00
|
|
|
|
|
|
|
Every bot accepts a number of comand-line arguments to load configuration. You
|
|
|
|
can use the `--help` option to see what is available (e.g. `python bot.py --help`).
|
|
|
|
|
2021-01-16 21:26:22 +00:00
|
|
|
```
|
|
|
|
usage: bot.py [-h] [-d] [-a ACCOUNT] [-p PASSWORD] [-n NICK]
|
|
|
|
[-av AVATAR] [-ru REDIS_URL] [-r ROOMS [ROOMS ...]]
|
|
|
|
[--no-auto-join]
|
|
|
|
|
|
|
|
XMPP bots for humans
|
|
|
|
|
|
|
|
optional arguments:
|
|
|
|
-h, --help show this help message and exit
|
|
|
|
-d, --debug Enable verbose debug logs
|
|
|
|
-a ACCOUNT, --account ACCOUNT
|
|
|
|
Account for the bot account
|
|
|
|
-p PASSWORD, --password PASSWORD
|
|
|
|
Password for the bot account
|
|
|
|
-n NICK, --nick NICK Nickname for the bot account
|
|
|
|
-av AVATAR, --avatar AVATAR
|
|
|
|
Avatar for the bot account
|
|
|
|
-ru REDIS_URL, --redis-url REDIS_URL
|
|
|
|
Redis storage connection URL
|
|
|
|
-r ROOMS [ROOMS ...], --rooms ROOMS [ROOMS ...]
|
|
|
|
Rooms to automatically join
|
|
|
|
--no-auto-join Disable automatically joining rooms when invited
|
2021-01-17 14:09:34 +00:00
|
|
|
-pt PORT, --port PORT
|
|
|
|
The port to serve from
|
2021-01-17 19:18:04 +00:00
|
|
|
-t TEMPLATE, --template TEMPLATE
|
|
|
|
The template to render
|
2021-01-16 21:26:22 +00:00
|
|
|
```
|
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
#### Using the environment
|
2021-01-12 20:39:50 +00:00
|
|
|
|
2021-01-14 21:26:13 +00:00
|
|
|
`xbotlib` will try to read the following configuration values from the
|
|
|
|
environment if it cannot read them from a configuration file or the
|
|
|
|
command-line interface. This can be useful when doing remote server
|
|
|
|
deployments.
|
2021-01-12 20:39:50 +00:00
|
|
|
|
2021-01-14 21:26:13 +00:00
|
|
|
- **XBOT_ACCOUNT**: The bot account
|
|
|
|
- **XBOT_PASSWORD**: The bot password
|
|
|
|
- **XBOT_NICK**: The bot nickname
|
2021-01-16 18:59:09 +00:00
|
|
|
- **XBOT_AVATAR**: The bot avatar icon
|
2021-01-16 19:21:22 +00:00
|
|
|
- **XBOT_REDIS_URL**: Redis key store connection URL
|
2021-01-16 20:16:22 +00:00
|
|
|
- **XBOT_ROOMS**: The rooms to automatically join
|
2021-01-16 20:46:22 +00:00
|
|
|
- **XBOT_NO_AUTO_JOIN**: Disable auto-joining on invite
|
2021-01-17 14:09:34 +00:00
|
|
|
- **XBOT_PORT**: The port to serve from
|
2021-01-12 20:39:50 +00:00
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
### Persistent storage
|
2021-01-15 20:13:17 +00:00
|
|
|
|
2021-01-17 19:20:56 +00:00
|
|
|
#### File system
|
|
|
|
|
|
|
|
Just use your local file system as you would in any other Python script. Please
|
|
|
|
note that when you deploy your bot, you might not have access to this local
|
|
|
|
filesystem in the same location. For remote server deployments
|
|
|
|
[Redis](#redis-key-value-storage) can be more convenient.
|
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
#### Redis key/value storage
|
2021-01-15 20:13:17 +00:00
|
|
|
|
|
|
|
`xbotlib` supports using [Redis](https://redis.io/) as a storage back-end. It
|
2021-01-16 19:21:22 +00:00
|
|
|
is simple to work with because the interface is exactly like a dictionary. You
|
2021-01-16 21:26:22 +00:00
|
|
|
can quickly run Redis locally using [Docker](https://docs.docker.com/engine/install/debian/)
|
|
|
|
(`docker run --network=host --name redis -d redis`) or if you're on a Debian system you can
|
|
|
|
also `sudo apt install -y redis`.
|
|
|
|
|
|
|
|
You can configure the connection URL using the command-line interface,
|
|
|
|
configuration or environment. Here is an example using the environment.
|
2021-01-15 20:13:17 +00:00
|
|
|
|
|
|
|
```bash
|
2021-01-16 19:21:22 +00:00
|
|
|
$ export XBOT_REDIS_URL=redis://localhost:6379/0
|
2021-01-15 20:13:17 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
And you access the interface via the `self.db` attribute.
|
|
|
|
|
|
|
|
```python
|
|
|
|
def direct(self, message):
|
2021-01-16 19:21:22 +00:00
|
|
|
self.db["mykey"] = message.text
|
2021-01-15 20:13:17 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
You should see `INFO Successfully connected to storage` when your bot
|
2021-01-18 19:54:33 +00:00
|
|
|
initialises. Please see the
|
|
|
|
[redis-py](https://redis-py.readthedocs.io/en/stable/) API documentation for
|
|
|
|
more.
|
2021-01-15 20:13:17 +00:00
|
|
|
|
2021-01-16 21:30:25 +00:00
|
|
|
### Loading Plugins
|
2021-01-16 19:37:25 +00:00
|
|
|
|
|
|
|
You can specify a `plugins = [...]` on your bot definition and they will be
|
|
|
|
automatically loaded.
|
|
|
|
|
|
|
|
```python
|
|
|
|
class MyBot(Bot):
|
|
|
|
plugins = ["xep_0066"]
|
|
|
|
```
|
|
|
|
|
2021-01-17 13:38:37 +00:00
|
|
|
### Serving HTTP
|
|
|
|
|
|
|
|
Your bot will automatically be running a web server at port `8080` when it is
|
|
|
|
run. If you're running your bot locally, just visit
|
|
|
|
[0.0.0.0:8080](http://0.0.0.0:8080) to see. The default response is just some
|
|
|
|
placeholder text. You can write your own responses using the
|
2021-01-17 19:18:04 +00:00
|
|
|
[Bot.serve](#bot-serve-request) function.
|
2021-01-17 13:38:37 +00:00
|
|
|
|
|
|
|
`xbotlib` provides a small wrapper API for
|
|
|
|
[Jinja2](https://jinja.palletsprojects.com/en/2.11.x/) which allows you to
|
|
|
|
easily template and generate HTML. The web server is provided by
|
|
|
|
[aiohttp](https://docs.aiohttp.org/).
|
|
|
|
|
2021-01-17 19:18:04 +00:00
|
|
|
The default template search path is `index.html.j2` in the current working
|
|
|
|
directory. This can be configured through the usual configuration entrypoints.
|
|
|
|
|
2021-01-17 19:23:01 +00:00
|
|
|
Here's a small example that renders a random ASCII letter.
|
|
|
|
|
|
|
|
```jinja
|
|
|
|
<h1>{{ letter }}</h1>
|
|
|
|
```
|
2021-01-17 19:18:04 +00:00
|
|
|
|
2021-01-17 13:38:37 +00:00
|
|
|
```python
|
2021-01-17 19:23:01 +00:00
|
|
|
from string import ascii_letters
|
2021-01-17 13:38:37 +00:00
|
|
|
|
|
|
|
def serve(self, request):
|
2021-01-17 19:23:01 +00:00
|
|
|
letter = choice(ascii_letters)
|
|
|
|
rendered = self.template.render(letter=letter)
|
2021-01-19 07:29:56 +00:00
|
|
|
return self.respond(body=rendered)
|
2021-01-17 13:38:37 +00:00
|
|
|
```
|
|
|
|
|
|
|
|
If you want to pass data from your `direct`/`group` functions to the `serve`
|
|
|
|
function, you'll need to make use of [some type of persistent
|
|
|
|
storage](#persistent-storage). Your `serve` function can read from the database
|
|
|
|
or file system and then respond with generated HTML from there.
|
|
|
|
|
|
|
|
Having your bot avaible on the web is useful for doing healthchecks with
|
|
|
|
something like [statping](https://statping.com/) so you be sure that your bot
|
|
|
|
is up and running.
|
|
|
|
|
2021-01-16 21:08:59 +00:00
|
|
|
## Deploy your bots
|
|
|
|
|
|
|
|
See [bots.varia.zone](https://bots.varia.zone/).
|
|
|
|
|
2021-01-10 15:51:20 +00:00
|
|
|
## Roadmap
|
|
|
|
|
2021-01-10 18:38:19 +00:00
|
|
|
See the [issue tracker](https://git.autonomic.zone/decentral1se/xbotlib/issues).
|
2021-01-10 15:51:20 +00:00
|
|
|
|
|
|
|
## Changes
|
|
|
|
|
|
|
|
See the [CHANGELOG.md](./CHANGELOG.md).
|
|
|
|
|
|
|
|
## License
|
|
|
|
|
|
|
|
See the [LICENSE](./LICENSE.md).
|