diff --git a/auth.py b/auth.py index b1740a8b..20960890 100644 --- a/auth.py +++ b/auth.py @@ -2,7 +2,7 @@ import json import requests -from config import GET_INBOUNDS_URL, API_URL +from config import API_URL, GET_INBOUNDS_URL session = None diff --git a/handlers/__pycache__/balance.cpython-310.pyc b/handlers/__pycache__/balance.cpython-310.pyc index 3100575b..4d200150 100644 Binary files a/handlers/__pycache__/balance.cpython-310.pyc and b/handlers/__pycache__/balance.cpython-310.pyc differ diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index f4b2f054..9a69ce57 100644 Binary files a/handlers/__pycache__/keys.cpython-310.pyc and b/handlers/__pycache__/keys.cpython-310.pyc differ diff --git a/handlers/__pycache__/pay.cpython-310.pyc b/handlers/__pycache__/pay.cpython-310.pyc index 8cf0dcc0..70f5d749 100644 Binary files a/handlers/__pycache__/pay.cpython-310.pyc and b/handlers/__pycache__/pay.cpython-310.pyc differ diff --git a/handlers/__pycache__/profile.cpython-310.pyc b/handlers/__pycache__/profile.cpython-310.pyc index a8600673..65878ea1 100644 Binary files a/handlers/__pycache__/profile.cpython-310.pyc and b/handlers/__pycache__/profile.cpython-310.pyc differ diff --git a/handlers/__pycache__/start.cpython-310.pyc b/handlers/__pycache__/start.cpython-310.pyc index 63472a32..c844f7d6 100644 Binary files a/handlers/__pycache__/start.cpython-310.pyc and b/handlers/__pycache__/start.cpython-310.pyc differ diff --git a/handlers/profile.py b/handlers/profile.py index dcb421c5..954699d7 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -6,6 +6,7 @@ from aiogram.fsm.state import State, StatesGroup from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from database import get_balance, get_key_count +from bot import bot # Импортируем объект bot router = Router() @@ -15,6 +16,7 @@ class ReplenishBalanceState(StatesGroup): async def process_callback_view_profile(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id + username = callback_query.from_user.full_name # Получаем имя пользователя try: # Получаем количество ключей @@ -26,38 +28,43 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta balance = 0 # По умолчанию 0, если баланс неизвестен profile_message = ( - f"Профиль пользователя\n\n" + f"Профиль: {username}\n\n" f"🔹 ID: {tg_id}\n" - f"🔹 Количество ключей: {key_count}\n" f"🔹 Баланс: {balance} RUB\n" - f"🔹 Дата регистрации: {datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S')}\n" + f"🔹 Количество ключей: {key_count}\n" ) - # Кнопки для действий в профиле - button_create_key = InlineKeyboardButton(text='Создать ключ', callback_data='create_key') - button_view_keys = InlineKeyboardButton(text='Мои ключи', callback_data='view_keys') - button_replenish_balance = InlineKeyboardButton(text='Пополнить баланс', callback_data='replenish_balance') + # Кнопки для действий в профиле с добавленными смайликами + button_create_key = InlineKeyboardButton(text='🔑 Создать ключ', callback_data='create_key') + button_view_keys = InlineKeyboardButton(text='🔐 Мои ключи', callback_data='view_keys') + button_replenish_balance = InlineKeyboardButton(text='💳 Пополнить баланс', callback_data='replenish_balance') button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu') # Добавляем кнопку "Назад" keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [button_create_key], # Добавляем кнопку "Создать ключ" + [button_create_key], [button_view_keys], [button_replenish_balance], - [button_back] # Добавляем кнопку "Назад" в клавиатуру + [button_back] ]) except Exception as e: profile_message = f"❗️ Ошибка при получении данных профиля: {e}" keyboard = None - # Редактируем существующее сообщение, а не отправляем новое - await callback_query.message.edit_text( + # Удаляем текущее сообщение + await callback_query.message.delete() + + # Отправляем новое сообщение с профилем через bot + await bot.send_message( + chat_id=tg_id, # ID чата, куда отправляем сообщение text=profile_message, parse_mode='HTML', reply_markup=keyboard ) + await callback_query.answer() @router.callback_query(lambda c: c.data == 'view_profile') async def view_profile_handler(callback_query: types.CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state) + diff --git a/handlers/solo_pic.png b/handlers/solo_pic.png new file mode 100644 index 00000000..484f1bdc Binary files /dev/null and b/handlers/solo_pic.png differ diff --git a/handlers/start.py b/handlers/start.py index c59fe02c..6aa6d43c 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -3,8 +3,9 @@ from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import (CallbackQuery, InlineKeyboardButton, - InlineKeyboardMarkup, KeyboardButton, Message, - ReplyKeyboardMarkup) + InlineKeyboardMarkup, Message) +from aiogram.types import BufferedInputFile +import os from bot import bot from config import ADMIN_ID, CHANNEL_URL @@ -14,37 +15,52 @@ router = Router() class FeedbackState(StatesGroup): waiting_for_feedback = State() -async def start_command(message: types.Message): +async def send_welcome_message(chat_id: int): + # Новый текст приветствия welcome_text = ( - "*Добро пожаловать в наш сервис!*\n\n" - "Вы можете воспользоваться следующими функциями:\n\n" - "🔑 *Создать ключ для подключения VPN* - Получите уникальный ключ для доступа к VPN.\n" - "📅 *Узнать дату окончания ключа* - Проверьте, когда истекает срок действия вашего ключа.\n" - "👤 *Просмотреть ваш профиль* - Получите информацию о вашем аккаунте и балансе.\n\n" - "Нажмите на соответствующую кнопку ниже, чтобы начать!" + "*SoloNet -- ваш провайдер в бесцензурный интернет!*\n\n" + "Получите высокую скорость и самый безопасный протокол VPN, " + "который работает даже в Китае." ) - button_view_profile = InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile') - button_about_vpn = InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn') - button_feedback = InlineKeyboardButton(text='📝 Обратная связь', callback_data='feedback') - button_channel = InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL) + # Путь к изображению + image_path = os.path.join(os.path.dirname(__file__), 'solo_pic.png') + # Проверка существования файла + if not os.path.isfile(image_path): + await bot.send_message(chat_id, "Файл изображения не найден.") + return + + # Создаем клавиатуру inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [button_view_profile], - [button_about_vpn], - [button_feedback], - [button_channel] + [InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile')], + [InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn')], + [InlineKeyboardButton(text='📝 Обратная связь', callback_data='feedback')], + [InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL)] ]) - await message.bot.send_message( - chat_id=message.chat.id, - text=welcome_text, - parse_mode='Markdown', - reply_markup=inline_keyboard - ) + # Используем BufferedInputFile для отправки изображения + with open(image_path, 'rb') as image_from_buffer: + await bot.send_photo( + chat_id, + BufferedInputFile( + image_from_buffer.read(), + filename="solo_pic.png" + ), + caption=welcome_text, + parse_mode='Markdown', + reply_markup=inline_keyboard + ) + +@router.message(Command('start')) +async def start_command(message: Message): + await send_welcome_message(message.chat.id) @router.callback_query(lambda c: c.data == 'about_vpn') async def handle_about_vpn(callback_query: CallbackQuery): + # Удаляем сообщение главного меню + await callback_query.message.delete() + info_message = ( "*О VPN*\n\n" "Мы используем VLESS для обеспечения безопасного и надежного подключения к интернету. " @@ -52,69 +68,39 @@ async def handle_about_vpn(callback_query: CallbackQuery): "Этот ключ необходим для использования нашего VPN-сервиса." ) - # Кнопка "Назад" button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu') + inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[[button_back]]) - # Обновлённая клавиатура с кнопкой "Назад" - inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[ - [button_back] - ]) - - await callback_query.message.edit_text( + await callback_query.message.answer( info_message, parse_mode='Markdown', - reply_markup=inline_keyboard_back # Добавление клавиатуры с кнопкой "Назад" + reply_markup=inline_keyboard_back ) await callback_query.answer() @router.callback_query(lambda c: c.data == 'back_to_menu') async def handle_back_to_menu(callback_query: CallbackQuery): - welcome_text = ( - "*Добро пожаловать в наш сервис!*\n\n" - "Вы можете воспользоваться следующими функциями:\n\n" - "🔑 *Создать ключ для подключения VPN* - Получите уникальный ключ для доступа к VPN.\n" - "📅 *Узнать дату окончания ключа* - Проверьте, когда истекает срок действия вашего ключа.\n" - "👤 *Просмотреть ваш профиль* - Получите информацию о вашем аккаунте и балансе.\n\n" - "Нажмите на соответствующую кнопку ниже, чтобы начать!" - ) - - button_view_profile = InlineKeyboardButton(text='👤 Мой профиль', callback_data='view_profile') - button_about_vpn = InlineKeyboardButton(text='🔒 О VPN', callback_data='about_vpn') - button_feedback = InlineKeyboardButton(text='📝 Обратная связь', callback_data='feedback') - button_channel = InlineKeyboardButton(text='📢 Наш канал', url=CHANNEL_URL) - - inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [button_view_profile], - [button_about_vpn], - [button_feedback], - [button_channel] - ]) - - # Редактирование сообщения на главное меню - await callback_query.message.edit_text( - welcome_text, - parse_mode='Markdown', - reply_markup=inline_keyboard - ) + # Удаляем текущее сообщение + await callback_query.message.delete() + + # Отправляем приветственное сообщение + await send_welcome_message(callback_query.from_user.id) await callback_query.answer() @router.callback_query(lambda c: c.data == 'feedback') async def handle_feedback(callback_query: CallbackQuery, state: FSMContext): + # Удаляем сообщение главного меню + await callback_query.message.delete() + feedback_text = "Напишите нам, если у вас возникли трудности с подключением, есть отзыв или предложение. @pocomacho" - # Кнопка "Назад" button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu') + inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[[button_back]]) - # Обновлённая клавиатура с кнопкой "Назад" - inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[ - [button_back] - ]) - - # Редактирование сообщения с кнопкой "Назад" - await callback_query.message.edit_text( + await callback_query.message.answer( feedback_text, parse_mode='Markdown', - reply_markup=inline_keyboard_back # Клавиатура с кнопкой "Назад" + reply_markup=inline_keyboard_back ) await state.set_state(FeedbackState.waiting_for_feedback) @@ -130,17 +116,12 @@ async def receive_feedback(message: types.Message, state: FSMContext): f"Обратная связь от пользователя {user_id}:\n\n" f"{message.text}" ) - + try: await bot.send_message(ADMIN_ID, feedback_message) await message.answer("Спасибо за ваше сообщение! Мы свяжемся с вами, если это будет необходимо.") except Exception as e: await message.answer("Произошла ошибка при отправке вашего сообщения.") print(f"Ошибка при отправке обратной связи: {e}") # Логирование ошибок - - await state.clear() -# Пример обработки команды старт -@router.message(Command('start')) -async def cmd_start(message: Message): - await start_command(message) \ No newline at end of file + await state.clear() diff --git a/key_management.py b/key_management.py index dafd6dc6..44feb0bf 100644 --- a/key_management.py +++ b/key_management.py @@ -45,7 +45,7 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC if trial_status == 1: await callback_query.message.edit_text( - "У вас уже был пробный ключ. Вы можете создать новый за 100 рублей. " + "У вас уже был пробный ключ. Новый стоит 100 рублей и сразу на месяц. \n\n" "Хотите продолжить?", reply_markup=InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='Да, создать новый ключ', callback_data='confirm_create_new_key')], @@ -68,11 +68,15 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex if balance < 100: replenish_button = InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile') keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]]) - await callback_query.message.edit_text("Недостаточно средств на балансе для создания нового ключа.", reply_markup=keyboard) + await callback_query.message.edit_text( + "❗️ Недостаточно средств на балансе для создания нового ключа. " + "Пожалуйста, пополните баланс.", + reply_markup=keyboard + ) await state.clear() return - await callback_query.message.edit_text("Пожалуйста, выберите имя для вашего нового ключа:") + await callback_query.message.edit_text("🔑 Пожалуйста, выберите имя для вашего нового ключа:") await state.set_state(Form.waiting_for_key_name) await state.update_data(creating_new_key=True) @@ -112,8 +116,8 @@ async def handle_key_name_input(message: Message, state: FSMContext): key_name = sanitize_key_name(message.text) if not key_name: - await message.bot.send_message(tg_id, "Назовите профиль на английском языке.") - return # Прерываем выполнение функции + await message.bot.send_message(tg_id, "📝 Пожалуйста, назовите профиль на английском языке.") + return data = await state.get_data() creating_new_key = data.get('creating_new_key', False) @@ -122,28 +126,32 @@ async def handle_key_name_input(message: Message, state: FSMContext): client_id = str(uuid.uuid4()) email = key_name.lower() current_time = datetime.utcnow() - expiry_time = int((current_time + timedelta(days=1)).timestamp() * 1000) + expiry_time = None - if creating_new_key: - # Проверяем, занято ли имя ключа - conn = await asyncpg.connect(DATABASE_URL) - try: - existing_key = await conn.fetchrow('SELECT * FROM keys WHERE email = $1', email) - if existing_key: - await message.bot.send_message(tg_id, "Это имя уже занято. Пожалуйста, выберите другое имя.") - return # Прерываем выполнение функции - finally: - await conn.close() + # Получаем статус пробного ключа из базы данных + conn = await asyncpg.connect(DATABASE_URL) + try: + existing_connection = await conn.fetchrow('SELECT trial FROM connections WHERE tg_id = $1', tg_id) + finally: + await conn.close() + trial_status = existing_connection['trial'] if existing_connection else 0 + + if trial_status == 0: + # Создаем пробный ключ на 1 день + expiry_time = int((current_time + timedelta(days=1)).timestamp() * 1000) + else: + # Проверяем баланс перед созданием нового ключа balance = await get_balance(tg_id) if balance < 100: replenish_button = InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile') keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]]) - await message.bot.send_message(tg_id, "Недостаточно средств на балансе для создания нового ключа.", reply_markup=keyboard) + await message.bot.send_message(tg_id, "❗️ Недостаточно средств на балансе для создания нового ключа.", reply_markup=keyboard) await state.clear() return - + await update_balance(tg_id, -100) + expiry_time = int((current_time + timedelta(days=30)).timestamp() * 1000) try: add_client(session, client_id, email, tg_id, limit_ip=1, total_gb=0, expiry_time=expiry_time, enable=True, flow="xtls-rprx-vision") @@ -165,32 +173,36 @@ async def handle_key_name_input(message: Message, state: FSMContext): await store_key(tg_id, client_id, email, expiry_time, connection_link) keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions')], - [InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile')] + [InlineKeyboardButton(text='📖 Инструкции по использованию', callback_data='instructions')], + [InlineKeyboardButton(text='🔙 Перейти в профиль', callback_data='view_profile')] ]) - key_message = f"Ключ создан:\n
{connection_link}
" + key_message = ( + "✅ Ключ успешно создан:\n" + f"
{connection_link}
" + ) await message.bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard) except Exception as e: - await message.bot.send_message(tg_id, f"Ошибка при создании ключа: {e}") + await message.bot.send_message(tg_id, f"❌ Ошибка при создании ключа: {e}") await state.clear() + @dp.callback_query(F.data == 'instructions') async def handle_instructions(callback_query: CallbackQuery): instructions_message = ( - "*Инструкции по использованию вашего ключа:*\n\n" + "*📋 Инструкции по использованию вашего ключа:*\n\n" "1. Скачайте приложение для вашего устройства:\n" " - Для Android: [V2Ray](https://play.google.com/store/apps/details?id=com.v2ray.ang&hl=ru&pli=1)\n" " - Для iPhone: [Streisand](https://apps.apple.com/ru/app/streisand/id6450534064)\n\n" "2. Скопируйте предоставленный ключ, который вы получили ранее.\n" "3. Откройте приложение и нажмите на плюсик сверху справа.\n" "4. Выберите 'Вставить из буфера обмена' для добавления ключа.\n\n" - "Если у вас возникнут вопросы, не стесняйтесь обращаться в поддержку." + "💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в поддержку." ) - back_button = InlineKeyboardButton(text='Назад', callback_data='back_to_main') + back_button = InlineKeyboardButton(text='🔙 Назад', callback_data='back_to_main') keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await callback_query.message.edit_text(instructions_message, parse_mode='Markdown', reply_markup=keyboard)