Repo improvements

- Improved README.md
- Added examples
- Added `session_storage/` to .gitignore so that examples do not introduce leftover files after execution
This commit is contained in:
2026-09-12 20:16:39 +03:00
parent 1fe4434e16
commit fa97e4b098
5 changed files with 216 additions and 49 deletions

1
.gitignore vendored
View File

@@ -1,4 +1,5 @@
__pycache__/ __pycache__/
session_storage/
*.vscode *.vscode
.venv/ .venv/
dist/ dist/

View File

@@ -1,67 +1,61 @@
# mab # 🤖 mab
**mab** *(MAtrix Bot)* is a **very** simple Python package that can be used to **mab** *(MAtrix Bot)* is a **very** simple Python package that can be used to
develop **very** simple Matrix bots. I have decided to make something like this develop **very** simple Matrix bots. It does not aim to be the best library out
because I wasn't satisfied by simplicity and usage of other libraries. So there, but it aims to be convenient and usable for relatively serious projects.
this library does not aim to be "the best matrix bot library", it only aims to
be good enough for me.
## Features ## ✨ Features
The package supports the following features: The library supports the following features:
- **Completely `asyncio` based**
- **Filter-based callback system** - **Filter-based callback system**
- **Images sending** - **Sending images**
- **Videos sending with automatic thumbnail generation (requires `ffmpeg`)** - **Sending videos with automatic thumbnail generation (requires `ffmpeg`)**
## Installation ## 🚀 Usage
Use `pip` to install this package: Use `apt` to install required system packages and `pip` to install the package.
You may need to use `root` privileges to use `apt`. It's highly recommended you
use `venv` or another Python virtual environment. Here are the commands to
install the latest version of the library:
```bash ```bash
apt install libmagic1-dev libolm-dev
python -m pip install git+https://git.tyukalov.su/nikita/mab@v0.4.0 python -m pip install git+https://git.tyukalov.su/nikita/mab@v0.4.0
``` ```
You should specify package version you want to use, because `main` without tags `libmagic1-dev` is needed for automatic file MIME type detection, `libolm-dev`
contains unstable code. is needed for E2EE to work.
## Basic usage Please inspect [`examples/shell_bot.py`](examples/shell_bot.py),
[`examples/echo_bot.py`](examples/echo_bot.py) or open [`examples/`](examples/)
directory to find usage examples. Examples require that you set
`MATRIX_HOMESERVER` and `MATRIX_USERNAME` environment variables. Examples create
`session_storage` directory in working directory.
This is the most simple bot you can create. It would respond to any message ## 🏷️ Versioning
that contains `hello` and `hi` words.
```python Releases are tagged in this repository using the `vX.Y.Z` format. If the commit
import asyncio is not tagged, it must be treated as versionless and should not be used for your
from pathlib import Path application.
from mab import MatrixBot, MatrixBotConfig - `X` **(Major)**: Breaking architectiral changes or complete rewrites. Existing
from mab import TextContainsFilter, SenderIsBotFilter code will break. Note that `0.Y.Z` versions are considered **very unstable**,
from mab.types import RoomEventData the API may change at any time and some features do not work as expected.
- `Y` **(Minor)**: Breaking API changes, feature removals, or behavioral
modifications. Existing code will likely break.
- `Z` **(Patch)**: Backward-compatible feature additions, bug fixes, or internal
changes. Existing code will not break.
async def on_message(data: RoomEventData) -> None: ## 🛠️ Development
text = f"Your message contains {len(data.event.body)} symbols"
await data.bot.send_text(data.room, text)
async def main() -> None: Here's the list of commands you should execute to get started with development
# create and start the bot (including cloning the repository and installing required packages). Please note
cfg = MatrixBotConfig( that your workflow may use something other than `venv`.
matrix_homeserver_url="matrix.domain.su", ```bash
matrix_username_localpart="nagibator666", apt install libmagic1-dev libolm-dev
storage_directory=Path("storage_nagibator666") git clone https://git.tyukalov.su/nikita/mab
) cd mab
bot = MatrixBot(matrix_bot_config) python3 -m venv .venv
bot.add_callback( . .venv/bin/activate
~SenderIsBotFilter() & TextContainsFilter(["hello", "hi"]), pip install -e .
on_message
)
await bot.start()
# wait for Ctrl+C
try:
while True:
await asyncio.sleep(1)
except:
pass
# stop the bot
await bot.stop()
if __name__ == "__main__":
asyncio.run(main())
``` ```

18
examples/_environment.py Normal file
View File

