141 lines
4.9 KiB
Python
141 lines
4.9 KiB
Python
"""
|
||
Управление постами в канале.
|
||
|
||
Использует интерфейс Notifier — не привязан к Telegram или любому другому
|
||
мессенджеру. Инициализируется один раз при старте бота через set_notifier().
|
||
|
||
Чтобы сменить канал: вызови set_notifier() с другим адаптером.
|
||
Чтобы отправлять в несколько каналов: используй MultiNotifier (создай адаптер).
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
import threading
|
||
|
||
import config
|
||
import database
|
||
from bot.keyboards import build_task_keyboard
|
||
from bot.messages import build_full_text
|
||
from bot.notifier import Notifier
|
||
|
||
log = logging.getLogger(__name__)
|
||
|
||
_notifier: Notifier | None = None
|
||
|
||
|
||
def set_notifier(notifier: Notifier) -> None:
|
||
"""Регистрирует активный адаптер уведомлений. Вызывается при старте бота."""
|
||
global _notifier
|
||
_notifier = notifier
|
||
|
||
|
||
def get_notifier() -> Notifier:
|
||
if _notifier is None:
|
||
raise RuntimeError("Notifier not initialized. Call set_notifier() first.")
|
||
return _notifier
|
||
|
||
|
||
# ── Публичные операции с постами ──────────────────────────────────────────────
|
||
|
||
def recreate_post(
|
||
task_id: int,
|
||
task_number: str,
|
||
base_text: str,
|
||
group_id: int = 0,
|
||
closed: bool = False,
|
||
new_event_lines: list[str] | None = None,
|
||
):
|
||
"""
|
||
Удаляет старый пост задачи и создаёт новый, накапливая события.
|
||
|
||
Изменения (• ...) дедуплицируются по тексту.
|
||
Комментарии (💬 ...) и описания (📝 ...) добавляются всегда.
|
||
"""
|
||
notifier = get_notifier()
|
||
|
||
stored = database.get_stored_events(task_id)
|
||
stored_set = set(stored)
|
||
|
||
if new_event_lines:
|
||
added = []
|
||
for line in new_event_lines:
|
||
if not line:
|
||
continue
|
||
is_change = line.startswith("•")
|
||
if is_change and line in stored_set:
|
||
continue
|
||
added.append(line)
|
||
stored_set.add(line)
|
||
stored = stored + added
|
||
else:
|
||
added = []
|
||
|
||
responsible = database.get_responsible(task_id)
|
||
full_text = build_full_text(base_text, responsible, stored, new_count=len(added))
|
||
keyboard = build_task_keyboard({
|
||
"task_number": task_number,
|
||
"group_id": group_id,
|
||
"closed": closed,
|
||
})
|
||
|
||
old = database.get_message_data(task_id)
|
||
if old:
|
||
threading.Thread(
|
||
target=notifier.delete,
|
||
args=(old["message_id"],),
|
||
daemon=True,
|
||
name=f"delete-{old['message_id']}",
|
||
).start()
|
||
|
||
try:
|
||
message_id = notifier.send(full_text, keyboard)
|
||
database.save_message_id(task_id, message_id, task_number, group_id, base_text)
|
||
if added:
|
||
database.save_stored_events(task_id, stored)
|
||
except Exception as e:
|
||
log.error(f"recreate_post failed for task {task_id}: {e}")
|
||
|
||
|
||
def refresh_responsible(task_id: int, task_number: str):
|
||
"""Редактирует текст поста — обновляет блок «В работе»."""
|
||
notifier = get_notifier()
|
||
data = database.get_message_data(task_id)
|
||
if not data or not data.get("base_text"):
|
||
return
|
||
|
||
responsible = database.get_responsible(task_id)
|
||
events_lines = database.get_stored_events(task_id)
|
||
full_text = build_full_text(data["base_text"], responsible, events_lines, new_count=0)
|
||
keyboard = build_task_keyboard({
|
||
"task_number": task_number,
|
||
"group_id": data.get("group_id") or 0,
|
||
})
|
||
|
||
try:
|
||
notifier.edit_text(data["message_id"], full_text, keyboard)
|
||
except Exception as e:
|
||
if not notifier.is_stale_error(e):
|
||
log.warning(f"refresh_responsible failed: {e}")
|
||
|
||
|
||
def send_gif_for_to(task_id: int):
|
||
"""
|
||
Отправляет GIF для заявок с ТО.
|
||
Предварительно удаляет предыдущую гифку если была.
|
||
Если GIF_URL пустой — ничего не делает.
|
||
"""
|
||
if not config.GIF_URL:
|
||
return
|
||
|
||
notifier = get_notifier()
|
||
old_gif_id = database.get_gif_message_id(task_id)
|
||
if old_gif_id:
|
||
notifier.delete(old_gif_id)
|
||
database.delete_gif_message_id(task_id)
|
||
|
||
try:
|
||
message_id = notifier.send_animation(config.GIF_URL, disable_notification=True)
|
||
database.save_gif_message_id(task_id, message_id)
|
||
log.info(f"GIF sent for task {task_id}, message_id={message_id}")
|
||
except Exception as e:
|
||
log.error(f"Failed to send GIF for task {task_id}: {e}")
|