This commit is contained in:
timka123 committed 2026-10-18 18:02:05 +03:00
commit 17a0a3c4c9
121 files changed
+17641

No files matched your search

+109
View File
@@ -0,0 +1,109 @@
"""Turning the server's policy into what this machine actually does.
Called on every poll with the policy the backend computed. It keeps the hosts
file and the process list in step with it, reports every turned-away attempt
back to the API (that is what makes the daily report fun), and — importantly —
puts everything back the moment the policy says blocking is off, including when
the app exits or crashes mid-interval.
"""
from __future__ import annotations
import logging
from typing import Callable
from kadence_desktop.blocking import processes
from kadence_desktop.blocking.hosts import HostsBlocker
log = logging.getLogger(__name__)
class BlockerManager:
def __init__(
self,
*,
helper_path: str,
report: Callable[[str, str, str, str | None], None],
notify: Callable[[str, str], None] | None = None,
) -> None:
self._hosts = HostsBlocker(helper_path)
self._report = report
self._notify = notify
self._active = False
self._applied: list[str] = []
self._announced: set[int] = set()
self.enforce_hosts = True
self.enforce_apps = True
self.last_error: str | None = None
self.killed_today = 0
# ---- state -------------------------------------------------------------
@property
def active(self) -> bool:
return self._active
@property
def helper_installed(self) -> bool:
return self._hosts.helper_installed
@property
def blocked_domains(self) -> list[str]:
return list(self._applied)
# ---- the one entry point ----------------------------------------------
def sync(self, policy: dict) -> None:
"""Bring the machine in line with `policy` (from /api/blocker/policy)."""
should_block = bool(policy.get("active")) and bool(policy.get("enabled"))
domains = [str(item) for item in policy.get("domains", [])]
apps = [str(item) for item in policy.get("apps", [])]
redirect_ip = str(policy.get("redirect_ip") or "127.0.0.1")
if should_block:
if self.enforce_hosts and domains != self._applied:
if self._hosts.apply(domains, redirect_ip):
self._applied = domains
self.last_error = None
log.info("blocking %s domain(s)", len(domains))
else:
self.last_error = self._hosts.last_error
if self.enforce_apps and apps:
self._enforce_apps(apps, kill=bool(policy.get("kill_apps", True)))
self._active = True
else:
self.release()
def release(self) -> None:
"""Unblock everything. Called when the interval ends, and on shutdown."""
if self._applied or self._hosts_dirty():
if self._hosts.clear():
self._applied = []
self.last_error = None
else:
self.last_error = self._hosts.last_error
self._active = False
self._announced.clear()
def _hosts_dirty(self) -> bool:
from kadence_desktop.blocking.hosts import current_block
return bool(current_block())
# ---- apps --------------------------------------------------------------
def _enforce_apps(self, patterns: list[str], *, kill: bool) -> None:
for app in processes.scan(patterns):
if app.pid in self._announced:
continue
self._announced.add(app.pid)
if kill and processes.terminate(app):
self.killed_today += 1
self._report("app", app.matched, "killed", f"{app.name} (pid {app.pid})")
if self._notify:
self._notify("Closed a distraction", f"{app.name} is blocked during focus.")
else:
self._report("app", app.matched, "warned", f"{app.name} (pid {app.pid})")
if self._notify:
self._notify("That app is blocked", f"{app.name} is on your block list.")