2021-06-12 20:47:12 +00:00
|
|
|
# mira
|
|
|
|
|
|
|
|
mira is a command-based bot framework for XMPP. It allows you to create
|
|
|
|
bots that react to your users' commands or notify them based on a
|
|
|
|
subscribtion system.
|
|
|
|
|
|
|
|
## Configuration
|
|
|
|
|
|
|
|
The main bot configuration is based on a TOML file:
|
|
|
|
|
|
|
|
```toml
|
|
|
|
# The JID to authenticate as
|
|
|
|
jid = "awesome-bot@example.org"
|
|
|
|
# The password
|
|
|
|
password = "s3cr3t_p4ssw0rd"
|
|
|
|
# Where the persistent data should be stored.
|
|
|
|
# Defaults to /etc/mira/storage.json
|
|
|
|
storage_path = "/home/mira/storage.json"
|
|
|
|
# Loads the file as an avatar and publishes it (OPTIONAL)
|
|
|
|
avatar = "/etc/mira/avatar.png"
|
|
|
|
|
|
|
|
# A list of modules that should be loaded
|
|
|
|
[[modules]]
|
|
|
|
# Load the "test" module that greets a user to writes
|
|
|
|
# "test" to awesome-bot@example.org
|
|
|
|
name = "mira.modules.test"
|
|
|
|
greeting = "Hello, Mr. %%user%%!"
|
|
|
|
|
|
|
|
[[modules]]
|
|
|
|
# This module reacts only to users whose JID has the
|
|
|
|
# same domain as the bot
|
|
|
|
name = "mira.modules.other_test"
|
|
|
|
restrict_local = true
|
|
|
|
```
|
|
|
|
|
|
|
|
By default, mira expects the config at `/etc/mira/config.toml`, but the path
|
|
|
|
can be overwritten by passing `--config`.
|
|
|
|
|
|
|
|
## Writing a module
|
|
|
|
|
|
|
|
Writing a module is pretty easy. The following is the base template:
|
|
|
|
|
|
|
|
```python
|
|
|
|
from mira.module import BaseModule
|
|
|
|
|
|
|
|
NAME = 'new'
|
|
|
|
|
|
|
|
class NewModule(BaseModule):
|
|
|
|
__instance = None
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_instance(base, **kwargs):
|
|
|
|
if NewModule.__instance == None:
|
|
|
|
NewModule(base, **kwargs)
|
|
|
|
|
|
|
|
return NewModule.__instance
|
|
|
|
|
|
|
|
def __init__(self, base, **kwargs):
|
|
|
|
if NewModule.__instance != None:
|
|
|
|
raise Exception('Trying to init singleton twice')
|
|
|
|
|
|
|
|
super().__init__(base, **kwargs)
|
|
|
|
NewModule.__instance = self
|
|
|
|
|
|
|
|
async def on_command(self, cmd, msg):
|
|
|
|
# ...
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_instance(base, **kwargs):
|
|
|
|
return TestModule.get_instance(base, **kwargs)
|
|
|
|
```
|
|
|
|
|
|
|
|
Currently, modules are implemented as singletons, so most of the code
|
|
|
|
is just for turning the class into a singleton. What's required is the
|
|
|
|
toplevel constant `NAME`! It is the name of the module as the same time
|
|
|
|
the "command". mira will pass all messages that start with the value of
|
|
|
|
`NAME` to the `on_command` function. It's parameters are the message body
|
2021-06-12 22:30:49 +00:00
|
|
|
whitespace separated (`cmd`) and the original `aioxmpp` message object (`msg`).
|
|
|
|
|
|
|
|
If you want to add configuration options to your bot, you can use the module's
|
|
|
|
`get_option(key, default=None)` function, which works exactly like `dict.get(...)`.
|
2021-06-12 20:47:12 +00:00
|
|
|
|
|
|
|
If you have subcommands, like "new something1" and "new something2", then you can
|
|
|
|
use a subcommand table. It is just a dictionary that tells mira which function
|
|
|
|
to call on which subcommand. In the example above, it looks like this:
|
|
|
|
|
|
|
|
```python
|
|
|
|
from mira.module import BaseModule
|
|
|
|
|
|
|
|
NAME = 'new'
|
|
|
|
|
|
|
|
class NewModule(BaseModule):
|
|
|
|
__instance = None
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_instance(base, **kwargs):
|
|
|
|
if NewModule.__instance == None:
|
|
|
|
NewModule(base, **kwargs)
|
|
|
|
|
|
|
|
return NewModule.__instance
|
|
|
|
|
|
|
|
def __init__(self, base, **kwargs):
|
|
|
|
if NewModule.__instance != None:
|
|
|
|
raise Exception('Trying to init singleton twice')
|
|
|
|
|
|
|
|
super().__init__(base, **kwargs)
|
|
|
|
NewModule.__instance = self
|
|
|
|
|
|
|
|
self._subcommand_table = {
|
|
|
|
'something1': self._something1,
|
|
|
|
'something2': self._something2,
|
|
|
|
'*': self._help
|
|
|
|
}
|
|
|
|
|
|
|
|
async def _something1(self, cmd, msg):
|
|
|
|
# ...
|
|
|
|
async def _something2(self, cmd, msg):
|
|
|
|
# ...
|
|
|
|
async def _help(self, cmd, msg):
|
|
|
|
# ...
|
|
|
|
|
|
|
|
def get_instance(base, **kwargs):
|
|
|
|
return TestModule.get_instance(base, **kwargs)
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that '*' tells mira that the function should be called
|
|
|
|
everytime no other subcommand matches.
|
|
|
|
|
|
|
|
To send a message, the simplest way is to use the module's
|
|
|
|
`send_message(to, body)` function, which takes an `aioxmpp`
|
|
|
|
JID as to and a string as body.
|
|
|
|
|
|
|
|
### Storage
|
|
|
|
|
|
|
|
If your module needs to store data, then you can use the
|
|
|
|
`StorageManager`. It is available via the module's
|
|
|
|
`_stm` property. It provides the following two functions:
|
|
|
|
|
|
|
|
- `get_data(section)`: Retrieve data (dictionary) stored in the module's section. A module may have multiple sections
|
|
|
|
- `set_data(section, data)`: Stores data (dictionary) in the module's section. A module may have multiple sections
|
|
|
|
|
|
|
|
### Subscriptions
|
|
|
|
|
|
|
|
If your users should be able to subscribe to certain events, then you can use the
|
|
|
|
`SubscriptionManager`, available via the module's `_sum` property. It exposes a lot more
|
|
|
|
functions, so it is probably the better to just look at it's code at `mira/subscription.py`.
|
|
|
|
|
2021-06-12 22:30:49 +00:00
|
|
|
### Examples
|
|
|
|
|
|
|
|
If the test module `mira.modules.test` is not advanced enough, then you can find more examples here:
|
|
|
|
|
|
|
|
- [JANINE](https://git.polynom.me/PapaTutuWawa/JANINE)
|
|
|
|
- [My personal modules](https://git.polynom.me/PapaTutuWawa/mira-modules)
|
|
|
|
|
2021-06-12 20:47:12 +00:00
|
|
|
## License
|
|
|
|
|
|
|
|
See `LICENSE`.
|