# 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`.