"""Router state provider — interface and mock implementation. Defines the protocol for accessing router state (peers, tunnels, bandwidth, config) and a mock provider for development/testing. """ from __future__ import annotations import random import time from dataclasses import dataclass, field from typing import Protocol, runtime_checkable @dataclass class RouterStatus: """Router status summary.""" version: str = "0.9.62" uptime_ms: int = 0 network_status: str = "OK" active_peers: int = 0 known_peers: int = 0 participating_tunnels: int = 0 share_ratio: float = 0.0 @dataclass class PeerInfo: """Information about a connected peer.""" hash: str = "" address: str = "" version: str = "" uptime_ms: int = 0 bandwidth_in: int = 0 bandwidth_out: int = 0 country: str = "" @dataclass class TunnelInfo: """Information about an active tunnel.""" tunnel_id: str = "" type: str = "" # "client" or "exploratory" direction: str = "" # "inbound" or "outbound" length: int = 0 status: str = "active" expiry_ms: int = 0 @dataclass class BandwidthStats: """Current bandwidth statistics.""" inbound_bps: int = 0 outbound_bps: int = 0 inbound_burst_bps: int = 0 outbound_burst_bps: int = 0 total_inbound_bytes: int = 0 total_outbound_bytes: int = 0 @dataclass class NetDbEntry: """Network database entry.""" hash: str = "" type: str = "router" # "router" or "leaseset" address: str = "" version: str = "" caps: str = "" published: int = 0 @runtime_checkable class RouterStateProvider(Protocol): """Protocol for accessing router state.""" def get_status(self) -> RouterStatus: ... def get_peers(self) -> list[PeerInfo]: ... def get_tunnels(self) -> list[TunnelInfo]: ... def get_bandwidth(self) -> BandwidthStats: ... def get_config(self) -> dict[str, str]: ... def set_config(self, updates: dict[str, str]) -> None: ... def get_log_entries(self, count: int) -> list[str]: ... def get_netdb_entries(self) -> list[NetDbEntry]: ... class MockStateProvider: """Mock state provider with realistic test data.""" def __init__(self) -> None: self._config: dict[str, str] = { "i2np.bandwidth.inBurstKBytes": "100", "i2np.bandwidth.outBurstKBytes": "100", "router.updatePolicy": "notify", "routerconsole.theme": "light", } self._start_time = time.monotonic() def get_status(self) -> RouterStatus: uptime = int((time.monotonic() - self._start_time) * 1000) return RouterStatus( version="0.9.62-py", uptime_ms=uptime, network_status="OK", active_peers=random.randint(20, 80), known_peers=random.randint(500, 2000), participating_tunnels=random.randint(10, 50), share_ratio=round(random.uniform(0.5, 2.0), 2), ) def get_peers(self) -> list[PeerInfo]: return [ PeerInfo( hash=f"peer{i:04x}" * 4, address=f"192.168.1.{i}", version="0.9.62", uptime_ms=random.randint(60000, 86400000), bandwidth_in=random.randint(1000, 50000), bandwidth_out=random.randint(1000, 50000), country=random.choice(["US", "DE", "FR", "JP", "BR", "CA"]), ) for i in range(1, random.randint(15, 40)) ] def get_tunnels(self) -> list[TunnelInfo]: tunnels = [] for i in range(random.randint(5, 20)): tunnels.append(TunnelInfo( tunnel_id=f"tun-{i:04x}", type=random.choice(["client", "exploratory"]), direction=random.choice(["inbound", "outbound"]), length=random.randint(2, 4), status="active", expiry_ms=int(time.time() * 1000) + random.randint(30000, 600000), )) return tunnels def get_bandwidth(self) -> BandwidthStats: return BandwidthStats( inbound_bps=random.randint(5000, 100000), outbound_bps=random.randint(5000, 100000), inbound_burst_bps=random.randint(50000, 200000), outbound_burst_bps=random.randint(50000, 200000), total_inbound_bytes=random.randint(10**8, 10**10), total_outbound_bytes=random.randint(10**8, 10**10), ) def get_config(self) -> dict[str, str]: return dict(self._config) def set_config(self, updates: dict[str, str]) -> None: self._config.update(updates) def get_log_entries(self, count: int) -> list[str]: entries = [ "Router started", "NTCP2 listening on 0.0.0.0:9700", "Reseed complete: 50 routers", "Establishing tunnels...", "4 exploratory tunnels built", "Network: OK (20 peers)", "Participating in 12 tunnels", "Bandwidth: 45.2 KBps in / 32.1 KBps out", "NetDB: 1,234 routers known", "Floodfill mode: enabled", ] return entries[:count] def get_netdb_entries(self) -> list[NetDbEntry]: return [ NetDbEntry( hash=f"router{i:04x}" * 4, type="router", address=f"10.0.0.{i}", version="0.9.62", caps=random.choice(["fR", "OfR", "LfR", "PfR"]), published=int(time.time() * 1000) - random.randint(0, 3600000), ) for i in range(1, random.randint(10, 30)) ]