diff --git a/cli/killswitch_monitor.py b/cli/killswitch_monitor.py index 079daf3..b3db515 100644 --- a/cli/killswitch_monitor.py +++ b/cli/killswitch_monitor.py @@ -2,6 +2,7 @@ import subprocess import threading import time from core.utils.encrypted_proxy import killswitch +from core.utils.encrypted_proxy.singbox import SingboxRunner drop_state: dict = {"count": 0} tunnel_state: dict = {"alive": True} @@ -17,14 +18,14 @@ class KillswitchMonitor: LOG_PREFIX = "hydraveil-drop" def __init__(self): - self._on_disconnect = None - self._on_leak = None - self._on_displaced = None - self._socks5_port = None - self._session_token = None - self._running = False - self._stop_event = threading.Event() - self._cause_event = threading.Event() + self._on_disconnect = None + self._on_leak = None + self._on_displaced = None + self._socks5_port = None + self._session_token = None + self._running = False + self._stop_event = threading.Event() + self._cause_event = threading.Event() self._cause: str | None = None self._last_poll_ts: str | None = None @@ -86,16 +87,7 @@ class KillswitchMonitor: self._trigger(CAUSE_DISCONNECT) return - try: - result = subprocess.run( - ['pgrep', '-x', 'sing-box'], - capture_output=True, - timeout=3, - ) - singbox_alive = result.returncode == 0 - except (OSError, subprocess.TimeoutExpired): - singbox_alive = True - + singbox_alive = SingboxRunner().is_running() tunnel_state["alive"] = singbox_alive if not singbox_alive: diff --git a/cli/observers.py b/cli/observers.py index 1c41366..ec7a907 100644 --- a/cli/observers.py +++ b/cli/observers.py @@ -5,30 +5,34 @@ from core.observers.ClientObserver import ClientObserver from core.observers.ConnectionObserver import ConnectionObserver from core.observers.InvoiceObserver import InvoiceObserver from core.observers.ProfileObserver import ProfileObserver -from cli.ui import Spinner, label_ok, label_error, label_warn, label_connected, label_disconnect, progress_bar, label_killswitch, label_dns +from cli.ui import ( + Spinner, label_ok, label_error, label_warn, + label_connected, label_disconnect, label_killswitch, + label_dns, label_ipv6, label_ipv4, progress_bar, +) from cli.helpers import sanitize_profile -from cli.killswitch_monitor import drop_state +from cli.killswitch_monitor import drop_state, tunnel_state import pprint application_version_observer = ApplicationVersionObserver() -client_observer = ClientObserver() -connection_observer = ConnectionObserver() -invoice_observer = InvoiceObserver() -profile_observer = ProfileObserver() +client_observer = ClientObserver() +connection_observer = ConnectionObserver() +invoice_observer = InvoiceObserver() +profile_observer = ProfileObserver() -_spinner = None -_monitor = None -_timer_state = None +_spinner = None +_monitor = None +_timer_state = None -application_version_observer.subscribe('downloading', lambda event: print(f'Downloading {ApplicationController.get(event.subject.application_code).name}, version {event.subject.version_number}...')) -application_version_observer.subscribe('download_progressing', lambda event: print(f'Current progress: {event.meta.get("progress"):.2f}%', flush=True, end='\r')) -application_version_observer.subscribe('downloaded', lambda event: print('\n')) +application_version_observer.subscribe('downloading', lambda event: print(f'Downloading {ApplicationController.get(event.subject.application_code).name}, version {event.subject.version_number}...')) +application_version_observer.subscribe('download_progressing',lambda event: print(f'Current progress: {event.meta.get("progress"):.2f}%', flush=True, end='\r')) +application_version_observer.subscribe('downloaded', lambda event: print('\n')) -client_observer.subscribe('synchronizing', lambda event: print('Synchronizing...\n')) -client_observer.subscribe('updating', lambda event: print('Updating client...')) -client_observer.subscribe('update_progressing', lambda event: print(f'Current progress: {event.meta.get("progress"):.2f}%', flush=True, end='\r')) -client_observer.subscribe('updated', lambda event: print('\n')) +client_observer.subscribe('synchronizing', lambda event: print('Synchronizing...\n')) +client_observer.subscribe('updating', lambda event: print('Updating client...')) +client_observer.subscribe('update_progressing', lambda event: print(f'Current progress: {event.meta.get("progress"):.2f}%', flush=True, end='\r')) +client_observer.subscribe('updated', lambda event: print('\n')) def _on_connecting(event): @@ -41,7 +45,7 @@ def _on_connecting(event): _timer_state['reconnecting'] = connecting_spinner() attempt = event.subject.get("attempt_count", "?") total = event.subject.get("maximum_number_of_attempts", "?") - _spinner = Spinner(f"[net] verify_ip attempt {attempt}/{total}...") + _spinner = Spinner(f"Connecting... attempt {attempt}/{total}") _spinner.start() @@ -55,20 +59,27 @@ def _on_connected(event): _timer_state['reconnecting'] = None if _timer_state: _timer_state['_retrying'] = False + d = event.subject or {} - label_connected(f"proxy={d.get('proxy_ip','?')} tunnel={d.get('tunnel_if','?')} port={d.get('socks5_port','?')}") + tunnel_if = d.get('tunnel_if', '?') + server_ip = d.get('server_ip') + socks5_port = d.get('socks5_port', '?') + + label_connected(f"tunnel={tunnel_if} port={socks5_port}") label_killswitch(killswitch.status()) label_dns( enabled=d.get('dns_enabled', True), active=d.get('dns_active', False), ) + label_ipv6(blocked=True) + if server_ip: + label_ipv4(server_ip=server_ip, reachable=True) + if _monitor is not None: - _monitor.set_connection_data( - socks5_port=d.get('socks5_port'), - ) + _monitor.set_connection_data(socks5_port=d.get('socks5_port')) + if _timer_state is not None: from cli.ui import session_timer - from cli.killswitch_monitor import tunnel_state _timer_state['stop'] = session_timer(drop_state=drop_state, tunnel_state=tunnel_state) @@ -138,11 +149,11 @@ connection_observer.subscribe('tor_bootstrapping', _on_tor_bootstrapping connection_observer.subscribe('tor_bootstrap_progressing', _on_tor_bootstrap_progressing) connection_observer.subscribe('tor_bootstrapped', _on_tor_bootstrapped) -invoice_observer.subscribe('retrieved', lambda event: print(f'\n{pprint.pp(event.subject)}\n')) +invoice_observer.subscribe('retrieved', lambda event: print(f'\n{pprint.pp(event.subject)}\n')) invoice_observer.subscribe('processing', lambda event: print('A payment has been detected and is being verified...\n')) -invoice_observer.subscribe('settled', lambda event: print('The payment has been successfully verified.\n')) +invoice_observer.subscribe('settled', lambda event: print('The payment has been successfully verified.\n')) -profile_observer.subscribe('created', lambda event: pprint.pp((sanitize_profile(event.subject), 'Created'))) +profile_observer.subscribe('created', lambda event: pprint.pp((sanitize_profile(event.subject), 'Created'))) profile_observer.subscribe('destroyed', lambda event: pprint.pp((sanitize_profile(event.subject), 'Destroyed'))) -profile_observer.subscribe('disabled', lambda event: pprint.pp((sanitize_profile(event.subject), 'Disabled')) if event.meta.get('explicitly') else None) -profile_observer.subscribe('enabled', lambda event: pprint.pp((sanitize_profile(event.subject), 'Enabled'))) \ No newline at end of file +profile_observer.subscribe('disabled', lambda event: pprint.pp((sanitize_profile(event.subject), 'Disabled')) if event.meta.get('explicitly') else None) +profile_observer.subscribe('enabled', lambda event: pprint.pp((sanitize_profile(event.subject), 'Enabled'))) \ No newline at end of file diff --git a/cli/ui.py b/cli/ui.py index 9d6354d..64d1199 100644 --- a/cli/ui.py +++ b/cli/ui.py @@ -20,6 +20,7 @@ BBLUE = "\033[1;34m" BCYAN = "\033[1;36m" BWHITE = "\033[1;37m" + def label_ok(msg: str): print(f" {BGREEN}[ OK ]{RESET} {msg}") @@ -159,8 +160,21 @@ def label_killswitch(armed: bool): print(f" {BRED}[ KS ]{RESET} Kill switch {BRED}INACTIVE — connection unprotected{RESET}") -def session_timer(drop_state: dict | None = None, tunnel_state: dict | None = None) -> threading.Event: +def label_ipv6(blocked: bool): + if blocked: + print(f" {BGREEN}[ IPv6 ]{RESET} IPv6 blocked") + else: + print(f" {BYELLOW}[ IPv6 ]{RESET} IPv6 {BYELLOW}not blocked{RESET}") + +def label_ipv4(server_ip: str, reachable: bool): + if reachable: + print(f" {BGREEN}[ IPv4 ]{RESET} Server {BGREEN}{server_ip}{RESET} reachable") + else: + print(f" {BRED}[ IPv4 ]{RESET} Server {BRED}{server_ip}{RESET} unreachable") + + +def session_timer(drop_state: dict | None = None, tunnel_state: dict | None = None) -> threading.Event: stop_event = threading.Event() start = time.time() @@ -173,7 +187,7 @@ def session_timer(drop_state: dict | None = None, tunnel_state: dict | None = No time_str = f"{h:02d}:{m:02d}:{s:02d}" alive = True if tunnel_state is None else tunnel_state.get("alive", True) - dot_color = BGREEN if alive else BRED + dot_color = BGREEN if alive else BRED status_word = "Protected" if alive else "Disconnected" if drop_state is not None: