From a2693393d3749ca1a6b3db5e8ca6da92e209645f Mon Sep 17 00:00:00 2001 From: Vladislav Lisitsyn <114032530+Vladless@users.noreply.github.com> Date: Wed, 12 Mar 2025 10:23:51 +0300 Subject: [PATCH] =?UTF-8?q?Revert=20"=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BD=D0=BE=D0=B2=D0=BE=D0=B3=D0=BE?= =?UTF-8?q?=20middleware=20=D0=B8=20=D1=80=D0=B5=D1=84=D0=B0=D0=BA=D1=82?= =?UTF-8?q?=D0=BE=D1=80=D0=B8=D0=BD=D0=B3"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/{coupons/router.py => coupons.py} | 52 ++--- handlers/coupons/__init__.py | 6 - handlers/donate.py | 8 +- handlers/instructions/instructions.py | 21 +- handlers/keys/key_management.py | 22 +- handlers/keys/keys.py | 9 +- handlers/pay.py | 6 +- handlers/payments/robokassa_pay.py | 10 +- handlers/profile.py | 255 +++++++++++++++++++++ handlers/profile/__init__.py | 6 - handlers/profile/router.py | 175 -------------- keyboards/coupons/__init__.py | 4 - keyboards/coupons/keyboards.py | 14 -- keyboards/profile/__init__.py | 4 - keyboards/profile/keyboards.py | 78 ------- middlewares/__init__.py | 2 - middlewares/message_handler.py | 46 ---- 17 files changed, 317 insertions(+), 401 deletions(-) rename handlers/{coupons/router.py => coupons.py} (61%) delete mode 100644 handlers/coupons/__init__.py create mode 100644 handlers/profile.py delete mode 100644 handlers/profile/__init__.py delete mode 100644 handlers/profile/router.py delete mode 100644 keyboards/coupons/__init__.py delete mode 100644 keyboards/coupons/keyboards.py delete mode 100644 keyboards/profile/__init__.py delete mode 100644 keyboards/profile/keyboards.py delete mode 100644 middlewares/message_handler.py diff --git a/handlers/coupons/router.py b/handlers/coupons.py similarity index 61% rename from handlers/coupons/router.py rename to handlers/coupons.py index cb1d20ed..fc230c49 100644 --- a/handlers/coupons/router.py +++ b/handlers/coupons.py @@ -3,7 +3,8 @@ from typing import Any from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import CallbackQuery, Message +from aiogram.types import CallbackQuery, InlineKeyboardButton, Message +from aiogram.utils.keyboard import InlineKeyboardBuilder from database import ( check_coupon_usage, @@ -12,8 +13,8 @@ from database import ( update_balance, update_coupon_usage_count, ) -from handlers.utils import edit_or_send_message -from keyboards.coupons import get_coupon_keyboard + +from .utils import edit_or_send_message class CouponActivationState(StatesGroup): @@ -25,16 +26,14 @@ router = Router() @router.callback_query(F.data == "activate_coupon") @router.message(F.text == "/activate_coupon") -async def handle_activate_coupon(state: FSMContext, target_message: Message): - """ - Обрабатывает запрос на активацию купона. +async def handle_activate_coupon(callback_query_or_message: Message | CallbackQuery, state: FSMContext): + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) - Args: - state: Контекст состояния FSM. - chat_id: ID чата (добавлено middleware). - target_message: Целевое сообщение для ответа (добавлено middleware). - """ - builder = get_coupon_keyboard() + if isinstance(callback_query_or_message, CallbackQuery): + target_message = callback_query_or_message.message + else: + target_message = callback_query_or_message await edit_or_send_message( target_message=target_message, @@ -47,37 +46,18 @@ async def handle_activate_coupon(state: FSMContext, target_message: Message): @router.message(CouponActivationState.waiting_for_coupon_code) -async def process_coupon_code(message: Message, state: FSMContext, session: Any, chat_id: int): - """ - Обрабатывает введенный код купона. - - Args: - message: Сообщение с кодом купона. - state: Контекст состояния FSM. - session: Сессия базы данных. - chat_id: ID чата (добавлено middleware). - """ +async def process_coupon_code(message: Message, state: FSMContext, session: Any): coupon_code = message.text.strip() - activation_result = await activate_coupon(chat_id, coupon_code, session) + activation_result = await activate_coupon(message.chat.id, coupon_code, session) - builder = get_coupon_keyboard() + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) await message.answer(activation_result, reply_markup=builder.as_markup()) await state.clear() -async def activate_coupon(user_id: int, coupon_code: str, session: Any) -> str: - """ - Активирует купон для пользователя. - - Args: - user_id: ID пользователя. - coupon_code: Код купона. - session: Сессия базы данных. - - Returns: - str: Сообщение о результате активации. - """ +async def activate_coupon(user_id: int, coupon_code: str, session: Any): coupon_record = await get_coupon_by_code(coupon_code, session) if not coupon_record: diff --git a/handlers/coupons/__init__.py b/handlers/coupons/__init__.py deleted file mode 100644 index 9095f9df..00000000 --- a/handlers/coupons/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from aiogram import Router - -from .router import router - - -__all__ = ["router"] diff --git a/handlers/donate.py b/handlers/donate.py index ce3cbd70..49fe0f3d 100644 --- a/handlers/donate.py +++ b/handlers/donate.py @@ -20,7 +20,7 @@ router = Router() @router.callback_query(F.data == "donate") -async def process_donate(callback_query: CallbackQuery, state: FSMContext, target_message: Message): +async def process_donate(callback_query: CallbackQuery, state: FSMContext): await state.clear() builder = InlineKeyboardBuilder() @@ -40,20 +40,20 @@ async def process_donate(callback_query: CallbackQuery, state: FSMContext, targe ) await edit_or_send_message( - target_message=target_message, + target_message=callback_query.message, text=text, reply_markup=builder.as_markup(), ) @router.callback_query(F.data == "enter_custom_donate_amount") -async def process_enter_donate_amount(callback_query: CallbackQuery, state: FSMContext, target_message: Message): +async def process_enter_donate_amount(callback_query: CallbackQuery, state: FSMContext): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="donate")) text = "💸 Введите сумму доната в рублях:" await edit_or_send_message( - target_message=target_message, + target_message=callback_query.message, text=text, reply_markup=builder.as_markup(), ) diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index 87a1034e..56343f64 100644 --- a/handlers/instructions/instructions.py +++ b/handlers/instructions/instructions.py @@ -27,7 +27,7 @@ router = Router() @router.callback_query(F.data == "instructions") @router.message(F.text == "/instructions") -async def send_instructions(target_message: Message): +async def send_instructions(callback_query_or_message: CallbackQuery | Message): instructions_message = INSTRUCTIONS image_path = os.path.join("img", "instructions.jpg") @@ -35,6 +35,11 @@ async def send_instructions(target_message: Message): builder.row(InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL)) builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) + if isinstance(callback_query_or_message, CallbackQuery): + target_message = callback_query_or_message.message + else: + target_message = callback_query_or_message + await edit_or_send_message( target_message=target_message, text=instructions_message, @@ -44,14 +49,14 @@ async def send_instructions(target_message: Message): @router.callback_query(F.data.startswith("connect_pc|")) -async def process_connect_pc(callback_query: CallbackQuery, session: Any, target_message: Message): +async def process_connect_pc(callback_query: CallbackQuery, session: Any): key_name = callback_query.data.split("|")[1] record = await get_key_details(key_name, session) if not record: builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) await edit_or_send_message( - target_message=target_message, + target_message=callback_query.message, text="❌ Ключ не найден. Проверьте имя ключа. 🔍", reply_markup=builder.as_markup(), media_path=None, @@ -70,7 +75,7 @@ async def process_connect_pc(callback_query: CallbackQuery, session: Any, target builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) await edit_or_send_message( - target_message=target_message, + target_message=callback_query.message, text=instruction_message, reply_markup=builder.as_markup(), media_path=None, @@ -78,7 +83,7 @@ async def process_connect_pc(callback_query: CallbackQuery, session: Any, target @router.callback_query(F.data.startswith("connect_tv|")) -async def process_connect_tv(callback_query: CallbackQuery, target_message: Message): +async def process_connect_tv(callback_query: CallbackQuery): key_name = callback_query.data.split("|")[1] builder = InlineKeyboardBuilder() @@ -87,7 +92,7 @@ async def process_connect_tv(callback_query: CallbackQuery, target_message: Mess builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) await edit_or_send_message( - target_message=target_message, + target_message=callback_query.message, text=CONNECT_TV_TEXT, reply_markup=builder.as_markup(), media_path=None, @@ -96,7 +101,7 @@ async def process_connect_tv(callback_query: CallbackQuery, target_message: Mess @router.callback_query(F.data.startswith("continue_tv|")) -async def process_continue_tv(callback_query: CallbackQuery, session: Any, target_message: Message): +async def process_continue_tv(callback_query: CallbackQuery, session: Any): key_name = callback_query.data.split("|")[1] record = await get_key_details(key_name, session) @@ -109,5 +114,5 @@ async def process_continue_tv(callback_query: CallbackQuery, session: Any, targe builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) await edit_or_send_message( - target_message=target_message, text=message_text, reply_markup=builder.as_markup(), media_path=None + target_message=callback_query.message, text=message_text, reply_markup=builder.as_markup(), media_path=None ) diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index a5d5401a..ed8344e7 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -71,18 +71,16 @@ class Form(FSMContext): @router.callback_query(F.data == "create_key") -async def confirm_create_new_key( - callback_query: CallbackQuery, state: FSMContext, session: Any, target_message: Message -): +async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContext, session: Any): tg_id = callback_query.message.chat.id - await handle_key_creation(tg_id, state, session, target_message) + await handle_key_creation(tg_id, state, session, callback_query) async def handle_key_creation( tg_id: int, state: FSMContext, session: Any, - target_message: Message, + message_or_query: Message | CallbackQuery, ): """Создание ключа с учётом выбора тарифного плана.""" current_time = datetime.now(moscow_tz) @@ -96,12 +94,14 @@ async def handle_key_creation( updated = await update_trial(tg_id, 1, session) if updated: await edit_or_send_message( - target_message=target_message, + target_message=message_or_query + if isinstance(message_or_query, Message) + else message_or_query.message, text="⏳ Пожалуйста, подождите, создаем вам подключение...", reply_markup=None, ) - await create_key(tg_id, expiry_time, state, session, target_message) + await create_key(tg_id, expiry_time, state, session, message_or_query) return else: logger.error(f"Не удалось обновить статус триального периода для пользователя {tg_id}.") @@ -122,6 +122,11 @@ async def handle_key_creation( ) builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) + if isinstance(message_or_query, CallbackQuery): + target_message = message_or_query.message + else: + target_message = message_or_query + await edit_or_send_message( target_message=target_message, text="💳 Выберите тарифный план для создания нового ключа:", @@ -190,12 +195,13 @@ async def create_key( expiry_time: datetime, state: FSMContext | None, session: Any, - target_message: Message, + message_or_query: Message | CallbackQuery | None = None, old_key_name: str = None, plan: int = None, ): """Создаёт ключ с заданным сроком действия.""" + target_message = message_or_query.message if isinstance(message_or_query, CallbackQuery) else message_or_query if not await check_connection_exists(tg_id): await add_connection(tg_id, balance=0.0, trial=0, session=session) logger.info(f"[Connection] Подключение создано для пользователя {tg_id}") diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 5e9eaaed..f5cb6148 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -74,9 +74,14 @@ router = Router() @router.callback_query(F.data == "view_keys") @router.message(F.text == "/subs") -async def process_callback_or_message_view_keys(session: Any, target_message: Message, chat_id: int): +async def process_callback_or_message_view_keys(callback_query_or_message: Message | CallbackQuery, session: Any): + if isinstance(callback_query_or_message, CallbackQuery): + target_message = callback_query_or_message.message + else: + target_message = callback_query_or_message + try: - records = await get_keys(chat_id, session) + records = await get_keys(target_message.chat.id, session) inline_keyboard, response_message = build_keys_response(records) image_path = os.path.join("img", "pic_keys.jpg") diff --git a/handlers/pay.py b/handlers/pay.py index 954c090b..cc041a3f 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -1,5 +1,5 @@ from aiogram import F, Router -from aiogram.types import CallbackQuery, InlineKeyboardButton, Message +from aiogram.types import CallbackQuery, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder from config import ( CRYPTO_BOT_ENABLE, @@ -17,7 +17,7 @@ router = Router() @router.callback_query(F.data == "pay") -async def handle_pay(callback_query: CallbackQuery, target_message: Message): +async def handle_pay(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() if YOOKASSA_ENABLE: @@ -71,7 +71,7 @@ async def handle_pay(callback_query: CallbackQuery, target_message: Message): ) await edit_or_send_message( - target_message=target_message, + target_message=callback_query.message, text=payment_text, reply_markup=builder.as_markup(), media_path=None, diff --git a/handlers/payments/robokassa_pay.py b/handlers/payments/robokassa_pay.py index 1c67ec16..9437e05d 100644 --- a/handlers/payments/robokassa_pay.py +++ b/handlers/payments/robokassa_pay.py @@ -75,18 +75,18 @@ async def process_callback_pay_robokassa(callback_query: types.CallbackQuery, st builder.row( InlineKeyboardButton( text=PAYMENT_OPTIONS[i]["text"], - callback_data=f"robokassa_amount|{PAYMENT_OPTIONS[i]['callback_data']}", + callback_data=f'robokassa_amount|{PAYMENT_OPTIONS[i]["callback_data"]}', ), InlineKeyboardButton( text=PAYMENT_OPTIONS[i + 1]["text"], - callback_data=f"robokassa_amount|{PAYMENT_OPTIONS[i + 1]['callback_data']}", + callback_data=f'robokassa_amount|{PAYMENT_OPTIONS[i + 1]["callback_data"]}', ), ) else: builder.row( InlineKeyboardButton( text=PAYMENT_OPTIONS[i]["text"], - callback_data=f"robokassa_amount|{PAYMENT_OPTIONS[i]['callback_data']}", + callback_data=f'robokassa_amount|{PAYMENT_OPTIONS[i]["callback_data"]}', ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="pay")) @@ -100,12 +100,12 @@ async def process_callback_pay_robokassa(callback_query: types.CallbackQuery, st logger.info(f"Created new connection for user {tg_id} with balance 0.0.") await callback_query.message.delete() - + new_message = await callback_query.message.answer( text="Выберите сумму пополнения:", reply_markup=builder.as_markup(), ) - await state.update_data(message_id=new_message.message_id, chat_id=new_message.chat.id) + await state.update_data(message_id=new_message.message_id, chat_id=new_message.chat.id) await state.set_state(ReplenishBalanceState.choosing_amount_robokassa) logger.info(f"Displayed amount selection for user {tg_id}.") diff --git a/handlers/profile.py b/handlers/profile.py new file mode 100644 index 00000000..b592c192 --- /dev/null +++ b/handlers/profile.py @@ -0,0 +1,255 @@ +import html +import os + +from typing import Any + +import asyncpg + +from aiogram import F, Router +from aiogram.enums import ParseMode +from aiogram.fsm.context import FSMContext +from aiogram.types import ( + CallbackQuery, + InlineKeyboardButton, + InlineQuery, + InlineQueryResultArticle, + InputTextMessageContent, + Message, +) +from aiogram.utils.keyboard import InlineKeyboardBuilder +from config import ( + DATABASE_URL, + INLINE_MODE, + INSTRUCTIONS_BUTTON, + NEWS_MESSAGE, + REFERRAL_OFFERS, + RENEWAL_PLANS, + TRIAL_TIME, + USERNAME_BOT, +) + +from database import get_balance, get_key_count, get_last_payments, get_referral_stats, get_trial +from handlers.buttons.profile import ( + ADD_SUB, + BALANCE, + BALANCE_HISTORY, + GIFTS, + INSTRUCTIONS, + INVITE, + MAIN_MENU, + MY_SUBS, + PAYMENT, +) +from handlers.texts import get_referral_link, invite_message_send, profile_message_send +from keyboards.admin.panel_kb import AdminPanelCallback +from logger import logger + +from .utils import edit_or_send_message + + +router = Router() + + +@router.callback_query(F.data == "profile") +@router.message(F.text == "/profile") +async def process_callback_view_profile( + callback_query_or_message: Message | CallbackQuery, + state: FSMContext, + admin: bool, +): + if isinstance(callback_query_or_message, CallbackQuery): + chat_id = callback_query_or_message.message.chat.id + username = html.escape(callback_query_or_message.from_user.full_name) + target_message = callback_query_or_message.message + else: + chat_id = callback_query_or_message.chat.id + username = html.escape(callback_query_or_message.from_user.full_name) + target_message = callback_query_or_message + + image_path = os.path.join("img", "profile.jpg") + logger.info(f"Переход в профиль. Используется изображение: {image_path}") + + key_count = await get_key_count(chat_id) + balance = await get_balance(chat_id) or 0 + + conn = await asyncpg.connect(DATABASE_URL) + try: + trial_status = await get_trial(chat_id, conn) + + profile_message = profile_message_send(username, chat_id, int(balance), key_count) + if key_count == 0: + profile_message += ( + "\n
🔧 Нажмите кнопку ➕ Подписка, чтобы настроить VPN-подключение
" + ) + else: + profile_message += f"\n
{NEWS_MESSAGE}
" + + builder = InlineKeyboardBuilder() + if trial_status == 0 or key_count == 0: + builder.row(InlineKeyboardButton(text=ADD_SUB, callback_data="create_key")) + else: + builder.row(InlineKeyboardButton(text=MY_SUBS, callback_data="view_keys")) + builder.row(InlineKeyboardButton(text=BALANCE, callback_data="balance")) + builder.row( + InlineKeyboardButton(text=INVITE, callback_data="invite"), + InlineKeyboardButton(text=GIFTS, callback_data="gifts"), + ) + if INSTRUCTIONS_BUTTON: + builder.row(InlineKeyboardButton(text=INSTRUCTIONS, callback_data="instructions")) + if admin: + builder.row( + InlineKeyboardButton(text="🔧 Администратор", callback_data=AdminPanelCallback(action="admin").pack()) + ) + + builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="start")) + + await edit_or_send_message( + target_message=target_message, + text=profile_message, + reply_markup=builder.as_markup(), + media_path=image_path, + disable_web_page_preview=False, + force_text=True, + ) + finally: + await conn.close() + + +@router.callback_query(F.data == "balance") +async def balance_handler(callback_query: CallbackQuery, session: Any): + result = await session.fetchrow( + "SELECT balance FROM connections WHERE tg_id = $1", + callback_query.from_user.id, + ) + balance = result["balance"] if result else 0.0 + balance = int(balance) + + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay")) + builder.row(InlineKeyboardButton(text=BALANCE_HISTORY, callback_data="balance_history")) + builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) + + text = f"Управление вашим балансом 💰\n\nВаш баланс: {balance}" + await edit_or_send_message( + target_message=callback_query.message, + text=text, + reply_markup=builder.as_markup(), + media_path=None, + disable_web_page_preview=False, + ) + + +@router.callback_query(F.data == "balance_history") +async def balance_history_handler(callback_query: CallbackQuery, session: Any): + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay")) + builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) + + records = await get_last_payments(callback_query.from_user.id, session) + + if records: + history_text = "📊 Последние 3 операции с балансом:\n\n" + for record in records: + amount = record["amount"] + payment_system = record["payment_system"] + status = record["status"] + date = record["created_at"].strftime("%Y-%m-%d %H:%M:%S") + history_text += ( + f"Сумма: {amount}₽\n" + f"Способ оплаты: {payment_system}\n" + f"Статус: {status}\n" + f"Дата: {date}\n\n" + ) + else: + history_text = "❌ У вас пока нет операций с балансом." + + await edit_or_send_message( + target_message=callback_query.message, + text=history_text, + reply_markup=builder.as_markup(), + media_path=None, + disable_web_page_preview=False, + ) + + +@router.message(F.text == "/tariffs") +@router.callback_query(F.data == "view_tariffs") +async def view_tariffs_handler(callback_query: CallbackQuery): + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) + + image_path = os.path.join("img", "tariffs.jpg") + tariffs_message = "🚀 Доступные тарифы VPN:\n\n" + "\n".join([ + f"{months} {'месяц' if months == '1' else 'месяца' if int(months) in [2, 3, 4] else 'месяцев'}: " + f"{RENEWAL_PLANS[months]['price']} " + f"{'💳' if months == '1' else '🌟' if months == '3' else '🔥' if months == '6' else '🚀'} рублей" + for months in sorted(RENEWAL_PLANS.keys(), key=int) + ]) + + await edit_or_send_message( + target_message=callback_query.message, + text=tariffs_message, + reply_markup=builder.as_markup(), + media_path=image_path, + disable_web_page_preview=False, + ) + + +@router.callback_query(F.data == "invite") +@router.message(F.text == "/invite") +async def invite_handler(callback_query_or_message: Message | CallbackQuery): + chat_id = None + if isinstance(callback_query_or_message, CallbackQuery): + chat_id = callback_query_or_message.message.chat.id + target_message = callback_query_or_message.message + else: + chat_id = callback_query_or_message.chat.id + target_message = callback_query_or_message + + referral_link = get_referral_link(chat_id) + referral_stats = await get_referral_stats(chat_id) + invite_message = invite_message_send(referral_link, referral_stats) + image_path = os.path.join("img", "pic_invite.jpg") + + builder = InlineKeyboardBuilder() + if INLINE_MODE: + builder.button(text="👥 Пригласить друга", switch_inline_query="invite") + else: + invite_text = f"\nПриглашаю тебя пользоваться действительно быстрым VPN вместе:\n\n{referral_link}" + builder.button(text="👥 Пригласить друга", switch_inline_query=invite_text) + builder.button(text="👤 Личный кабинет", callback_data="profile") + builder.adjust(1) + + await edit_or_send_message( + target_message=target_message, + text=invite_message, + reply_markup=builder.as_markup(), + media_path=image_path, + disable_web_page_preview=False, + ) + + +@router.inline_query(F.query.in_(["referral", "ref", "invite"])) +async def inline_referral_handler(inline_query: InlineQuery): + referral_link = f"https://t.me/{USERNAME_BOT}?start=referral_{inline_query.from_user.id}" + + results: list[InlineQueryResultArticle] = [] + + for index, offer in enumerate(REFERRAL_OFFERS): + description = offer["description"][:64] + message_text = offer["message"].format(trial_time=TRIAL_TIME)[:4096] + + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text=offer["title"], url=referral_link)) + + results.append( + InlineQueryResultArticle( + id=str(index), + title=offer["title"], + description=description, + input_message_content=InputTextMessageContent(message_text=message_text, parse_mode=ParseMode.HTML), + reply_markup=builder.as_markup(), + ) + ) + + await inline_query.answer(results=results, cache_time=86400, is_personal=True) diff --git a/handlers/profile/__init__.py b/handlers/profile/__init__.py deleted file mode 100644 index 9095f9df..00000000 --- a/handlers/profile/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from aiogram import Router - -from .router import router - - -__all__ = ["router"] diff --git a/handlers/profile/router.py b/handlers/profile/router.py deleted file mode 100644 index acbc4125..00000000 --- a/handlers/profile/router.py +++ /dev/null @@ -1,175 +0,0 @@ -import html -import os - -from typing import Any - -from aiogram import F, Router -from aiogram.fsm.context import FSMContext -from aiogram.types import CallbackQuery, InlineQuery, InlineQueryResultArticle, InputTextMessageContent, Message -from config import INLINE_MODE, REFERRAL_OFFERS, TRIAL_TIME, USERNAME_BOT - -from database import get_balance, get_key_count, get_last_payments, get_referral_stats, get_trial -from handlers.texts import get_referral_link, invite_message_send, profile_message_send -from handlers.utils import edit_or_send_message -from keyboards.profile import get_balance_keyboard, get_invite_keyboard, get_profile_keyboard - - -router = Router() - - -@router.callback_query(F.data == "profile") -@router.message(F.text == "/profile") -async def process_callback_view_profile( - state: FSMContext, - admin: bool, - chat_id: int, - target_message: Message, -): - """ - Обрабатывает запрос на просмотр профиля пользователя. - - Args: - state: Контекст состояния FSM. - admin: Флаг, указывающий, является ли пользователь администратором. - chat_id: ID чата (добавлено middleware). - target_message: Целевое сообщение для ответа (добавлено middleware). - """ - # Получаем информацию о профиле - profile_message = await profile_message_send(chat_id) - - # Получаем клавиатуру профиля - builder = get_profile_keyboard(admin) - - # Отправляем сообщение с профилем - await edit_or_send_message( - target_message=target_message, - text=profile_message, - reply_markup=builder.as_markup(), - media_path=None, - ) - - -@router.callback_query(F.data == "balance") -async def balance_handler(callback_query: CallbackQuery, session: Any, chat_id: int, target_message: Message): - """ - Обрабатывает запрос на просмотр баланса. - - Args: - callback_query: Колбэк запрос. - session: Сессия базы данных. - chat_id: ID чата (добавлено middleware). - target_message: Целевое сообщение для ответа (добавлено middleware). - """ - balance = await get_balance(chat_id, session) - - # Получаем клавиатуру баланса - builder = get_balance_keyboard() - - # Отправляем сообщение с балансом - await edit_or_send_message( - target_message=target_message, - text=f"💰 Ваш текущий баланс: {balance} руб.\n\n" - "Вы можете пополнить баланс через раздел 💸 Пополнить баланс в личном кабинете.", - reply_markup=builder.as_markup(), - media_path=None, - ) - - -@router.callback_query(F.data == "balance_history") -async def balance_history_handler(callback_query: CallbackQuery, session: Any, chat_id: int, target_message: Message): - """ - Обрабатывает запрос на просмотр истории баланса. - - Args: - callback_query: Колбэк запрос. - session: Сессия базы данных. - chat_id: ID чата (добавлено middleware). - target_message: Целевое сообщение для ответа (добавлено middleware). - """ - payments = await get_last_payments(chat_id, session) - - if not payments: - history_text = "📊 История операций:\n\nУ вас пока нет операций по балансу." - else: - history_text = "📊 История операций:\n\n" - for payment in payments: - amount = payment["amount"] - date = payment["created_at"].strftime("%d.%m.%Y %H:%M") - description = html.escape(payment["description"] or "") - - if amount > 0: - history_text += f"➕ {amount} руб. - {description} ({date})\n" - else: - history_text += f"➖ {abs(amount)} руб. - {description} ({date})\n" - - # Получаем клавиатуру баланса - builder = get_balance_keyboard() - - # Отправляем сообщение с историей баланса - await edit_or_send_message( - target_message=target_message, - text=history_text, - reply_markup=builder.as_markup(), - media_path=None, - ) - - -@router.callback_query(F.data == "invite") -@router.message(F.text == "/invite") -async def invite_handler(chat_id: int, target_message: Message): - """ - Обрабатывает запрос на приглашение друзей. - - Args: - chat_id: ID чата (добавлено middleware). - target_message: Целевое сообщение для ответа (добавлено middleware). - """ - referral_link = get_referral_link(chat_id) - referral_stats = await get_referral_stats(chat_id) - invite_message = invite_message_send(referral_link, referral_stats) - image_path = os.path.join("img", "pic_invite.jpg") - - # Получаем клавиатуру для приглашений - builder = get_invite_keyboard(chat_id, referral_link) - - # Отправляем сообщение с приглашением - await edit_or_send_message( - target_message=target_message, - text=invite_message, - reply_markup=builder.as_markup(), - media_path=image_path, - disable_web_page_preview=False, - ) - - -@router.inline_query(F.query.in_(["referral", "ref", "invite"])) -async def inline_referral_handler(inline_query: InlineQuery): - """ - Обрабатывает инлайн-запрос для реферальной программы. - - Args: - inline_query: Инлайн-запрос. - """ - - results = [] - - for index, offer in enumerate(REFERRAL_OFFERS): - description = offer["description"][:64] - message_text = offer["message"].format(trial_time=TRIAL_TIME)[:4096] - - results.append( - InlineQueryResultArticle( - id=f"ref_{index}", - title=offer["title"], - description=description, - input_message_content=InputTextMessageContent( - message_text=message_text, - parse_mode="HTML", - ), - thumbnail_url=offer.get("thumbnail_url"), - thumbnail_width=100, - thumbnail_height=100, - ) - ) - - await inline_query.answer(results=results, cache_time=300) diff --git a/keyboards/coupons/__init__.py b/keyboards/coupons/__init__.py deleted file mode 100644 index 020edffb..00000000 --- a/keyboards/coupons/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .keyboards import get_coupon_keyboard - - -__all__ = ["get_coupon_keyboard"] diff --git a/keyboards/coupons/keyboards.py b/keyboards/coupons/keyboards.py deleted file mode 100644 index 6c77ba4f..00000000 --- a/keyboards/coupons/keyboards.py +++ /dev/null @@ -1,14 +0,0 @@ -from aiogram.types import InlineKeyboardButton -from aiogram.utils.keyboard import InlineKeyboardBuilder - - -def get_coupon_keyboard() -> InlineKeyboardBuilder: - """ - Создает клавиатуру для работы с купонами. - - Returns: - InlineKeyboardBuilder: Построитель клавиатуры с кнопками для купонов. - """ - builder = InlineKeyboardBuilder() - builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) - return builder diff --git a/keyboards/profile/__init__.py b/keyboards/profile/__init__.py deleted file mode 100644 index 96b8726b..00000000 --- a/keyboards/profile/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -from .keyboards import get_balance_keyboard, get_invite_keyboard, get_profile_keyboard - - -__all__ = ["get_profile_keyboard", "get_balance_keyboard", "get_invite_keyboard"] diff --git a/keyboards/profile/keyboards.py b/keyboards/profile/keyboards.py deleted file mode 100644 index ec0bc391..00000000 --- a/keyboards/profile/keyboards.py +++ /dev/null @@ -1,78 +0,0 @@ -from aiogram.types import InlineKeyboardButton -from aiogram.utils.keyboard import InlineKeyboardBuilder -from config import INLINE_MODE - - -def get_profile_keyboard(admin: bool = False) -> InlineKeyboardBuilder: - """ - Создает клавиатуру для профиля пользователя. - - Args: - admin: Флаг, указывающий, является ли пользователь администратором. - - Returns: - InlineKeyboardBuilder: Построитель клавиатуры с кнопками профиля. - """ - builder = InlineKeyboardBuilder() - - # Основные кнопки профиля - builder.button(text="💰 Баланс", callback_data="balance") - builder.button(text="🔑 Мои подписки", callback_data="my_subs") - builder.button(text="💸 Пополнить баланс", callback_data="payment") - builder.button(text="🎁 Подарки", callback_data="gifts") - builder.button(text="👥 Пригласить друга", callback_data="invite") - builder.button(text="📚 Инструкции", callback_data="instructions") - builder.button(text="🎫 Активировать купон", callback_data="activate_coupon") - - # Кнопка админ-панели для администраторов - if admin: - builder.button(text="⚙️ Админ-панель", callback_data="admin_panel") - - # Кнопка главного меню - builder.button(text="🏠 Главное меню", callback_data="main_menu") - - # Настройка расположения кнопок (2 кнопки в ряд) - builder.adjust(2) - - return builder - - -def get_balance_keyboard() -> InlineKeyboardBuilder: - """ - Создает клавиатуру для раздела баланса. - - Returns: - InlineKeyboardBuilder: Построитель клавиатуры с кнопками для баланса. - """ - builder = InlineKeyboardBuilder() - - builder.button(text="📊 История баланса", callback_data="balance_history") - builder.button(text="👤 Личный кабинет", callback_data="profile") - builder.adjust(1) - - return builder - - -def get_invite_keyboard(chat_id: int, referral_link: str) -> InlineKeyboardBuilder: - """ - Создает клавиатуру для приглашения друзей. - - Args: - chat_id: ID чата пользователя. - referral_link: Реферальная ссылка. - - Returns: - InlineKeyboardBuilder: Построитель клавиатуры с кнопками для приглашений. - """ - builder = InlineKeyboardBuilder() - - if INLINE_MODE: - builder.button(text="👥 Пригласить друга", switch_inline_query="invite") - else: - invite_text = f"\nПриглашаю тебя пользоваться действительно быстрым VPN вместе:\n\n{referral_link}" - builder.button(text="👥 Пригласить друга", switch_inline_query=invite_text) - - builder.button(text="👤 Личный кабинет", callback_data="profile") - builder.adjust(1) - - return builder diff --git a/middlewares/__init__.py b/middlewares/__init__.py index 8235a424..f2b1d9d9 100644 --- a/middlewares/__init__.py +++ b/middlewares/__init__.py @@ -6,7 +6,6 @@ from aiogram.dispatcher.middlewares.base import BaseMiddleware from .admin import AdminMiddleware from .loggings import LoggingMiddleware -from .message_handler import MessageHandlerMiddleware from .session import SessionMiddleware from .throttling import ThrottlingMiddleware from .user import UserMiddleware @@ -35,7 +34,6 @@ def register_middleware( "logging": LoggingMiddleware(), "throttling": ThrottlingMiddleware(), "user": UserMiddleware(), - "message_handler": MessageHandlerMiddleware(), } # Фильтруем middleware по списку исключений diff --git a/middlewares/message_handler.py b/middlewares/message_handler.py deleted file mode 100644 index 2b1b4e77..00000000 --- a/middlewares/message_handler.py +++ /dev/null @@ -1,46 +0,0 @@ -from collections.abc import Awaitable, Callable -from typing import Any, Dict, Union - -from aiogram import BaseMiddleware -from aiogram.types import CallbackQuery, Message, TelegramObject - - -class MessageHandlerMiddleware(BaseMiddleware): - """ - Middleware для обработки сообщений и callback-запросов. - - Добавляет в контекст обработчика следующие данные: - - chat_id: ID чата или пользователя - - target_message: Объект сообщения для ответа или редактирования - - is_callback: Флаг, указывающий, является ли запрос callback-запросом - """ - - async def __call__( - self, - handler: Callable[[TelegramObject, dict[str, Any]], Awaitable[Any]], - event: Message | CallbackQuery, - data: dict[str, Any], - ) -> Any: - """ - Обрабатывает входящее событие и добавляет в контекст необходимые данные. - - Args: - handler: Обработчик события. - event: Событие (сообщение или callback-запрос). - data: Словарь с данными контекста. - - Returns: - Any: Результат выполнения обработчика. - """ - # Определяем тип события и извлекаем нужные данные - if isinstance(event, CallbackQuery): - data["chat_id"] = event.from_user.id - data["target_message"] = event.message - data["is_callback"] = True - else: - data["chat_id"] = event.chat.id - data["target_message"] = event - data["is_callback"] = False - - # Вызываем следующий обработчик - return await handler(event, data)