from aiogram import Router, types from aiogram.types import CallbackQuery import logging from datetime import datetime from aiogram.enums.parse_mode import ParseMode import locale from instences.config import BASE_URL_FASTAPI import aiohttp from keyboard.keyboards import ( account_keyboard, popup_keyboard, tranhist_keyboard, confirm_popup_keyboard, guide_keyboard, balance_keyboard, ) locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8") router = Router() logger = logging.getLogger(__name__) async def call_api(method, endpoint, data=None): """ Выполняет HTTP-запрос к FastAPI. """ url = f"{BASE_URL_FASTAPI}{endpoint}" logger.info(f"Инициализация запроса: {method} {url} с данными {data}") try: async with aiohttp.ClientSession() as session: async with session.request(method, url, json=data) as response: logger.info( f"Получен ответ от {url}: статус {response.status}") if response.status in {200, 201}: result = await response.json() logger.debug(f"Ответ JSON: {result}") return result if response.status == 404: logger.debug(f"Код {response.status}, возвращаю ничего") return None logger.error( f"Ошибка в запросе: статус {response.status}, причина {response.reason}" ) return "ERROR" except Exception as e: logger.exception(f"Исключение при выполнении запроса к {url}: {e}") return "ERROR" @router.callback_query(lambda callback: callback.data == "profile") async def profile_callback_handler(callback: CallbackQuery): """ Профиль пользователя. Логика работы с API сохранена, переписан только текст/визуал. """ try: user_data = await call_api("GET", f"/user/{callback.from_user.id}") if not user_data: await callback.message.answer( "Произошла ошибка, попробуйте позже или свяжитесь с администрацией." ) await callback.answer() return sub_data = await call_api("GET", f"/subscription/{user_data['id']}/last") if sub_data == "ERROR" or not isinstance(sub_data, dict): sub_data = None balance_text = f"💰 Баланс: {user_data['balance']} ₽" username = callback.from_user.username or "пользователь" # Нет подписки if not sub_data: text = ( f"📜 Профиль {username}\n\n" f"{balance_text}\n\n" "У тебя пока нет активной подписки.\n" "Пополняй баланс и подключай тариф Lark, чтобы получить статус 🐣 или 🦅." ) else: expiry_date = sub_data.get("expiry_date") formatted_date = ( datetime.fromisoformat(expiry_date).strftime("%d %B %Y г.") if expiry_date else None ) is_expired = ( datetime.fromisoformat(expiry_date) < datetime.now() if expiry_date else True ) status_icon = "✖️" if is_expired else "☑️" profile_status = "🦅" if "Pro" in sub_data.get("plan", "") else "🐣" if not is_expired and formatted_date: sub_text = ( f"Подписка активна до {formatted_date} {status_icon}\n" f"Текущий статус: {profile_status}" ) else: sub_text = ( f"Подписка неактивна {status_icon}\n" "Ты можешь продлить её в разделе подписок." ) text = ( f"📜 Профиль {username} {profile_status}\n\n" f"{sub_text}\n\n" f"{balance_text}" ) await callback.message.edit_text(text, reply_markup=account_keyboard()) except Exception as e: logger.exception(f"Ошибка в обработчике профиля: {e}") await callback.message.answer("Произошла ошибка. Попробуйте позже.") finally: await callback.answer() @router.callback_query(lambda callback: callback.data == "balance") async def balance_callback_handler(callback: CallbackQuery): """ Обработчик callback_query для баланса. """ user_data = await call_api("GET", f"/user/{callback.from_user.id}") if not user_data: await callback.message.answer( "Произошла ошибка, попробуйте позже или свяжитесь с администрацией." ) await callback.answer() return await callback.message.edit_text( f"💰 Текущий баланс: {user_data['balance']} ₽\n\n" "Выбери сумму, на которую хочешь пополнить счёт.", reply_markup=balance_keyboard(), ) await callback.answer() @router.callback_query(lambda callback: callback.data == "popup") async def popup_callback_handler(callback: CallbackQuery): """ Обработчик callback_query для выбора суммы пополнения. """ user = await call_api("GET", f"/user/{callback.from_user.id}") if not user: await callback.message.answer( "Произошла ошибка, попробуйте позже или свяжитесь с администрацией." ) await callback.answer() return await callback.message.edit_text( "Выбери сумму для пополнения баланса.", reply_markup=popup_keyboard(), ) await callback.answer() @router.callback_query(lambda callback: callback.data == "tranhist") async def tranhist_callback_handler(callback: CallbackQuery): """ Обработчик callback_query для истории транзакций. (Логику и формат Markdown_V2 не трогаем, чтобы не поймать новые баги) """ user_data = await call_api("GET", f"/user/{callback.from_user.id}") if not user_data: await callback.message.edit_text("Вы еще не зарегистрированы.") await callback.answer() return try: transactions = await call_api("GET", f"/user/{user_data['id']}/transactions") if not transactions: await callback.message.edit_text( "У вас нет транзакций.", reply_markup=tranhist_keyboard() ) await callback.answer() return result = "Ваши транзакции:```\n" for count, tran in enumerate(transactions, start=1): dt = datetime.fromisoformat(tran["created_at"]).strftime( "%d.%m.%Y %H:%M:%S" ) result += f"{count}. Сумма: {tran['amount']}, Дата: {dt}\n" if len(result) > 4000: result += "...\nСлишком много транзакций для отображения." break result += "```" await callback.message.edit_text( result, parse_mode=ParseMode.MARKDOWN_V2, reply_markup=tranhist_keyboard(), ) except Exception as e: logger.error(f"Ошибка обработки транзакций: {e}") await callback.message.edit_text("Произошла ошибка. Попробуйте позже.") finally: await callback.answer() @router.callback_query(lambda callback: callback.data.startswith("popup:")) async def popup_confirm_callback_handler(callback: CallbackQuery): """ Обработчик подтверждения пополнения баланса. """ data = callback.data.split(":") popup_info = data[1] result = await call_api( "POST", f"/user/{callback.from_user.id}/balance/{float(popup_info)}", ) if result == "ERROR": await callback.message.answer( "Произошла ошибка, попробуйте позже или свяжитесь с администрацией." ) await callback.answer() return text = f"✅ Баланс пополнен на {popup_info} ₽. Спасибо, что остаёшься с Lark." await callback.message.edit_text( text=text, reply_markup=confirm_popup_keyboard() ) await callback.answer() @router.callback_query(lambda callback: callback.data == "guide") async def guide_callback_handler(callback: CallbackQuery): """ Обработчик callback_query для руководства. """ await callback.message.edit_text( "Выбери платформу, для которой нужно руководство по подключению:", reply_markup=guide_keyboard(), ) await callback.answer()