Stage 03.1 - mock exchange integration

This commit is contained in:
2026-04-13 22:54:01 +03:00
parent aa21342116
commit 9166022b3c
11 changed files with 421 additions and 17 deletions

View File

@@ -1,17 +1,11 @@
from __future__ import annotations
import os
from dataclasses import dataclass
from pathlib import Path
from dotenv import load_dotenv
BASE_DIR = Path(__file__).resolve().parents[2]
ENV_FILE = BASE_DIR / ".env"
load_dotenv(ENV_FILE)
@dataclass(slots=True)
class Settings:
bot_token: str
@@ -19,17 +13,40 @@ class Settings:
app_env: str
log_level: str
tz: str
exchange_enabled: bool
exchange_name: str
exchange_base_url: str
exchange_api_key: str
exchange_api_secret: str
exchange_timeout_sec: int
exchange_testnet: bool
default_symbol: str
def _parse_bool(raw_value: str, default: bool = False) -> bool:
value = (raw_value or "").strip().lower()
if not value:
return default
return value in {"1", "true", "yes", "on"}
def _parse_int(raw_value: str, default: int) -> int:
value = (raw_value or "").strip()
if not value:
return default
return int(value)
def load_settings() -> Settings:
bot_token = os.getenv("BOT_TOKEN", "").strip()
if not bot_token:
raise RuntimeError("BOT_TOKEN is not set in app/.env")
return Settings(
bot_token=bot_token,
bot_parse_mode=os.getenv("BOT_PARSE_MODE", "HTML").strip() or "HTML",
app_env=os.getenv("APP_ENV", "dev").strip() or "dev",
log_level=os.getenv("LOG_LEVEL", "INFO").strip().upper() or "INFO",
tz=os.getenv("TZ", "Europe/Madrid").strip() or "Europe/Madrid",
)
exchange_enabled=_parse_bool(os.getenv("EXCHANGE_ENABLED", "false")),
exchange_name=os.getenv("EXCHANGE_NAME", "dzengi").strip() or "dzengi",
exchange_base_url=os.getenv("EXCHANGE_BASE_URL", "").strip(),
exchange_api_key=os.getenv("EXCHANGE_API_KEY", "").strip(),
exchange_api_secret=os.getenv("EXCHANGE_API_SECRET", "").strip(),
exchange_timeout_sec=_parse_int(os.getenv("EXCHANGE_TIMEOUT_SEC", "10"), 10),
exchange_testnet=_parse_bool(os.getenv("EXCHANGE_TESTNET", "false")),
default_symbol=os.getenv("DEFAULT_SYMBOL", "BTCUSDT").strip() or "BTCUSDT",
)

View File

@@ -3,8 +3,9 @@ from __future__ import annotations
import platform
from dataclasses import dataclass
from src.core.constants import APP_NAME, APP_VERSION
from src.core.config import load_settings
from src.core.constants import APP_NAME, APP_VERSION
from src.integrations.exchange.service import ExchangeService
@dataclass(slots=True)
@@ -22,11 +23,22 @@ class SystemSnapshot:
python_version: str
os_name: str
timezone_name: str
exchange_enabled: bool
exchange_name: str
components: list[ComponentStatus]
def get_system_snapshot() -> SystemSnapshot:
settings = load_settings()
exchange_service = ExchangeService()
exchange_health = exchange_service.get_health()
if exchange_health.ok and exchange_health.mode == "mock":
exchange_state = "🟡 mock mode"
elif exchange_health.ok:
exchange_state = "🟢 OK"
else:
exchange_state = "🔴 attention"
components = [
ComponentStatus(
@@ -41,8 +53,8 @@ def get_system_snapshot() -> SystemSnapshot:
),
ComponentStatus(
name="Биржа",
state="🟡 не подключена",
details="Интеграция с биржей будет добавлена на следующем этапе.",
state=exchange_state,
details=exchange_health.message,
),
ComponentStatus(
name="База данных",
@@ -58,6 +70,8 @@ def get_system_snapshot() -> SystemSnapshot:
python_version=platform.python_version(),
os_name=f"{platform.system()} {platform.release()}",
timezone_name=settings.tz,
exchange_enabled=settings.exchange_enabled,
exchange_name=settings.exchange_name,
components=components,
)
@@ -83,7 +97,9 @@ def build_system_text() -> str:
f"- env: {snapshot.app_env}\n"
f"- python: {snapshot.python_version}\n"
f"- os: {snapshot.os_name}\n"
f"- timezone: {snapshot.timezone_name}\n\n"
f"- timezone: {snapshot.timezone_name}\n"
f"- exchange_enabled: {snapshot.exchange_enabled}\n"
f"- exchange_name: {snapshot.exchange_name}\n\n"
"<b>Справка</b>\n"
"/start — стартовый экран\n"
"/menu — показать меню\n"