#!/usr/bin/env python3 """Download community blocklists defined in config.json to disk. Saves each community blocklist to blocklists/. Local blocklists are managed by the dashboard and are not downloaded here. Run this on a schedule (e.g. daily) to refresh remote lists, then run sudo python3 core.py --merge-blocklists to merge, update SQLite, and reload dnsmasq without restarting it. Usage: sudo python3 dl_blocklists.py """ import json import os import sys import urllib.request from pathlib import Path SCRIPT_DIR = Path(__file__).parent CONFIG_FILE = SCRIPT_DIR / "config.json" BLOCKLIST_DIR = SCRIPT_DIR / "blocklists" def die(msg): print(f"ERROR: {msg}", file=sys.stderr) sys.exit(1) def check_root(): if os.geteuid() != 0: die("This script must be run as root (sudo).") def load_config(): if not CONFIG_FILE.exists(): die(f"Config file not found: {CONFIG_FILE}") with open(CONFIG_FILE) as f: return json.load(f) def download_blocklists(data): any_fail = False BLOCKLIST_DIR.mkdir(exist_ok=True) for bl in data.get("dns_blocking", {}).get("blocklists", []): if bl.get("bl_type") == "local": continue url = bl.get("url", "") save_as = bl.get("save_as", "") name = bl.get("name", "?") if not url or not save_as: print(f" Skipped '{name}': missing url or save_as") continue try: req = urllib.request.Request(url, headers={"User-Agent": "routlin/1.0"}) with urllib.request.urlopen(req, timeout=30) as r: content = r.read() (BLOCKLIST_DIR / save_as).write_bytes(content) print(f" Downloaded: {name} ({len(content):,} bytes)") except Exception as e: print(f" ERROR: Failed to download '{name}': {e}") any_fail = True return not any_fail def main(): check_root() data = load_config() print("Downloading blocklists ==============================================") success = download_blocklists(data) if not success: print("WARNING: One or more downloads failed.") sys.exit(1) if __name__ == "__main__": main()