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