From ec55994fc7579c6810985ba4c0c07f29475e66db Mon Sep 17 00:00:00 2001 From: h Date: Sun, 30 Aug 2026 20:34:17 +0200 Subject: [PATCH] fix(config,vibegram): triage wakes master by alias with wake urgency, event times in local tz --- config.py | 9 ++++++--- mcps/vibegram.py | 28 +++++++++++++++++++--------- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/config.py b/config.py index afbd8be..1b3a225 100644 --- a/config.py +++ b/config.py @@ -204,6 +204,7 @@ VIBEGRAM = ( token=os.environ["VIBEGRAM_TOKEN"], nick=os.environ.get("VIBEGRAM_NICK", "beaver-test"), repo=VIBEGRAM_REPO if VIBEGRAM_REPO.exists() else None, + tz=TZ, ) if os.environ.get("VIBEGRAM_TOKEN") else None @@ -560,9 +561,11 @@ _vibegram_backlog: list[str] = [] VIBEGRAM_BRIEF = ( "Новое в вайбграме (комната {room}, ты там {nick}; остальные - чужие агенты):\n" "{items}\n" - "Будить мастера - inject(master, резюме до 3 строк: кто, что, чего ждёт). " - "Мастер прочитает подробности через vibegram(read) и ответит через " - "vibegram(send), если решит." + 'Будить мастера - inject(conversation="master", urgency="wake", ' + "text=резюме до 3 строк: кто, что, чего ждёт). Мастер прочитает подробности " + "через vibegram(read) и ответит через vibegram(send), если решит. " + "Если inject вернул ошибку - это сбой доставки, а не «не срочно»: повтори " + "один раз с тем же текстом; без inject резюме никто не увидит." ) diff --git a/mcps/vibegram.py b/mcps/vibegram.py index 03cbd1f..20346ed 100644 --- a/mcps/vibegram.py +++ b/mcps/vibegram.py @@ -20,6 +20,7 @@ from dataclasses import dataclass, field from datetime import UTC, datetime from pathlib import Path # noqa: TC003 - в аннотации dataclass from typing import Any, Literal +from zoneinfo import ZoneInfo import aiohttp @@ -49,6 +50,8 @@ class Vibegram: token: str nick: str repo: Path | None = None + tz: str = "UTC" + """Время в строках событий - в этой зоне (хаб отдаёт UTC).""" timeout: float = 15.0 max_chars: int = 8_000 _cursor: int = 0 @@ -126,7 +129,11 @@ class Vibegram: async def pending(self, limit: int = 50) -> list[Event]: """Новые события с курсора хаба (для крона); запоминает их.""" data = await self._get(f"/api/pending?limit={limit}") - events = [e for e in map(_event, data.get("events", [])) if e is not None] + events = [ + e + for e in (_event(raw, self.tz) for raw in data.get("events", [])) + if e is not None + ] fresh = [e for e in events if e.id > self._cursor] for e in fresh: self._recent.append(e) @@ -139,7 +146,7 @@ class Vibegram: nick="system", kind="plan", text=f"план изменился (ревизия {ack}), ты его не подтвердил", - at=_now(), + at=_now(self.tz), ) ) return fresh @@ -210,18 +217,21 @@ class HubError(RuntimeError): self.code = code -def _now() -> str: - return datetime.now(UTC).strftime("%m-%d %H:%M") +def _now(tz: str = "UTC") -> str: + return datetime.now(UTC).astimezone(ZoneInfo(tz)).strftime("%m-%d %H:%M") -def _when(iso: Any) -> str: +def _when(iso: Any, tz: str = "UTC") -> str: try: - return datetime.fromisoformat(str(iso)).strftime("%m-%d %H:%M") + when = datetime.fromisoformat(str(iso)) except ValueError: - return _now() + return _now(tz) + if when.tzinfo is None: + when = when.replace(tzinfo=UTC) + return when.astimezone(ZoneInfo(tz)).strftime("%m-%d %H:%M") -def _event(raw: dict[str, Any]) -> Event | None: +def _event(raw: dict[str, Any], tz: str = "UTC") -> Event | None: who = raw.get("nick") or "system" kind = str(raw.get("kind") or "") p = raw.get("payload") or {} @@ -249,7 +259,7 @@ def _event(raw: dict[str, Any]) -> Event | None: nick=str(who), kind=kind, text=text, - at=_when(raw.get("createdAt")), + at=_when(raw.get("createdAt"), tz), )