2 Commits
0.1 ... 0.3

Author SHA1 Message Date
4a947cec3d Updated mab version; added filters support 2026-09-02 18:34:56 +03:00
b28b7c583d Updated mab version; improved README 2026-09-02 14:51:27 +03:00
3 changed files with 62 additions and 75 deletions

View File

@@ -6,24 +6,24 @@
## Для запуска рекомендуется `docker` ## Для запуска рекомендуется `docker`
1. Соберите образ: 1. [Скачайте образ](https://git.tyukalov.su/nikita/-/packages/container/2026-matrix-csonac) (измените версию на нужную вам):
```bash ```bash
sudo docker build -t csonac . sudo docker pull git.tyukalov.su/nikita/2026-matrix-csonac:0.2
``` ```
2. Выполните первый запуск в интерактивном режиме 2. Выполните первый запуск в интерактивном режиме
```bash ```bash
sudo mkdir runtime sudo mkdir runtime
sudo chown 1000:1000 runtime sudo chown 1000:1000 runtime
sudo docker run -v ./runtime:/runtime -ti csonac:latest sudo docker run -v ./runtime:/runtime -ti git.tyukalov.su/nikita/2026-matrix-csonac:0.2
``` ```
3. В папке `runtime` появится файл `config.json`. Внесите туда нужные настройки 3. В папке `runtime` появится файл `config.json`. Внесите туда нужные настройки
4. Запустите ещё раз в интерактивном режиме, чтобы авторизоваться 4. Запустите ещё раз в интерактивном режиме, чтобы авторизоваться
```bash ```bash
sudo docker run -v ./runtime:/runtime -ti csonac:latest sudo docker run -v ./runtime:/runtime -ti git.tyukalov.su/nikita/2026-matrix-csonac:0.2
``` ```
5. Дальше можно запускать контейнер в фоне 5. Дальше можно запускать контейнер в фоне
```bash ```bash
sudo docker run -p 127.0.0.1:4980:4980 -v ./runtime:/runtime --detach csonac:latest sudo docker run -p 127.0.0.1:4980:4980 -v ./runtime:/runtime --detach git.tyukalov.su/nikita/2026-matrix-csonac:0.2
``` ```
> Рекомендуется запускать контейнер при помощи `docker compose`. > Рекомендуется запускать контейнер при помощи `docker compose`.

View File

@@ -4,7 +4,7 @@ import html
import time import time
import traceback import traceback
from mab import MatrixBot from mab import MatrixBot, TextCommandFilter
from nio import MatrixRoom, RoomMessageText from nio import MatrixRoom, RoomMessageText
import util import util
@@ -17,6 +17,18 @@ from datatypes import *
_bot: MatrixBot = None # type: ignore _bot: MatrixBot = None # type: ignore
_db: Database = None # type: ignore _db: Database = None # type: ignore
_COMMANDS = {
"help": "Получить справку",
"info": "Получить сведения о комнате",
"tokens": "Получить список токенов",
"auth": "Создать новый токен",
"deauth": "Удалить существующий токен",
"name": "Задать (или удалить) имя для токена",
"ban": "Забанить IP адрес",
"unban": "Разбанить IP адрес",
"bans": "Получить список забаненных IP адресов",
}
def _generate_help_message() -> str: def _generate_help_message() -> str:
"""Generates help message in HTML markup""" """Generates help message in HTML markup"""
result = "<strong><i>Как использовать</i></strong><br><ol>" result = "<strong><i>Как использовать</i></strong><br><ol>"
@@ -27,48 +39,18 @@ def _generate_help_message() -> str:
result += "</ol>" result += "</ol>"
result += "<br><br><strong><i>Команды</i></strong>" result += "<br><br><strong><i>Команды</i></strong>"
for aliases in _COMMANDS: for key in _COMMANDS:
result += f"<br><code>!{aliases[0]}</code> - <i>{html.escape(_COMMANDS[aliases][1])}</i>" result += f"<br><code>!{key}</code> - <i>{html.escape(_COMMANDS[key])}</i>"
return result return result
#
# GENERIC EVENT HANDLERS
#
async def _on_text(room: MatrixRoom, event: RoomMessageText) -> None:
"""This callback is called when text message is received"""
if event.sender == _bot.get_client().user_id:
return
text = event.body
parts = [p for p in text.split() if p.strip()]
if len(parts) < 1:
return
cmd = parts[0].lower()
if not cmd.startswith("!"):
return
cb = None
for aliases in _COMMANDS:
for alias in aliases:
if alias == cmd.lstrip("!"):
cb = _COMMANDS[aliases][0]
break
if cb:
break
if cb is None:
await _bot.send_text_to_room(room.room_id, "<strong>Используйте <code>!help</code></strong>")
return
try:
await cb(room, parts[1:])
except:
traceback.print_exc()
# #
# COMMAND HANDLERS # COMMAND HANDLERS
# #
async def _on_cmd_help(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_help(room: MatrixRoom, event: RoomMessageText) -> None:
"""!help""" """!help"""
await _bot.send_text_to_room(room.room_id, _generate_help_message(), is_html=True) await _bot.send_text_to_room(room.room_id, _generate_help_message(), is_html=True)
async def _on_cmd_info(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_info(room: MatrixRoom, event: RoomMessageText) -> None:
"""!info""" """!info"""
# get room info (or add it) # get room info (or add it)
room_info = await _db.room_get(room.room_id) room_info = await _db.room_get(room.room_id)
@@ -87,7 +69,7 @@ async def _on_cmd_info(room: MatrixRoom, args: list[str]) -> None:
response += f"<strong>Канал:</strong> <code>{html.escape(room_info.code)}</code>" response += f"<strong>Канал:</strong> <code>{html.escape(room_info.code)}</code>"
await _bot.send_text_to_room(room.room_id, response, is_html=True) await _bot.send_text_to_room(room.room_id, response, is_html=True)
async def _on_cmd_tokens(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_tokens(room: MatrixRoom, event: RoomMessageText) -> None:
"""!info""" """!info"""
# get all tokens and check if there are none # get all tokens and check if there are none
tokens = await _db.token_get_all() tokens = await _db.token_get_all()
@@ -112,13 +94,13 @@ async def _on_cmd_tokens(room: MatrixRoom, args: list[str]) -> None:
# respond # respond
await _bot.send_text_to_room(room.room_id, response, is_html=True) await _bot.send_text_to_room(room.room_id, response, is_html=True)
async def _on_cmd_auth(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_auth(room: MatrixRoom, event: RoomMessageText) -> None:
"""!auth""" """!auth"""
# create new token # create new token
token = await _db.token_create() token = await _db.token_create()
# set the name if it is provided # set the name if it is provided
if args: if event.command_args:
token.name = " ".join(args) token.name = " ".join(event.command_args)
await _db.token_set_name(token.code, token.name) await _db.token_set_name(token.code, token.name)
# create the response # create the response
response = "<strong>Создан новый токен</strong>" response = "<strong>Создан новый токен</strong>"
@@ -131,13 +113,13 @@ async def _on_cmd_auth(room: MatrixRoom, args: list[str]) -> None:
# respond # respond
await _bot.send_text_to_room(room.room_id, response, is_html=True) await _bot.send_text_to_room(room.room_id, response, is_html=True)
async def _on_cmd_deauth(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_deauth(room: MatrixRoom, event: RoomMessageText) -> None:
"""!deauth""" """!deauth"""
# no token provided # no token provided
if len(args) != 1: if len(event.command_args) != 1:
await _bot.send_text_to_room(room.room_id, "<strong>Укажите токен, который надо удалить (должен быть ровно один аргумент)</strong>", is_html=True) await _bot.send_text_to_room(room.room_id, "<strong>Укажите токен, который надо удалить (должен быть ровно один аргумент)</strong>", is_html=True)
return return
token = args[0] token = event.command_args[0]
# check if token does not exist # check if token does not exist
if await _db.token_get(token) is None: if await _db.token_get(token) is None:
await _bot.send_text_to_room(room.room_id, "<strong>Токен не найден</strong>", is_html=True) await _bot.send_text_to_room(room.room_id, "<strong>Токен не найден</strong>", is_html=True)
@@ -148,15 +130,15 @@ async def _on_cmd_deauth(room: MatrixRoom, args: list[str]) -> None:
response = f"<strong>Удалён токен <code>{token}</code></strong>" response = f"<strong>Удалён токен <code>{token}</code></strong>"
await _bot.send_text_to_room(room.room_id, response, is_html=True) await _bot.send_text_to_room(room.room_id, response, is_html=True)
async def _on_cmd_name(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_name(room: MatrixRoom, event: RoomMessageText) -> None:
"""!name""" """!name"""
# check arguments # check arguments
if len(args) < 1: if len(event.command_args) < 1:
error = "<strong>Требуется указать как минимум токен. Если хотите убрать имя, то имя указывать не надо. Если имя нужно назначить или изменить, то после токена укажите новое имя.</strong>" error = "<strong>Требуется указать как минимум токен. Если хотите убрать имя, то имя указывать не надо. Если имя нужно назначить или изменить, то после токена укажите новое имя.</strong>"
await _bot.send_text_to_room(room.room_id, error, is_html=True) await _bot.send_text_to_room(room.room_id, error, is_html=True)
return return
token = args[0] token = event.command_args[0]
new_name = " ".join(args[1:]) new_name = " ".join(event.command_args[1:])
if not new_name.strip(): if not new_name.strip():
new_name = None new_name = None
# check if token exists # check if token exists
@@ -173,18 +155,18 @@ async def _on_cmd_name(room: MatrixRoom, args: list[str]) -> None:
# respond # respond
await _bot.send_text_to_room(room.room_id, response, is_html=True) await _bot.send_text_to_room(room.room_id, response, is_html=True)
async def _on_cmd_ban(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_ban(room: MatrixRoom, event: RoomMessageText) -> None:
"""!ban""" """!ban"""
# check arguments # check arguments
if len(args) < 2: if len(event.command_args) < 2:
error = "<strong>Формат: <code>!ban &lt;IP&gt; &lt;SECONDS&gt; [REASON]</code></strong>" error = "<strong>Формат: <code>!ban &lt;IP&gt; &lt;SECONDS&gt; [REASON]</code></strong>"
await _bot.send_text_to_room(room.room_id, error, is_html=True) await _bot.send_text_to_room(room.room_id, error, is_html=True)
return return
# get args # get event.command_args
try: try:
ip = args[0] ip = event.command_args[0]
duration = float(args[1]) duration = float(event.command_args[1])
reason = " ".join(args[2:]) if args[2:] else "Manual ban" reason = " ".join(event.command_args[2:]) if event.command_args[2:] else "Manual ban"
except: except:
error = "<strong>Возникла ошибка. Наверняка неправильно указаны секунды.</strong>" error = "<strong>Возникла ошибка. Наверняка неправильно указаны секунды.</strong>"
await _bot.send_text_to_room(room.room_id, error, is_html=True) await _bot.send_text_to_room(room.room_id, error, is_html=True)
@@ -196,21 +178,21 @@ async def _on_cmd_ban(room: MatrixRoom, args: list[str]) -> None:
except: except:
await _bot.send_text_to_room(room.room_id, "<strong>Возникла ошибка</strong>", is_html=True) await _bot.send_text_to_room(room.room_id, "<strong>Возникла ошибка</strong>", is_html=True)
async def _on_cmd_unban(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_unban(room: MatrixRoom, event: RoomMessageText) -> None:
"""!unban""" """!unban"""
# check arguments # check arguments
if len(args) != 1: if len(event.command_args) != 1:
error = "<strong>Формат: <code>!ban &lt;IP&gt;</code></strong>" error = "<strong>Формат: <code>!ban &lt;IP&gt;</code></strong>"
await _bot.send_text_to_room(room.room_id, error, is_html=True) await _bot.send_text_to_room(room.room_id, error, is_html=True)
return return
# unban # unban
try: try:
await _db.ban_delete(args[0]) await _db.ban_delete(event.command_args[0])
await _bot.send_text_to_room(room.room_id, "<strong>IP адрес разблокирован (если он был заблокирован)</strong>", is_html=True) await _bot.send_text_to_room(room.room_id, "<strong>IP адрес разблокирован (если он был заблокирован)</strong>", is_html=True)
except: except:
await _bot.send_text_to_room(room.room_id, "<strong>Возникла ошибка</strong>", is_html=True) await _bot.send_text_to_room(room.room_id, "<strong>Возникла ошибка</strong>", is_html=True)
async def _on_cmd_bans(room: MatrixRoom, args: list[str]) -> None: async def _on_cmd_bans(room: MatrixRoom, event: RoomMessageText) -> None:
"""!bans""" """!bans"""
# list # list
bans = _db.ban_get_all() bans = _db.ban_get_all()
@@ -228,18 +210,6 @@ async def _on_cmd_bans(room: MatrixRoom, args: list[str]) -> None:
await _bot.send_text_to_room(room.room_id, result, is_html=True) await _bot.send_text_to_room(room.room_id, result, is_html=True)
_COMMANDS = {
("help", "h", "?"): (_on_cmd_help, "Получить справку"),
("info", "room", "i", "r"): (_on_cmd_info, "Получить сведения о комнате"),
("tokens", "t"): (_on_cmd_tokens, "Получить список токенов"),
("auth", "create", "a", "c"): (_on_cmd_auth, "Создать новый токен"),
("deauth", "delete", "d"): (_on_cmd_deauth, "Удалить существующий токен"),
("name", "n"): (_on_cmd_name, "Задать (или удалить) имя для токена"),
("ban", "b"): (_on_cmd_ban, "Забанить IP адрес"),
("unban", "u"): (_on_cmd_unban, "Разбанить IP адрес"),
("bans", "l"): (_on_cmd_bans, "Получить список забаненных IP адресов"),
}
# #
# PUBLIC # PUBLIC
# #
@@ -248,4 +218,21 @@ def setup(bot: MatrixBot, db: Database) -> None:
global _bot, _db global _bot, _db
_db = db _db = db
_bot = bot _bot = bot
_bot.add_event_callback(_on_text, RoomMessageText) # type: ignore _bot.add_event_callback(
_on_cmd_help, TextCommandFilter(["help", "h", "?"]))
_bot.add_event_callback(
_on_cmd_info, TextCommandFilter(["info", "room", "i", "r"]))
_bot.add_event_callback(
_on_cmd_tokens, TextCommandFilter(["tokens", "t"]))
_bot.add_event_callback(
_on_cmd_auth, TextCommandFilter(["auth", "create", "a", "c"]))
_bot.add_event_callback(
_on_cmd_deauth, TextCommandFilter(["deauth", "delete", "d"]))
_bot.add_event_callback(
_on_cmd_name, TextCommandFilter(["name", "n"]))
_bot.add_event_callback(
_on_cmd_ban, TextCommandFilter(["ban", "b"]))
_bot.add_event_callback(
_on_cmd_unban, TextCommandFilter(["unban", "u"]))
_bot.add_event_callback(
_on_cmd_bans, TextCommandFilter(["bans", "l"]))

View File

@@ -34,7 +34,7 @@ idna==3.19
Jinja2==3.1.6 Jinja2==3.1.6
jsonschema==4.26.0 jsonschema==4.26.0
jsonschema-specifications==2025.9.1 jsonschema-specifications==2025.9.1
mab @ git+https://git.tyukalov.su/nikita/mab@c3046307c7bc4e65113aab62b07bad2a148b49e5 mab @ git+https://git.tyukalov.su/nikita/mab@v0.1.0
markdown-it-py==4.2.0 markdown-it-py==4.2.0
MarkupSafe==3.0.3 MarkupSafe==3.0.3
matrix-nio==0.26.0 matrix-nio==0.26.0