@@ -0,0 +1,18 @@
import os
import sys
def check_environment() -> None:
"""
This function checks if required environment variables are set. It prints
problem resolution guide and exits using `sys.exit(1)` on problem.
"""
if "MATRIX_HOMESERVER" not in os.environ:
print("Please set `MATRIX_HOMESERVER` environment variable!")
print("P.S. use something like this in your shell:")
print(" export MATRIX_HOMESERVER=\"https://matrix.server.net\"")
sys.exit(1)
if "MATRIX_USERNAME" not in os.environ:
print("Please set `MATRIX_USERNAME` environment variable!")
print("P.S. use something like this in your shell:")
print(" export MATRIX_USERNAME=\"megakiller228\"")
sys.exit(1)

51
examples/echo_bot.py Normal file
View File

@@ -0,0 +1,51 @@
"""
This example implements Matrix bot that echoes all text messages it receives.
It uses environment variables to specify authorization data. Use Ctrl+C to stop
the bot.
"""
import asyncio
import os
import logging
from mab import (
MatrixBot,
MatrixBotConfig,
RoomEventData,
BodyExistsFilter,
MessageTypeFilter,
SenderIsBotFilter,
MessageType
)
from _environment import check_environment
async def on_text_message(data: RoomEventData) -> None:
"""This callback is called when a text message arrives."""
await data.bot.send_text(data.room, data.event.body) # type: ignore
async def main() -> None:
"""Application entry point"""
logging.basicConfig(level=logging.INFO)
logging.getLogger("nio").setLevel(logging.CRITICAL+1)
check_environment()
config = MatrixBotConfig(
matrix_homeserver_url=os.environ["MATRIX_HOMESERVER"],
matrix_username_localpart=os.environ["MATRIX_USERNAME"],
storage_directory="session_storage"
)
bot = MatrixBot(config)
bot.add_callback(
~SenderIsBotFilter() & BodyExistsFilter() & MessageTypeFilter(MessageType.TEXT),
on_text_message)
# run until Ctrl+C
try:
await bot.run()
except asyncio.CancelledError:
pass
if __name__ == "__main__":
asyncio.run(main())

103
examples/image_gen_bot.py Normal file
View File

@@ -0,0 +1,103 @@
"""
This example implements Matrix bot that generates a pixelized noise image with
specified maximum R, G and B values.
It uses environment variables to specify authorization data. Use Ctrl+C to stop
the bot.
"""
import asyncio
from io import BytesIO
import os
import logging
import random
from PIL import Image
from mab import (
MatrixBot,
MatrixBotConfig,
RoomEventData,
MessageTypeFilter,
BodyCommandFilter,
SenderIsBotFilter,
MessageType
)
from _environment import check_environment
async def on_gen_command(data: RoomEventData) -> None:
"""This callback is called when `!gen R G B` command is received."""
# convert R, G and B to floats
try:
r, g, b = [float(v) for v in data.event.command_args] # type: ignore
except:
await data.bot.send_text(data.room, "Invalid arguments")
return
await data.bot.send_text(data.room, "Generating the noise...")
# create the basic noise
img = Image.new("RGB", (16, 16))
for x in range(img.width):
for y in range(img.height):
col = (random.random() * r, random.random() * g, random.random() * b)
img.putpixel(
(x, y),
tuple(int(c * 255) for c in col)
)
# pixelized upscale
img = img.resize((2048, 2048), resample=Image.Resampling.NEAREST)
# save to buffer
buf = BytesIO()
img.save(buf, format="PNG")
buf.seek(0)
buf = buf.read()
# send
await data.bot.send_image_bytes(data.room, buf, "noise.png")
async def on_wrong_message(data: RoomEventData) -> None:
"""This callback is called when a wrong message is received."""
await data.bot.send_text(
data.room,
"Text me something like <code>!gen 0.1 0.7 1.0</code>"
)
async def main() -> None:
"""Application entry point"""
logging.basicConfig(level=logging.INFO)
logging.getLogger("nio").setLevel(logging.CRITICAL+1)
check_environment()
config = MatrixBotConfig(
matrix_homeserver_url=os.environ["MATRIX_HOMESERVER"],
matrix_username_localpart=os.environ["MATRIX_USERNAME"],
storage_directory="session_storage"
)
bot = MatrixBot(config)
command_filter = MessageTypeFilter(MessageType.TEXT) & BodyCommandFilter(
verbs=["gen"],
min_args=3,
max_args=3
)
# callback for message that
# 1. are sent not by this bot
# 2. do match the command filter
bot.add_callback(
~SenderIsBotFilter() & command_filter,
on_gen_command)
# callback for message that
# 1. are sent not by this bot
# 2. do NOT match the command filter
bot.add_callback(
~SenderIsBotFilter() & ~command_filter,
on_wrong_message
)
# run until Ctrl+C
try:
await bot.run()
except asyncio.CancelledError:
pass
if __name__ == "__main__":
asyncio.run(main())