diff --git a/auth.py b/auth.py index 8827e109..20960890 100644 --- a/auth.py +++ b/auth.py @@ -1,13 +1,15 @@ -import requests -from config import ADMIN_USERNAME, ADMIN_PASSWORD, GET_INBOUNDS_URL import json +import requests + +from config import API_URL, GET_INBOUNDS_URL + session = None def login_with_credentials(username, password): global session session = requests.Session() - auth_url = "https://solonet.pocomacho.ru:62553/solonet/login/" + auth_url = f"{API_URL}/login/" data = { "username": username, "password": password @@ -39,7 +41,7 @@ def link(session, client_id: str, email: str): if 'obj' not in response or len(response['obj']) == 0: raise Exception("Не удалось получить данные клиентов.") - inbounds = response['obj'][0] # Убедитесь, что это правильный индекс + inbounds = response['obj'][0] settings = json.loads(inbounds['settings']) # Найти клиентский ID в настройках diff --git a/bot.py b/bot.py index 3a8f1b84..b22f25db 100644 --- a/bot.py +++ b/bot.py @@ -1,17 +1,17 @@ from aiogram import Bot, Dispatcher, Router from aiogram.fsm.storage.memory import MemoryStorage -from config import API_TOKEN + from auth import login_with_credentials +from config import API_TOKEN bot = Bot(token=API_TOKEN) storage = MemoryStorage() dp = Dispatcher(bot=bot, storage=storage) router = Router() -from handlers import start, profile, keys, balance, pay +from handlers import balance, keys, pay, profile, start from key_management import router as key_management_router - # Регистрация обработчиков dp.include_router(start.router) dp.include_router(profile.router) diff --git a/client.py b/client.py index 8522dc46..87587bab 100644 --- a/client.py +++ b/client.py @@ -1,7 +1,10 @@ import json +from config import API_URL + + def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, total_gb: int, expiry_time: int, enable: bool, flow: str): - url = 'https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/addClient' + url = f'{API_URL}/panel/api/inbounds/addClient' email = email.lower() @@ -41,7 +44,7 @@ def add_client(session, client_id: str, email: str, tg_id: str, limit_ip: int, t print(f"Ошибка при добавлении клиента: {response.status_code}, {response.text}") def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: int) -> bool: - response = session.get(f"https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/getClientTraffics/{email}") + response = session.get(f"{API_URL}/panel/api/inbounds/getClientTraffics/{email}") print(f"GET {response.url} Status: {response.status_code}") print(f"GET Response: {response.text}") @@ -89,7 +92,7 @@ def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: in } try: - response = session.post(f"https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/updateClient/{client_id}", json=payload, headers=headers) + response = session.post(f"{API_URL}/panel/api/inbounds/updateClient/{client_id}", json=payload, headers=headers) print(f"POST {response.url} Status: {response.status_code}") print(f"POST Request Data: {json.dumps(payload, indent=2)}") print(f"POST Response: {response.text}") @@ -104,7 +107,7 @@ def extend_client_key(session, tg_id, client_id, email: str, new_expiry_time: in return False def delete_client(session, client_id: str) -> bool: - url = f"https://solonet.pocomacho.ru:62553/solonet/panel/api/inbounds/1/delClient/{client_id}" + url = f"{API_URL}/panel/api/inbounds/1/delClient/{client_id}" headers = { 'Accept': 'application/json' } diff --git a/database.py b/database.py index 4e620f18..3f4bd37b 100644 --- a/database.py +++ b/database.py @@ -1,6 +1,9 @@ -import asyncpg from datetime import datetime -from config import DATABASE_URL + +import asyncpg + +from config import DATABASE_URL + async def init_db(): conn = await asyncpg.connect(DATABASE_URL) 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/balance.py b/handlers/balance.py index ccd01086..1ee5eb8c 100644 --- a/handlers/balance.py +++ b/handlers/balance.py @@ -1,5 +1,5 @@ -from aiogram import types, Router import aiosqlite +from aiogram import Router, types router = Router() diff --git a/handlers/keys.py b/handlers/keys.py index 64875e1e..424da168 100644 --- a/handlers/keys.py +++ b/handlers/keys.py @@ -1,12 +1,13 @@ -import asyncpg -from aiogram import types, Router -from bot import bot from datetime import datetime, timedelta -from database import get_balance, update_balance -from client import extend_client_key -from config import ADMIN_USERNAME, DATABASE_URL, ADMIN_PASSWORD + +import asyncpg +from aiogram import Router, types + from auth import login_with_credentials -from client import extend_client_key, delete_client +from bot import bot +from client import delete_client, extend_client_key +from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL +from database import get_balance, update_balance router = Router() diff --git a/handlers/pay.py b/handlers/pay.py index 0e1e5101..5c31cdb1 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -1,12 +1,13 @@ -from aiogram import types, Router -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup +import asyncpg +from aiogram import Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from config import ADMIN_ID, DATABASE_URL -from database import get_balance, update_balance, get_key_count +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup + from bot import bot +from config import ADMIN_ID, DATABASE_URL +from database import get_balance, get_key_count, update_balance from handlers.profile import process_callback_view_profile -import asyncpg router = Router() diff --git a/handlers/profile.py b/handlers/profile.py index 6089ff66..954699d7 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -1,9 +1,12 @@ -from aiogram import types, Router -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup +from datetime import datetime + +from aiogram import Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from datetime import datetime +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup + from database import get_balance, get_key_count +from bot import bot # Импортируем объект bot router = Router() @@ -13,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: # Получаем количество ключей @@ -24,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 b7e0fc55..6aa6d43c 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,47 +1,66 @@ from aiogram import Router, types from aiogram.filters import Command -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message, ReplyKeyboardMarkup, KeyboardButton from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup +from aiogram.types import (CallbackQuery, InlineKeyboardButton, + InlineKeyboardMarkup, Message) +from aiogram.types import BufferedInputFile +import os + +from bot import bot from config import ADMIN_ID, CHANNEL_URL -from bot import bot # Убедитесь, что bot и dp импортируются из правильного места 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 для обеспечения безопасного и надежного подключения к интернету. " @@ -49,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) @@ -127,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 95b35bfa..44feb0bf 100644 --- a/key_management.py +++ b/key_management.py @@ -1,21 +1,24 @@ +import asyncio import re import uuid -import asyncio from datetime import datetime, timedelta -from aiogram import Router, types, F -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message -from aiogram.fsm.context import FSMContext -from aiogram.fsm.state import State, StatesGroup - -from auth import login_with_credentials, link -from client import add_client -from config import API_TOKEN, ADMIN_PASSWORD, ADMIN_USERNAME, ADMIN_CHAT_ID, DATABASE_URL -from database import add_connection, has_active_key, get_balance, store_key, update_balance -from bot import dp, bot -from handlers.profile import process_callback_view_profile -from handlers.start import start_command import asyncpg +from aiogram import F, Router, types +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 bot, dp +from client import add_client +from config import (ADMIN_CHAT_ID, ADMIN_PASSWORD, ADMIN_USERNAME, API_TOKEN, + DATABASE_URL) +from database import (add_connection, get_balance, has_active_key, store_key, + update_balance) +from handlers.profile import process_callback_view_profile +from handlers.start import start_command router = Router() @@ -42,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')], @@ -65,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) @@ -109,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) @@ -119,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") @@ -162,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)