DispelEvilBot/common/repositories/templates_repo.py

129 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Репозиторий шаблонов сообщений (Блок 4.1: редактирование в админке).
Каждое изменение текста пишет запись в ``message_template_history`` — это аудит
и материал для отката. Бот подключится к этим данным в Блоке 4.5.
"""
from __future__ import annotations
from typing import Any
from common.db.pool import get_pool
async def load_texts() -> dict[str, str]:
"""Актуальные тексты включённых шаблонов (для кеша бота в ``common.texts.store``)."""
async with get_pool().connection() as conn:
cur = await conn.execute("SELECT key, text FROM bot.message_templates WHERE enabled ORDER BY key")
rows = await cur.fetchall()
return {row["key"]: row["text"] for row in rows}
async def list_templates() -> list[dict[str, Any]]:
"""Вернуть список шаблонов без текста (для таблицы в админке)."""
async with get_pool().connection() as conn:
cur = await conn.execute(
"""
SELECT key, description, enabled, updated_at, updated_by,
length(text) AS text_length
FROM bot.message_templates
ORDER BY key
"""
)
return await cur.fetchall()
async def get_template(key: str) -> dict[str, Any] | None:
"""Вернуть шаблон целиком (с текстом) или ``None``."""
async with get_pool().connection() as conn:
cur = await conn.execute(
"""
SELECT key, text, description, enabled, updated_at, updated_by
FROM bot.message_templates
WHERE key = %s
""",
(key,),
)
return await cur.fetchone()
async def create_template(
key: str,
text: str,
description: str,
*,
enabled: bool = True,
editor_id: int | None = None,
) -> bool:
"""Создать новый шаблон. False — если ключ уже занят."""
async with get_pool().connection() as conn:
cur = await conn.execute("SELECT 1 FROM bot.message_templates WHERE key = %s", (key,))
if await cur.fetchone() is not None:
return False
await conn.execute(
"""
INSERT INTO bot.message_templates (key, text, description, enabled, updated_by)
VALUES (%s, %s, %s, %s, %s)
""",
(key, text, description, enabled, editor_id),
)
await conn.execute(
"""
INSERT INTO bot.message_template_history (key, old_text, new_text, editor_id)
VALUES (%s, NULL, %s, %s)
""",
(key, text, editor_id),
)
return True
async def update_template(key: str, text: str, *, editor_id: int | None = None) -> dict[str, Any] | None:
"""Обновить текст шаблона и записать историю.
Возвращает ``None``, если шаблона нет. Старое значение читается под
``FOR UPDATE``, поэтому параллельные правки не потеряют историю.
"""
async with get_pool().connection() as conn:
cur = await conn.execute("SELECT text FROM bot.message_templates WHERE key = %s FOR UPDATE", (key,))
row = await cur.fetchone()
if row is None:
return None
old_text = row["text"]
if old_text == text:
return {"key": key, "old_text": old_text, "text": text, "changed": False}
await conn.execute(
"""
UPDATE bot.message_templates
SET text = %s, updated_at = now(), updated_by = %s
WHERE key = %s
""",
(text, editor_id, key),
)
await conn.execute(
"""
INSERT INTO bot.message_template_history (key, old_text, new_text, editor_id)
VALUES (%s, %s, %s, %s)
""",
(key, old_text, text, editor_id),
)
return {"key": key, "old_text": old_text, "text": text, "changed": True}
async def template_history(key: str, *, limit: int = 10) -> list[dict[str, Any]]:
"""Последние изменения шаблона (для блока истории в редакторе)."""
async with get_pool().connection() as conn:
cur = await conn.execute(
"""
SELECT id, old_text, new_text, editor_id, changed_at
FROM bot.message_template_history
WHERE key = %s
ORDER BY changed_at DESC
LIMIT %s
""",
(key, limit),
)
return await cur.fetchall()