diff --git a/handlers/start.py b/handlers/start.py index f1c9ce94..549822bf 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,22 +1,22 @@ import os - from aiogram import Router from aiogram.filters import Command from aiogram.fsm.state import State, StatesGroup from aiogram.types import (BufferedInputFile, CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message) from handlers.texts import ABOUT_VPN, WELCOME_TEXT - from bot import bot from config import CHANNEL_URL, SUPPORT_CHAT_URL -from database import add_connection, add_referral, check_connection_exists +from database import add_connection, add_referral, check_connection_exists, get_trial +from handlers.trial_key import create_trial_key +from handlers.texts import INSTRUCTIONS_TRIAL router = Router() class FeedbackState(StatesGroup): waiting_for_feedback = State() -async def send_welcome_message(chat_id: int): +async def send_welcome_message(chat_id: int, trial_status: int): welcome_text = WELCOME_TEXT image_path = os.path.join(os.path.dirname(__file__), 'pic.jpg') @@ -26,12 +26,15 @@ async def send_welcome_message(chat_id: int): return inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text='🔗 Подключить VPN', callback_data='connect_vpn')] if trial_status == 0 else [], [InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')], [InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn')], - [InlineKeyboardButton(text='📞 Поддержка', url=SUPPORT_CHAT_URL)], - [InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL)] + [InlineKeyboardButton(text='📞 Поддержка', url=SUPPORT_CHAT_URL)], + [InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL)], ]) + inline_keyboard.inline_keyboard = [row for row in inline_keyboard.inline_keyboard if row] + with open(image_path, 'rb') as image_from_buffer: await bot.send_photo( chat_id, @@ -49,13 +52,43 @@ async def start_command(message: Message): print(f"Referral ID: {referrer_tg_id}") if not await check_connection_exists(message.from_user.id): await add_connection(message.from_user.id) - await add_referral(message.from_user.id, referrer_tg_id) - await message.answer("Вас пригласил друг, добро пожаловать!") else: await message.answer("Вы уже зарегистрированы в системе!") - await send_welcome_message(message.chat.id) + + trial_status = await get_trial(message.from_user.id) + await send_welcome_message(message.chat.id, trial_status) + +@router.callback_query(lambda c: c.data == 'connect_vpn') +async def handle_connect_vpn(callback_query: CallbackQuery): + await callback_query.message.delete() + user_id = callback_query.from_user.id + + # Создаём триальный ключ + trial_key_info = await create_trial_key(user_id) + + if 'error' in trial_key_info: + await callback_query.message.answer(trial_key_info['error']) + else: + # Формируем сообщение с ключом и инструкциями + key_message = ( + f"Ваш ключ доступа:\n
{trial_key_info['key']}\n\n"
+ f"Инструкции:\n{INSTRUCTIONS_TRIAL}"
+ )
+
+ # Кнопка "В профиль"
+ button_profile = InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')
+ inline_keyboard_profile = InlineKeyboardMarkup(inline_keyboard=[[button_profile]])
+
+ # Отправляем текст с ключом и инструкциями в виде цитаты
+ await callback_query.message.answer(
+ key_message,
+ parse_mode='HTML',
+ reply_markup=inline_keyboard_profile
+ )
+
+ await callback_query.answer()
@router.callback_query(lambda c: c.data == 'about_vpn')
async def handle_about_vpn(callback_query: CallbackQuery):
@@ -75,5 +108,6 @@ async def handle_about_vpn(callback_query: CallbackQuery):
@router.callback_query(lambda c: c.data == 'back_to_menu')
async def handle_back_to_menu(callback_query: CallbackQuery):
await callback_query.message.delete()
- await send_welcome_message(callback_query.from_user.id)
+ trial_status = await get_trial(callback_query.from_user.id)
+ await send_welcome_message(callback_query.from_user.id, trial_status)
await callback_query.answer()
diff --git a/handlers/texts.py b/handlers/texts.py
index 11d04a3e..173bda4f 100644
--- a/handlers/texts.py
+++ b/handlers/texts.py
@@ -68,15 +68,28 @@ INSTRUCTIONS = (
"💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в [поддержку](https://t.me/solonet_sup)."
)
-KEY_EXPIRY_10H = "🔔 Уведомление: Ваш ключ для сервера {server_id} истекает через 10 часов.\n" \
- "Email: {email}\n" \
- "Дата истечения: {expiry_date}"
+INSTRUCTIONS_TRIAL = (
+ "📋 Инструкции по использованию вашего ключа:\n\n"
+ "1. Скачайте приложение для вашего устройства:\n"
+ " - Для Android: V2Ray\n"
+ " - Для iPhone: Streisand\n"
+ " - Для Windows: Hiddify Next\n\n"
+ "2. Скопируйте предоставленный ключ, который вы получили ранее.\n"
+ "3. Откройте приложение и нажмите на плюсик сверху справа.\n"
+ "4. Выберите 'Вставить из буфера обмена' для добавления ключа.\n\n"
+ "💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в поддержку."
+)
-KEY_EXPIRY_24H = "⏳ Уведомление: Ваш ключ для сервера {server_id} истекает через 24 часа.\n" \
- "Email: {email}\n" \
+
+KEY_EXPIRY_10H = "🔔 Уведомление: Ваш ключ {email} для сервера {server_id} истекает через 10 часов.\n" \
+ "Дата истечения: {expiry_date}"\
+ "Перейдите в профиль и пополните баланс, всего 100 рублей на целый месяц"\
+ "ключ продлится автоматически"
+
+KEY_EXPIRY_24H = "🔔 Уведомление: Ваш ключ {email} для сервера {server_id} истекает через 24 часа.\n" \
"Осталось времени: {hours_left} часов\n" \
"Дата истечения: {expiry_date}\n" \
- "Баланс: {balance:.2f} руб."
+ "Баланс: {balance:.2f} руб."\
KEY_RENEWED = "Ваш ключ был продлен на месяц."
KEY_RENEWAL_FAILED = "Не удалось продлить ключ на панели, обратитесь в поддержку."
diff --git a/handlers/trial_key.py b/handlers/trial_key.py
new file mode 100644
index 00000000..db3388d2
--- /dev/null
+++ b/handlers/trial_key.py
@@ -0,0 +1,67 @@
+import random
+import asyncpg
+import time
+import uuid # Импортируем модуль для генерации UUID
+from config import DATABASE_URL, SERVERS, ADMIN_USERNAME, ADMIN_PASSWORD
+from auth import login_with_credentials, link # Импортируйте необходимые функции
+from client import add_client
+from database import store_key, add_connection
+from handlers.texts import INSTRUCTIONS
+from datetime import datetime, timedelta
+
+def generate_random_email():
+ """Генерирует случайный набор символов."""
+ random_string = ''.join(random.choices('abcdefghijklmnopqrstuvwxyz0123456789', k=6))
+ return random_string
+
+async def create_trial_key(tg_id: int):
+ conn = await asyncpg.connect(DATABASE_URL)
+ try:
+ server_id = await get_least_loaded_server(conn)
+ session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD)
+ current_time = datetime.utcnow()
+
+ expiry_time = current_time + timedelta(days=30, hours=3)
+ expiry_timestamp = int(expiry_time.timestamp() * 1000)
+
+ client_id = str(uuid.uuid4())
+ email = generate_random_email()
+ response = await add_client(
+ session, server_id, client_id, email, tg_id,
+ limit_ip=1, total_gb=0, expiry_time=expiry_timestamp,
+ enable=True, flow="xtls-rprx-vision"
+ )
+ if response.get("success"):
+ connection_link = await link(session, server_id, client_id, email)
+
+ existing_connection = await conn.fetchrow('SELECT * FROM connections WHERE tg_id = $1', tg_id)
+
+ if existing_connection:
+ await conn.execute('UPDATE connections SET trial = 1 WHERE tg_id = $1', tg_id)
+ else:
+ await add_connection(tg_id, 0, 1)
+
+ await store_key(tg_id, client_id, email, expiry_timestamp, connection_link, server_id)
+
+ instructions = INSTRUCTIONS
+ return {
+ 'key': connection_link,
+ 'instructions': instructions
+ }
+ else:
+ return {'error': 'Не удалось добавить клиента на панель'}
+ finally:
+ await conn.close()
+
+async def get_least_loaded_server(conn):
+ least_loaded_server_id = None
+ min_load_percentage = float('inf')
+
+ for server_id, server in SERVERS.items():
+ count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE server_id = $1', server_id)
+ percent_full = (count / 60) * 100 if count <= 60 else 100
+ if percent_full < min_load_percentage:
+ min_load_percentage = percent_full
+ least_loaded_server_id = server_id
+
+ return least_loaded_server_id