docs: Add a readme
This commit is contained in:
parent
5e5d3083f3
commit
6bfa10a4e2
147
README.md
Normal file
147
README.md
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
# 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
|
||||||
|
whitespace separated (`cmd`) and the original `aioxmpp` message object (`msg`)
|
||||||
|
|
||||||
|
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`.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
See `LICENSE`.
|
Loading…
Reference in New Issue
Block a user