code-review fixes: fsync, entropy, is_dm propagation, tests

- Add os.fsync with Windows-compatible directory flush in pairing store
- Increase pairing code length from 6 -> 8 characters for higher entropy
- Remove SystemExit on empty allowFrom; empty list now defers to pairing
- Update is_allowed docstring to document pairing fallback semantics
- Propagate is_dm to Matrix (direct rooms) and Slack (im channels)
- Slack _is_allowed now checks pairing store for DM allowlist mode
- Fix /pairing revoke to accept optional channel argument
- Move inline import time to module top-level
- Add WebSocket comment explaining is_dm=True assumption
- Add comprehensive tests for store and BaseChannel pairing integration
- Fix existing tests that expected empty allowFrom to hard-exit

Refs #3774
This commit is contained in:
chengyongru
2026-05-15 15:46:44 +08:00
committed by Xubin Ren
parent 4c4a9ae590
commit f8e7e50759
9 changed files with 265 additions and 33 deletions
+14 -2
View File
@@ -8,6 +8,7 @@ private-assistant scale: small JSON file, simple locking, no external DB.
from __future__ import annotations
import json
import os
import secrets
import string
import threading
@@ -20,8 +21,9 @@ from loguru import logger
from nanobot.config.paths import get_data_dir
_LOCK = threading.Lock()
_ALPHABET = string.ascii_uppercase + string.digits
_CODE_LENGTH = 6 # e.g. XK9-42F
_CODE_LENGTH = 8 # e.g. XK9-42F-MP
_TTL_DEFAULT_S = 600 # 10 minutes
@@ -48,7 +50,17 @@ def _save(data: dict[str, Any]) -> None:
with open(tmp, "w", encoding="utf-8") as f:
json.dump(data, f, indent=2, ensure_ascii=False)
f.flush()
os.fsync(f.fileno())
tmp.replace(path)
# Ensure directory entry is flushed for durability (Unix only; no-op on Windows)
try:
fd = os.open(path.parent, os.O_RDONLY)
try:
os.fsync(fd)
finally:
os.close(fd)
except (OSError, NotImplementedError):
pass
def _gc_pending(data: dict[str, Any]) -> None:
@@ -75,7 +87,7 @@ def generate_code(
# Ensure uniqueness
for _ in range(100):
raw = "".join(secrets.choice(_ALPHABET) for _ in range(_CODE_LENGTH))
code = f"{raw[:3]}-{raw[3:]}"
code = f"{raw[:4]}-{raw[4:]}"
if code not in data.get("pending", {}):
break
else: # pragma: no cover