feat(state): persist last_update_id and rolling processed_ids
This commit is contained in:
parent
c7ca7099b2
commit
16b75e212a
|
|
@ -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)
|
||||
|
|
@ -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)
|
||||
Loading…
Reference in New Issue