diff --git a/auth.py b/auth.py index 8427a6e8..2a49a0e3 100644 --- a/auth.py +++ b/auth.py @@ -1,4 +1,5 @@ import json + import aiohttp from config import SERVERS diff --git a/backup.py b/backup.py index 196a71f7..5c9ca24c 100644 --- a/backup.py +++ b/backup.py @@ -1,10 +1,13 @@ -import subprocess -import os import logging +import os +import subprocess from datetime import datetime + from aiogram.types import BufferedInputFile + from bot import bot -from config import ADMIN_ID, DB_USER, DB_NAME, DB_PASSWORD +from config import ADMIN_ID, DB_NAME, DB_PASSWORD, DB_USER + async def backup_database(): diff --git a/bot.py b/bot.py index 2771c565..4caba5f1 100644 --- a/bot.py +++ b/bot.py @@ -8,7 +8,8 @@ storage = MemoryStorage() dp = Dispatcher(bot=bot, storage=storage) router = Router() -from handlers import key_management, keys, notifications, pay, profile, start, backup_handler +from handlers import (backup_handler, key_management, keys, notifications, pay, + profile, start) dp.include_router(start.router) dp.include_router(profile.router) diff --git a/client.py b/client.py index 56f51458..b2b87c75 100644 --- a/client.py +++ b/client.py @@ -1,7 +1,8 @@ import json + import aiohttp -from config import SERVERS +from config import SERVERS async def add_client(session, server_id: str, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str): diff --git a/database.py b/database.py index 07c87fa9..4e968733 100644 --- a/database.py +++ b/database.py @@ -126,7 +126,6 @@ async def update_balance(tg_id: int, amount: float): WHERE tg_id = $2 ''', amount, tg_id) - # Проверка и начисление реферального бонуса await handle_referral_on_balance_update(tg_id, amount) await conn.close() @@ -157,7 +156,6 @@ async def add_referral(referred_tg_id: int, referrer_tg_id: int): async def handle_referral_on_balance_update(tg_id: int, amount: float): conn = await asyncpg.connect(DATABASE_URL) - # Проверяем, есть ли реферал в таблице referral = await conn.fetchrow(''' SELECT referrer_tg_id FROM referrals WHERE referred_tg_id = $1 ''', tg_id) @@ -165,17 +163,13 @@ async def handle_referral_on_balance_update(tg_id: int, amount: float): if referral: referrer_tg_id = referral['referrer_tg_id'] - # Начисляем бонус (25% от платежа) пригласившему - bonus = amount * 0.25 # 25% от платежа + bonus = amount * 0.25 - # Проверяем, чтобы бонус не был отрицательным if bonus < 0: bonus = 0 - # Обновляем баланс пригласившего await update_balance(referrer_tg_id, bonus) - # Обновляем статус бонуса для пригласившего await conn.execute(''' UPDATE referrals SET reward_issued = TRUE WHERE referrer_tg_id = $1 AND referred_tg_id = $2 diff --git a/handlers/backup_handler.py b/handlers/backup_handler.py index 61069dea..f7b3531c 100644 --- a/handlers/backup_handler.py +++ b/handlers/backup_handler.py @@ -9,6 +9,6 @@ router = Router() @router.message(Command('backup')) async def backup_command(message: Message): await message.answer("Запускаю бэкап базы данных...") - await backup_database() # Запуск функции бэкапа + await backup_database() await message.answer("Бэкап завершен и отправлен админу.") diff --git a/handlers/instructions.py b/handlers/instructions.py index c9c2dc5e..3c073f60 100644 --- a/handlers/instructions.py +++ b/handlers/instructions.py @@ -1,28 +1,21 @@ import os + from aiogram import types -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup -from aiogram.types import BufferedInputFile +from aiogram.types import (BufferedInputFile, InlineKeyboardButton, + InlineKeyboardMarkup) + +from handlers.texts import INSTRUCTIONS + async def send_instructions(callback_query: types.CallbackQuery): - # Удаляем предыдущее сообщение await callback_query.message.delete() instructions_message = ( - "*📋 Инструкции по использованию вашего ключа:*\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" - " - **Для Windows:** [Hiddify Next](https://github.com/hiddify/hiddify-next/releases/latest/download/Hiddify-Windows-Setup-x64.Msix)\n\n" - "2. **Скопируйте предоставленный ключ**, который вы получили ранее.\n" - "3. **Откройте приложение и нажмите на плюсик сверху справа.**\n" - "4. **Выберите 'Вставить из буфера обмена' для добавления ключа.**\n\n" - "💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в [поддержку](https://t.me/solonet_sup)." + INSTRUCTIONS ) - # Путь к изображению image_path = os.path.join(os.path.dirname(__file__), 'instructions.jpg') - # Проверка существования файла изображения if not os.path.isfile(image_path): await callback_query.message.answer("Файл изображения не найден.") await callback_query.answer() @@ -31,7 +24,6 @@ async def send_instructions(callback_query: types.CallbackQuery): back_button = InlineKeyboardButton(text='🔙 Назад', callback_data='back_to_main') keyboard = InlineKeyboardMarkup(inline_keyboard=[[back_button]]) - # Отправка изображения и сообщения with open(image_path, 'rb') as image_from_buffer: await callback_query.message.answer_photo( BufferedInputFile(image_from_buffer.read(), filename="instructions.jpg"), diff --git a/handlers/key_management.py b/handlers/key_management.py index 0b255e43..f6dc8197 100644 --- a/handlers/key_management.py +++ b/handlers/key_management.py @@ -1,25 +1,27 @@ import re import uuid from datetime import datetime, timedelta -from aiogram.filters import Command import asyncpg from aiogram import F, Router, types +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, Message) from auth import link, login_with_credentials -from bot import dp, bot +from bot import bot, dp from client import add_client -from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS, ADMIN_ID +from config import (ADMIN_ID, ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, + SERVERS) from database import add_connection, get_balance, store_key, update_balance +from handlers.backup_handler import backup_command from handlers.instructions import send_instructions +from handlers.pay import ReplenishBalanceState, process_custom_amount_input from handlers.profile import process_callback_view_profile from handlers.start import start_command -from handlers.pay import ReplenishBalanceState, process_custom_amount_input -from handlers.backup_handler import backup_command +from handlers.texts import KEY, KEY_TRIAL, NULL_BALANCE router = Router() @@ -41,16 +43,14 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC try: for server_id, server in SERVERS.items(): count = await conn.fetchval('SELECT COUNT(*) FROM keys WHERE server_id = $1', server_id) - # Используем 60 как максимальное количество ключей percent_full = (count / 60) * 100 if count <= 60 else 100 server_name = f"{server['name']} ({percent_full:.1f}%)" server_buttons.append([InlineKeyboardButton(text=server_name, callback_data=f'select_server|{server_id}')]) finally: await conn.close() - # Добавляем кнопку "Назад" button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='view_profile') - server_buttons.append([button_back]) # Кнопка "Назад" внизу списка серверов + server_buttons.append([button_back]) await callback_query.message.edit_text( "⚙️ Выберите сервер для создания ключа:", @@ -78,9 +78,7 @@ async def select_server(callback_query: CallbackQuery, state: FSMContext): if trial_status == 1: await callback_query.message.edit_text( - "⚠️ У вас уже был пробный ключ.\n\n" - "Новый ключ будет выдан на один месяц и стоит 100 рублей.\n\n" - "Хотите продолжить?", + KEY, parse_mode="HTML", reply_markup=InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='✅ Да, создать новый ключ', callback_data='confirm_create_new_key')], @@ -90,8 +88,7 @@ async def select_server(callback_query: CallbackQuery, state: FSMContext): await state.update_data(creating_new_key=True) else: await callback_query.message.edit_text( - "🎉 Вам будет выдан пробный ключ на 24 часа!\n\n" - "Пожалуйста, введите название для вашего пробного ключа:", + KEY_TRIAL, parse_mode="HTML" ) await state.set_state(Form.waiting_for_key_name) @@ -109,8 +106,7 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex replenish_button = InlineKeyboardButton(text='Перейти в профиль', callback_data='view_profile') keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]]) await callback_query.message.edit_text( - "❗️ Недостаточно средств на балансе для создания нового ключа. " - "Пожалуйста, пополните баланс.", + NULL_BALANCE, reply_markup=keyboard ) await state.clear() @@ -140,7 +136,6 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext): try: conn = await asyncpg.connect(DATABASE_URL) try: - # Получаем всех пользователей с не использованным пробным ключом records = await conn.fetch(''' SELECT tg_id FROM connections WHERE trial = 0 ''') @@ -153,10 +148,8 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext): "Пожалуйста, воспользуйтесь им, чтобы протестировать наш сервис." ) try: - # Отправляем сообщение каждому пользователю await bot.send_message(chat_id=tg_id, text=trial_message) except Exception as e: - # Если бот был заблокирован пользователем, просто пропускаем его if "Forbidden: bot was blocked by the user" in str(e): print(f"Бот заблокирован пользователем с tg_id: {tg_id}") else: @@ -179,11 +172,11 @@ async def send_message_to_all_clients(message: types.Message, state: FSMContext) return await message.answer("Введите текст сообщения, который вы хотите отправить всем клиентам:") - await state.set_state(Form.waiting_for_message) # Устанавливаем состояние ожидания сообщения + await state.set_state(Form.waiting_for_message) @router.message(Form.waiting_for_message) async def process_message_to_all(message: types.Message, state: FSMContext): - text_message = message.text # Получаем текст сообщения + text_message = message.text try: conn = await asyncpg.connect(DATABASE_URL) @@ -203,14 +196,14 @@ async def process_message_to_all(message: types.Message, state: FSMContext): finally: await conn.close() - await state.clear() # Очистка состояния после отправки сообщения + await state.clear() @router.message() async def handle_text(message: types.Message, state: FSMContext): current_state = await state.get_state() if message.text in ["/send_to_all"]: - await send_message_to_all_clients(message, state) # Передаем состояние + await send_message_to_all_clients(message, state) return if message.text == "Мой профиль": @@ -225,7 +218,6 @@ async def handle_text(message: types.Message, state: FSMContext): return if current_state == ReplenishBalanceState.entering_custom_amount.state: - # Здесь вызовите ваш обработчик ввода суммы await process_custom_amount_input(message, state) return @@ -235,7 +227,7 @@ async def handle_text(message: types.Message, state: FSMContext): if message.text == "/backup": await backup_command(message) - elif current_state is None: # Если состояние не задано, обрабатываем обычные сообщения + elif current_state is None: await start_command(message) diff --git a/handlers/keys.py b/handlers/keys.py index 3e7b6cba..cce4ca63 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -1,3 +1,4 @@ +import locale from datetime import datetime, timedelta import asyncpg @@ -8,7 +9,7 @@ from bot import bot from client import add_client, delete_client, extend_client_key from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS from database import get_balance, update_balance -import locale +from handlers.texts import NO_KEYS locale.setlocale(locale.LC_TIME, 'ru_RU.UTF-8') @@ -33,7 +34,6 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): button = types.InlineKeyboardButton(text=f"🔑 {key_name}", callback_data=f'view_key|{key_name}|{client_id}') buttons.append([button]) - # Добавляем кнопку "Назад" в конце списка back_button = types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile') buttons.append([back_button]) @@ -46,9 +46,7 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): await bot.edit_message_text(response_message, chat_id=tg_id, message_id=callback_query.message.message_id, reply_markup=inline_keyboard, parse_mode="HTML") else: response_message = ( - "У вас нет подключённых устройств 😕\n\n" - "➕ Создайте первое устройство, чтобы подключиться к VPN.\n" - "Ваши устройства будут отображаться здесь." + NO_KEYS ) create_key_button = types.InlineKeyboardButton(text='➕ Создать ключ', callback_data='create_key') back_button = types.InlineKeyboardButton(text='🔙 Назад', callback_data='view_profile') @@ -99,7 +97,6 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): hours_left = time_left.seconds // 3600 days_left_message = f"Осталось часов: {hours_left}" - # Форматируем дату окончания в удобочитаемом виде formatted_expiry_date = expiry_date.strftime('%d %B %Y года') response_message = (f"🔑 Ваш ключ:\n
{key}
\n" @@ -318,7 +315,6 @@ async def process_callback_select_server(callback_query: types.CallbackQuery): try: conn = await asyncpg.connect(DATABASE_URL) try: - # Начинаем транзакцию с блокировкой строки по client_id, чтобы предотвратить параллельную миграцию async with conn.transaction(): record = await conn.fetchrow( 'SELECT email, expiry_time, server_id FROM keys WHERE client_id = $1 FOR UPDATE', client_id @@ -333,7 +329,6 @@ async def process_callback_select_server(callback_query: types.CallbackQuery): await callback_query.answer("Клиент уже на этом сервере.") return - # Проверка наличия клиента на новом сервере перед добавлением session_new = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) new_client_data = await add_client( session_new, server_id, client_id, email, tg_id, limit_ip=1, total_gb=0, @@ -344,16 +339,13 @@ async def process_callback_select_server(callback_query: types.CallbackQuery): if not new_client_data: raise Exception("Ошибка при создании клиента на новом сервере.") - # Генерация нового ключа new_key = await link(session_new, server_id, client_id, email) - # Обновляем запись в БД await conn.execute( 'UPDATE keys SET server_id = $1, key = $2 WHERE client_id = $3', server_id, new_key, client_id ) - # Удаление клиента со старого сервера try: session_old = await login_with_credentials(current_server_id, ADMIN_USERNAME, ADMIN_PASSWORD) success_delete = await delete_client(session_old, current_server_id, client_id) diff --git a/handlers/notifications.py b/handlers/notifications.py index c2257ced..1830917b 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -1,14 +1,11 @@ from datetime import datetime, timedelta + import asyncpg -from aiogram import Bot, Router, types -from aiogram.filters import Command +from aiogram import Bot, Router from aiogram.fsm.state import State, StatesGroup -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup -from auth import login_with_credentials -from bot import bot -from client import delete_client, extend_client_key -from config import ADMIN_ID, ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL -from database import get_balance, update_balance + +from config import DATABASE_URL +from database import get_balance router = Router() @@ -23,7 +20,6 @@ async def notify_expiring_keys(bot: Bot): threshold_time_10h = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000 threshold_time_24h = (datetime.utcnow() + timedelta(days=1)).timestamp() * 1000 - # Уведомления за 10 часов records = await conn.fetch(''' SELECT tg_id, email, expiry_time, client_id, server_id FROM keys WHERE expiry_time <= $1 AND expiry_time > $2 AND notified = FALSE @@ -35,17 +31,14 @@ async def notify_expiring_keys(bot: Bot): expiry_time = record['expiry_time'] server_id = record['server_id'] - # Логика формирования и отправки сообщения для уведомлений за 10 часов message = f"🔔 Уведомление: Ваш ключ для сервера {server_id} истекает через 10 часов.\n" \ f"Email: {email}\n" \ f"Дата истечения: {datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S')}" await bot.send_message(tg_id, message) - # Обновление статуса уведомления await conn.execute('UPDATE keys SET notified = TRUE WHERE client_id = $1', record['client_id']) - # Уведомления за 24 часа records_24h = await conn.fetch(''' SELECT tg_id, email, expiry_time, client_id, server_id FROM keys WHERE expiry_time <= $1 AND expiry_time > $2 AND notified_24h = FALSE @@ -63,7 +56,6 @@ async def notify_expiring_keys(bot: Bot): expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime('%Y-%m-%d %H:%M:%S') balance = await get_balance(tg_id) - # Логика формирования и отправки сообщения для уведомлений за 24 часа message_24h = f"⏳ Уведомление: Ваш ключ для сервера {server_id} истекает через 24 часа.\n" \ f"Email: {email}\n" \ f"Осталось времени: {hours_left} часов\n" \ @@ -72,7 +64,6 @@ async def notify_expiring_keys(bot: Bot): await bot.send_message(tg_id, message_24h) - # Обновление статуса уведомления за 24 часа await conn.execute('UPDATE keys SET notified_24h = TRUE WHERE client_id = $1', record['client_id']) finally: diff --git a/handlers/pay.py b/handlers/pay.py index 607b5232..bd14da17 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -6,7 +6,7 @@ from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiohttp import web -from yookassa import Configuration, Payment +from yookassa import Configuration, Payment from bot import bot from config import YOOKASSA_SECRET_KEY, YOOKASSA_SHOP_ID @@ -58,7 +58,6 @@ async def process_callback_replenish_balance(callback_query: types.CallbackQuery if not exists: await add_connection(tg_id, balance=0.0, trial=0) - # Клавиатура с фиксированными суммами и новой кнопкой "Ввести сумму" amount_keyboard = InlineKeyboardMarkup(inline_keyboard=[ [InlineKeyboardButton(text='100 RUB', callback_data='amount_100'), InlineKeyboardButton(text='300 RUB', callback_data='amount_300')], [InlineKeyboardButton(text='600 RUB', callback_data='amount_600'), InlineKeyboardButton(text='1000 RUB', callback_data='amount_1000')], @@ -99,7 +98,6 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F customer_name = callback_query.from_user.full_name customer_id = callback_query.from_user.id - # Используем tg_id для email customer_email = f"{customer_id}@solo.net" payment = Payment.create({ @@ -116,7 +114,7 @@ async def process_amount_selection(callback_query: types.CallbackQuery, state: F "receipt": { "customer": { "full_name": customer_name, - "email": customer_email, # Используем email в формате "tg_id@solo.net" + "email": customer_email, "phone": "79000000000" }, "items": [ @@ -201,7 +199,6 @@ async def process_enter_custom_amount(callback_query: types.CallbackQuery, state @router.message(State(ReplenishBalanceState.entering_custom_amount)) async def process_custom_amount_input(message: types.Message, state: FSMContext): - # Проверяем, является ли введенное значение числом if message.text.isdigit(): amount = int(message.text) if amount <= 0: @@ -211,7 +208,6 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) await state.update_data(amount=amount) await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation) - # Создание платежа try: payment = Payment.create({ "amount": { @@ -227,8 +223,8 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) "receipt": { "customer": { "full_name": message.from_user.full_name, - "email": f"{message.from_user.id}@solo.net", # Используем tg_id как email - "phone": "79000000000" # Здесь можно использовать номер телефона клиента, если он доступен + "email": f"{message.from_user.id}@solo.net", + "phone": "79000000000" }, "items": [ { diff --git a/handlers/profile.py b/handlers/profile.py index 56fbd69f..24f46759 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -1,10 +1,11 @@ from aiogram import Router, types from aiogram.fsm.context import FSMContext -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.fsm.state import State, StatesGroup +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup + +from bot import bot +from database import get_balance, get_key_count, get_referral_stats -from bot import bot -from database import get_balance, get_key_count, get_referral_stats, get_keys class ReplenishBalanceState(StatesGroup): choosing_transfer_method = State() @@ -43,10 +44,10 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu') keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [button_create_key, button_view_keys], # В один ряд "➕ Устройство" и "📱 Мои устройства" - [button_replenish_balance], # Отдельная кнопка для пополнения баланса - [button_invite, button_instructions], # В один ряд "👥 Пригласить" и "📘 Инструкции" - [button_back] # Отдельная кнопка "Назад" + [button_create_key, button_view_keys], + [button_replenish_balance], + [button_invite, button_instructions], + [button_back] ]) except Exception as e: diff --git a/handlers/start.py b/handlers/start.py index 493f027d..25b822e7 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,13 +1,15 @@ 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 texts import ABOUT_VPN, WELCOME_TEXT from bot import bot -from database import add_referral, check_connection_exists, add_connection -from config import CHANNEL_URL, SUPPORT_CHAT_URL, BOT_VERSION # Импорт BOT_VERSION +from config import CHANNEL_URL, SUPPORT_CHAT_URL +from database import add_connection, add_referral, check_connection_exists router = Router() @@ -15,20 +17,7 @@ class FeedbackState(StatesGroup): waiting_for_feedback = State() async def send_welcome_message(chat_id: int): - welcome_text = ( - "🎉 SoloNet — твой доступ в свободный интернет! 🌐✨\n\n" - "Наши преимущества:\n" - "
" - "🚀 Высокая скорость\n" - "🔄 Стабильность\n" - "🌍 Смена локаций\n" - "💬 Отзывчивая поддержка\n" - "📱💻 Для телефонов, компьютеров и планшетов\n" - "💰 Реферальная программа: 25% от покупки\n" - "
" - "\n\n🎁 1 день бесплатно!\n\n" # Добавление версии бота - "Перейдите в профиль для продолжения 👇" - ) + welcome_text = WELCOME_TEXT image_path = os.path.join(os.path.dirname(__file__), 'pic.jpg') @@ -71,19 +60,7 @@ async def start_command(message: Message): @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" - "🚀 Высокоскоростные серверы\n" - "Мы используем высокоскоростные серверы в различных локациях для обеспечения стабильного и быстрого соединения.\n\n" - - "🔐 Безопасность данных\n" - "Для защиты ваших данных мы применяем новейшие протоколы шифрования, которые гарантируют вашу конфиденциальность.\n\n" - - "🔑 Ваш ключ — ваша безопасность!\n" - "Не передавайте своё шифрование сторонним лицам, чтобы избежать рисков.\n\n" - - f"Версия бота: {BOT_VERSION}" # Добавление версии бота в информацию о VPN - ) + info_message = ABOUT_VPN button_back = InlineKeyboardButton(text='⬅️ Назад', callback_data='back_to_menu') inline_keyboard_back = InlineKeyboardMarkup(inline_keyboard=[[button_back]]) diff --git a/handlers/texts.py b/handlers/texts.py new file mode 100644 index 00000000..2824dd17 --- /dev/null +++ b/handlers/texts.py @@ -0,0 +1,64 @@ +from config import BOT_VERSION + +WELCOME_TEXT = ( + "🎉 SoloNet — твой доступ в свободный интернет! 🌐✨\n\n" + "Наши преимущества:\n" + "
" + "🚀 Высокая скорость\n" + "🔄 Стабильность\n" + "🌍 Смена локаций\n" + "💬 Отзывчивая поддержка\n" + "📱💻 Для телефонов, компьютеров и планшетов\n" + "💰 Реферальная программа: 25% от покупки\n" + "
" + "\n\n🎁 1 день бесплатно!\n\n" + "Перейдите в профиль для продолжения 👇" + ) + +ABOUT_VPN = ( + "*О VPN*\n\n" + "🚀 Высокоскоростные серверы\n" + "Мы используем высокоскоростные серверы в различных локациях для обеспечения стабильного и быстрого соединения.\n\n" + + "🔐 Безопасность данных\n" + "Для защиты ваших данных мы применяем новейшие протоколы шифрования, которые гарантируют вашу конфиденциальность.\n\n" + + "🔑 Ваш ключ — ваша безопасность!\n" + "Не передавайте своё шифрование сторонним лицам, чтобы избежать рисков.\n\n" + + f"Версия бота: {BOT_VERSION}" + ) + +KEY = ( + "⚠️ У вас уже был пробный ключ.\n\n" + "Новый ключ будет выдан на один месяц и стоит 100 рублей.\n\n" + "Хотите продолжить?" +) + +KEY_TRIAL = ( + "🎉 Вам будет выдан пробный ключ на 24 часа!\n\n" + "Пожалуйста, введите название для вашего пробного ключа:" +) + +NULL_BALANCE = ( + "❗️ Недостаточно средств на балансе для создания нового ключа. " + "Пожалуйста, пополните баланс." +) + +NO_KEYS = ( + "У вас нет подключённых устройств 😕\n\n" + "➕ Создайте первое устройство, чтобы подключиться к VPN.\n" + "Ваши устройства будут отображаться здесь." +) + +INSTRUCTIONS = ( + "*📋 Инструкции по использованию вашего ключа:*\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" + " - **Для Windows:** [Hiddify Next](https://github.com/hiddify/hiddify-next/releases/latest/download/Hiddify-Windows-Setup-x64.Msix)\n\n" + "2. **Скопируйте предоставленный ключ**, который вы получили ранее.\n" + "3. **Откройте приложение и нажмите на плюсик сверху справа.**\n" + "4. **Выберите 'Вставить из буфера обмена' для добавления ключа.**\n\n" + "💬 Если у вас возникнут вопросы, не стесняйтесь обращаться в [поддержку](https://t.me/solonet_sup)." +) \ No newline at end of file