Manual

This is a manual for simplematrixbotlib that includes documentation, examples, and more.

Installation

The simplematrixbotlib package can be installed from pypi or from the git repository.

Installing from PyPi

Run the following command in your terminal or cmd prompt to install simplematrixbotlib from pypi

python -m pip install simplematrixbotlib

See Encryption to lean how to install E2E encryption support.

Installing from Git Repo

Run the following command in your terminal or cmd prompt to download the repository to your machine.

git clone --branch master https://github.com/KrazyKirby99999/simple-matrix-bot-lib.git

The package is located under (current directory)/simple-matrix-bot-lib as simplematrixbotlib.

Importing

Importing simplematrixbotlib requires installation of the package first.

How to import simplematrixbotlib

Importing the simplematrixbotlib package is done with the following python code.

import simplematrixbotlib as botlib

Referring to the package as “botlib” is optional, however this is how the simplematrixbotlib will be referred to throughout this manual and the rest of the documentation.

E2E Encryption

Requirements

End-to-end encryption support requires some additional dependencies to be installed, namely the e2e extra of matrix-nio. In turn, matrix-nio[e2e] requires libolm version 3.0.0 or newer. You can install it using you distribution’s package manager or from source.

More information is available at matrix-nio.

Finally, install e2e support for matrix-nio by running:

python -m pip install matrix-nio[e2e]

If there are issues installing the e2e extra with pip from PyPI, additional packages may be required to build python-olm on your distribution, for example python3-devel on openSUSE.

Enabling

Encryption needs to be enabled in simplematrixbotlib’s Config before calling bot.run(). When the dependencies are met, it will be enabled automatically but can be turned off if required.

config = botlib.Config()
config.encryption_enabled = True
config.emoji_verify = True
config.ignore_unverified_devices = False
config.store_path = './crypto_store/'
bot = botlib.Bot(creds, config)
bot.run()

Configuration Options

See the Config class manual to learn about settings regarding encryption provided by the Config class.

Additionally, you can manage trusted and distrusted devices using nio directly using the following methods. There are 4 states:

  • default: Initially, devices are not trusted. Trying to send a message when such a device is present will cause an Exception, unless ignore_unverified_devices is enabled. This state resembles Element’s setting “Never send encrypted messages to unverified sessions from this session”.

  • ignored: Nio will ignore that this device is not verified and send encrypted messages to it regardless. This resembles the default “gray shield” used by Element.

  • verified: This is an explicitly trusted device and will receive messages. This resembles the “green shield” in Element.

  • blacklisted: This device is explicitly untrusted and will not receive encrypted messages. This resembles the “red shield” in Element.

# set a device's trust state
# verifying a blacklisted or ignored device will automatically remove the former state
bot.async_client.olm.verify_device(device)
bot.async_client.olm.ignore_device(device)
bot.async_client.olm.blacklist_device(device)

# unset a device's trust state
bot.async_client.olm.unverify_device(device)
bot.async_client.olm.unignore_device(device)
bot.async_client.olm.unblacklist_device(device)

# check a device's trust state
bot.async_client.olm.is_device_verified(device)
bot.async_client.olm.is_device_ignored(device)
bot.async_client.olm.is_device_blacklisted(device)

Verification

The library supports 2 common types of verification.

Manual “Session key” fingerprint verification

Upon startup, when encryption is enabled, simplematrixbotlib will print some information about its device similar to this:

Connected to https://client.matrix.org as @simplematrixbotlib:matrix.org (ABCDEFGHIJKL)
This bot's public fingerprint ("Session key") for one-sided verification is: 0123 4567 89ab cdef ghij klmn opqr stuv wxyz ACBD EFG
  1. Using the “Session ID” (e.g. ABCDEFGHIJKL) given in braces after the bot’s Matrix ID and the fingerprint given in the next line, we can proceed to do verify our bot from our Matrix client.

  2. In Element Web or Desktop, open the bot user’s info and click on “X session(s)” - NOT on “Verify”.

  3. The bot’s current sessions named “Bot Client using Simple-Matrix-Bot-Lib” will be listed with gray shields next to them.

  4. Click the session with the correct Session ID, then select “Manually Verify by Text”.

  5. Confirm that Session ID and Session key shown in Element match those printed by your bot, then click “Verify session”.

