From bfc7ffe02423eca89885d3633a61a62007884800 Mon Sep 17 00:00:00 2001 From: Vlad Date: Wed, 18 Sep 2024 04:09:38 +0300 Subject: [PATCH] =?UTF-8?q?=D0=BE=D0=BF=D1=82=D0=B8=D0=BC=D0=B8=D0=B7?= =?UTF-8?q?=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- handlers/__pycache__/keys.cpython-310.pyc | Bin 1624 -> 1921 bytes handlers/__pycache__/pay.cpython-310.pyc | Bin 4504 -> 4673 bytes handlers/__pycache__/profile.cpython-310.pyc | Bin 2645 -> 2680 bytes handlers/__pycache__/start.cpython-310.pyc | Bin 1308 -> 4654 bytes handlers/keys.py | 9 +- handlers/pay.py | 90 +++++++++-------- handlers/profile.py | 20 ++-- handlers/start.py | 97 +++++++++++++++---- key_management.py | 68 ++++++++----- 9 files changed, 188 insertions(+), 96 deletions(-) diff --git a/handlers/__pycache__/keys.cpython-310.pyc b/handlers/__pycache__/keys.cpython-310.pyc index 1c9d0b6eb2f9270d303421b605076ebcad9fae56..5d50a89d45f9ac129745940175bb3839b92a0512 100644 GIT binary patch delta 693 zcmaJ-&1(}u6rVTQt;uHd9k4011eM}?kzi5CQ4m|S9#kw!VcBH1Azz!YJ0VTFv5A6s zFrv&sQH10_@Z(^pR1vQpb`OH?$&&{?37*x7X+%BvhR2)td++_`_vXFai-c>Tkc7D9 z7C&!%+xH|~fzvWi$JiV0)=faD2R1695_F(_m*Rns3PV^zD~Ot1(u z+#Alw8Ks?9&danja(m;8^TNI5Jm>ML^Vr$t=xt9f8WuGhd0MO)mV!Nj>YLOH<+VyB zr{&8k(Ws_?7bzN*qM@s0y_KtJCh>&ji>qgOzf{*uOHZiP8qq!3)a#X&TGh;Qqt3qi z4({ipi3vz+YE`!^t)Qz#!jeK3D+$&T)-xvN+P&n;uLsWT@t`(#RAd$x&o3nYTu9ALc>Gh{EH55|EW0PpABaQP7lk=2;5oqEBVP{x f5W3-62^PWM#oyzCnPG3m6r|WE@dRYp!c6uj5D3>6 delta 419 zcmYjNKTE@K5WTznX_BU?+Ny&JA_$F>h$wV(kk&=S4k8F8n6$REsic8uNoBDCVR7OFkCrARs88#Z?@R970$00Z#jRXxN|vEw{g zqH5UswK$H05JEpW_zj2rwg6^gxAmkg{;S7jVGh}=xWsfR%kIVLnUv?8*3OH5qgZ#E zO{?q_E4Deo^3qZKL)|_*c250D!)0%hsmhky3>?qY*p$4_F6FhoRsJaIC`6P-8AVC| zCdojN6jU(HA=AoNW+--y$r)5SG@KWv(97^6Q~!url}6dK>K<|J38R)~$=D`lST2_R E0cOKqOaK4? diff --git a/handlers/__pycache__/pay.cpython-310.pyc b/handlers/__pycache__/pay.cpython-310.pyc index 544e555dd4d8acda4ae9bcae0cb53752ac207ff6..e6aecc6131e2e42031651bf7c217c6cbebe30f2e 100644 GIT binary patch delta 2064 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 Fe*ut9T15Z= literal 1308 zcmZ`(&2G~`5Z+z?CO9bw5PuRfH!M}n3m}Am0|-$eKpcFD-0U`l+HtgYT7pukP$ZCO zK|&n4aO74Hg+ggL@B-`WUOB-d00L&$2~-Fic{2O$%r`UZnN4f8DuSnKf0?BYLZ8%P z=VQU*6ujA05EM}yqXdUIAtAwPO=2T4LsRQUY$bMRCr;=jZs;bJu!0dI%P6eU3bh!% zv5VTwn>DFJ-Mdy;qvjP9R6heV3W&7wL62q9zMS@R#se&!Gij1UT^g8j-}!Fb>9PxK zFq1}{p6=&)+Wi~781cD&Pwu_UdhuXvIs)tCb?G`SorGms)Mm2QisE=CYRxrilt(S1 zvKc*aAV?j+M|iU#h})RrMUq3=)S%`nSu{YmsI7G~$3vryhh`hM33XQSqLq`}pf0Vf z;x-1&DmB=x(p}(f577;CNQM^mR>^rd&T|MOz${uD+B-3d;GpW${i7Hm@kDHk7sZm; z_QeNKUyC)d3I8v}T5+dXIp&MU#RFfw*IVnw9kK2gw_)){yechx@lI?O4~u&~Ae+Ub zlIGPI73*S4Yyf=i()1+ksYpNod_cb>fU#HsMj(cGV5qbW@vd0*)k3GVS*$=fol?m) z=@-j@Y>g8rGXalnh`KGdihEE*8TEG>YrqGEZTv3DqzrSBOJq4$?fAL9gzQY!pJ)X{XVg@}q(b0{lD0CqlnSvKFelE!IsCe0hyVGd))vqqLjJfG?fWKC;Lh_7Z;<(nM`L6!(1KRw4Yh+$&W zaK96`N~)OrAg29oU&X`(e!GA0BVZih>7Xv1sFSvNlyEhB+yEgbM$i--J6r}CHx*hN zqxz=!=V{qo-yYJrI2vyw%ejuF3)~?ND@ON3lG1+6PQqZJOdaZO{key}\nДата окончания: {expiry_date}" + + # Добавляем кнопку с инструкциями + instructions_button = types.InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions') + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[instructions_button]]) + + await bot.send_message(tg_id, response_message, parse_mode="HTML", reply_markup=keyboard) else: response_message = "У вас нет ключей." + await bot.send_message(tg_id, response_message, reply_to_message_id=callback_query.message.message_id) except Exception as e: response_message = f"Ошибка при получении ключей: {e}" + await bot.send_message(tg_id, response_message, reply_to_message_id=callback_query.message.message_id) - await bot.send_message(tg_id, response_message, parse_mode="HTML", reply_to_message_id=callback_query.message.message_id) await callback_query.answer() diff --git a/handlers/pay.py b/handlers/pay.py index 9c06aeea..bf99bba6 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -17,100 +17,98 @@ class ReplenishBalanceState(StatesGroup): async def process_callback_replenish_balance(callback_query: types.CallbackQuery, state: FSMContext): await state.set_state(ReplenishBalanceState.choosing_transfer_method) keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [InlineKeyboardButton(text='По реквизитам', callback_data='transfer_method_requisites')], - [InlineKeyboardButton(text='По СБП', callback_data='transfer_method_sbp')] + [InlineKeyboardButton(text='По реквизитам', callback_data='transfer_method_requisites')] ]) - await callback_query.message.answer("Выберите метод перевода:", reply_markup=keyboard) - await callback_query.answer() + await bot.send_message(callback_query.from_user.id, "Выберите метод перевода:", reply_markup=keyboard) + await callback_query.answer() # Уведомляем Telegram, что запрос обработан @router.callback_query(lambda c: c.data.startswith('transfer_method_')) async def process_transfer_method_selection(callback_query: types.CallbackQuery, state: FSMContext): data = callback_query.data.split('_', 2) if len(data) != 3: - await callback_query.answer("Неверные данные для выбора метода перевода.") + await bot.send_message(callback_query.from_user.id, "Неверные данные для выбора метода перевода.") return - transfer_method = data[2] # 'requisites' или 'sbp' + transfer_method = data[2] # 'requisites' if transfer_method == 'requisites': message = ( - "Реквизиты для пополнения:\n" - f'\n' - "Банк: Пример Банк\n" - "Расчетный счет: 12345678901234567890\n" - "Получатель: ИП Иванов Иван Иванович" - f'\n' - "после перевода отправьте чек и ждите подтверждения" + "Банк: Т-Банк\n" + "2200701036597224\n" + "Получатель: Лисицын В.Д.\n" + f"\n" + "После перевода отправьте чек и дождитесь подтверждения." ) - elif transfer_method == 'sbp': - message = ( - "СБП для пополнения:\n" - f'\n' - "Номер банка: 1234567890\n" - "ФИО: Иванов Иван Иванович\n" - f'\n' - "после перевода отправьте чек и ждите подтверждения" + + # Удаляем старое сообщение о выборе метода + await bot.edit_message_text( + text="Вы выбрали метод перевода. Теперь вы увидите реквизиты.", + chat_id=callback_query.from_user.id, + message_id=callback_query.message.message_id ) + + # Отправляем реквизиты в новом сообщении + await bot.send_message(callback_query.from_user.id, message) + else: - await callback_query.answer("Неверный метод перевода.") + await bot.send_message(callback_query.from_user.id, "Неверный метод перевода.") return - await callback_query.message.answer(message) + await state.update_data(transfer_method=transfer_method) + await state.set_state(ReplenishBalanceState.waiting_for_admin_confirmation) + + admin_keyboard = InlineKeyboardMarkup(inline_keyboard=[ + [InlineKeyboardButton(text='Подтвердить', callback_data=f'admin_confirm_{callback_query.from_user.id}_{transfer_method}')], + [InlineKeyboardButton(text='Отклонить', callback_data=f'admin_decline_{callback_query.from_user.id}_{transfer_method}')] + ]) + + admin_message = ( + f"Пользователь {callback_query.from_user.full_name} запросил пополнение баланса.\n" + f"Метод перевода: {transfer_method}\n" + "Пожалуйста, подтвердите или отклоните запрос." + ) + + await bot.send_message(ADMIN_ID, admin_message, reply_markup=admin_keyboard) await callback_query.answer() @router.callback_query(lambda c: c.data.startswith('admin_')) async def process_admin_confirmation(callback_query: types.CallbackQuery, state: FSMContext): if callback_query.from_user.id != ADMIN_ID: - await callback_query.answer("Вы не являетесь администратором.") + await bot.send_message(callback_query.from_user.id, "Вы не являетесь администратором.") return data = callback_query.data.split('_', 4) if len(data) < 4: - await callback_query.answer("Неверные данные для обработки запроса.") + await bot.send_message(callback_query.from_user.id, "Неверные данные для обработки запроса.") return action = data[1] # 'confirm' или 'decline' - user_id_str = data[3] + user_id_str = data[2] + transfer_method = data[3] try: user_id = int(user_id_str) # Преобразование user_id в целое число if action == 'confirm': - - amount_str = data[4].split('_')[0] - - # Убедимся, что amount_str действительно является числом - if amount_str.isdigit(): - amount = int(amount_str) # Преобразование amount в целое число - else: - await callback_query.answer("Неверная сумма для пополнения. Проверьте формат данных.") - print(f"Ошибка преобразования суммы: amount_str='{amount_str}'") # Отладочное сообщение в консоль - return + amount = 100 # Пример суммы, на которую нужно пополнить баланс async with aiosqlite.connect(DATABASE_PATH) as db: - # Обновляем баланс пользователя await update_balance(user_id, amount) await db.commit() balance = await get_balance(user_id) - await callback_query.message.answer(f"Баланс пользователя успешно пополнен на {amount} единиц.\nТекущий баланс: {balance}") - - # Сообщаем пользователю о пополнении + await bot.send_message(callback_query.from_user.id, f"Баланс пользователя успешно пополнен на {amount} RUB.\nТекущий баланс: {balance}") await bot.send_message(user_id, f"Ваш баланс был успешно пополнен на {amount} RUB.") elif action == 'decline': - await callback_query.message.answer("Пополнение баланса отклонено.") - - # Сообщаем пользователю об отклонении + await bot.send_message(callback_query.from_user.id, "Пополнение баланса отклонено.") await bot.send_message(user_id, "Ваш запрос на пополнение баланса был отклонен.") except Exception as e: - # Отладочное сообщение - await callback_query.message.answer(f"Ошибка при пополнении баланса: {e}") + await bot.send_message(callback_query.from_user.id, f"Ошибка при пополнении баланса: {e}") print(f"Ошибка при пополнении баланса: {e}") # Отладочное сообщение в консоль - # Очистка состояния await state.clear() await callback_query.answer() diff --git a/handlers/profile.py b/handlers/profile.py index 03eefefd..b39c6a8e 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -35,24 +35,32 @@ async def process_callback_view_profile(callback_query: types.CallbackQuery, sta balance = await get_balance(tg_id) profile_message = ( f"Профиль \n\n" - f"ID: @{tg_id}\n" - f"Ключ: {email if email else 'Нет активного ключа'}\n" + f"ID: {tg_id}\n" + f"Ключ: {email if expiry_date else 'Нет активного ключа'}\n" f"Баланс: {balance} RUB\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') - keyboard = InlineKeyboardMarkup(inline_keyboard=[[button_view_keys], [button_replenish_balance]]) + keyboard = InlineKeyboardMarkup(inline_keyboard=[ + [button_create_key], # Добавляем кнопку "Создать ключ" + [button_view_keys], + [button_replenish_balance] + ]) - profile_message += "\nВы можете просмотреть ваши ключи или пополнить баланс ниже:" - except Exception as e: profile_message = f"Ошибка при получении данных профиля: {e}" keyboard = None # Отправляем сообщение с клавиатурой - await callback_query.message.reply(profile_message, reply_markup=keyboard) + await callback_query.message.bot.send_message( + chat_id=callback_query.message.chat.id, + text=profile_message, + parse_mode='Markdown', + reply_markup=keyboard + ) await callback_query.answer() @router.callback_query(lambda c: c.data == 'view_profile') diff --git a/handlers/start.py b/handlers/start.py index ddac6a2e..b9b9dab0 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,31 +1,37 @@ -from aiogram import types, Router +from aiogram import Bot, Dispatcher, Router, types from aiogram.filters import Command -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, ReplyKeyboardMarkup, KeyboardButton +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message, ReplyKeyboardMarkup, KeyboardButton +from aiogram.fsm.storage.memory import MemoryStorage +from aiogram.fsm.context import FSMContext +from aiogram.fsm.state import State, StatesGroup +from config import API_TOKEN, ADMIN_ID +from bot import dp, bot # Убедитесь, что bot и dp импортируются из правильного места router = Router() -async def start_command(message: types.Message, callback_data: str = None): +class FeedbackState(StatesGroup): + waiting_for_feedback = State() + +async def start_command(message: types.Message): welcome_text = ( - "Добро пожаловать! Вы можете создать ключ для подключения VPN, " - "просмотреть статистику использования, узнать дату окончания ключа или " - "просмотреть ваш профиль." + "*Добро пожаловать в наш сервис!*\n\n" + "Вы можете воспользоваться следующими функциями:\n\n" + "🔑 *Создать ключ для подключения VPN* - Получите уникальный ключ для доступа к VPN.\n" + "📅 *Узнать дату окончания ключа* - Проверьте, когда истекает срок действия вашего ключа.\n" + "👤 *Просмотреть ваш профиль* - Получите информацию о вашем аккаунте и балансе.\n\n" + "Нажмите на соответствующую кнопку ниже, чтобы начать!" ) - # Создаем кнопки для команд - button_create_key = InlineKeyboardButton(text='Создать ключ', callback_data='create_key') -# button_view_stats = InlineKeyboardButton(text='Посмотреть статистику', callback_data='view_stats') -# button_view_expiry = InlineKeyboardButton(text='Дата окончания ключа', callback_data='view_expiry') 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') - # Создаем клавиатуру с кнопками inline_keyboard = InlineKeyboardMarkup(inline_keyboard=[ - [button_create_key], -# [button_view_stats], -# [button_view_expiry], - [button_view_profile] + [button_view_profile], + [button_about_vpn], + [button_feedback] ]) - # Создаем кнопку для возврата в начало reply_keyboard = ReplyKeyboardMarkup( keyboard=[ [KeyboardButton(text="В начало")], @@ -34,9 +40,58 @@ async def start_command(message: types.Message, callback_data: str = None): resize_keyboard=True ) - # Отправляем сообщение с кнопками - await message.reply(welcome_text, reply_markup=inline_keyboard) - await message.answer( - "", + await message.bot.send_message( + chat_id=message.chat.id, + text=welcome_text, + parse_mode='Markdown', + reply_markup=inline_keyboard + ) + + await message.bot.send_message( + chat_id=message.chat.id, + text="Выберите действие:", reply_markup=reply_keyboard - ) \ No newline at end of file + ) + +@router.callback_query(lambda c: c.data == 'about_vpn') +async def handle_about_vpn(callback_query: CallbackQuery): + info_message = ( + "*О VPN*\n\n" + "Мы используем VLESS для обеспечения безопасного и надежного подключения к интернету. " + "Каждому пользователю предоставляется индивидуальный ключ для подключения. " + "Этот ключ необходим для использования нашего VPN-сервиса." + ) + await callback_query.message.answer( + info_message, + parse_mode='Markdown' + ) + 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.answer( + "Напишите нам, если у вас возникли трудности с подключением, есть отзыв или предложение. Ваше сообщение будет отправлено админу.", + parse_mode='Markdown' + ) + await state.set_state(FeedbackState.waiting_for_feedback) + await state.update_data(user_id=callback_query.from_user.id) + await callback_query.answer() + +@router.message(FeedbackState.waiting_for_feedback) +async def receive_feedback(message: types.Message, state: FSMContext): + user_data = await state.get_data() + user_id = user_data.get('user_id') + + feedback_message = ( + 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() diff --git a/key_management.py b/key_management.py index 5ba71195..98ca6ba7 100644 --- a/key_management.py +++ b/key_management.py @@ -1,6 +1,6 @@ from aiogram import Bot, Dispatcher, Router, F, types from aiogram.filters import Command -from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message, ReplyKeyboardMarkup, KeyboardButton +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup, CallbackQuery, Message from aiogram.fsm.storage.memory import MemoryStorage from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup @@ -19,7 +19,6 @@ from handlers.profile import process_callback_view_profile router = Router() - def escape_markdown(text: str) -> str: """ Экранирование специальных символов для Markdown. @@ -27,21 +26,26 @@ def escape_markdown(text: str) -> str: escape_chars = r'_*[]()~`>#+-=|{}.!' return re.sub(r'([%s])' % re.escape(escape_chars), r'\\\1', text) +def sanitize_key_name(key_name: str) -> str: + """ + Очищает имя ключа от недопустимых символов и приводит его к нижнему регистру. + """ + # Оставляем только строчные буквы, цифры, символы @._- + return re.sub(r'[^a-z0-9@._-]', '', key_name.lower()) + class Form(StatesGroup): waiting_for_key_name = State() waiting_for_admin_confirmation = State() waiting_for_statistics = State() waiting_for_expiry_date = State() - viewing_profile = State() - + viewing_profile = State() @dp.callback_query(F.data == 'create_key') async def process_callback_create_key(callback_query: types.CallbackQuery, state: FSMContext): - await callback_query.message.reply("Введите имя вашего профиля VPN:") + await bot.send_message(callback_query.from_user.id, "Введите имя вашего профиля VPN:") await state.set_state(Form.waiting_for_key_name) await callback_query.answer() - @dp.message() async def handle_text(message: types.Message, state: FSMContext): current_state = await state.get_state() @@ -68,16 +72,16 @@ async def handle_text(message: types.Message, state: FSMContext): if current_state == Form.waiting_for_key_name.state: tg_id = message.from_user.id - key_name = message.text + key_name = sanitize_key_name(message.text) tg_name = message.from_user.username if not key_name: - await message.reply("Имя клиента не указано.") + await bot.send_message(tg_id, "Имя клиента не указано.") await state.clear() return if await has_active_key(tg_id): - await message.reply("У вас уже есть активный ключ. Вы не можете создать новый.") + await bot.send_message(tg_id, "У вас уже есть активный ключ. Вы не можете создать новый.") await state.clear() return @@ -98,8 +102,7 @@ async def handle_text(message: types.Message, state: FSMContext): ]) await bot.send_message(ADMIN_CHAT_ID, admin_message, reply_markup=keyboard) - await state.set_state(Form.waiting_for_admin_confirmation) - await message.reply("Ожидайте подтверждения администратора.") + await bot.send_message(tg_id, "Ожидайте подтверждения администратора.") await state.clear() @dp.callback_query(F.data.startswith('confirm_key_')) @@ -114,7 +117,7 @@ async def handle_admin_confirmation(callback_query: CallbackQuery, state: FSMCon try: client_id = str(uuid.uuid4()) - email = key_name + email = key_name.lower() # Приведение email к нижнему регистру limit_ip = 1 total_gb = 0 current_time = datetime.utcnow() @@ -123,25 +126,46 @@ async def handle_admin_confirmation(callback_query: CallbackQuery, state: FSMCon flow = "xtls-rprx-vision" balance = 0 + # Проверка данных перед созданием ключа + print(f"Creating key with client_id={client_id}, email={email}, tg_id={tg_id}") + add_client(session, client_id, email, tg_id, limit_ip, total_gb, expiry_time, enable, flow) connection_link = link(session, client_id, email) await add_connection(tg_id, client_id, email, expiry_time, balance) await store_key(client_id, email, connection_link) - await bot.send_message(tg_id, f"Ключ создан:\n
{connection_link}
", parse_mode="HTML") - await callback_query.message.reply("Ключ создан и отправлен клиенту.") + + # Создаем кнопки + instructions_button = InlineKeyboardButton(text='Инструкции по использованию', callback_data='instructions') + keyboard = InlineKeyboardMarkup(inline_keyboard=[[instructions_button]]) + + # Отправляем сообщение с ключом и кнопкой + key_message = f"Ключ создан:\n
{connection_link}
" + await bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard) + await bot.send_message(callback_query.message.chat.id, "Ключ создан и отправлен клиенту.") except Exception as e: - await callback_query.message.reply(f"Ошибка при создании ключа: {e}") + await bot.send_message(callback_query.message.chat.id, f"Ошибка при создании ключа: {e}") + print(f"Ошибка при создании ключа: {e}") # Отладочное сообщение в консоль await callback_query.answer() -@dp.callback_query(F.data.startswith('reject_key_')) -async def handle_rejection(callback_query: CallbackQuery, state: FSMContext): - if callback_query.message.chat.id != int(ADMIN_CHAT_ID): - return - tg_id = int(callback_query.data.split('_', 3)[2]) - await bot.send_message(tg_id, "Создание ключа отклонено.") - await callback_query.message.reply("Запрос отклонен.") + +@dp.callback_query(F.data == 'instructions') +async def handle_instructions(callback_query: CallbackQuery): + 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\n" + "2. Скопируйте предоставленный ключ, который вы получили ранее.\n" + "3. Откройте приложение и нажмите на плюсик сверху справа.\n" + "4. Выберите 'Вставить из буфера обмена' для добавления ключа.\n\n" + "Если у вас возникнут вопросы, не стесняйтесь обращаться в поддержку." + ) + await callback_query.message.answer( + instructions_message, + parse_mode='Markdown' + ) await callback_query.answer()