from __future__ import annotations import logging from aiogram import F, Router from aiogram.filters import Command from aiogram.types import Message from bot.access import access_denied_text, is_allowed from bot.config import Settings from bot.filters import NotLinked from bot.ha_client import HaApiError, HaClient from bot.storage import Storage router = Router() logger = logging.getLogger(__name__) async def _link_token(message: Message, settings: Settings, storage: Storage, token: str) -> None: if not message.from_user: return telegram_id = message.from_user.id client = HaClient(settings.ha_api_base_url) try: login_result = await client.login(token) except HaApiError as exc: await message.answer(f"Не удалось привязать токен: {exc}") return except Exception: logger.exception("Login failed for telegram_id=%s", telegram_id) await message.answer("Ошибка соединения с Home Assistant. Проверь HA_API_BASE_URL на сервере бота.") return api_token = str(login_result.get("token") or token).strip() user_info = login_result.get("user") or {} ha_user_id = int(user_info.get("id") or 0) display_name = str(user_info.get("display_name") or user_info.get("username") or "") username = str(user_info.get("username") or "") ha_client = HaClient(settings.ha_api_base_url, api_token) try: session_id = await ha_client.find_or_create_telegram_session() except Exception: logger.exception("Session setup failed for telegram_id=%s", telegram_id) await message.answer("Токен принят, но не удалось создать чат-сессию на сервере.") return await storage.link_user( telegram_id=telegram_id, api_token=api_token, ha_user_id=ha_user_id, display_name=display_name, username=username, session_id=session_id, ) name = display_name or username or f"user #{ha_user_id}" await message.answer( f"Готово! Привязан аккаунт: {name}\n" f"Чат-сессия Telegram: #{session_id}\n\n" "Рекомендую удалить сообщение с токеном из истории чата.\n" "Можешь писать ассистенту обычными сообщениями." ) @router.message(Command("logout")) async def cmd_logout(message: Message, settings: Settings, storage: Storage) -> None: if not is_allowed(message, settings): await message.answer(access_denied_text()) return if not message.from_user: return removed = await storage.unlink_user(message.from_user.id) if removed: await message.answer("API-токен отвязан. Чтобы снова пользоваться ботом, отправь новый токен.") else: await message.answer("Токен не был привязан.") @router.message(Command("whoami")) async def cmd_whoami(message: Message, settings: Settings, storage: Storage) -> None: if not is_allowed(message, settings): await message.answer(access_denied_text()) return if not message.from_user: return linked = await storage.get_user(message.from_user.id) if not linked: await message.answer("Токен не привязан. Отправь API-токен или /start.") return client = HaClient(settings.ha_api_base_url, linked.api_token) try: me = await client.me() user = me.get("user") or {} name = user.get("display_name") or user.get("username") or linked.display_name username = user.get("username") or linked.username await message.answer( f"Home Assistant: {name} (@{username})\n" f"HA user id: {linked.ha_user_id}\n" f"Telegram-сессия: #{linked.session_id}" ) except Exception: await message.answer( f"Привязан локально: {linked.display_name or linked.username}\n" f"Telegram-сессия: #{linked.session_id}\n" "(Не удалось проверить токен на сервере — возможно, он отозван.)" ) @router.message(F.text & ~F.text.startswith("/"), NotLinked()) async def handle_possible_token(message: Message, settings: Settings, storage: Storage) -> None: if not is_allowed(message, settings): return if not message.from_user or not message.text: return token = message.text.strip() if len(token) < 8: return await _link_token(message, settings, storage, token)