push to repo
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
config.ini
|
||||||
|
venv
|
||||||
|
env
|
||||||
|
.venv
|
69
README.md
Normal file
69
README.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# AdGuard Scheduler
|
||||||
|
|
||||||
|
Skrypt do zarządzania dynamicznymi regułami filtrowania AdGuard na podstawie harmonogramów i konfiguracji klientów.
|
||||||
|
|
||||||
|
## 📦 Wymagania
|
||||||
|
|
||||||
|
- Python 3.6+
|
||||||
|
- Biblioteki:
|
||||||
|
- `requests`
|
||||||
|
|
||||||
|
Zainstaluj:
|
||||||
|
```bash
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚙️ Konfiguracja
|
||||||
|
|
||||||
|
Skonfiguruj serwery i klientów w pliku `.ini`. Przykład:
|
||||||
|
```ini
|
||||||
|
[server:home]
|
||||||
|
url = http://192.168.1.1:3000
|
||||||
|
username = admin
|
||||||
|
password = haslo
|
||||||
|
|
||||||
|
[client:tv]
|
||||||
|
ip = 192.168.1.101
|
||||||
|
services = youtube.com, netflix.com
|
||||||
|
schedule = whole
|
||||||
|
```
|
||||||
|
|
||||||
|
Obsługiwane harmonogramy:
|
||||||
|
- `whole` — zawsze blokuj
|
||||||
|
- `custom:HH-HH` — tylko w określonych godzinach (np. `custom:22-6`)
|
||||||
|
|
||||||
|
## 🚀 Użycie
|
||||||
|
|
||||||
|
### Uruchomienie
|
||||||
|
```bash
|
||||||
|
python adguard_scheduler.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Z własnym plikiem config:
|
||||||
|
```bash
|
||||||
|
python adguard_scheduler.py /ścieżka/do/config.ini
|
||||||
|
```
|
||||||
|
|
||||||
|
### Z katalogiem:
|
||||||
|
```bash
|
||||||
|
python adguard_scheduler.py --config-dir /etc/adguard_configs
|
||||||
|
```
|
||||||
|
|
||||||
|
### Tryb cron (bez outputu):
|
||||||
|
```bash
|
||||||
|
python adguard_scheduler.py --cron
|
||||||
|
```
|
||||||
|
|
||||||
|
### Instalacja zadania cron co godzinę:
|
||||||
|
```bash
|
||||||
|
python adguard_scheduler.py --install-cron
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pomoc:
|
||||||
|
```bash
|
||||||
|
python adguard_scheduler.py --help
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📁 Przykład konfiguracji
|
||||||
|
|
||||||
|
Zobacz plik: [`config.example.ini`](./config.example.ini)
|
175
adguard_scheduler.py
Normal file
175
adguard_scheduler.py
Normal file
@ -0,0 +1,175 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import configparser
|
||||||
|
import requests
|
||||||
|
from requests.auth import HTTPBasicAuth
|
||||||
|
import subprocess
|
||||||
|
from glob import glob
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
REQUIRED_SERVER_KEYS = {"url", "username", "password"}
|
||||||
|
REQUIRED_CLIENT_KEYS = {"ip", "services", "schedule"}
|
||||||
|
|
||||||
|
def should_block(schedule):
|
||||||
|
if schedule == "whole":
|
||||||
|
return True
|
||||||
|
elif schedule.startswith("custom:"):
|
||||||
|
try:
|
||||||
|
hours = schedule.split(":")[1]
|
||||||
|
start, end = map(int, hours.split("-"))
|
||||||
|
now = datetime.now().hour
|
||||||
|
if start <= end:
|
||||||
|
return start <= now < end
|
||||||
|
else:
|
||||||
|
return now >= start or now < end
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return False
|
||||||
|
|
||||||
|
def show_block_details(ip, service, schedule):
|
||||||
|
print(f"Blokada: IP={ip} | Serwis={service} | Harmonogram={schedule}")
|
||||||
|
|
||||||
|
def install_cron():
|
||||||
|
python_exec = sys.executable
|
||||||
|
script_path = os.path.abspath(__file__)
|
||||||
|
config_path = os.path.join(os.path.dirname(script_path), "config.ini")
|
||||||
|
cron_line = f"0 * * * * {python_exec} {script_path} {config_path} --cron > /dev/null 2>&1"
|
||||||
|
|
||||||
|
result = subprocess.run(["crontab", "-l"], capture_output=True, text=True)
|
||||||
|
existing_cron = result.stdout if result.returncode == 0 else ""
|
||||||
|
|
||||||
|
if cron_line in existing_cron:
|
||||||
|
print("Cron już istnieje.")
|
||||||
|
return
|
||||||
|
|
||||||
|
updated_cron = existing_cron.strip() + f"\n{cron_line}\n"
|
||||||
|
subprocess.run(["crontab", "-"], input=updated_cron, text=True)
|
||||||
|
print("Dodano do crona.")
|
||||||
|
|
||||||
|
def print_help():
|
||||||
|
print("Użycie:")
|
||||||
|
print(" python adguard_scheduler.py [config.ini] [--config-dir DIR] [--install-cron|--cron|--help|-h]")
|
||||||
|
print("\nOpcje:")
|
||||||
|
print(" --install-cron Dodaje zadanie do crona co godzinę")
|
||||||
|
print(" --cron Uruchomienie bez outputu (do crona)")
|
||||||
|
print(" --config-dir DIR Wczytaj wszystkie pliki .ini z katalogu DIR")
|
||||||
|
print(" --help, -h Pokazuje pomoc")
|
||||||
|
print("\nPrzykłady:")
|
||||||
|
print(" python adguard_scheduler.py")
|
||||||
|
print(" python adguard_scheduler.py /ścieżka/config.ini")
|
||||||
|
print(" python adguard_scheduler.py --config-dir /etc/adguard_configs")
|
||||||
|
print(" python adguard_scheduler.py --install-cron")
|
||||||
|
print(" python adguard_scheduler.py /cfg.ini --cron")
|
||||||
|
|
||||||
|
def validate_config(config):
|
||||||
|
for section in config.sections():
|
||||||
|
keys = set(config[section].keys())
|
||||||
|
if section.startswith("server") and not REQUIRED_SERVER_KEYS.issubset(keys):
|
||||||
|
raise ValueError(f"Błędna konfiguracja w {section}: wymagane {REQUIRED_SERVER_KEYS}")
|
||||||
|
if section.startswith("client:") and not REQUIRED_CLIENT_KEYS.issubset(keys):
|
||||||
|
raise ValueError(f"Błędna konfiguracja w {section}: wymagane {REQUIRED_CLIENT_KEYS}")
|
||||||
|
|
||||||
|
def load_configs(files):
|
||||||
|
merged = configparser.ConfigParser()
|
||||||
|
for file in files:
|
||||||
|
if os.path.isfile(file):
|
||||||
|
conf = configparser.ConfigParser()
|
||||||
|
conf.read(file)
|
||||||
|
for section in conf.sections():
|
||||||
|
merged[section] = conf[section]
|
||||||
|
validate_config(merged)
|
||||||
|
return merged
|
||||||
|
|
||||||
|
def test_server_connection(url, auth):
|
||||||
|
try:
|
||||||
|
r = requests.get(f"{url}/control/status", auth=auth, timeout=5)
|
||||||
|
return r.status_code == 200
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main(config_paths, silent=False):
|
||||||
|
config = load_configs(config_paths)
|
||||||
|
|
||||||
|
servers = {
|
||||||
|
section: {
|
||||||
|
"url": config.get(section, "url").rstrip("/"),
|
||||||
|
"username": config.get(section, "username"),
|
||||||
|
"password": config.get(section, "password"),
|
||||||
|
}
|
||||||
|
for section in config.sections() if section.startswith("server")
|
||||||
|
}
|
||||||
|
|
||||||
|
clients = {
|
||||||
|
section: {
|
||||||
|
"ip": config.get(section, "ip"),
|
||||||
|
"services": [s.strip() for s in config.get(section, "services").split(",")],
|
||||||
|
"schedule": config.get(section, "schedule"),
|
||||||
|
}
|
||||||
|
for section in config.sections() if section.startswith("client:")
|
||||||
|
}
|
||||||
|
|
||||||
|
for srv_name, srv in servers.items():
|
||||||
|
if not silent:
|
||||||
|
print(f"--- Przetwarzanie serwera {srv_name} ---")
|
||||||
|
|
||||||
|
auth = HTTPBasicAuth(srv["username"], srv["password"])
|
||||||
|
if not test_server_connection(srv["url"], auth):
|
||||||
|
if not silent:
|
||||||
|
print(f"[{srv['url']}] Niedostępny / brak połączenia.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
try:
|
||||||
|
rules_set = set()
|
||||||
|
for cdata in clients.values():
|
||||||
|
if should_block(cdata["schedule"]):
|
||||||
|
for service in cdata["services"]:
|
||||||
|
rule = f"||{service}^$client={cdata['ip']}"
|
||||||
|
rules_set.add(rule)
|
||||||
|
if not silent:
|
||||||
|
show_block_details(cdata["ip"], service, cdata["schedule"])
|
||||||
|
|
||||||
|
payload = {"rules": sorted(rules_set)}
|
||||||
|
endpoint = f"{srv['url']}/control/filtering/set_rules"
|
||||||
|
response = requests.post(endpoint, json=payload, auth=auth,
|
||||||
|
headers={"Content-Type": "application/json"})
|
||||||
|
|
||||||
|
if not silent:
|
||||||
|
if response.status_code in [200, 204]:
|
||||||
|
print(f"[{srv['url']}] Reguły zaktualizowane.")
|
||||||
|
else:
|
||||||
|
print(f"[{srv['url']}] Błąd HTTP: {response.status_code} {response.text}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
if not silent:
|
||||||
|
print(f"[{srv['url']}] Błąd: {e}")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = sys.argv[1:]
|
||||||
|
|
||||||
|
if "--help" in args or "-h" in args:
|
||||||
|
print_help()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
if "--install-cron" in args:
|
||||||
|
install_cron()
|
||||||
|
sys.exit(0)
|
||||||
|
|
||||||
|
silent = "--cron" in args
|
||||||
|
args = [a for a in args if not a.startswith("--")]
|
||||||
|
|
||||||
|
config_files = []
|
||||||
|
|
||||||
|
if "--config-dir" in sys.argv:
|
||||||
|
try:
|
||||||
|
dir_index = sys.argv.index("--config-dir") + 1
|
||||||
|
config_dir = sys.argv[dir_index]
|
||||||
|
config_files = glob(os.path.join(config_dir, "*.ini"))
|
||||||
|
except IndexError:
|
||||||
|
print("Brak ścieżki po --config-dir")
|
||||||
|
sys.exit(1)
|
||||||
|
elif args:
|
||||||
|
config_files = [args[0]]
|
||||||
|
else:
|
||||||
|
config_files = [os.path.join(os.path.dirname(__file__), "config.ini")]
|
||||||
|
|
||||||
|
main(config_files, silent=silent)
|
34
config.example.ini
Normal file
34
config.example.ini
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
[server:home]
|
||||||
|
url = http://192.168.1.1:3000
|
||||||
|
username = admin
|
||||||
|
password = mypass
|
||||||
|
|
||||||
|
[server:office]
|
||||||
|
url = http://10.0.0.1:3000
|
||||||
|
username = admin
|
||||||
|
password = secret123
|
||||||
|
|
||||||
|
[client:tv]
|
||||||
|
ip = 192.168.1.101
|
||||||
|
services = youtube.com, netflix.com
|
||||||
|
schedule = whole
|
||||||
|
|
||||||
|
[client:tablet]
|
||||||
|
ip = 192.168.1.102
|
||||||
|
services = facebook.com, instagram.com
|
||||||
|
schedule = custom:22-6
|
||||||
|
|
||||||
|
[client:child-pc]
|
||||||
|
ip = 192.168.1.150
|
||||||
|
services = steamcommunity.com, epicgames.com
|
||||||
|
schedule = custom:8-20
|
||||||
|
|
||||||
|
[client:guest]
|
||||||
|
ip = 192.168.1.200
|
||||||
|
services = tiktok.com
|
||||||
|
schedule = custom:0-0 ; zero godzin = nie blokuj
|
||||||
|
|
||||||
|
[client:office-laptop]
|
||||||
|
ip = 10.0.0.55
|
||||||
|
services = reddit.com, twitter.com
|
||||||
|
schedule = whole
|
2
cron.txt
Normal file
2
cron.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
@hourly /root/adguard_blocker/venv/bin/python3.13 /root/adguard_blocker/adguard_scheduler.py /root/adguard_blocker/config.ini
|
||||||
|
|
3
requirements.txt
Normal file
3
requirements.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
requests
|
||||||
|
croniter
|
||||||
|
adguardhome
|
Reference in New Issue
Block a user