You have now verified your bot session one-sided from Element. This means, Element now knows that it really is your bot and be able to detect any attacks and show a red shield. However, since this is one-sided verification, your bot does not know the same about your Element session.

Interactive SAS verification using Emoji

The library is able to perform interactive to-device verification using the SAS method and Emoji. In-room verification is not supported by nio at this time, thus only single devices can be verified with each other individually. This method appears not to be supported by some clients, such as Element Android, at the time of writing.

Enable this method by the setting provided in the config class:

config.emoji_verify = True

Your bot now listens for incoming verification requests. Because this method is interactive, you need interactive access to your bot’s console! Perform the following steps on Element Web/Desktop to verify your session and the bot’s session with each other.

  1. In Element Web or Desktop, open the bot user’s info and click on “X session(s)” - NOT on “Verify”.

  2. The bot’s current sessions named “Bot Client using Simple-Matrix-Bot-Lib” will be listed with gray shields next to them.

  3. Click the session with the correct Session ID printed by your bot during startup, then select “Interactively verify by Emoji”.

  4. Compare the Emoji shown by Element and printed by your bot.

  5. Select the appropriate button and enter the appropriate letter into the console depending on whether the Emoji match.

Usage of Creds class

The Creds class is a class that handles login credentials. The source is located at simplematrixbotlib/auth.py.

Creating an instance of the Creds class

An instance can be created using the following python code.

creds = botlib.Creds(
    homeserver="https://example.org",
    username="username",
    password="password",
    session_stored_file="session.txt"
    )

or

creds = botlib.Creds(
    homeserver="https://example.org",
    username="username",
    login_token="MDA..gZ2",
    session_stored_file="session.txt"
    )

or

creds = botlib.Creds(
    homeserver="https://example.org",
    username="username",
    access_token="syt_c2...DTJ",
    session_stored_file="session.txt"
    )

The homeserver and username arguments are always required. The password argument may be replaced by either the login_token argument or the access_token argument. The login_token is used with handling SSO logins (See the Matrix Docs) and can only be used to authenticate once. The access_token is generated by logging in using a different login method.

The optional session_stored_file argument is the location of a file used by the bot to store session information such as the generated access token and device name. When a session_stored_file is present, the Api class will prefer an existing access_token over a password or login token given in the Creds class automatically.

Usage of Config class

The Config class is a class that handles whether certain features are enabled or disabled. The source is located at simplematrixbotlib/config.py

Creating an instance of the Config class

An instance can be created using the following python code.

config = botlib.Config()

Built-in Values

The following Config values are may implement validation logic. They can be interacted with as if they were public member variables:

config.join_on_invite = True
print(config.join_on_invite)

See also: Additional Methods

join_on_invite

Boolean: whether the bot accepts all invites automatically.

encryption_enabled

Boolean: whether to enable encryption. Other settings depend on the value of this setting, e.g. setting encryption to false will also set emoji_verify to false. Encryption requires additional encryption-specific dependencies to be installed.

emoji_verify

Boolean: whether the bot’s built-in emoji verification callback should be enabled. Requires encryption to be enabled. Learn more at Interactive SAS verification using Emoji.

ignore_unverified_devices

Boolean: whether to automatically ignore unverified devices in order to send encrypted messages to them without verifying. See Encryption Configuration Options to learn more about the different trust states, including ignoring. When encryption is not enabled, messages will always be sent to all devices.

store_path

String: path in the filesystem where the crypto-session gets stored. Can be relative (./store/) or absolute (/home/example). Needs to be readable and writable by the bot.

allowlist

List of strings: Regular expressions of matrix user IDs who are allowed to send commands to the bot. Defaults to allow everyone on the bot’s homeserver. If the list is non-empty, user IDs that are not on it are blocked. Thus to allow anybody, set it to []. You can check using Match.is_from_allowed_user if the sender of a command is allowed to use the bot and act accordingly. IMPORTANT: This only applies to Match.is_from_allowed_user!

