"""
Telegram-бот для отображения состояния системы.
Функциональность:
- команда /start отправляет информацию о сервере
- отображаются CPU, RAM, диск, uptime и load average
- токен загружается из переменной окружения BOT_TOKEN или .env
Зависимости:
pip install aiogram psutil python-dotenv
"""
from __future__ import annotations
import asyncio
import os
from datetime import datetime
from platform import uname, system
from typing import Final
import psutil
from aiogram import Bot, Dispatcher
from aiogram.client.default import DefaultBotProperties
from aiogram.filters import CommandStart
from aiogram.types import Message
from dotenv import load_dotenv
# ---------------------------------------------------------------------
# Загрузка переменных окружения
# ---------------------------------------------------------------------
load_dotenv()
BOT_TOKEN: Final[str | None] = os.getenv("BOT_TOKEN")
if BOT_TOKEN is None:
raise RuntimeError(
"Переменная окружения BOT_TOKEN не найдена.\n"
"Создайте файл .env и добавьте:\n"
"BOT_TOKEN=your_telegram_token"
)
# ---------------------------------------------------------------------
# Инициализация Telegram-бота
# ---------------------------------------------------------------------
bot: Bot = Bot(
token=BOT_TOKEN,
default=DefaultBotProperties(parse_mode="HTML"),
)
dp: Dispatcher = Dispatcher()
# ---------------------------------------------------------------------
# Системная информация
# ---------------------------------------------------------------------
def get_system_info() -> str:
"""
Собирает информацию о состоянии системы.
Возвращает:
str: форматированная строка с информацией о системе
"""
# Информация о системе
system_info = uname()
# Время загрузки системы
boot_time_timestamp: float = psutil.boot_time()
boot_time: str = datetime.fromtimestamp(
boot_time_timestamp
).strftime("%Y-%m-%d %H:%M:%S")
# Память
memory = psutil.virtual_memory()
# CPU
cpu_percent: float = psutil.cpu_percent(interval=1)
physical_cores: int | None = psutil.cpu_count(logical=False)
logical_cores: int | None = psutil.cpu_count(logical=True)
# Определяем корневой диск
root_path: str = "C:\\" if system() == "Windows" else "/"
disk = psutil.disk_usage(root_path)
# Load average (только UNIX)
load_str: str = ""
if hasattr(os, "getloadavg"):
load_avg = os.getloadavg()
load_str = (
f"└ Load average: "
f"{load_avg[0]:.2f}, {load_avg[1]:.2f}, {load_avg[2]:.2f}\n"
)
# Формирование сообщения
info: str = f"""
🖥️ Состояние сервера
OS: {system_info.system} {system_info.release}
Uptime: {boot_time}
CPU:
├ Ядер: {physical_cores} (логических: {logical_cores})
├ Загрузка: {cpu_percent}%
{load_str}RAM:
├ Всего: {memory.total / 1024 ** 3:.2f} GB
├ Используется: {memory.used / 1024 ** 3:.2f} GB
└ Свободно: {memory.available / 1024 ** 3:.2f} GB
Диск ({root_path}):
├ Всего: {disk.total / 1024 ** 3:.2f} GB
├ Используется: {disk.used / 1024 ** 3:.2f} GB
└ Свободно: {disk.free / 1024 ** 3:.2f} GB
""".strip()
return info
# ---------------------------------------------------------------------
# Обработчики
# ---------------------------------------------------------------------
@dp.message(CommandStart())
async def start_handler(message: Message) -> None:
"""
Обработчик команды /start.
Отправляет пользователю текущее состояние системы.
"""
system_info: str = get_system_info()
await message.answer(system_info)
# ---------------------------------------------------------------------
# Основной запуск
# ---------------------------------------------------------------------
async def main() -> None:
"""
Основная функция запуска бота.
Запускает polling и корректно завершает соединение.
"""
try:
await dp.start_polling(bot)
finally:
await bot.session.close()
if __name__ == "__main__":
asyncio.run(main())