fix(hands): watchdog seen moves only on a new peer beat, tracked by message id
This commit is contained in:
1 parent
a39e0b27af
commit
78a0293819
2 files changed
+94
-7
No files matched your search
@@ -20,9 +20,12 @@
|
||||
«что нового с курсора»: курсор отдаёт строку соседа один раз, и следующий
|
||||
прогон на живой комнате увидел бы пустое окно.
|
||||
|
||||
Опора тревоги - `seen`, то есть когда Я увидел строку: `at=` внутри строки
|
||||
это показание соседа о себе, и остановившиеся у него часы не должны
|
||||
выглядеть как живая цепь.
|
||||
Опора тревоги - `seen`, то есть когда Я ВПЕРВЫЕ увидел строку: `at=` внутри
|
||||
строки это показание соседа о себе, и остановившиеся у него часы не должны
|
||||
выглядеть как живая цепь. Окно - это последние сообщения комнаты, поэтому
|
||||
одна и та же отметка видна в нём много прогонов подряд, и новой её делает
|
||||
новый id сообщения, а не текст: двигать `seen` на старой отметке значит
|
||||
никогда не поднять тревогу в тихой комнате.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -264,6 +267,10 @@ class Watch:
|
||||
"""Последний период, который сосед назвал читаемым числом."""
|
||||
every_unknown: bool = False
|
||||
"""В последней отметке стояло `every=unknown`: порог взят с запасом назад."""
|
||||
beat_id: int = 0
|
||||
"""id сообщения с последней зачтённой отметкой: по нему видно, что отметка
|
||||
в окне новая, а не та же. Нуль - поля в файле не было (состояние старое)
|
||||
или отметок не видели ни разу."""
|
||||
top: int = 0
|
||||
"""Максимальный id, который мы видели. Поехал назад - окно смотрит в прошлое."""
|
||||
alerted: datetime | None = None
|
||||
@@ -285,6 +292,7 @@ class Watch:
|
||||
seen=_parse_iso(raw.get("seen")),
|
||||
every=timedelta(seconds=every) if every else None,
|
||||
every_unknown=bool(raw.get("every_unknown")),
|
||||
beat_id=int(raw.get("beat_id", 0)),
|
||||
top=int(raw.get("top", 0)),
|
||||
alerted=_parse_iso(raw.get("alerted")),
|
||||
unarmed_since=_parse_iso(raw.get("unarmed_since")),
|
||||
@@ -298,6 +306,7 @@ class Watch:
|
||||
"seen": _iso(self.seen),
|
||||
"every": int(self.every.total_seconds()) if self.every else None,
|
||||
"every_unknown": self.every_unknown,
|
||||
"beat_id": self.beat_id,
|
||||
"top": self.top,
|
||||
"alerted": _iso(self.alerted),
|
||||
"unarmed_since": _iso(self.unarmed_since),
|
||||
@@ -327,6 +336,22 @@ class Report:
|
||||
"""Строка для лога: почему прогон решил именно так."""
|
||||
|
||||
|
||||
def _is_new_beat(state: Watch, eid: int, beat: Beat) -> bool:
|
||||
"""Впервые ли мы видим ЭТУ отметку соседа.
|
||||
|
||||
Различитель - id сообщения: `at=` внутри строки это показание соседа о
|
||||
себе, и повторить его он может как угодно. Нуля в `beat_id` два повода:
|
||||
отметок не видели ни разу (тогда любая - новая) и состояние записано до
|
||||
появления поля - там единственное, чем можно сверить отметку со зачтённой,
|
||||
это её `at`, и одного прогона хватает, чтобы запомнить id.
|
||||
"""
|
||||
if state.beat_id:
|
||||
return eid > state.beat_id
|
||||
if state.seen is None:
|
||||
return True
|
||||
return beat.at != state.at
|
||||
|
||||
|
||||
def look(
|
||||
state: Watch,
|
||||
events: Sequence[Event],
|
||||
@@ -350,8 +375,8 @@ def look(
|
||||
top = max((e.id for e in events), default=None)
|
||||
stale = top is not None and top < state.top
|
||||
beats = [
|
||||
beat
|
||||
for beat in (parse_beat(e.text) for e in events)
|
||||
(event.id, beat)
|
||||
for event, beat in ((event, parse_beat(event.text)) for event in events)
|
||||
if beat is not None and beat.nick == peer
|
||||
]
|
||||
heard = bool(beats) or any(e.nick == peer for e in events)
|
||||
@@ -360,9 +385,13 @@ def look(
|
||||
else:
|
||||
if top is not None:
|
||||
state.top = max(state.top, top)
|
||||
again = False
|
||||
if beats:
|
||||
last = beats[-1]
|
||||
state.at, state.seen = last.at, now
|
||||
eid, last = beats[-1]
|
||||
again = not _is_new_beat(state, eid, last)
|
||||
if not again:
|
||||
state.seen = now
|
||||
state.at, state.beat_id = last.at, eid
|
||||
state.every_unknown = last.every is None
|
||||
if last.every is not None:
|
||||
state.every = last.every
|
||||
@@ -371,6 +400,8 @@ def look(
|
||||
f"отметок соседа в окне: {len(beats)} "
|
||||
f"(сообщений комнаты: {len(events)} из {window}, {read})"
|
||||
)
|
||||
if again:
|
||||
note += "; отметка та же, что в прошлый прогон - seen не двигаем"
|
||||
|
||||
armed = state.seen is not None
|
||||
threshold = state.every * FACTOR if state.every is not None else None
|
||||
|
||||
Reference in new issue
Block a user