blocklist

List of strings: Regular expressions of matrix user IDs who are not allowed to send commands to the bot. Defaults to empty, blocking nobody. Blocks user IDs on it if non-empty, even overriding the allowlist. For example: this way it is possible to allow all users from a homeserver, but block single ones. You can check using Match.is_from_allowed_user if the sender of a command is allowed to use the bot and act accordingly. IMPORTANT: This only applies to Match.is_from_allowed_user!

Additional methods

Configuration settings can additionally be manipulated in special ways using the following methods.

Method

Description

add_allowlist(list)

Merge this list into the list of users who are allowed to interact with the bot.

remove_allowlist(list)

Subtract this list from the list of users who are allowed to interact with the bot.

add_blocklist(list)

Merge this list into the list of users who are disallowed to interact with the bot.

remove_blocklist(list)

Subtract this list from the list of users who are disallowed to interact with the bot.

Loading and saving config values

Configuration settings can be set to values read from a file using the following python code.

config.load_toml("config.toml")

Depending on the file format, a specific method may be used for reading the file. A table of the appropriate method to use for each format is shown below.

Format

Method

TOML

load_toml(file)

Similarly, settings can be written to file after manipulating them at runtime.

config.save_toml("config.toml")

Format

Method

TOML

save_toml(file)

Example configuration files for each file format can be under the examples section of the documentation. An example of a toml config file can be found here.

Extending the Config class with custom settings

The Config class is designed to be easily extensible with any custom field you may need for your specific bot. This allows you to simply add your settings to the existing bot config file, right next to the other settings.

Extending the Config class is done by deriving your own Config class from it and adding your new values as well as functions if required.

First create your new class, called MyConfig for example, based on Config. Because Config is a dataclass, you need to add the dataclass decorator to your class as well. Then add your new custom field by adding an attribute to your class, and make sure to add a type annotation so it gets properly picked up as a dataclass field. When creating a simple attribute like that, its name may not start with an underscore _ in order to make it save and load properly.

import simplematrixbotlib as botlib
from dataclasses import dataclass


@dataclass
class MyConfig(botlib.Config):
    custom_setting: str = "My setting"

It is possible to add additional logic to your new setting by adding getter and setter methods. Most built-in settings are implemented this way similar to the example below.

Create your custom field by adding a “private” attribute to your class, i.e. its name starts with an underscore _. Then add a getter method by using the @property decorator, and a setter method using the setter decorator @name-of-your-field-without-underscore.setter. The name for each function is also the name of your field without the leading underscore. Your setting can then be accessed publicly by using the name without underscore, similar to the default Config settings. The functions for loading and saving your config file will automatically use the getter and setter methods and apply any logic in them.

If you wanted, you could add additional methods, e.g. to implement behavior like that of ``add_allowlist()` etc. <#additional-methods>`_ Take a look at the Config class source code if you are unsure how to do this.

import simplematrixbotlib as botlib
from dataclasses import dataclass


@dataclass
class MyConfig(botlib.Config):
    _my_setting: str = "Hello"

    @property
    def my_setting(self) -> str:
        return self._my_setting

    @my_setting.setter
    def my_setting(self, value: str) -> None:
        # validate(value)
        self._my_setting = value

Finally, use your custom Config class by instantiating it and passing the instance when creating your Bot instance.

config = MyConfig()
config.load_toml('config.toml')
bot = botlib.Bot(creds, config)

A complete example implementation of a custom Config class can be found here.

Usage of Bot class

The Bot class is a class that handles most of the functionality of a bot created with Simple-Matrix-Bot-Lib. The source is located at simplematrixbotlib/bot.py.

Creating an instance of the Bot class

An instance can be created using the following python code.

bot = botlib.Bot(
    creds=creds,
    config=config
    )

The creds argument is neccesary, and is an instance of the Creds class. The config argument is optional, and is an instance of the Config class.

Running the Bot

When the Bot is ready to be started, the run method can be used to run the Bot. An example is shown in the following python code.

bot.run()

Usage of Listener class

