150 lines
4.5 KiB
Python
150 lines
4.5 KiB
Python
from __future__ import annotations
|
||
|
||
import platform
|
||
import re
|
||
from dataclasses import dataclass
|
||
|
||
from src.core.config import load_settings
|
||
from src.core.constants import APP_NAME, APP_VERSION
|
||
from src.integrations.exchange.service import ExchangeService
|
||
from src.storage.session import check_database_health
|
||
|
||
|
||
@dataclass(slots=True)
|
||
class ComponentStatus:
|
||
name: str
|
||
state: str
|
||
details: str = ""
|
||
|
||
|
||
@dataclass(slots=True)
|
||
class SystemSnapshot:
|
||
app_name: str
|
||
app_version: str
|
||
db_label: str
|
||
timezone_name: str
|
||
mode_label: str
|
||
default_symbol: str
|
||
components: list[ComponentStatus]
|
||
|
||
|
||
def _extract_postgres_version(raw: str) -> str:
|
||
if not raw:
|
||
return "PostgreSQL"
|
||
|
||
match = re.search(r"PostgreSQL\s+(\d+(?:\.\d+)?)", raw)
|
||
if match:
|
||
return f"PostgreSQL {match.group(1)}"
|
||
|
||
return "PostgreSQL"
|
||
|
||
|
||
def _build_exchange_status(exchange_service: ExchangeService, default_symbol: str) -> ComponentStatus:
|
||
try:
|
||
symbol_validation = exchange_service.validate_symbol(default_symbol)
|
||
except Exception as exc:
|
||
return ComponentStatus(
|
||
name="Биржа",
|
||
state="🔴",
|
||
details=f"Не удалось проверить инструмент: {exc}",
|
||
)
|
||
|
||
exchange_health = exchange_service.get_health()
|
||
if exchange_health.ok and symbol_validation.is_valid:
|
||
return ComponentStatus(name="Биржа", state="🟢")
|
||
|
||
if not exchange_health.ok:
|
||
return ComponentStatus(
|
||
name="Биржа",
|
||
state="🔴",
|
||
details=exchange_health.message or "Ошибка подключения к API биржи.",
|
||
)
|
||
|
||
return ComponentStatus(
|
||
name="Биржа",
|
||
state="🔴",
|
||
details=symbol_validation.message or "Инструмент не прошел проверку.",
|
||
)
|
||
|
||
|
||
def _build_account_status(exchange_service: ExchangeService) -> ComponentStatus:
|
||
private_auth_health = exchange_service.get_private_auth_health()
|
||
if private_auth_health.ok:
|
||
return ComponentStatus(name="Аккаунт", state="🟢")
|
||
|
||
return ComponentStatus(
|
||
name="Аккаунт",
|
||
state="🔴",
|
||
details=private_auth_health.message or "Ошибка private API.",
|
||
)
|
||
|
||
|
||
def _build_database_status() -> tuple[ComponentStatus, str]:
|
||
db_ok, db_message = check_database_health()
|
||
db_label = _extract_postgres_version(db_message)
|
||
|
||
if db_ok:
|
||
return ComponentStatus(name="База данных", state="🟢"), db_label
|
||
|
||
return (
|
||
ComponentStatus(
|
||
name="База данных",
|
||
state="🔴",
|
||
details=db_message or "Ошибка подключения к БД.",
|
||
),
|
||
db_label,
|
||
)
|
||
|
||
|
||
def _resolve_mode_label(exchange_testnet: bool) -> str:
|
||
return "ДЕМО аккаунт" if exchange_testnet else "РЕАЛЬНЫЙ аккаунт"
|
||
|
||
|
||
def get_system_snapshot() -> SystemSnapshot:
|
||
settings = load_settings()
|
||
exchange_service = ExchangeService()
|
||
|
||
database_status, db_label = _build_database_status()
|
||
exchange_status = _build_exchange_status(exchange_service, settings.default_symbol)
|
||
account_status = _build_account_status(exchange_service)
|
||
|
||
components = [
|
||
ComponentStatus(name="Приложение", state="🟢"),
|
||
database_status,
|
||
ComponentStatus(name="Telegram", state="🟢"),
|
||
exchange_status,
|
||
account_status,
|
||
]
|
||
|
||
return SystemSnapshot(
|
||
app_name=APP_NAME,
|
||
app_version=APP_VERSION,
|
||
db_label=db_label,
|
||
timezone_name=settings.tz,
|
||
mode_label=_resolve_mode_label(settings.exchange_testnet),
|
||
default_symbol=settings.default_symbol,
|
||
components=components,
|
||
)
|
||
|
||
|
||
def _render_component(component: ComponentStatus) -> str:
|
||
if component.state == "🟢":
|
||
return f"{component.state} <b>{component.name}</b>"
|
||
|
||
return f"{component.state} <b>{component.name}</b>\n— {component.details}"
|
||
|
||
|
||
def build_system_text() -> str:
|
||
snapshot = get_system_snapshot()
|
||
components_block = "\n".join(_render_component(component) for component in snapshot.components)
|
||
|
||
return (
|
||
"<b>⚙️ Система</b>\n\n"
|
||
f"{components_block}\n\n"
|
||
"<b>🌐 Окружение</b>\n"
|
||
f"• приложение: {snapshot.app_name} {snapshot.app_version}\n"
|
||
f"• база данных: {snapshot.db_label}\n"
|
||
f"• часовой пояс: {snapshot.timezone_name}\n"
|
||
f"• режим: {snapshot.mode_label}\n"
|
||
f"• инструмент: {snapshot.default_symbol}"
|
||
) |