214 lines
7.4 KiB
Python
214 lines
7.4 KiB
Python
from datetime import datetime, timedelta
|
|
from typing import Any
|
|
|
|
import pytz
|
|
|
|
from aiogram import F, Router
|
|
from aiogram.fsm.context import FSMContext
|
|
from aiogram.types import CallbackQuery, InlineKeyboardButton, Message
|
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
|
|
|
from config import (
|
|
NOTIFY_EXTRA_DAYS,
|
|
RENEWAL_PRICES,
|
|
TRIAL_TIME,
|
|
TRIAL_TIME_DISABLE,
|
|
USE_COUNTRY_SELECTION,
|
|
USE_NEW_PAYMENT_FLOW,
|
|
)
|
|
from database import (
|
|
add_user,
|
|
check_user_exists,
|
|
create_temporary_data,
|
|
get_balance,
|
|
get_trial,
|
|
)
|
|
from handlers.buttons import (
|
|
MAIN_MENU,
|
|
PAYMENT,
|
|
)
|
|
from handlers.payments.robokassa_pay import handle_custom_amount_input
|
|
from handlers.payments.yookassa_pay import process_custom_amount_input
|
|
from handlers.texts import (
|
|
CREATING_CONNECTION_MSG,
|
|
DISCOUNTS,
|
|
INSUFFICIENT_FUNDS_MSG,
|
|
SELECT_TARIFF_PLAN_MSG,
|
|
)
|
|
from handlers.utils import edit_or_send_message
|
|
from logger import logger
|
|
|
|
from .key_cluster_mode import key_cluster_mode
|
|
from .key_country_mode import key_country_mode
|
|
|
|
|
|
router = Router()
|
|
|
|
moscow_tz = pytz.timezone("Europe/Moscow")
|
|
|
|
|
|
class Form(FSMContext):
|
|
waiting_for_server_selection = "waiting_for_server_selection"
|
|
|
|
|
|
@router.callback_query(F.data == "create_key")
|
|
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, callback_query)
|
|
|
|
|
|
async def handle_key_creation(
|
|
tg_id: int,
|
|
state: FSMContext,
|
|
session: Any,
|
|
message_or_query: Message | CallbackQuery,
|
|
):
|
|
"""Создание ключа с учётом выбора тарифного плана."""
|
|
current_time = datetime.now(moscow_tz)
|
|
|
|
if not TRIAL_TIME_DISABLE:
|
|
trial_status = await get_trial(tg_id, session)
|
|
if trial_status in [0, -1]:
|
|
extra_days = NOTIFY_EXTRA_DAYS if trial_status == -1 else 0
|
|
expiry_time = current_time + timedelta(days=TRIAL_TIME + extra_days)
|
|
logger.info(f"Доступен {TRIAL_TIME + extra_days}-дневный пробный период пользователю {tg_id}.")
|
|
await edit_or_send_message(
|
|
target_message=message_or_query if isinstance(message_or_query, Message) else message_or_query.message,
|
|
text=CREATING_CONNECTION_MSG,
|
|
reply_markup=None,
|
|
)
|
|
await state.update_data(is_trial=True)
|
|
await create_key(tg_id, expiry_time, state, session, message_or_query)
|
|
return
|
|
|
|
builder = InlineKeyboardBuilder()
|
|
for index, (plan_id, price) in enumerate(RENEWAL_PRICES.items()):
|
|
discount_text = ""
|
|
if DISCOUNTS and plan_id in DISCOUNTS:
|
|
discount_percentage = DISCOUNTS[plan_id]
|
|
discount_text = f" ({discount_percentage}% скидка)"
|
|
if index == len(RENEWAL_PRICES) - 1:
|
|
discount_text = f" ({discount_percentage}% 🔥)"
|
|
builder.row(
|
|
InlineKeyboardButton(
|
|
text=f"📅 {plan_id} мес. - {price}₽{discount_text}",
|
|
callback_data=f"select_plan_{plan_id}",
|
|
)
|
|
)
|
|
builder.row(InlineKeyboardButton(text=MAIN_MENU, 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=SELECT_TARIFF_PLAN_MSG,
|
|
reply_markup=builder.as_markup(),
|
|
media_path=None,
|
|
)
|
|
|
|
await state.update_data(tg_id=tg_id)
|
|
await state.set_state(Form.waiting_for_server_selection)
|
|
|
|
|
|
@router.callback_query(F.data.startswith("select_plan_"))
|
|
async def select_tariff_plan(callback_query: CallbackQuery, session: Any, state: FSMContext):
|
|
tg_id = callback_query.message.chat.id
|
|
plan_id = callback_query.data.split("_")[-1]
|
|
plan_price = RENEWAL_PRICES.get(plan_id)
|
|
if plan_price is None:
|
|
await callback_query.message.answer("🚫 Неверный тарифный план.")
|
|
return
|
|
duration_days = int(plan_id) * 30
|
|
balance = await get_balance(tg_id)
|
|
if balance < plan_price:
|
|
required_amount = plan_price - balance
|
|
await create_temporary_data(
|
|
session,
|
|
tg_id,
|
|
"waiting_for_payment",
|
|
{
|
|
"plan_id": plan_id,
|
|
"plan_price": plan_price,
|
|
"duration_days": duration_days,
|
|
"required_amount": required_amount,
|
|
},
|
|
)
|
|
if USE_NEW_PAYMENT_FLOW == "YOOKASSA":
|
|
await process_custom_amount_input(callback_query, session)
|
|
elif USE_NEW_PAYMENT_FLOW == "ROBOKASSA":
|
|
await handle_custom_amount_input(callback_query, session)
|
|
else:
|
|
builder = InlineKeyboardBuilder()
|
|
builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay"))
|
|
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
|
|
await edit_or_send_message(
|
|
target_message=callback_query.message,
|
|
text=INSUFFICIENT_FUNDS_MSG.format(required_amount=required_amount),
|
|
reply_markup=builder.as_markup(),
|
|
media_path=None,
|
|
)
|
|
return
|
|
builder = InlineKeyboardBuilder()
|
|
builder.row(InlineKeyboardButton(text="⏳ Подождите...", callback_data="creating_key"))
|
|
|
|
await edit_or_send_message(
|
|
target_message=callback_query.message,
|
|
text=CREATING_CONNECTION_MSG,
|
|
reply_markup=builder.as_markup(),
|
|
)
|
|
|
|
expiry_time = datetime.now(moscow_tz) + timedelta(days=duration_days)
|
|
await state.update_data(plan_id=plan_id)
|
|
await create_key(tg_id, expiry_time, state, session, callback_query, plan=int(plan_id))
|
|
|
|
|
|
async def create_key(
|
|
tg_id: int,
|
|
expiry_time,
|
|
state,
|
|
session,
|
|
message_or_query=None,
|
|
old_key_name: str = None,
|
|
plan: int = None,
|
|
):
|
|
"""
|
|
Универсальная точка входа для создания ключа.
|
|
Делегирует выполнение в зависимости от выбранного режима (страна или кластер).
|
|
Также отвечает за первичное подключение пользователя.
|
|
"""
|
|
if not await check_user_exists(tg_id):
|
|
from_user = message_or_query.from_user if isinstance(message_or_query, CallbackQuery | Message) else None
|
|
if from_user:
|
|
await add_user(
|
|
tg_id=from_user.id,
|
|
username=from_user.username,
|
|
first_name=from_user.first_name,
|
|
last_name=from_user.last_name,
|
|
language_code=from_user.language_code,
|
|
is_bot=from_user.is_bot,
|
|
session=session,
|
|
)
|
|
logger.info(f"[User] Новый пользователь {tg_id} добавлен")
|
|
|
|
if USE_COUNTRY_SELECTION:
|
|
await key_country_mode(
|
|
tg_id=tg_id,
|
|
expiry_time=expiry_time,
|
|
state=state,
|
|
session=session,
|
|
message_or_query=message_or_query,
|
|
old_key_name=old_key_name,
|
|
)
|
|
else:
|
|
await key_cluster_mode(
|
|
tg_id=tg_id,
|
|
expiry_time=expiry_time,
|
|
state=state,
|
|
session=session,
|
|
message_or_query=message_or_query,
|
|
plan=plan,
|
|
)
|