From 5e17847717da858f28ce376ee40dccad201039dd Mon Sep 17 00:00:00 2001 From: Vladless Date: Sun, 10 Nov 2024 02:25:36 +0300 Subject: [PATCH] bug and fix --- handlers/admin/admin.py | 2 +- handlers/admin/admin_panel.py | 24 +++-- handlers/admin/user_editor.py | 6 +- handlers/commands.py | 4 +- handlers/instructions/instructions.py | 3 +- handlers/keys/key_management.py | 36 +------ handlers/keys/key_utils.py | 106 +++++++++++++++++++++ handlers/keys/keys.py | 130 ++++++-------------------- handlers/keys/trial_key.py | 30 +----- handlers/notifications.py | 3 - handlers/payment/yookassa_pay.py | 8 +- handlers/profile.py | 9 +- handlers/start.py | 42 +++++---- handlers/utils.py | 18 ++++ main.py | 16 +--- middlewares/database.py | 2 +- 16 files changed, 221 insertions(+), 218 deletions(-) create mode 100644 handlers/keys/key_utils.py diff --git a/handlers/admin/admin.py b/handlers/admin/admin.py index fb3ee279..b27fed36 100644 --- a/handlers/admin/admin.py +++ b/handlers/admin/admin.py @@ -1,7 +1,7 @@ from datetime import datetime import asyncpg -from aiogram import F, Router, types +from aiogram import Router, types from aiogram.filters import Command from loguru import logger diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py index 8b81f86e..16c4fb59 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -9,14 +9,13 @@ from aiogram.fsm.state import State, StatesGroup from aiogram.types import ( CallbackQuery, InlineKeyboardButton, - InlineKeyboardMarkup, Message, ) from aiogram.utils.keyboard import InlineKeyboardBuilder from backup import backup_database from bot import bot -from config import ADMIN_ID, DATABASE_URL +from config import DATABASE_URL from handlers.commands import send_message_to_all_clients router = Router() @@ -46,9 +45,11 @@ async def handle_admin_command(message: types.Message, is_admin: bool): text="Отправить сообщение всем клиентам", callback_data="send_to_alls" ) ) - builder.row(InlineKeyboardButton(text="Создать бэкап", callback_data="backups")) + builder.row(InlineKeyboardButton( + text="Создать бэкап", callback_data="backups")) builder.row( - InlineKeyboardButton(text="Перезапустить бота", callback_data="restart_bot") + InlineKeyboardButton(text="Перезапустить бота", + callback_data="restart_bot") ) await bot.send_message( message.chat.id, "Панель администратора.", reply_markup=builder.as_markup() @@ -83,7 +84,8 @@ async def user_stats_menu(callback_query: CallbackQuery, is_admin: bool): builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text="Назад", callback_data="back_to_admin_menu") + InlineKeyboardButton( + text="Назад", callback_data="back_to_admin_menu") ) await callback_query.message.edit_text( @@ -140,10 +142,12 @@ async def user_editor_menu(callback_query: CallbackQuery, is_admin: bool): ) ) builder.row( - InlineKeyboardButton(text="Поиск по tg_id", callback_data="search_by_tg_id") + InlineKeyboardButton(text="Поиск по tg_id", + callback_data="search_by_tg_id") ) builder.row( - InlineKeyboardButton(text="Назад", callback_data="back_to_admin_menu") + InlineKeyboardButton( + text="Назад", callback_data="back_to_admin_menu") ) await callback_query.message.edit_text( "Выберите метод поиска:", reply_markup=builder.as_markup() @@ -176,9 +180,11 @@ async def back_to_admin_menu(callback_query: CallbackQuery, is_admin: bool): callback_data="send_to_alls", ) ) - builder.row(InlineKeyboardButton(text="Создать бэкап", callback_data="backups")) + builder.row(InlineKeyboardButton( + text="Создать бэкап", callback_data="backups")) builder.row( - InlineKeyboardButton(text="Перезапустить бота", callback_data="restart_bot") + InlineKeyboardButton(text="Перезапустить бота", + callback_data="restart_bot") ) await bot.send_message( tg_id, "Панель администратора", reply_markup=builder.as_markup() diff --git a/handlers/admin/user_editor.py b/handlers/admin/user_editor.py index f7c69e25..0e58d04d 100644 --- a/handlers/admin/user_editor.py +++ b/handlers/admin/user_editor.py @@ -5,14 +5,16 @@ 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 +from aiogram.types import (CallbackQuery, InlineKeyboardButton, + InlineKeyboardMarkup) from loguru import logger from auth import login_with_credentials from bot import bot from client import delete_client, extend_client_key_admin from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, SERVERS -from database import get_client_id_by_email, get_tg_id_by_client_id, update_key_expiry +from database import (get_client_id_by_email, get_tg_id_by_client_id, + update_key_expiry) from handlers.admin.admin_panel import back_to_admin_menu from handlers.utils import sanitize_key_name diff --git a/handlers/commands.py b/handlers/commands.py index c4715fbb..833cb70e 100644 --- a/handlers/commands.py +++ b/handlers/commands.py @@ -1,5 +1,5 @@ import asyncpg -from aiogram import F, Router, types +from aiogram import Router, types from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup @@ -7,7 +7,7 @@ from aiogram.types import Message from loguru import logger from bot import bot -from config import ADMIN_ID, DATABASE_URL +from config import DATABASE_URL from handlers.admin.admin import cmd_add_balance from handlers.keys.key_management import handle_key_name_input from handlers.payment.yookassa_pay import ( diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index 87394869..3d4d41f1 100644 --- a/handlers/instructions/instructions.py +++ b/handlers/instructions/instructions.py @@ -1,7 +1,8 @@ import os from aiogram import types -from aiogram.types import BufferedInputFile, InlineKeyboardButton, InlineKeyboardMarkup +from aiogram.types import (BufferedInputFile, InlineKeyboardButton, + InlineKeyboardMarkup) from handlers.texts import INSTRUCTIONS diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index 331c6be0..d27419f8 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -12,14 +12,9 @@ from aiogram.types import ( InlineKeyboardMarkup, Message, ) -from loguru import logger -from auth import login_with_credentials from bot import bot, dp -from client import add_client from config import ( - ADMIN_PASSWORD, - ADMIN_USERNAME, APP_URL, DATABASE_URL, PUBLIC_LINK, @@ -28,6 +23,7 @@ from config import ( from database import add_connection, get_balance, store_key, update_balance from handlers.instructions.instructions import send_instructions from handlers.profile import process_callback_view_profile +from handlers.keys.key_utils import create_key_on_server from handlers.texts import ( KEY, KEY_TRIAL, @@ -189,7 +185,8 @@ async def handle_key_name_input(message: Message, state: FSMContext): replenish_button = InlineKeyboardButton( text="Перейти в профиль", callback_data="view_profile" ) - keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]]) + keyboard = InlineKeyboardMarkup( + inline_keyboard=[[replenish_button]]) await message.bot.send_message( tg_id, "❗️ Недостаточно средств на балансе для создания подписки на новое устройство.", @@ -278,33 +275,6 @@ async def handle_key_name_input(message: Message, state: FSMContext): await state.clear() -async def create_key_on_server(server_id, tg_id, client_id, email, expiry_timestamp): - try: - session = await login_with_credentials( - server_id, ADMIN_USERNAME, ADMIN_PASSWORD - ) - response = await add_client( - session, - server_id, - client_id, - email, - tg_id, - limit_ip=1, - total_gb=0, - expiry_time=expiry_timestamp, - enable=True, - flow="xtls-rprx-vision", - ) - if not response.get("success", True): - error_msg = response.get("msg", "Неизвестная ошибка.") - if "Duplicate email" in error_msg: - raise ValueError(f"Имя {email} уже занято на сервере {server_id}") - else: - raise Exception(error_msg) - except Exception as e: - logger.error(f"Ошибка на сервере {server_id}: {e}") - - @dp.callback_query(F.data == "instructions") async def handle_instructions(callback_query: CallbackQuery): await send_instructions(callback_query) diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py new file mode 100644 index 00000000..5545fd07 --- /dev/null +++ b/handlers/keys/key_utils.py @@ -0,0 +1,106 @@ +import asyncpg +from loguru import logger + +from auth import login_with_credentials +from client import add_client, delete_client, extend_client_key +from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL + + +async def create_key_on_server(server_id, tg_id, client_id, email, expiry_timestamp): + try: + session = await login_with_credentials( + server_id, ADMIN_USERNAME, ADMIN_PASSWORD + ) + response = await add_client( + session, + server_id, + client_id, + email, + tg_id, + limit_ip=1, + total_gb=0, + expiry_time=expiry_timestamp, + enable=True, + flow="xtls-rprx-vision", + ) + if not response.get("success", True): + error_msg = response.get("msg", "Неизвестная ошибка.") + if "Duplicate email" in error_msg: + raise ValueError( + f"Имя {email} уже занято на сервере {server_id}") + else: + raise Exception(error_msg) + except Exception as e: + logger.error(f"Ошибка на сервере {server_id}: {e}") + + +async def renew_server_key(server_id, tg_id, client_id, email, new_expiry_time): + try: + session = await login_with_credentials( + server_id, ADMIN_USERNAME, ADMIN_PASSWORD + ) + await extend_client_key( + session, server_id, tg_id, client_id, email, new_expiry_time + ) + except Exception as e: + logger.error( + f"Не удалось продлить ключ {client_id} на сервере {server_id}: {e}" + ) + + +async def delete_key_from_db(client_id): + """Удаление ключа из базы данных""" + try: + conn = await asyncpg.connect(DATABASE_URL) + await conn.execute("DELETE FROM keys WHERE client_id = $1", client_id) + except Exception as e: + logger.error( + f"Ошибка при удалении ключа {client_id} из базы данных: {e}") + finally: + await conn.close() + + +async def delete_key_from_server(server_id, client_id): + """Удаление ключа с сервера""" + try: + async with login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) as session: + success = await delete_client(session, server_id, client_id) + + if not success: + logger.error( + f"Ошибка удаления ключа {client_id} на сервере {server_id}") + except Exception as e: + logger.error( + f"Ошибка при удалении ключа {client_id} с сервера {server_id}: {e}") + + +async def update_key_on_server(tg_id, client_id, email, expiry_time, server_id): + try: + session = await login_with_credentials( + server_id, ADMIN_USERNAME, ADMIN_PASSWORD + ) + response = await add_client( + session, + server_id, + client_id, + email, + tg_id, + limit_ip=1, + total_gb=0, + expiry_time=expiry_time, + enable=True, + flow="xtls-rprx-vision", + ) + + if not response.get("success"): + logger.error( + f"Ошибка при обновлении ключа на сервере {server_id} для {client_id}" + ) + else: + logger.info( + f"Ключ успешно обновлен на сервере {server_id} для {client_id}") + + except Exception as e: + logger.error( + f"Ошибка при обновлении ключа на сервере {server_id} для {client_id}: {e}" + ) diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index ca234ccf..4581df38 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -8,12 +8,8 @@ from aiogram import F, Router, types from aiogram.types import BufferedInputFile from loguru import logger -from auth import login_with_credentials from bot import bot -from client import add_client, delete_client, extend_client_key from config import ( - ADMIN_PASSWORD, - ADMIN_USERNAME, APP_URL, DATABASE_URL, PUBLIC_LINK, @@ -36,6 +32,15 @@ from handlers.texts import ( key_message, ) +from handlers.utils import handle_error + +from handlers.keys.key_utils import ( + update_key_on_server, + delete_key_from_db, + renew_server_key, + delete_key_from_server +) + locale.setlocale(locale.LC_TIME, "ru_RU.UTF-8") router = Router() @@ -71,7 +76,8 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): ) buttons.append([back_button]) - inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons) + inline_keyboard = types.InlineKeyboardMarkup( + inline_keyboard=buttons) response_message = ( "Это ваши устройства:\n\n" "Нажмите на имя устройства для управления его подпиской." @@ -153,7 +159,8 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): expiry_time = record["expiry_time"] server_id = record["server_id"] - server_name = SERVERS.get(server_id, {}).get("name", "мультисервер") + server_name = SERVERS.get(server_id, {}).get( + "name", "мультисервер") expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) current_date = datetime.utcnow() time_left = expiry_date - current_date @@ -213,9 +220,11 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): inline_keyboard.append([back_button]) - keyboard = types.InlineKeyboardMarkup(inline_keyboard=inline_keyboard) + keyboard = types.InlineKeyboardMarkup( + inline_keyboard=inline_keyboard) - image_path = os.path.join(os.path.dirname(__file__), "pic_view.jpg") + image_path = os.path.join( + os.path.dirname(__file__), "pic_view.jpg") if not os.path.isfile(image_path): await bot.send_message(tg_id, "Файл изображения не найден.") @@ -317,7 +326,8 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue back_button = types.InlineKeyboardButton( text="🔙 Назад в профиль", callback_data="view_profile" ) - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + keyboard = types.InlineKeyboardMarkup( + inline_keyboard=[[back_button]]) await bot.send_message( tg_id, response_message, reply_markup=keyboard, parse_mode="HTML" @@ -345,37 +355,6 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue await callback_query.answer() -async def update_key_on_server(tg_id, client_id, email, expiry_time, server_id): - try: - session = await login_with_credentials( - server_id, ADMIN_USERNAME, ADMIN_PASSWORD - ) - response = await add_client( - session, - server_id, - client_id, - email, - tg_id, - limit_ip=1, - total_gb=0, - expiry_time=expiry_time, - enable=True, - flow="xtls-rprx-vision", - ) - - if not response.get("success"): - logger.error( - f"Ошибка при обновлении ключа на сервере {server_id} для {client_id}" - ) - else: - logger.info(f"Ключ успешно обновлен на сервере {server_id} для {client_id}") - - except Exception as e: - logger.error( - f"Ошибка при обновлении ключа на сервере {server_id} для {client_id}: {e}" - ) - - @router.callback_query(F.data.startswith("delete_key|")) async def process_callback_delete_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id @@ -531,7 +510,8 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): back_button = types.InlineKeyboardButton( text="Назад", callback_data="view_keys" ) - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + keyboard = types.InlineKeyboardMarkup( + inline_keyboard=[[back_button]]) await delete_key(client_id) await bot.edit_message_text( @@ -545,12 +525,14 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): try: tasks = [] for server_id in SERVERS: - tasks.append(delete_key_from_server(server_id, client_id)) + tasks.append(delete_key_from_server( + server_id, client_id)) await asyncio.gather(*tasks) except Exception as e: - logger.error(f"Ошибка при удалении ключа {client_id}: {e}") + logger.error( + f"Ошибка при удалении ключа {client_id}: {e}") asyncio.create_task(delete_key_from_servers()) @@ -561,7 +543,8 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): back_button = types.InlineKeyboardButton( text="Назад", callback_data="view_keys" ) - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + keyboard = types.InlineKeyboardMarkup( + inline_keyboard=[[back_button]]) await bot.edit_message_text( response_message, @@ -583,33 +566,6 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): await callback_query.answer() -async def delete_key_from_server(server_id, client_id): - """Удаление ключа с сервера""" - try: - session = await login_with_credentials( - server_id, ADMIN_USERNAME, ADMIN_PASSWORD - ) - success = await delete_client(session, server_id, client_id) - - if not success: - logger.error(f"Ошибка удаления ключа {client_id} на сервере {server_id}") - except Exception as e: - logger.error( - f"Ошибка при удалении ключа {client_id} с сервера {server_id}: {e}" - ) - - -async def delete_key_from_db(client_id): - """Удаление ключа из базы данных""" - try: - conn = await asyncpg.connect(DATABASE_URL) - await conn.execute("DELETE FROM keys WHERE client_id = $1", client_id) - except Exception as e: - logger.error(f"Ошибка при удалении ключа {client_id} из базы данных: {e}") - finally: - await conn.close() - - @router.callback_query(F.data.startswith("renew_plan|")) async def process_callback_renew_plan(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id @@ -677,7 +633,8 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): back_button = types.InlineKeyboardButton( text="Назад", callback_data="view_profile" ) - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + keyboard = types.InlineKeyboardMarkup( + inline_keyboard=[[back_button]]) await bot.send_message( tg_id, response_message, reply_markup=keyboard, parse_mode="HTML" @@ -712,32 +669,3 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): ) await callback_query.answer() - - -async def renew_server_key(server_id, tg_id, client_id, email, new_expiry_time): - try: - session = await login_with_credentials( - server_id, ADMIN_USERNAME, ADMIN_PASSWORD - ) - await extend_client_key( - session, server_id, tg_id, client_id, email, new_expiry_time - ) - except Exception as e: - logger.error( - f"Не удалось продлить ключ {client_id} на сервере {server_id}: {e}" - ) - - -async def handle_error(tg_id, callback_query, message): - try: - try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) - except Exception: - pass - - await bot.send_message(tg_id, message, parse_mode="HTML") - - except Exception as e: - logger.error(f"Ошибка при обработке ошибки: {e}") diff --git a/handlers/keys/trial_key.py b/handlers/keys/trial_key.py index 69b6e588..4b0bb8f9 100644 --- a/handlers/keys/trial_key.py +++ b/handlers/keys/trial_key.py @@ -5,10 +5,9 @@ from datetime import datetime, timedelta import asyncpg from loguru import logger -from auth import login_with_credentials -from client import add_client -from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, PUBLIC_LINK, SERVERS +from config import DATABASE_URL, PUBLIC_LINK, SERVERS from database import store_key +from handlers.keys.key_utils import create_key_on_server from handlers.texts import INSTRUCTIONS from handlers.utils import generate_random_email @@ -46,7 +45,7 @@ async def generate_and_store_keys( tasks = [] for server_id in SERVERS: task = create_key_on_server( - server_id, client_id, email, tg_id, expiry_timestamp + server_id, tg_id, client_id, email, expiry_timestamp ) tasks.append(task) @@ -72,27 +71,8 @@ async def generate_and_store_keys( tg_id, ) else: - logger.error("Не удалось создать ключ на одном или нескольких серверах.") + logger.error( + "Не удалось создать ключ на одном или нескольких серверах.") finally: await conn.close() - - -async def create_key_on_server( - server_id: str, client_id: str, email: str, tg_id: int, expiry_timestamp: int -): - """Асинхронно создает ключ на указанном сервере и возвращает результат.""" - session = await login_with_credentials(server_id, ADMIN_USERNAME, ADMIN_PASSWORD) - response = await add_client( - session, - server_id, - client_id, - email, - tg_id, - limit_ip=1, - total_gb=0, - expiry_time=expiry_timestamp, - enable=True, - flow="xtls-rprx-vision", - ) - return response diff --git a/handlers/notifications.py b/handlers/notifications.py index 7ae371eb..898aef88 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta import asyncpg from aiogram import Bot, Router, types -from aiogram.fsm.state import State, StatesGroup from loguru import logger from auth import login_with_credentials @@ -52,7 +51,6 @@ async def notify_expiring_keys(bot: Bot): async def is_bot_blocked(bot: Bot, chat_id: int) -> bool: try: - # Проверка статуса бота в чате с пользователем member = await bot.get_chat_member(chat_id, bot.id) blocked = member.status == "left" logger.info( @@ -60,7 +58,6 @@ async def is_bot_blocked(bot: Bot, chat_id: int) -> bool: ) return blocked except Exception as e: - # Обработка ошибок при проверке статуса logger.warning( f"Не удалось проверить статус бота для пользователя {chat_id}: {e}" ) diff --git a/handlers/payment/yookassa_pay.py b/handlers/payment/yookassa_pay.py index 1e3dae74..9944c960 100644 --- a/handlers/payment/yookassa_pay.py +++ b/handlers/payment/yookassa_pay.py @@ -10,12 +10,8 @@ from yookassa import Configuration, Payment from bot import bot from config import YOOKASSA_ENABLE, YOOKASSA_SECRET_KEY, YOOKASSA_SHOP_ID -from database import ( - add_connection, - check_connection_exists, - get_key_count, - update_balance, -) +from database import (add_connection, check_connection_exists, get_key_count, + update_balance) from handlers.profile import process_callback_view_profile from handlers.texts import PAYMENT_OPTIONS diff --git a/handlers/profile.py b/handlers/profile.py index 1bbd597a..a868675c 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -2,15 +2,16 @@ import os from aiogram import F, Router, types from aiogram.fsm.context import FSMContext -from aiogram.fsm.state import State, StatesGroup -from aiogram.types import BufferedInputFile, InlineKeyboardButton, InlineKeyboardMarkup +from aiogram.types import BufferedInputFile, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder from loguru import logger from bot import bot -from config import CHANNEL_URL, FREEKASSA_ENABLE, PAYMENT_METHOD, YOOKASSA_ENABLE +from config import (CHANNEL_URL, FREEKASSA_ENABLE, PAYMENT_METHOD, + YOOKASSA_ENABLE) from database import get_balance, get_key_count, get_referral_stats -from handlers.texts import get_referral_link, invite_message_send, profile_message_send +from handlers.texts import (get_referral_link, invite_message_send, + profile_message_send) router = Router() diff --git a/handlers/start.py b/handlers/start.py index 9b6e564d..112925dc 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -3,12 +3,10 @@ import os import asyncpg from aiogram import F, Router from aiogram.filters import Command -from aiogram.fsm.state import State, StatesGroup from aiogram.types import ( BufferedInputFile, CallbackQuery, InlineKeyboardButton, - InlineKeyboardMarkup, Message, ) from aiogram.utils.keyboard import InlineKeyboardBuilder @@ -29,22 +27,26 @@ async def send_welcome_message(chat_id: int, trial_status: int): builder = InlineKeyboardBuilder() if trial_status == 0: builder.row( - InlineKeyboardButton(text="🔗 Подключить VPN", callback_data="connect_vpn") + InlineKeyboardButton(text="🔗 Подключить VPN", + callback_data="connect_vpn") ) builder.row( - InlineKeyboardButton(text="👤 Мой профиль", callback_data="view_profile") + InlineKeyboardButton(text="👤 Мой профиль", + callback_data="view_profile") ) builder.row( InlineKeyboardButton(text="📞 Поддержка", url=SUPPORT_CHAT_URL), InlineKeyboardButton(text="📢 Наш канал", url=CHANNEL_URL), ) - builder.row(InlineKeyboardButton(text="🔒 О VPN", callback_data="about_vpn")) + builder.row(InlineKeyboardButton( + text="🔒 О VPN", callback_data="about_vpn")) if os.path.isfile(image_path): with open(image_path, "rb") as image_from_buffer: await bot.send_photo( chat_id=chat_id, - photo=BufferedInputFile(image_from_buffer.read(), filename="pic.jpg"), + photo=BufferedInputFile( + image_from_buffer.read(), filename="pic.jpg"), caption=WELCOME_TEXT, parse_mode="HTML", reply_markup=builder.as_markup(), @@ -108,18 +110,8 @@ async def handle_connect_vpn(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text="👤 Мой профиль", callback_data="view_profile") - ) - - builder.row( - InlineKeyboardButton( - text="🍏 Подключить", - url=f'{APP_URL}/?url=v2raytun://import/{trial_key_info["key"]}', - ), - InlineKeyboardButton( - text="🤖 Подключить", - url=f'{APP_URL}/?url=v2raytun://import-sub?url={trial_key_info["key"]}', - ), + InlineKeyboardButton(text="👤 Мой профиль", + callback_data="view_profile") ) builder.row( @@ -133,6 +125,17 @@ async def handle_connect_vpn(callback_query: CallbackQuery): ), ) + builder.row( + InlineKeyboardButton( + text="🍏 Подключить", + url=f'{APP_URL}/?url=v2raytun://import/{trial_key_info["key"]}', + ), + InlineKeyboardButton( + text="🤖 Подключить", + url=f'{APP_URL}/?url=v2raytun://import-sub?url={trial_key_info["key"]}', + ), + ) + await callback_query.message.answer( key_message, parse_mode="HTML", reply_markup=builder.as_markup() ) @@ -145,7 +148,8 @@ async def handle_about_vpn(callback_query: CallbackQuery): await callback_query.message.delete() builder = InlineKeyboardBuilder() - builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_menu")) + builder.row(InlineKeyboardButton( + text="⬅️ Назад", callback_data="back_to_menu")) await callback_query.message.answer( ABOUT_VPN, parse_mode="HTML", reply_markup=builder.as_markup() diff --git a/handlers/utils.py b/handlers/utils.py index b3678a9f..bab8c9c5 100644 --- a/handlers/utils.py +++ b/handlers/utils.py @@ -1,7 +1,10 @@ import random import re +from bot import bot + from config import SERVERS +from loguru import logger def sanitize_key_name(key_name: str) -> str: @@ -29,3 +32,18 @@ async def get_least_loaded_server(conn): least_loaded_server_id = server_id return least_loaded_server_id + + +async def handle_error(tg_id, callback_query, message): + try: + try: + await bot.delete_message( + chat_id=tg_id, message_id=callback_query.message.message_id + ) + except Exception: + pass + + await bot.send_message(tg_id, message, parse_mode="HTML") + + except Exception as e: + logger.error(f"Ошибка при обработке ошибки: {e}") \ No newline at end of file diff --git a/main.py b/main.py index 4c34bb4f..a2e26f97 100644 --- a/main.py +++ b/main.py @@ -2,21 +2,15 @@ import asyncio import signal import traceback -from aiogram.webhook.aiohttp_server import SimpleRequestHandler, setup_application +from aiogram.webhook.aiohttp_server import (SimpleRequestHandler, + setup_application) from aiohttp import web from loguru import logger from backup import backup_database from bot import bot, dp, router -from config import ( - FREEKASSA_ENABLE, - SUB_PATH, - WEBAPP_HOST, - WEBAPP_PORT, - WEBHOOK_PATH, - WEBHOOK_URL, - YOOKASSA_ENABLE, -) +from config import (FREEKASSA_ENABLE, SUB_PATH, WEBAPP_HOST, WEBAPP_PORT, + WEBHOOK_PATH, WEBHOOK_URL, YOOKASSA_ENABLE) from database import init_db from handlers.keys.subscriptions import handle_subscription from handlers.notifications import notify_expiring_keys @@ -69,7 +63,7 @@ async def main(): app.router.add_post("/yookassa/webhook", yookassa_webhook) if FREEKASSA_ENABLE: app.router.add_post("/freekassa/webhook", freekassa_webhook) - # app.router.add_get(f"{SUB_PATH}{{email}}", handle_subscription) + app.router.add_get(f"{SUB_PATH}{{email}}", handle_subscription) SimpleRequestHandler(dispatcher=dp, bot=bot).register(app, path=WEBHOOK_PATH) setup_application(app, dp, bot=bot) diff --git a/middlewares/database.py b/middlewares/database.py index 9e505506..759004fb 100644 --- a/middlewares/database.py +++ b/middlewares/database.py @@ -3,7 +3,7 @@ from typing import Any, Awaitable, Callable, Dict import asyncpg from aiogram import BaseMiddleware -from aiogram.types import CallbackQuery, Message, TelegramObject +from aiogram.types import TelegramObject from config import DATABASE_URL