From aa213421168474db823d59e757e117fc1001c99b Mon Sep 17 00:00:00 2001 From: Sergey Date: Mon, 13 Apr 2026 21:47:59 +0300 Subject: [PATCH] Stage 02 - system status screen --- app/src/core/system_status.py | 91 ++++++++++++++++++++++++++++ app/src/telegram/handlers/start.py | 25 ++++++-- app/src/telegram/handlers/system.py | 13 +--- docs/decisions/0004-system-screen.md | 14 +++++ docs/stages/stage-02-system.md | 46 ++++++++++++++ 5 files changed, 174 insertions(+), 15 deletions(-) create mode 100644 app/src/core/system_status.py create mode 100644 docs/decisions/0004-system-screen.md create mode 100644 docs/stages/stage-02-system.md diff --git a/app/src/core/system_status.py b/app/src/core/system_status.py new file mode 100644 index 0000000..3bc652a --- /dev/null +++ b/app/src/core/system_status.py @@ -0,0 +1,91 @@ +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 + + +@dataclass(slots=True) +class ComponentStatus: + name: str + state: str + details: str + + +@dataclass(slots=True) +class SystemSnapshot: + app_name: str + app_version: str + app_env: str + python_version: str + os_name: str + timezone_name: str + components: list[ComponentStatus] + + +def get_system_snapshot() -> SystemSnapshot: + settings = load_settings() + + components = [ + ComponentStatus( + name="Бот", + state="🟢 работает", + details="Процесс бота запущен и обрабатывает команды.", + ), + ComponentStatus( + name="Telegram", + state="🟢 OK", + details="Polling активен, базовая маршрутизация подключена.", + ), + ComponentStatus( + name="Биржа", + state="🟡 не подключена", + details="Интеграция с биржей будет добавлена на следующем этапе.", + ), + ComponentStatus( + name="База данных", + state="🟡 не подключена", + details="Слой хранения пока только подготовлен структурно.", + ), + ] + + return SystemSnapshot( + app_name=APP_NAME, + app_version=APP_VERSION, + app_env=settings.app_env, + python_version=platform.python_version(), + os_name=f"{platform.system()} {platform.release()}", + timezone_name=settings.tz, + components=components, + ) + + +def build_system_text() -> str: + snapshot = get_system_snapshot() + + component_lines = [] + for component in snapshot.components: + component_lines.append( + f"{component.state} {component.name}\n" + f"— {component.details}" + ) + + components_block = "\n\n".join(component_lines) + + return ( + "⚙️ Система\n\n" + "Статус компонентов\n" + f"{components_block}\n\n" + "Окружение\n" + f"- приложение: {snapshot.app_name} {snapshot.app_version}\n" + 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" + "Справка\n" + "/start — стартовый экран\n" + "/menu — показать меню\n" + "/help — открыть системную справку" + ) diff --git a/app/src/telegram/handlers/start.py b/app/src/telegram/handlers/start.py index 5b2f50c..0b9dfab 100644 --- a/app/src/telegram/handlers/start.py +++ b/app/src/telegram/handlers/start.py @@ -1,9 +1,12 @@ +from __future__ import annotations + from aiogram import F, Router from aiogram.filters import Command from aiogram.types import Message +from src.core.system_status import build_system_text from src.telegram.keyboards.reply import build_main_menu_keyboard -from src.telegram.menus import MAIN_MENU_TEXT, SYSTEM_TEXT +from src.telegram.menus import MAIN_MENU_TEXT router = Router(name="start") @@ -11,19 +14,31 @@ router = Router(name="start") @router.message(Command("start")) async def cmd_start(message: Message) -> None: - await message.answer(MAIN_MENU_TEXT, reply_markup=build_main_menu_keyboard()) + await message.answer( + MAIN_MENU_TEXT, + reply_markup=build_main_menu_keyboard(), + ) @router.message(Command("menu")) async def cmd_menu(message: Message) -> None: - await message.answer(MAIN_MENU_TEXT, reply_markup=build_main_menu_keyboard()) + await message.answer( + MAIN_MENU_TEXT, + reply_markup=build_main_menu_keyboard(), + ) @router.message(Command("help")) async def cmd_help(message: Message) -> None: - await message.answer(SYSTEM_TEXT, reply_markup=build_main_menu_keyboard()) + await message.answer( + build_system_text(), + reply_markup=build_main_menu_keyboard(), + ) @router.message(F.text == "Меню") async def menu_shortcut(message: Message) -> None: - await message.answer(MAIN_MENU_TEXT, reply_markup=build_main_menu_keyboard()) + await message.answer( + MAIN_MENU_TEXT, + reply_markup=build_main_menu_keyboard(), + ) diff --git a/app/src/telegram/handlers/system.py b/app/src/telegram/handlers/system.py index 5d9b22b..6548cca 100644 --- a/app/src/telegram/handlers/system.py +++ b/app/src/telegram/handlers/system.py @@ -1,10 +1,9 @@ -import platform +from __future__ import annotations from aiogram import F, Router from aiogram.types import Message -from src.core.constants import APP_NAME, APP_VERSION -from src.telegram.menus import SYSTEM_TEXT +from src.core.system_status import build_system_text router = Router(name="system") @@ -12,10 +11,4 @@ router = Router(name="system") @router.message(F.text.in_({"⚙️ Система", "⚙ Система"})) async def open_system(message: Message) -> None: - runtime_info = ( - "\n\nRuntime\n" - f"- app: {APP_NAME} {APP_VERSION}\n" - f"- python: {platform.python_version()}\n" - f"- os: {platform.system()} {platform.release()}" - ) - await message.answer(SYSTEM_TEXT + runtime_info) + await message.answer(build_system_text()) diff --git a/docs/decisions/0004-system-screen.md b/docs/decisions/0004-system-screen.md new file mode 100644 index 0000000..67e90f9 --- /dev/null +++ b/docs/decisions/0004-system-screen.md @@ -0,0 +1,14 @@ +# 0004 — System Screen + +## Решение +Сделать `Система` отдельным системным экраном, который показывает не только справку, но и снимок состояния приложения. + +## Причины +- это удобно для диагностики +- это удобно для локальной разработки +- это удобно для проверки на Synology +- это закладывает основу для health checks + +## Последствия +- справка больше не живет отдельно от системного экрана +- системный экран становится точкой входа для технического контроля проекта diff --git a/docs/stages/stage-02-system.md b/docs/stages/stage-02-system.md new file mode 100644 index 0000000..d41205c --- /dev/null +++ b/docs/stages/stage-02-system.md @@ -0,0 +1,46 @@ +# Stage 02 — System + +## Цель +Сделать раздел `⚙️ Система` реальным центром контроля приложения, а не просто статическим экраном. + +## Что добавляется +- сервис `system_status`, который собирает снимок состояния приложения +- единый текст системы, который используется и кнопкой `⚙️ Система`, и командой `/help` +- отображение: + - статуса бота + - статуса Telegram-слоя + - статуса интеграции с биржей + - статуса слоя хранения + - текущего окружения + +## Что должен увидеть пользователь +Экран вида: + +- Бот: работает +- Telegram: OK +- Биржа: не подключена +- База данных: не подключена +- env / python / os / timezone + +## Почему это важно +На следующих этапах сюда будут добавляться: +- ping биржи +- статус WebSocket +- статус БД +- статус Redis +- последние ошибки +- health checks + +## Как проверить +1. Запустить бота локально +2. Открыть Telegram +3. Нажать `⚙️ Система` +4. Проверить команду `/help` +5. Убедиться, что экран одинаково полезен из кнопки и из команды + +## Commit message +Рекомендуемый commit: + +```bash +git commit -m "stage 02 system status screen" +```