#!/usr/bin/env python3 import os import psutil import re import subprocess import sys import time BLOCKER = "blocker.py" BLOCKER_CONFIG_DIR = "/home/{}/.config/focusfriend" BLOCKED_BROWSER_WORDS = ["mogelpower", "DER SPIEGEL", "nitter"] def is_window_blocked(window_name, blocked): for b in blocked: if type(b) is str and b == window_name: return True elif type(b) is re.Pattern and b.findall(window_name): return True return False def get_active_window_name_and_pid(): CMD = ["xdotool", "getactivewindow", "getwindowname", "getwindowpid"] p = subprocess.run(CMD, capture_output=True) if p.returncode != 0: return "", "" window_name, window_pid, _ = p.stdout.decode().split("\n") return window_name, window_pid def find_window_name(window_name): CMD = ["xdotool", "search", window_name, "getwindowpid"] p = subprocess.run(CMD, capture_output=True) if p.returncode != 0: return "" l = p.stdout.decode().split("\n") def init_kill_sequence(blocked): count = 5 while True: window_name, window_pid = get_active_window_name_and_pid() if not is_window_blocked(window_name, blocked): notify(f"{window_name} is okay. Return from kill sequence.") return notify(f"{window_name} is blocked. Kill in {count} seconds.") if count == 0: p = psutil.Process(int(window_pid)) p.kill() return time.sleep(1) count -= 1 def notify(message): env = { **os.environ, "DBUS_SESSION_BUS_ADDRESS": "unix:path=/run/user/1000/bus" } user = env["SUDO_USER"] CMD = ["runuser", "-m", "-u", user, "notify-send", message] p = subprocess.run(CMD, env=env) def get_config_dir() -> str: user = os.environ["SUDO_USER"] config_dir = BLOCKER_CONFIG_DIR.format(user) assert(os.path.isdir(config_dir)) return config_dir def write_pid_file() -> str: p = psutil.Process() pid_file = os.path.join(get_config_dir(), f"{p.pid}.pid") with open(pid_file, "w") as f: f.write(pid_file) return pid_file def terminate_existing_blocker() -> None: this_pid = psutil.Process().pid config_dir = get_config_dir() pid_files = [f for f in os.listdir(config_dir) if f.endswith(".pid")] for pid_file in pid_files: pid = int(pid_file.replace(".pid", "")) if this_pid == pid: continue try: p = psutil.Process(pid) p.terminate() except psutil.NoSuchProcess: pass os.remove(os.path.join(config_dir, pid_file)) def init() -> None: terminate_existing_blocker() write_pid_file() def main() -> None: init() blocked = [re.compile(f"{word}.*(Firefox|Chromium)", flags=re.IGNORECASE) for word in BLOCKED_BROWSER_WORDS] while True: time.sleep(1) window_name, window_pid = get_active_window_name_and_pid() if is_window_blocked(window_name, blocked): init_kill_sequence(blocked) if __name__ == "__main__": if os.geteuid() == 0: newpid = os.fork() if newpid == 0: main() else: cmd = ["sudo", BLOCKER] + sys.argv[1:] subprocess.Popen(cmd)