303 lines
12 KiB
Python
303 lines
12 KiB
Python
import os
|
||
from typing import Any
|
||
|
||
from aiogram import F, Router
|
||
from aiogram.filters import Command
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.types import (
|
||
BufferedInputFile,
|
||
CallbackQuery,
|
||
InlineKeyboardButton,
|
||
Message,
|
||
)
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||
|
||
from config import (
|
||
CHANNEL_EXISTS,
|
||
CHANNEL_URL,
|
||
CONNECT_ANDROID,
|
||
CONNECT_IOS,
|
||
DONATIONS_ENABLE,
|
||
DOWNLOAD_ANDROID,
|
||
DOWNLOAD_IOS,
|
||
SUPPORT_CHAT_URL,
|
||
)
|
||
from database import (
|
||
add_connection,
|
||
add_referral,
|
||
check_connection_exists,
|
||
get_trial,
|
||
use_trial,
|
||
)
|
||
from handlers.buttons.add_subscribe import (
|
||
DOWNLOAD_ANDROID_BUTTON,
|
||
DOWNLOAD_IOS_BUTTON,
|
||
IMPORT_ANDROID,
|
||
IMPORT_IOS,
|
||
PC_BUTTON,
|
||
TV_BUTTON,
|
||
)
|
||
from handlers.keys.key_management import create_key
|
||
from handlers.keys.trial_key import create_trial_key
|
||
from handlers.texts import INSTRUCTIONS_TRIAL, WELCOME_TEXT, get_about_vpn
|
||
from logger import logger
|
||
|
||
router = Router()
|
||
|
||
|
||
@router.callback_query(F.data == "start")
|
||
async def handle_start_callback_query(
|
||
callback_query: CallbackQuery, state: FSMContext, session: Any, admin: bool
|
||
):
|
||
await start_command(callback_query.message, state, session, admin)
|
||
|
||
|
||
@router.message(Command("start"))
|
||
async def start_command(message: Message, state: FSMContext, session: Any, admin: bool):
|
||
"""Обрабатывает команду /start, включает логику рефералов и подарков."""
|
||
logger.info(f"Вызвана функция start_command для пользователя {message.chat.id}")
|
||
|
||
await state.clear()
|
||
|
||
if message.text:
|
||
try:
|
||
connection_exists = await check_connection_exists(message.chat.id)
|
||
logger.info(f"Проверка существования подключения: {connection_exists}")
|
||
|
||
if not connection_exists:
|
||
await add_connection(tg_id=message.chat.id, session=session)
|
||
logger.info(
|
||
f"Пользователь {message.chat.id} успешно добавлен в базу данных."
|
||
)
|
||
|
||
if "gift_" in message.text:
|
||
logger.info(f"Обнаружена ссылка на подарок: {message.text}")
|
||
parts = message.text.split("gift_")[1].split("_")
|
||
gift_id = parts[0]
|
||
|
||
recipient_tg_id = message.chat.id
|
||
|
||
gift_info = await session.fetchrow(
|
||
"SELECT * FROM gifts WHERE gift_id = $1 AND is_used = FALSE",
|
||
gift_id,
|
||
)
|
||
|
||
if gift_info is None:
|
||
logger.warning(
|
||
f"Подарок с ID {gift_id} уже был использован или не существует."
|
||
)
|
||
await message.answer(
|
||
"Этот подарок уже был использован или не существует."
|
||
)
|
||
return await show_start_menu(message, admin, session)
|
||
|
||
if gift_info["sender_tg_id"] == recipient_tg_id:
|
||
logger.warning(
|
||
f"Пользователь {recipient_tg_id} попытался активировать подарок, который был отправлен им самим."
|
||
)
|
||
await message.answer(
|
||
"❌ Вы не можете получить подарок от самого себя."
|
||
)
|
||
return await show_start_menu(message, admin, session)
|
||
|
||
selected_months = gift_info["selected_months"]
|
||
expiry_time = gift_info["expiry_time"]
|
||
expiry_time_naive = expiry_time.replace(tzinfo=None)
|
||
logger.info(
|
||
f"Подарок с ID {gift_id} успешно найден для пользователя {recipient_tg_id}."
|
||
)
|
||
|
||
await create_key(
|
||
recipient_tg_id, expiry_time_naive, state, session, message
|
||
)
|
||
logger.info(
|
||
f"Ключ создан для пользователя {recipient_tg_id} на срок {selected_months} месяцев."
|
||
)
|
||
|
||
await session.execute(
|
||
"UPDATE gifts SET is_used = TRUE, recipient_tg_id = $1 WHERE gift_id = $2",
|
||
recipient_tg_id,
|
||
gift_id,
|
||
)
|
||
|
||
await message.answer(
|
||
f"🎉 Ваш подарок на {selected_months} {'месяц' if selected_months == 1 else 'месяца' if selected_months in [2, 3, 4] else 'месяцев'} активирован!"
|
||
)
|
||
logger.info(
|
||
f"Подарок на {selected_months} месяцев активирован для пользователя {recipient_tg_id}."
|
||
)
|
||
return
|
||
|
||
elif "referral_" in message.text:
|
||
try:
|
||
referrer_tg_id = int(message.text.split("referral_")[1])
|
||
|
||
if connection_exists:
|
||
logger.info(
|
||
f"Пользователь {message.chat.id} уже зарегистрирован и не может стать рефералом."
|
||
)
|
||
await message.answer(
|
||
"❌ Вы уже зарегистрированы и не можете использовать реферальную ссылку."
|
||
)
|
||
return await show_start_menu(message, admin, session)
|
||
|
||
if referrer_tg_id == message.chat.id:
|
||
logger.warning(
|
||
f"Пользователь {message.chat.id} попытался стать рефералом самого себя."
|
||
)
|
||
await message.answer(
|
||
"❌ Вы не можете быть рефералом самого себя."
|
||
)
|
||
return await show_start_menu(message, admin, session)
|
||
|
||
existing_referral = await session.fetchrow(
|
||
"SELECT * FROM referrals WHERE referred_tg_id = $1",
|
||
message.chat.id,
|
||
)
|
||
|
||
if existing_referral:
|
||
logger.info(f"Реферал с ID {message.chat.id} уже существует.")
|
||
return await show_start_menu(message, admin, session)
|
||
|
||
await add_referral(message.chat.id, referrer_tg_id, session)
|
||
logger.info(
|
||
f"Реферал {message.chat.id} использовал ссылку от пользователя {referrer_tg_id}"
|
||
)
|
||
return await show_start_menu(message, admin, session)
|
||
|
||
except (ValueError, IndexError) as e:
|
||
logger.error(f"Ошибка при обработке реферальной ссылки: {e}")
|
||
return
|
||
|
||
else:
|
||
logger.info(
|
||
f"Пользователь {message.chat.id} зашел без реферальной ссылки или подарка."
|
||
)
|
||
|
||
await show_start_menu(message, admin, session)
|
||
|
||
except (ValueError, IndexError) as e:
|
||
logger.error(
|
||
f"Ошибка при обработке сообщения пользователя {message.chat.id}: {e}"
|
||
)
|
||
await message.answer("❌ Произошла ошибка. Пожалуйста, попробуйте снова.")
|
||
else:
|
||
await show_start_menu(message, admin, session)
|
||
|
||
|
||
async def show_start_menu(message: Message, admin: bool, session: Any):
|
||
"""Функция для отображения стандартного меню"""
|
||
logger.info(f"Показываю главное меню для пользователя {message.chat.id}")
|
||
trial_status = await get_trial(message.chat.id, session)
|
||
image_path = os.path.join("img", "pic.jpg")
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
if trial_status == 0:
|
||
builder.row(
|
||
InlineKeyboardButton(text="🔗 Подключить VPN", callback_data="connect_vpn")
|
||
)
|
||
|
||
builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"))
|
||
|
||
if CHANNEL_EXISTS:
|
||
builder.row(
|
||
InlineKeyboardButton(text="📞 Поддержка", url=SUPPORT_CHAT_URL),
|
||
InlineKeyboardButton(text="📢 Канал", url=CHANNEL_URL)
|
||
)
|
||
else:
|
||
builder.row(
|
||
InlineKeyboardButton(text="📞 Поддержка", url=SUPPORT_CHAT_URL)
|
||
)
|
||
|
||
if admin:
|
||
builder.row(
|
||
InlineKeyboardButton(text="🔧 Администратор", callback_data="admin")
|
||
)
|
||
|
||
builder.row(InlineKeyboardButton(text="🌐 О VPN", callback_data="about_vpn"))
|
||
|
||
if os.path.isfile(image_path):
|
||
with open(image_path, "rb") as image_from_buffer:
|
||
await message.answer_photo(
|
||
photo=BufferedInputFile(image_from_buffer.read(), filename="pic.jpg"),
|
||
caption=WELCOME_TEXT,
|
||
reply_markup=builder.as_markup(),
|
||
)
|
||
else:
|
||
await message.answer(
|
||
text=WELCOME_TEXT,
|
||
reply_markup=builder.as_markup(),
|
||
)
|
||
|
||
|
||
@router.callback_query(F.data == "connect_vpn")
|
||
async def handle_connect_vpn(callback_query: CallbackQuery, session: Any):
|
||
user_id = callback_query.message.chat.id
|
||
|
||
trial_key_info = await create_trial_key(user_id, session)
|
||
|
||
if "error" in trial_key_info:
|
||
await callback_query.message.answer(trial_key_info["error"])
|
||
else:
|
||
await use_trial(user_id, session)
|
||
|
||
key_message = (
|
||
f"🔑 <b>Ваш персональный ключ доступа:</b>\n"
|
||
f"<code>{trial_key_info['key']}</code>\n\n"
|
||
f"📋 <b>Быстрая инструкция по подключению:</b>\n{INSTRUCTIONS_TRIAL}"
|
||
)
|
||
|
||
email = trial_key_info["email"]
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
builder.row(InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL))
|
||
builder.row(
|
||
InlineKeyboardButton(text=DOWNLOAD_IOS_BUTTON, url=DOWNLOAD_IOS),
|
||
InlineKeyboardButton(text=DOWNLOAD_ANDROID_BUTTON, url=DOWNLOAD_ANDROID),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(
|
||
text=IMPORT_IOS,
|
||
url=f'{CONNECT_IOS}{trial_key_info["key"]}',
|
||
),
|
||
InlineKeyboardButton(
|
||
text=IMPORT_ANDROID,
|
||
url=f'{CONNECT_ANDROID}{trial_key_info["key"]}',
|
||
),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text=PC_BUTTON, callback_data=f"connect_pc|{email}"),
|
||
InlineKeyboardButton(text=TV_BUTTON, callback_data=f"connect_tv|{email}"),
|
||
)
|
||
builder.row(
|
||
InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")
|
||
)
|
||
|
||
await callback_query.message.answer(
|
||
key_message, reply_markup=builder.as_markup()
|
||
)
|
||
|
||
|
||
@router.callback_query(F.data == "about_vpn")
|
||
async def handle_about_vpn(callback_query: CallbackQuery):
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
if DONATIONS_ENABLE:
|
||
builder.row(
|
||
InlineKeyboardButton(text="💰 Поддержать проект", callback_data="donate")
|
||
)
|
||
|
||
builder.row(
|
||
InlineKeyboardButton(text="📞 Техническая поддержка", url=SUPPORT_CHAT_URL),
|
||
)
|
||
if CHANNEL_EXISTS:
|
||
builder.row(
|
||
InlineKeyboardButton(text="📢 Официальный канал", url=CHANNEL_URL),
|
||
)
|
||
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="start"))
|
||
|
||
await callback_query.message.answer(
|
||
get_about_vpn("3.2.3-minor"), reply_markup=builder.as_markup()
|
||
)
|