diff --git a/README.md b/README.md new file mode 100644 index 0000000..5d778e9 --- /dev/null +++ b/README.md @@ -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`. \ No newline at end of file