feat(state): persist last_update_id and rolling processed_ids

This commit is contained in:
beo3000 2026-06-15 15:08:53 +02:00
parent c7ca7099b2
commit 16b75e212a
2 changed files with 80 additions and 0 deletions

46
src/journal_bot/state.py Normal file
View File

@ -0,0 +1,46 @@
import json
from collections import deque
from pathlib import Path
class State:
"""Persistent state: last_update_id + rolling processed_ids."""
def __init__(self, state_dir: Path, processed_ids_max: int = 500):
self.state_dir = state_dir
self.state_dir.mkdir(parents=True, exist_ok=True)
self._last_path = state_dir / "last_update_id.json"
self._processed_path = state_dir / "processed_ids.json"
self._max = processed_ids_max
self._processed: deque[int] = deque(maxlen=processed_ids_max)
self._load()
def _load(self) -> None:
if self._last_path.exists():
self._last_update_id = json.loads(self._last_path.read_text())["value"]
else:
self._last_update_id = 0
if self._processed_path.exists():
ids = json.loads(self._processed_path.read_text())["ids"]
self._processed = deque(ids[-self._max:], maxlen=self._max)
@property
def last_update_id(self) -> int:
return self._last_update_id
def set_last_update_id(self, value: int) -> None:
self._last_update_id = value
tmp = self._last_path.with_suffix(".tmp")
tmp.write_text(json.dumps({"value": value}))
tmp.replace(self._last_path)
def has_processed(self, update_id: int) -> bool:
return update_id in self._processed
def mark_processed(self, update_id: int) -> None:
if update_id in self._processed:
return
self._processed.append(update_id)
tmp = self._processed_path.with_suffix(".tmp")
tmp.write_text(json.dumps({"ids": list(self._processed)}))
tmp.replace(self._processed_path)

34
tests/test_state.py Normal file
View File

@ -0,0 +1,34 @@
from pathlib import Path
from journal_bot.state import State
def test_state_initial_values(tmp_path):
s = State(tmp_path)
assert s.last_update_id == 0
assert not s.has_processed(123)
def test_state_persists_last_update_id(tmp_path):
s = State(tmp_path)
s.set_last_update_id(456)
s2 = State(tmp_path)
assert s2.last_update_id == 456
def test_state_processed_ids_rolling_window(tmp_path):
s = State(tmp_path, processed_ids_max=3)
s.mark_processed(1)
s.mark_processed(2)
s.mark_processed(3)
s.mark_processed(4)
assert not s.has_processed(1) # dropped
assert s.has_processed(2)
assert s.has_processed(3)
assert s.has_processed(4)
def test_state_processed_ids_persist(tmp_path):
s = State(tmp_path)
s.mark_processed(42)
s2 = State(tmp_path)
assert s2.has_processed(42)