Files
dzentra_bot/app/src/core/system_status.py

150 lines
4.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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}"
)