feat(p3): add discovery, telegram notifications, and one-click favorites

Implement WBI video search with legacy fallback, classification scoring, candidate pool, permanent seen-aid dedupe, blacklist filtering, and batched Telegram notifications. Add write client for fav deal/sort, target-folder selection, insertion position calculation, read-back verification, fav_action locking, retry on failure, ignore and blacklist actions. Wire discovery, candidate cleanup, Telegram long polling, and new discover/candidates CLI commands into the scheduler. Add migration v3 for candidate duration and index stats.
This commit is contained in:
kairee committed 2026-10-31 21:23:12 +08:00
1 parent 6a21b5d905
commit d971e718e9
31 files changed
+2886 -55

No files matched your search

+56 -1
View File
@@ -51,7 +51,7 @@ def test_init_and_status():
path = write_config(tmp)
code, output = run_cli(["init", "--config", str(path)])
assert code == 0
assert '"schema_version": 2' in output
assert '"schema_version": 3' in output
db = Database(Path(tmp) / "app.db")
db.initialize()
@@ -149,6 +149,14 @@ def test_run_once_offline():
def check_login(self):
return LoginState(True, 1, "tester", 0, "OK")
class FakeDiscovery:
def __init__(self, *args, **kwargs):
pass
def run(self):
from reaction_board.services.discovery import DiscoverySummary
return DiscoverySummary(started_at=1, finished_at=1)
with tempfile.TemporaryDirectory() as tmp:
path = write_config(tmp)
data = json.loads(path.read_text(encoding="utf-8"))
@@ -157,6 +165,7 @@ def test_run_once_offline():
path.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8")
original = cli._client
original_discovery = cli.DiscoveryService
selected = []
def fake_client(credentials, *args, **kwargs):
@@ -164,10 +173,12 @@ def test_run_once_offline():
return FakeClient()
cli._client = fake_client
cli.DiscoveryService = FakeDiscovery
try:
code, output = run_cli(["run", "--once", "--config", str(path)])
finally:
cli._client = original
cli.DiscoveryService = original_discovery
assert code == 0
assert '"name": "folder_sync"' in output
assert '"name": "snapshot"' in output
@@ -209,6 +220,49 @@ def test_snapshot_backup_failure_returns_error_and_retries():
assert Path(payload["backup_path"]).exists()
def test_candidates_and_discover_commands():
from reaction_board.services.discovery import DiscoverySummary
with tempfile.TemporaryDirectory() as tmp:
path = write_config(tmp)
assert run_cli(["init", "--config", str(path)])[0] == 0
db = Database(Path(tmp) / "app.db")
db.initialize()
try:
repo = Repository(db)
repo.upsert_candidate(
aid=123, bvid="BV123", title="咻咻满 reaction",
up_mid=100, up_name="UP", pubdate=1,
cover=None, score=3, channel="search:test#p1",
)
finally:
db.close()
code, output = run_cli(["candidates", "--config", str(path)])
assert code == 0
assert '"count": 1' in output
assert '"aid": 123' in output
class FakeDiscovery:
def __init__(self, *args, **kwargs):
pass
def run(self):
return DiscoverySummary(
started_at=1, finished_at=2, candidates=3, notified=1
)
original = cli.DiscoveryService
cli.DiscoveryService = FakeDiscovery
try:
code, output = run_cli(["discover", "--config", str(path)])
finally:
cli.DiscoveryService = original
assert code == 0
assert '"candidates": 3' in output
assert '"notified": 1' in output
def main_test():
test_init_and_status()
test_missing_config()
@@ -218,6 +272,7 @@ def main_test():
test_auth_check_all_and_auth_dispatch()
test_run_once_offline()
test_snapshot_backup_failure_returns_error_and_retries()
test_candidates_and_discover_commands()
print("OK: CLI init/sync/snapshot/auth-check/run/status/错误路径测试全部通过")