diff --git a/client.py b/client.py index ecdb403d..77ddc23d 100644 --- a/client.py +++ b/client.py @@ -66,14 +66,16 @@ async def extend_client_key(xui, email: str, new_expiry_time: int, client_id: st logger.info( f"Обновление ключа клиента {client.email} с ID {client.id} до нового времени: {new_expiry_time}" ) - + client.id = client_id client.expiry_time = new_expiry_time - + if TOTAL_GB > 0: client.total_gb = TOTAL_GB - logger.info(f"Установлен объем трафика для клиента {client.email}: {TOTAL_GB} ГБ") - + logger.info( + f"Установлен объем трафика для клиента {client.email}: {TOTAL_GB} ГБ" + ) + await xui.client.update(client.id, client) logger.info( f"Ключ клиента {client.email} успешно продлён до {new_expiry_time}." @@ -83,7 +85,6 @@ async def extend_client_key(xui, email: str, new_expiry_time: int, client_id: st logger.error(f"Ошибка при обновлении клиента с email {email}: {e}") - async def delete_client( xui, email: str, diff --git a/database.py b/database.py index 901f1c4c..811ce558 100644 --- a/database.py +++ b/database.py @@ -1,6 +1,7 @@ from datetime import datetime -from loguru import logger + import asyncpg +from loguru import logger from config import DATABASE_URL @@ -74,12 +75,11 @@ async def init_db(): await conn.close() + async def restore_trial(tg_id: int): conn = await asyncpg.connect(DATABASE_URL) try: - await conn.execute( - "UPDATE connections SET trial = 0 WHERE tg_id = $1", tg_id - ) + await conn.execute("UPDATE connections SET trial = 0 WHERE tg_id = $1", tg_id) except Exception as e: logger.error(f"Ошибка при установке значения триала: {e}") @@ -87,8 +87,6 @@ async def restore_trial(tg_id: int): await conn.close() - - async def add_connection(tg_id: int, balance: float = 0.0, trial: int = 0): conn = await asyncpg.connect(DATABASE_URL) await conn.execute( diff --git a/handlers/admin/admin_commands.py b/handlers/admin/admin_commands.py index 8a701b18..fd42ad71 100644 --- a/handlers/admin/admin_commands.py +++ b/handlers/admin/admin_commands.py @@ -3,12 +3,12 @@ from aiogram import Router, types from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from handlers.filters.admin import IsAdminFilter from loguru import logger from bot import bot from config import DATABASE_URL from database import add_balance_to_client, check_connection_exists +from handlers.filters.admin import IsAdminFilter from handlers.texts import TRIAL router = Router() diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py index 1ab09779..4c878d9b 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -3,18 +3,17 @@ from datetime import datetime 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, Message from aiogram.utils.keyboard import InlineKeyboardBuilder -from handlers.filters.admin import IsAdminFilter from backup import backup_database from bot import bot -from config import DATABASE_URL, ADMIN_ID +from config import DATABASE_URL from handlers.admin.admin_commands import send_message_to_all_clients +from handlers.filters.admin import IsAdminFilter router = Router() diff --git a/handlers/admin/admin_user_editor.py b/handlers/admin/admin_user_editor.py index bab13ab0..45b8510c 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -7,13 +7,13 @@ from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder -from handlers.filters.admin import IsAdminFilter from loguru import logger from bot import bot from config import DATABASE_URL, SERVERS -from database import get_client_id_by_email, update_key_expiry, restore_trial +from database import get_client_id_by_email, restore_trial, update_key_expiry from handlers.admin.admin_panel import back_to_admin_menu +from handlers.filters.admin import IsAdminFilter from handlers.keys.key_utils import delete_key_from_server, renew_server_key from handlers.utils import sanitize_key_name @@ -101,12 +101,13 @@ async def handle_restore_trial(callback_query: types.CallbackQuery): builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text="🔙 Назад в меню администратора", callback_data="back_to_user_editor") + InlineKeyboardButton( + text="🔙 Назад в меню администратора", callback_data="back_to_user_editor" + ) ) await callback_query.message.edit_text( - "✅ Триал успешно восстановлен.", - reply_markup=builder.as_markup() + "✅ Триал успешно восстановлен.", reply_markup=builder.as_markup() ) @@ -261,7 +262,10 @@ async def handle_key_name_input(message: types.Message, state: FSMContext): if not user_records: builder = InlineKeyboardBuilder() builder.row( - InlineKeyboardButton(text="🔙 Назад в меню администратора", callback_data="back_to_user_editor") + InlineKeyboardButton( + text="🔙 Назад в меню администратора", + callback_data="back_to_user_editor", + ) ) await message.reply( diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index c2b46cbe..ba6a8c92 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -1,16 +1,16 @@ import asyncio import uuid from datetime import datetime, timedelta -from loguru import logger import asyncpg from aiogram import F, Router from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message +from loguru import logger from bot import bot, dp -from config import DATABASE_URL, PUBLIC_LINK, SERVERS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, CONNECT_ANDROID, CONNECT_IOS +from config import CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, SERVERS from database import add_connection, get_balance, store_key, update_balance from handlers.instructions.instructions import send_instructions from handlers.keys.key_utils import create_key_on_server @@ -107,13 +107,16 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex logger.info(f"Balance for user {tg_id} is sufficient. Asking for device name.") - await callback_query.message.edit_text("🔑 Пожалуйста, введите имя подключаемого устройства:") + await callback_query.message.edit_text( + "🔑 Пожалуйста, введите имя подключаемого устройства:" + ) await state.set_state(Form.waiting_for_key_name) logger.info(f"State set to waiting_for_key_name for user {tg_id}") await state.update_data(creating_new_key=True) await callback_query.answer() + @dp.callback_query(F.data == "cancel_create_key") async def cancel_create_key(callback_query: CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state) @@ -136,7 +139,9 @@ async def handle_key_name_input(message: Message, state: FSMContext): conn = await asyncpg.connect(DATABASE_URL) try: - logger.info(f"Checking if key name '{key_name}' already exists in the database.") + logger.info( + f"Checking if key name '{key_name}' already exists in the database." + ) existing_key = await conn.fetchrow( "SELECT * FROM keys WHERE email = $1", key_name.lower() ) @@ -145,7 +150,9 @@ async def handle_key_name_input(message: Message, state: FSMContext): tg_id, "❌ Упс! Это имя уже используется. Выберите другое уникальное название для ключа.", ) - logger.warning(f"Key name '{key_name}' already exists in the database for user {tg_id}.") + logger.warning( + f"Key name '{key_name}' already exists in the database for user {tg_id}." + ) await state.set_state(Form.waiting_for_key_name) return finally: @@ -206,9 +213,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): url=f"{CONNECT_ANDROID}{public_link}", ) - button_download_ios = InlineKeyboardButton( - text="🍏 Скачать", url=DOWNLOAD_IOS - ) + button_download_ios = InlineKeyboardButton(text="🍏 Скачать", url=DOWNLOAD_IOS) button_download_android = InlineKeyboardButton( text="🤖 Скачать", url=DOWNLOAD_ANDROID, @@ -272,7 +277,6 @@ async def handle_key_name_input(message: Message, state: FSMContext): await state.clear() - @dp.callback_query(F.data == "instructions") async def handle_instructions(callback_query: CallbackQuery): await send_instructions(callback_query) diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 9f494db8..161359c2 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -9,7 +9,7 @@ from aiogram.types import BufferedInputFile from loguru import logger from bot import bot -from config import DATABASE_URL, PUBLIC_LINK, SERVERS, DOWNLOAD_ANDROID, DOWNLOAD_IOS, CONNECT_ANDROID, CONNECT_IOS +from config import CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, SERVERS from database import delete_key, get_balance, store_key, update_balance, update_key_expiry from handlers.keys.key_utils import delete_key_from_db, delete_key_from_server, renew_server_key, update_key_on_server from handlers.texts import INSUFFICIENT_FUNDS_MSG, KEY_NOT_FOUND_MSG, NO_KEYS, PLAN_SELECTION_MSG, RENEWAL_PLANS, SUCCESS_RENEWAL_MSG, key_message @@ -69,7 +69,9 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): with open(image_path, "rb") as image_file: await bot.send_photo( chat_id=tg_id, - photo=BufferedInputFile(image_file.read(), filename="pic_keys.jpg"), + photo=BufferedInputFile( + image_file.read(), filename="pic_keys.jpg" + ), caption=response_message, parse_mode="HTML", reply_markup=inline_keyboard, @@ -108,7 +110,9 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): with open(image_path, "rb") as image_file: await bot.send_photo( chat_id=tg_id, - photo=BufferedInputFile(image_file.read(), filename="pic_keys.jpg"), + photo=BufferedInputFile( + image_file.read(), filename="pic_keys.jpg" + ), caption=response_message, parse_mode="HTML", reply_markup=keyboard, @@ -130,7 +134,6 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): await callback_query.answer() - @router.callback_query(F.data.startswith("view_key|")) async def process_callback_view_key(callback_query: types.CallbackQuery): tg_id = callback_query.from_user.id diff --git a/handlers/notifications.py b/handlers/notifications.py index da1c0e05..c98a71fc 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -217,7 +217,7 @@ async def notify_24h_keys( async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: float): logger.info("Проверка истекших ключей...") - adjusted_current_time = current_time + (3 * 60 * 60 * 1000) + adjusted_current_time = current_time + (3 * 60 * 60 * 1000) expiring_keys = await conn.fetch( """ SELECT tg_id, client_id, expiry_time, email FROM keys @@ -271,22 +271,32 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: ) if not success: all_success = False - logger.error(f"Не удалось продлить ключ для пользователя {tg_id} на сервере {server_id}.") + logger.error( + f"Не удалось продлить ключ для пользователя {tg_id} на сервере {server_id}." + ) if all_success: try: - await bot.send_message(tg_id, KEY_RENEWED, reply_markup=keyboard) + await bot.send_message( + tg_id, KEY_RENEWED, reply_markup=keyboard + ) logger.info(f"Ключ для пользователя {tg_id} успешно продлен.") except Exception as e: - logger.error(f"Ошибка при отправке уведомления пользователю {tg_id}: {e}") + logger.error( + f"Ошибка при отправке уведомления пользователю {tg_id}: {e}" + ) else: try: - await bot.send_message(tg_id, message_expired, reply_markup=keyboard) + await bot.send_message( + tg_id, message_expired, reply_markup=keyboard + ) except Exception as e: if "chat not found" in str(e): - logger.warning(f"Чат для клиента {tg_id} не найден. Пропуск отправки сообщения.") - + logger.warning( + f"Чат для клиента {tg_id} не найден. Пропуск отправки сообщения." + ) + await delete_key(client_id) for server_id in SERVERS: @@ -297,12 +307,15 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: ) success = await delete_client(xui, email, client_id) if success: - logger.info(f"Ключ для клиента {tg_id} успешно удален с сервера {server_id}.") + logger.info( + f"Ключ для клиента {tg_id} успешно удален с сервера {server_id}." + ) else: - logger.error(f"Не удалось удалить ключ для клиента {tg_id} на сервере {server_id}.") + logger.error( + f"Не удалось удалить ключ для клиента {tg_id} на сервере {server_id}." + ) except Exception as e: logger.error(f"Ошибка при обработке ключа для клиента {tg_id}: {e}") await asyncio.sleep(1) - diff --git a/handlers/payments/freekassa_pay.py b/handlers/payments/freekassa_pay.py index e0db3b22..a916e587 100644 --- a/handlers/payments/freekassa_pay.py +++ b/handlers/payments/freekassa_pay.py @@ -8,11 +8,9 @@ import requests from aiogram import Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import InlineKeyboardButton +from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder from aiohttp import web -from aiogram.types import InlineKeyboardMarkup - from bot import bot from config import FREEKASSA_API_KEY, FREEKASSA_SHOP_ID diff --git a/handlers/payments/stars_pay.py b/handlers/payments/stars_pay.py index e655407b..695ed355 100644 --- a/handlers/payments/stars_pay.py +++ b/handlers/payments/stars_pay.py @@ -1,12 +1,10 @@ from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import InlineKeyboardButton +from aiogram.types import InlineKeyboardButton, LabeledPrice from aiogram.utils.keyboard import InlineKeyboardBuilder from loguru import logger -from aiogram.types import LabeledPrice - from bot import bot from config import RUB_TO_XTR from database import add_connection, check_connection_exists, get_key_count, update_balance diff --git a/handlers/payments/yookassa_pay.py b/handlers/payments/yookassa_pay.py index e57cab33..894e9090 100644 --- a/handlers/payments/yookassa_pay.py +++ b/handlers/payments/yookassa_pay.py @@ -63,7 +63,7 @@ async def process_callback_pay_yookassa( builder = InlineKeyboardBuilder() - for i in range(0, 4, 2): + for i in range(0, 4, 2): builder.row( InlineKeyboardButton( text=PAYMENT_OPTIONS[i]["text"], @@ -75,7 +75,7 @@ async def process_callback_pay_yookassa( ), ) - for i in range(4, len(PAYMENT_OPTIONS)): + for i in range(4, len(PAYMENT_OPTIONS)): builder.row( InlineKeyboardButton( text=PAYMENT_OPTIONS[i]["text"], @@ -107,7 +107,6 @@ async def process_callback_pay_yookassa( await callback_query.answer() - @router.callback_query(F.data == "back_to_profile") async def back_to_profile_handler( callback_query: types.CallbackQuery, state: FSMContext diff --git a/handlers/profile.py b/handlers/profile.py index 0b5377c6..2560fbc7 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -109,7 +109,9 @@ async def invite_handler(callback_query: types.CallbackQuery): with open(image_path, "rb") as image_file: await bot.send_photo( chat_id=chat_id, - photo=BufferedInputFile(image_file.read(), filename="pic_invite.jpg"), + photo=BufferedInputFile( + image_file.read(), filename="pic_invite.jpg" + ), caption=invite_message, parse_mode="HTML", reply_markup=builder.as_markup(), @@ -132,8 +134,6 @@ async def invite_handler(callback_query: types.CallbackQuery): await callback_query.answer() - - @router.callback_query(F.data == "view_profile") async def view_profile_handler(callback_query: types.CallbackQuery, state: FSMContext): await process_callback_view_profile(callback_query, state)