The Listener class is a class that is used to specify reactions to the events that occur in Matrix rooms. The source is located at simplematrixbotlib/listener.py

Accessing a Listener instance

An instance of the Listener class is automatically created when an instance of the Bot class is created. An example is shown in the following python code.

bot = botlib.Bot(creds)
bot.listener #Instance of the Listener class

Using the on_message_event decorator

The on_message_event method of the Listener class may be used to execute actions based on messages that are sent in rooms that the bot is a member of. Example usage of on_message_event is shown in the following python code.

@bot.listener.on_message_event
async def example(room, message):
    print(f"A message({message.content}) was sent in a room({room.room_id}).")

When any message is sent, the function will be called with room as a Room object representing each room that that the bot is a member of, and message as a RoomMessage object representing the message that was sent.

Using the on_reaction_event decorator

The on_reaction_event decorator method of the Listener class may be used to execute actions based on reactions that are sent in rooms that the bot is a member of. Example usage of on_reaction_event is shown in the following python code.

@bot.listener.on_reaction_event
async def example(room, event, reaction):
    print(f"User {event.source['sender']} reacted with {reaction} to message {event.source['content']['m.relates_to']['event_id']}")

As of the time of writing, m.reaction events are not supported via matrix-nio. To work around this, it is recommended to use the event’s source via event.source as a dictionary. An example m.reaction event source is provided for convenience below:

{
    "events": [
        {
            "content": {
                "m.relates_to": {
                    "event_id": "$FNP1EnwKRuzH38LjuYptDSkJpzomVt3tijlBy6yfc10",
                    "key": "😆",
                    "rel_type": "m.annotation"
                }
            },
            "origin_server_ts": 1641348447462,
            "sender": "@krazykirby99999:matrix.org",
            "type": "m.reaction",
            "unsigned": {
                "age": 341
            },
            "event_id": "$rGchfmQQmt2NxnlJ88HzWdVTIW-cfo-DGZFUYbqihBI"
        }
    ]
}

Using the on_custom_event decorator

The on_custom_event method of the Listener class may be used to execute actions based on any event that is sent in rooms that the bot is a member of. Example usage of on_custom_event is shown in the following python code.

import nio

@bot.listener.on_custom_event(nio.InviteMemberEvent)
async def example(room, event):
    if event.membership == "join":
        print(f"A user joined the room({room.room_id}).")
    if event.membership == "leave":
        print(f"A user left the room({room.room_id}).")

The on_custom_event method is almost identical to the on_message_event method. on_custom_event takes an argument that allows the developer to specify the event type for the Bot to respond to. Information on events can be found in the matrix-nio docs.

Using the on_startup decorator

The on_startup method of the Listener class may be used to execute actions upon the starting of the Bot. Example usage of the on_startup method is show in the following python code.

@bot.listener.on_startup
async def room_joined(room_id):
    print(f"This account is a member of a room with the id {room_id}")

When the bot is run, for each room that the Bot is a member of, the function will be called with room_id as a string that corresponds to the room_id of the room.

Usage of Match and MessageMatch classes

How to use the Match class

The Match class is a class that handles matching/filtering of the content of events. The source is located at simplematrixbotlib/match.py

Creating an instance of the Match class

An instance can be created using the following python code.

match = botlib.Match(
    room=room,
    event=event,
    bot=bot
)

The room, event, and bot arguments are neccesary. The room and event arguments should be the same as the arguments of the handler function. The bot argument should be the same as the instance of the Bot class. This class is intended to be used with non-message events, as the MessageMatch class is a child class of this class, and has message-specific methods. A list of methods for the Match class is shown below.

List of Methods:

Method

Explanation

Match.is_from_user_id(userid)

Returns True if the userid argument matches the event’s sender.

Match.is_not_from_this_bot()

Returns True if the event is not sent by this bot.

Example:

bot.listener.on_message_event
async def example(room, event):
    match = botlib.Match(room, event, bot)
    if match.is_not_from_this_bot():
        print(f"A user sent a message in room {room.room_id}")

How to use the MessageMatch class

