intrabot/bot/poster.py
dv 2338e5666a
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
клоунада 1
2026-03-16 00:03:27 +03:00

137 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.

"""
Управление постами в канале.
Использует интерфейс Notifier — не привязан к Telegram или любому другому
мессенджеру. Инициализируется один раз при старте бота через set_notifier().
Чтобы сменить канал: вызови set_notifier() с другим адаптером.
Чтобы отправлять в несколько каналов: используй MultiNotifier (создай адаптер).
"""
from __future__ import annotations
import logging
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)
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}")
return
if old:
notifier.delete(old["message_id"])
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}")