from aiogram import types, Dispatcher from aiogram.filters import Command import aiohttp import logging from datetime import datetime from instences.config import BASE_URL_FASTAPI import locale locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8") from keyboard.keyboards import subhist_keyboard,confirm_popup_keyboard,tarif_confirm_keyboard, popup_keyboard, main_keyboard,faq_keyboard, account_keyboard, buy_keyboard,balance_keyboard,guide_keyboard,tarif_Lark_keyboard,tarif_Lark_pro_keyboard,tranhist_keyboard logger = logging.getLogger(__name__) # Инициализируем менеджер базы данных async def call_api(method, endpoint, data=None): """ Выполняет HTTP-запрос к FastAPI. :param method: HTTP метод (GET, POST, и т.д.) :param endpoint: конечная точка API :param data: тело запроса (если необходимо) :return: JSON-ответ или "ERROR" при неуспехе """ 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 logger.error(f"Ошибка в запросе: статус {response.status}, причина {response.reason}") return "ERROR" except Exception as e: logger.exception(f"Исключение при выполнении запроса к {url}: {e}") return "ERROR" async def popup_command(message: types.Message): """ Обработчик команды для отправки popup-сообщения. """ await message.answer("HAHAHHAHAHAHAHHAHA", reply_markup=popup_keyboard()) async def subhist_command(message: types.Message): """ Обработчик команды для отправки истории подписок. """ await message.answer("subhist", reply_markup=subhist_keyboard()) async def start_command(message: types.Message): """ Обработчик команды /start. """ await message.answer(f"""Приветствуем в рядах птенец {message.from_user.username}🐣 \nСкорее пополни баланс, приобрети подписку и получай доступ к TikTok, YouTube, Discord, Instagram на всех устройствах без ограничений ❕❕❕ \nОзнакомься с руководством по пользованию и выбери подходящий тариф 🦅 \nСледи за акциями, спец-предложениями, а также розыгрышами по ссылке ниже \n👇👇👇 \nhttps://t.me/+0z5xqn3F1m02OTJi \nС любовью ваши пернатые разработчики 🤍🤍🤍""", reply_markup=main_keyboard()) async def start_callback_handler(callback: types.CallbackQuery): """ Обработчик callback_query с data="base". """ await callback.message.edit_text( f"""Приветствуем в рядах птенец {callback.from_user.username}🐣 \nСкорее пополни баланс, приобрети подписку и получай доступ к TikTok, YouTube, Discord, Instagram на всех устройствах без ограничений ❕❕❕ \nОзнакомься с руководством по пользованию и выбери подходящий тариф 🦅 \nСледи за акциями, спец-предложениями, а также розыгрышами по ссылке ниже \n👇👇👇 \nhttps://t.me/+0z5xqn3F1m02OTJi \nС любовью ваши пернатые разработчики 🤍🤍🤍""", reply_markup=main_keyboard() ) async def profile_callback_handler(callback: types.CallbackQuery): try: # Создание пользователя user_data = await call_api("POST", "/user/create", {"telegram_id": callback.from_user.id}) if user_data == "ERROR" or not isinstance(user_data, dict): raise ValueError("Не удалось создать пользователя.") # Получение подписки 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']} ₽" # Если подписки нет if not sub_data: text = f"Профиль {callback.from_user.username}:\n{balance_text}\nПополните баланс и приобретите подписку чтобы получить активный статус (🐣,🦅)" else: # Получение даты окончания подписки expiry_date = sub_data.get("expiry_date") formatted_date = None # Преобразование времени в презентабельный формат if expiry_date: expiry_datetime = datetime.fromisoformat(expiry_date) formatted_date = expiry_datetime.strftime("%d %B %Y г.") # Проверка на истечение подписки is_expired = datetime.fromisoformat(expiry_date) < datetime.now() if expiry_date else True plan_type = sub_data.get("plan", "") status_icon = "✖️" if is_expired else "☑️" # Определение статуса if "Pro" in plan_type: profile_status = "🦅" else: profile_status = "🐣" # Формирование текста профиля sub_text = ( f"Ваша подписка действует до {formatted_date} {status_icon}" if not is_expired else f"Статус подписки: {status_icon}" ) text = f"Профиль {callback.from_user.username} {profile_status}:\n{sub_text}\n{balance_text}" # Отправка текста пользователю await callback.message.edit_text(text, reply_markup=account_keyboard()) except ValueError as e: await callback.message.answer(f"Ошибка: {e}") except Exception as e: logger.exception(f"Ошибка в обработчике профиля: {e}") await callback.message.answer("Произошла ошибка. Попробуйте позже.") finally: await callback.answer() async def balance_callback_handler(callback: types.CallbackQuery): 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']} ₽. Выберите сумму для пополнения 🐥", reply_markup=balance_keyboard() ) await callback.answer() async def popup_callback_handler(callback: types.CallbackQuery): """ Обработчик callback_query с data="popup". """ user = await call_api("POST", "/user/create", {"telegram_id": callback.from_user.id}) if user == "ERROR": await callback.message.answer( "Произошла ошибка, попробуйте позже или свяжитесь с администрацией." ) await callback.answer() return if user: await callback.message.edit_text( f"Работает в режиме теста!!!", reply_markup=popup_keyboard() ) else: await callback.message.edit_text("Вы еще не зарегистрированы.") await callback.answer() async def tranhist_callback_handler(callback: types.CallbackQuery): 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 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): result += f"{count}. Сумма: {tran['amount']}, Дата: {tran['created_at']}\n" await callback.message.edit_text(result, reply_markup=tranhist_keyboard()) await callback.answer() async def subhist_callback_handler(callback: types.CallbackQuery): 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 subscriptions = await call_api("GET", f"/subscription/{user_data['id']}/last") if not subscriptions: await callback.message.edit_text("У вас нет активных подписок.", reply_markup=account_keyboard()) await callback.answer() return result = "Ваши подписки:\n" for count, sub in enumerate(subscriptions, start=1): result += f"{count}. Тариф: {sub['plan']}, Истекает: {sub['expiry_date']}\n" await callback.message.edit_text(result, reply_markup=account_keyboard()) await callback.answer() async def buy_subscription_callback_handler(callback: types.CallbackQuery): """ Обработчик callback_query с data="buy_subscription". """ await callback.message.edit_text( f"Ознакомься с условиями в вкладке \"О тарифах\" и выбери подходящий 🦅", reply_markup=buy_keyboard() ) async def guide_callback_handler(callback:types.CallbackQuery): """ Обработчик callback_query с data="guide". """ await callback.message.edit_text( f"Руководство по использованию продкута что мы высрали;)", reply_markup=guide_keyboard() ) async def subs_callback_handler(callback: types.CallbackQuery): """ Обработчик callback_query с data="subs". """ await callback.message.edit_text( f"Подписки птенчик", reply_markup=tarif_Lark_keyboard() ) async def subs_pro_callback_handler(callback: types.CallbackQuery): """ Обработчик callback_query с data="subs_pro". """ await callback.message.edit_text( f"Подписки птенчик ПРО", reply_markup=tarif_Lark_pro_keyboard() ) # async def about_tarifs_callback_handler(callback: types.CallbackQuery): # """ # Обработчик callback_query с data="about_tarifs". # """ # await callback.message.edit_text( # f"Бла бла бла, хуйня на хуйне", # reply_markup=about_tarifs_keyboard() # ) async def faq_callback_handler(callback:types.CallbackQuery): """ Обработчик callback_query с data="faq". """ await callback.message.edit_text( f"FAQ YOU", reply_markup=faq_keyboard() ) async def lark_tariff_callback_handler(callback: types.CallbackQuery): """ Обработчик для выбора тарифа Lark. """ data = callback.data.split(":") tariff_name = data[0] tariff_class = data[1] tariff_time = int(data[2]) # Определение окончания для месяцев if tariff_time == 1: months = f"{tariff_time} месяц" elif 2 <= tariff_time <= 4: months = f"{tariff_time} месяца" else: months = f"{tariff_time} месяцев" text = f"Тариф {tariff_name} на {months}. Продолжите покупку..." # Рендеринг клавиатуры keyboard = tarif_confirm_keyboard(tariff_name, tariff_time, tariff_class) await callback.message.edit_text(text=text, reply_markup=keyboard) async def popup_confirm_callback_handler(callback: types.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}. P.S. Мы завтра закрываемся" await callback.message.edit_text(text=text, reply_markup=confirm_popup_keyboard()) async def confirm_callback_handler(callback: types.CallbackQuery): data = callback.data.split(":")[1] tariff_info = data.split("_") plan_id = f"{tariff_info[0]}_{tariff_info[1]}_{tariff_info[2]}" result = await call_api("POST", "/subscription/buy", {"telegram_id": callback.from_user.id, "plan_id": plan_id}) error_detail = result.get("detail",{}) error_code = error_detail.get("code", "") if error_code == "ERROR": await callback.message.edit_text("Произошла ошибка при оформлении подписки.") if error_code == "INSUFFICIENT_FUNDS": await callback.message.edit_text("Денег на вашем балансе не достаточно.") if error_code == "TARIFF_NOT_FOUND": await callback.message.edit_text("Ваш тариф не найден.") if error_code == "ACTIVE_SUBSCRIPTION_EXISTS": await callback.message.edit_text("Вы уже имеете активную подписку.") else: await callback.message.edit_text(f"Подписка успешно оформлена!") await callback.answer() def register_handlers(dp: Dispatcher): """ Регистрация хэндлеров в диспетчере. """ dp.callback_query.register(popup_callback_handler, lambda c: c.data == "popup") dp.callback_query.register(start_callback_handler, lambda c: c.data == "base") dp.callback_query.register(subs_callback_handler, lambda c: c.data == "subs") dp.callback_query.register(subs_pro_callback_handler, lambda c: c.data == "subs_pro") dp.callback_query.register(faq_callback_handler, lambda c: c.data == "faq") dp.callback_query.register(profile_callback_handler, lambda c: c.data == "profile") dp.callback_query.register(tranhist_callback_handler, lambda c: c.data == "tranhist") dp.callback_query.register(buy_subscription_callback_handler, lambda c: c.data == "buy_subscription") dp.message.register(start_command, Command("start")) dp.callback_query.register(balance_callback_handler, lambda c: c.data == "balance") dp.callback_query.register(guide_callback_handler, lambda c: c.data == "guide") # dp.callback_query.register(about_tarifs_callback_handler, lambda c: c.data == "about_tarifs") dp.callback_query.register(lark_tariff_callback_handler, lambda c: c.data.startswith("Lark:")) dp.callback_query.register(confirm_callback_handler, lambda c: c.data.startswith("confirm:")) dp.callback_query.register(popup_confirm_callback_handler, lambda c: c.data.startswith("popup:"))