The MessageMatch class is a class that handles matching/filtering of message events. It is a subclass of the Match class, and thus methods of the Match class can also be used with the MessageMatch class. The source is located at simplematrixbotlib/match.py

Creating an instance of the MessageMatch class

An instance can be created using the following python code.

match = botlib.MessageMatch(
    room=room,
    event=event,
    bot=bot,
    prefix="/"
)

The room, event, and bot arguments are necessary. The bot argument is an instance of the Bot class. The room and event arguments are the same as the arguments specified when creating a handler function to be used with the Listener.on_message_event method. The prefix argument is usually used as the beginning of messages that are intended to be commands, usually “!”, “/” or another short string. An example handler function that uses MessageMatch is shown in the following python code.

bot.listener.on_message_event
async def example(room, message):
    match = botlib.MessageMatch(room, message, bot, "!")
    if match.command("help") and match.prefix(): # Matches any message that begins with "!help "
        #Respond to help command

As said earlier, the prefix argument is optional. An example handler function without it is shown in the following python code.

bot.listener.on_message_event
async def example(room, message):
    match = botlib.MessageMatch(room, message, bot)
    if match.command("help"): # Matches any message that begins with "help "
        #Respond to help command

A list of methods for the Match class is shown below. Methods from the Match class can also be used with the MessageMatch class.

List of Methods:

Method

Explanation

MessageMatch.command() or MessageMatch.command(command)

The “command” is the beginning of messages that are intended to be commands, but after the prefix; e.g. “help”. Returns the command if the command argument is empty. Returns True if the command argument is equivalent to the command.

MessageMatch.prefix()

Returns True if the message begins with the prefix specified during the initialization of the instance of the MessageMatch class. Returns True if no prefix was specified during the initialization.

MessageMatch.args()

Returns a list of strings; each string is part of the message separated by a space, with the exception of the part of the message before the first space (the prefix and command). Returns an empty list if it is a single-word command.

MessageMatch.contains(string)

Returns True if the message contains the value specified in the string argument.

Usage of Api class

The Api class is a class that is used to simplify interaction with the matrix-nio library that Simple-Matrix-Bot-Lib is built upon. The source is located at simplematrixbotlib/api.py

Accessing an Api instance

An instance of the Api class is automatically created when an instance of the Bot class is created. An example is shown in the following python code.

bot = botlib.Bot(creds)
bot.api #Instance of the Api class

Using the send_text_message method

The send_text_message method of the Api class can be used to send text messages in Matrix rooms. An example is shown in the following python code.

async def example(room, message):
    match = botlib.MessageMatch(room, message, bot)
    example_message = "Hello World"
    if match.is_not_from_this_bot():
        await bot.api.send_text_message(
            room_id=room.room_id,
            message=example_message,
            msgtype="m.notice")

The first two arguments are required. The room_id argument is the id of the destination room. The message argument is the string that is to be sent as a message. The msgtype argument can be “m.text” (default) or “m.notice”.

Using the send_image_message method

The send_image_message method of the Api class can be used to send image messages in Matrix rooms. An example is shown in the following python code.

async def example(room, message):
    match = botlib.MessageMatch(room, message, bot)
    example_image="./img/example.png"
    if match.is_not_from_this_bot():
        await bot.api.send_image_message(
            room_id=room.room_id,
            image_filepath=example_image)

Both arguments are required. The room_id argument is the id of the destination room. The image_filepath argument is a string that is the path to the image file that is to be sent as a message.

Using the send_markdown_message method

The send_markdown_message method of the Api class can be used to send markdown messages in Matrix rooms. An example is shown in the following python code.

async def example(room, message):
    match = botlib.MessageMatch(room, message, bot)
    example_markdown = "# Hello World from [simplematrixbotlib](https://github.com/KrazyKirby99999/simple-matrix-bot-lib)!"
    if match.is_not_from_this_bot():
        await bot.api.send_markdown_message(
            room_id=room.room_id,
            message=example_markdown,
            msgtype="m.notice")

The first two arguments are required. The room_id argument is the id of the destination room. The message argument is the string with markdown syntax that is to be sent as a message. The msgtype argument can be “m.text” (default) or “m.notice”.