import asyncio,json,os from pathlib import Path from core.threads import run_blocking def _read(p): if not p.exists():p.write_text("[]") return p.stat().st_mtime_ns,frozenset(int(x) for x in json.loads(p.read_text())) def _write(p,ids): t=p.with_suffix(".tmp");t.write_text(json.dumps(sorted(ids),indent=2));os.replace(t,p);return p.stat().st_mtime_ns class Whitelist: def __init__(s,path,hook): s.path=Path(path);s.hook=hook;s.ids=frozenset();s.m=None;s.lock=asyncio.Lock();s.task=None def __contains__(s,i):return i in s.ids async def load(s): try:m,ids=await run_blocking(_read,s.path) except Exception as e: s.hook.put(f"whitelist unreadable: {e!r}, keeping previous list") try:s.m=(await run_blocking(s.path.stat)).st_mtime_ns except OSError:pass return if s.m is not None and ids!=s.ids:s.hook.put(f"whitelist reloaded ({len(ids)} ids)") s.ids=ids;s.m=m async def watch(s): while True: await asyncio.sleep(1) try:m=(await run_blocking(s.path.stat)).st_mtime_ns except OSError:m=None if m!=s.m:await s.load() async def start(s): await s.load();s.task=asyncio.create_task(s.watch()) async def mutate(s,i,add): async with s.lock: n=set(s.ids) if add:n.add(i) else:n.discard(i) s.m=await run_blocking(_write,s.path,n);s.ids=frozenset(n)