Revert "Добавление нового middleware и рефакторинг"

This commit is contained in:
Vladislav Lisitsyn
2025-03-12 10:23:51 +03:00
committed by GitHub
parent e85b884985
commit a2693393d3
17 changed files with 317 additions and 401 deletions
@@ -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:
-6
View File
@@ -1,6 +0,0 @@
from aiogram import Router
from .router import router
__all__ = ["router"]
+4 -4
View File
@@ -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(),
)
+13 -8
View File
@@ -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="❌ <b>Ключ не найден. Проверьте имя ключа.</b> 🔍",
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
)
+14 -8
View File
@@ -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}")
+7 -2
View File
@@ -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")
+3 -3
View File
@@ -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,
+5 -5
View File
@@ -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}.")
+255
View File
@@ -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<blockquote>🔧 <i>Нажмите кнопку ➕ Подписка, чтобы настроить VPN-подключение</i></blockquote>"
)
else:
profile_message += f"\n<blockquote> <i>{NEWS_MESSAGE}</i></blockquote>"
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"<b>Управление вашим балансом 💰</b>\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 = "📊 <b>Последние 3 операции с балансом:</b>\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"<b>Сумма:</b> {amount}\n"
f"<b>Способ оплаты:</b> {payment_system}\n"
f"<b>Статус:</b> {status}\n"
f"<b>Дата:</b> {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 = "<b>🚀 Доступные тарифы VPN:</b>\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)
-6
View File
@@ -1,6 +0,0 @@
from aiogram import Router
from .router import router
__all__ = ["router"]
-175
View File
@@ -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"<b>💰 Ваш текущий баланс:</b> {balance} руб.\n\n"
"Вы можете пополнить баланс через раздел <b>💸 Пополнить баланс</b> в личном кабинете.",
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 = "<b>📊 История операций:</b>\n\nУ вас пока нет операций по балансу."
else:
history_text = "<b>📊 История операций:</b>\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" <b>{amount}</b> руб. - {description} ({date})\n"
else:
history_text += f" <b>{abs(amount)}</b> руб. - {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)
-4
View File
@@ -1,4 +0,0 @@
from .keyboards import get_coupon_keyboard
__all__ = ["get_coupon_keyboard"]
-14
View File
@@ -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
-4
View File
@@ -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"]
-78
View File
@@ -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
-2
View File
@@ -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 по списку исключений
-46
View File
@@ -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)