Files
Solo_bot/handlers/start.py
T
2025-02-02 05:50:14 +03:00

312 lines
16 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import os
from typing import Any
import aiofiles
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 bot import bot
from config import (
CAPTCHA_ENABLE,
CHANNEL_EXISTS,
CHANNEL_ID,
CHANNEL_REQUIRED,
CHANNEL_URL,
DONATIONS_ENABLE,
SUPPORT_CHAT_URL,
)
from database import (
add_connection,
add_referral,
check_connection_exists,
get_coupon_details,
get_referral_by_referred_id,
get_trial,
update_balance,
)
from handlers.captcha import generate_captcha
from handlers.keys.key_management import create_key
from handlers.texts import 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, captcha: bool = False
):
await start_command(callback_query.message, state, session, admin, captcha)
@router.message(Command("start"))
async def start_command(message: Message, state: FSMContext, session: Any, admin: bool, captcha: bool = True):
"""Обрабатывает команду /start, включая логику проверки подписки, рефералов и подарков."""
logger.info(f"Вызвана функция start_command для пользователя {message.chat.id}")
await state.clear()
if CAPTCHA_ENABLE and captcha:
captcha_data = await generate_captcha(message, state)
await message.answer(text=captcha_data["text"], reply_markup=captcha_data["markup"])
return
if CHANNEL_EXISTS and CHANNEL_REQUIRED:
try:
member = await bot.get_chat_member(CHANNEL_ID, message.chat.id)
if member.status not in ["member", "administrator", "creator"]:
await state.update_data(start_text=message.text)
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data="check_subscription"))
await message.answer(
f"Для использования бота, пожалуйста, подпишитесь на наш канал: {CHANNEL_URL}",
reply_markup=builder.as_markup(),
)
return
except Exception as e:
logger.error(f"Ошибка проверки подписки пользователя {message.chat.id}: {e}")
await state.update_data(start_text=message.text)
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data="check_subscription"))
await message.answer(
f"Пожалуйста, подпишитесь на наш канал: {CHANNEL_URL}", reply_markup=builder.as_markup()
)
return
await process_start_logic(message, state, session, admin)
async def process_start_logic(message: Message, state: FSMContext, session: Any, admin: bool):
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 "coupons_" in message.text:
logger.info(f"Обнаружена ссылка на купон: {message.text}")
coupon_code = message.text.split("coupons_")[1].strip()
logger.info(f"Пользователь {message.chat.id} ввёл купон: {coupon_code}")
coupon = await session.fetchrow(
"SELECT id, code, amount, usage_limit, usage_count, is_used FROM coupons WHERE code = $1",
coupon_code,
)
if coupon is None:
logger.warning(f"Купон {coupon_code} не найден.")
await message.answer("❌ Купон не найден!")
return await show_start_menu(message, admin, session)
if coupon["is_used"] or coupon["usage_count"] >= coupon["usage_limit"]:
logger.info(f"Купон {coupon_code} уже использован или исчерпан.")
await message.answer("❌ Этот купон уже использован!")
return await show_start_menu(message, admin, session)
await update_balance(message.chat.id, coupon["amount"])
logger.info(f"Начислено {coupon['amount']} единиц для пользователя {message.chat.id}")
new_usage_count = coupon["usage_count"] + 1
is_used = new_usage_count >= coupon["usage_limit"]
await session.execute(
"UPDATE coupons SET usage_count = $1, is_used = $2 WHERE code = $3",
new_usage_count,
is_used,
coupon_code,
)
logger.info(f"Купон {coupon_code} успешно использован, начислено {coupon['amount']} RUB.")
await message.answer(f"🎉 Ваш баланс пополнен на {coupon['amount']} RUB по купону!")
return await show_start_menu(message, admin, session)
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 get_coupon_details(gift_id, session)
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)
if not connection_exists:
await add_referral(recipient_tg_id, gift_info["sender_tg_id"], session)
logger.info(
f"Пользователь {recipient_tg_id} теперь является рефералом отправителя подарка {gift_info['sender_tg_id']}."
)
else:
logger.info(f"Пользователь {recipient_tg_id} уже зарегистрирован, реферал не добавляется.")
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} "
f"{'месяц' 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 get_referral_by_referred_id(message.chat.id, session)
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)
@router.callback_query(F.data == "check_subscription")
async def check_subscription_callback(callback_query: CallbackQuery, state: FSMContext, session: Any, admin: bool):
user_id = callback_query.from_user.id
logger.info(f"[CALLBACK] Получен callback 'check_subscription' от пользователя {user_id}")
try:
logger.info(f"[CALLBACK] Запрос информации о подписке для пользователя {user_id} на канал {CHANNEL_ID}")
member = await bot.get_chat_member(CHANNEL_ID, user_id)
logger.info(f"[CALLBACK] Статус подписки пользователя {user_id}: {member.status}")
if member.status not in ["member", "administrator", "creator"]:
logger.info(f"[CALLBACK] Пользователь {user_id} НЕ подписан на канал {CHANNEL_ID}. Текущий статус: {member.status}")
await callback_query.answer("Вы еще не подписаны на канал!", show_alert=True)
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data="check_subscription"))
await callback_query.message.answer(
f"Для использования бота, пожалуйста, подпишитесь на наш канал: {CHANNEL_URL}",
reply_markup=builder.as_markup()
)
logger.info(f"[CALLBACK] Обновлено сообщение с приглашением подписаться для пользователя {user_id}")
else:
logger.info(f"[CALLBACK] Пользователь {user_id} подписан на канал {CHANNEL_ID} - подписка подтверждена")
await callback_query.answer("Подписка подтверждена!")
logger.info(f"[CALLBACK] Перед вызовом process_start_logic для пользователя {user_id}. Текущее сообщение: {callback_query.message.text}")
await process_start_logic(callback_query.message, state, session, admin)
logger.info(f"[CALLBACK] Завершен вызов process_start_logic для пользователя {user_id}")
except Exception as e:
logger.error(f"[CALLBACK] Ошибка проверки подписки для пользователя {user_id}: {e}", exc_info=True)
await callback_query.answer("Ошибка проверки подписки, повторите попытку", show_alert=True)
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="create_key"))
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):
async with aiofiles.open(image_path, "rb") as image_from_buffer:
image_data = await image_from_buffer.read()
await message.answer_photo(
photo=BufferedInputFile(image_data, 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 == "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())