From d1b7a01b4f175fddd5ae24143460b58f330bca00 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 22 Nov 2024 21:24:55 +0300 Subject: [PATCH] Update config --- .flake8 | 4 +- .isort.cfg | 3 - Makefile | 4 +- backup.py | 19 +- client.py | 12 +- database.py | 20 ++- handlers/admin/admin_commands.py | 30 +--- handlers/admin/admin_coupons.py | 48 ++--- handlers/admin/admin_panel.py | 137 +++++++------- handlers/admin/admin_user_editor.py | 150 ++++++---------- handlers/coupons.py | 18 +- handlers/donate.py | 31 +--- handlers/instructions/instructions.py | 23 +-- handlers/keys/key_management.py | 87 ++++----- handlers/keys/key_utils.py | 24 +-- handlers/keys/keys.py | 248 +++++++++++--------------- handlers/keys/subscriptions.py | 42 ++--- handlers/keys/trial_key.py | 10 +- handlers/notifications.py | 82 +++------ handlers/pay.py | 10 +- handlers/payments/cryprobot_pay.py | 59 ++---- handlers/payments/freekassa_pay.py | 47 ++--- handlers/payments/robokassa_pay.py | 68 +++---- handlers/payments/stars_pay.py | 45 ++--- handlers/payments/yookassa_pay.py | 61 +++---- handlers/profile.py | 39 ++-- handlers/start.py | 79 +++----- handlers/utils.py | 18 +- middlewares/database.py | 2 +- pyproject.toml | 27 +++ 30 files changed, 556 insertions(+), 891 deletions(-) delete mode 100644 .isort.cfg create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 index b8031c58..b51e9062 100644 --- a/.flake8 +++ b/.flake8 @@ -1,6 +1,6 @@ [flake8] -max-line-length = 250 +max-line-length = 120 ignore = E203, E266, E501, W503, F541, E704, W293, W291, E126, E121, E123, E128, E302, E131, E231, W292, E402, E261, E305, E701 -max-complexity = 25 +max-complexity = 15 select = B, C, E, F, W, T4, B9 exclude = .venv,.git,.tox,dist,doc,*lib/python*,*egg,build,.txt,config.py \ No newline at end of file diff --git a/.isort.cfg b/.isort.cfg deleted file mode 100644 index 575f47ca..00000000 --- a/.isort.cfg +++ /dev/null @@ -1,3 +0,0 @@ -[settings] -profile=black -line_length = 250 \ No newline at end of file diff --git a/Makefile b/Makefile index 1f142115..12da8fd2 100644 --- a/Makefile +++ b/Makefile @@ -1,2 +1,4 @@ formatting: - @black . && isort . && flake8 + @echo "Running black..." && black . + @echo "Running isort..." && isort . + @echo "Running flake8..." && flake8 --config .flake8 diff --git a/backup.py b/backup.py index 9a71da8b..b3ec4395 100644 --- a/backup.py +++ b/backup.py @@ -1,6 +1,6 @@ +from datetime import datetime import os import subprocess -from datetime import datetime from typing import Union from aiogram.types import BufferedInputFile @@ -31,7 +31,18 @@ def _create_database_backup(): try: subprocess.run( - ["pg_dump", "-U", USER, "-h", HOST, "-F", "c", "-f", BACKUP_FILE, DB_NAME], + [ + "pg_dump", + "-U", + USER, + "-h", + HOST, + "-F", + "c", + "-f", + BACKUP_FILE, + DB_NAME, + ], check=True, ) logger.info(f"Бэкап базы данных создан: {BACKUP_FILE}") @@ -46,9 +57,7 @@ def _create_database_backup(): async def _send_backup_to_admin(bot, backup_file_path): try: with open(backup_file_path, "rb") as backup_file: - backup_input_file = BufferedInputFile( - backup_file.read(), filename=os.path.basename(backup_file_path) - ) + backup_input_file = BufferedInputFile(backup_file.read(), filename=os.path.basename(backup_file_path)) admin_ids: Union[int, list[int]] = ADMIN_ID if isinstance(admin_ids, list): for id in admin_ids: diff --git a/client.py b/client.py index f0b1e0ff..31f8d168 100644 --- a/client.py +++ b/client.py @@ -44,9 +44,7 @@ async def add_client( return {"status": "failed", "error": str(e)} -async def extend_client_key( - xui, email: str, new_expiry_time: int, client_id: str, total_gb: int -): +async def extend_client_key(xui, email: str, new_expiry_time: int, client_id: str, total_gb: int): """ Функция для обновления срока действия ключа клиента по email. """ @@ -62,9 +60,7 @@ async def extend_client_key( logger.warning(f"Ошибка: клиент {email} не имеет действительного ID.") return - logger.info( - f"Обновление ключа клиента {client.email} с ID {client.id} до нового времени: {new_expiry_time}" - ) + logger.info(f"Обновление ключа клиента {client.email} с ID {client.id} до нового времени: {new_expiry_time}") client.id = client_id client.expiry_time = new_expiry_time @@ -75,9 +71,7 @@ async def extend_client_key( client.limit_ip = 1 await xui.client.update(client.id, client) - logger.info( - f"Ключ клиента {client.email} успешно продлён до {new_expiry_time}." - ) + logger.info(f"Ключ клиента {client.email} успешно продлён до {new_expiry_time}.") except Exception as e: logger.error(f"Ошибка при обновлении клиента с email {email}: {e}") diff --git a/database.py b/database.py index a60d10d6..db48ef93 100644 --- a/database.py +++ b/database.py @@ -216,7 +216,12 @@ async def check_connection_exists(tg_id: int): async def store_key( - tg_id: int, client_id: str, email: str, expiry_time: int, key: str, server_id: str + tg_id: int, + client_id: str, + email: str, + expiry_time: int, + key: str, + server_id: str, ): conn = await asyncpg.connect(DATABASE_URL) await conn.execute( @@ -273,9 +278,7 @@ async def has_active_key(tg_id: int) -> bool: async def get_balance(tg_id: int) -> float: conn = await asyncpg.connect(DATABASE_URL) - balance = await conn.fetchval( - "SELECT balance FROM connections WHERE tg_id = $1", tg_id - ) + balance = await conn.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) await conn.close() return balance if balance is not None else 0.0 @@ -378,7 +381,10 @@ async def get_referral_stats(referrer_tg_id: int): await conn.close() - return {"total_referrals": total_referrals, "active_referrals": active_referrals} + return { + "total_referrals": total_referrals, + "active_referrals": active_referrals, + } async def update_key_expiry(client_id: str, new_expiry_time: int): @@ -445,9 +451,7 @@ async def get_client_id_by_email(email: str): async def get_tg_id_by_client_id(client_id: str): conn = await asyncpg.connect(DATABASE_URL) try: - result = await conn.fetchrow( - "SELECT tg_id FROM keys WHERE client_id = $1", client_id - ) + result = await conn.fetchrow("SELECT tg_id FROM keys WHERE client_id = $1", client_id) return result["tg_id"] if result else None finally: await conn.close() diff --git a/handlers/admin/admin_commands.py b/handlers/admin/admin_commands.py index efc8a652..c5454fc5 100644 --- a/handlers/admin/admin_commands.py +++ b/handlers/admin/admin_commands.py @@ -1,10 +1,10 @@ -import asyncpg from aiogram import Router, types from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder +import asyncpg from bot import bot from config import DATABASE_URL @@ -34,9 +34,7 @@ async def cmd_add_balance(message: types.Message): return await add_balance_to_client(int(client_id), amount) - await message.reply( - f"✅ Баланс клиента {client_id} успешно пополнен на {amount}" - ) + await message.reply(f"✅ Баланс клиента {client_id} успешно пополнен на {amount}") except ValueError: await message.reply( "❓ Неверный формат команды!\n" @@ -53,9 +51,7 @@ async def backup_command(message: types.Message): await message.answer("🔄 Инициализация резервного копирования базы данных...") await backup_database() - await message.answer( - "✅ Бэкап базы данных успешно завершен и отправлен администратору." - ) + await message.answer("✅ Бэкап базы данных успешно завершен и отправлен администратору.") @router.message(Command("send_trial"), IsAdminFilter()) @@ -83,14 +79,10 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext): except Exception as e: if "Forbidden: bot was blocked by the user" in str(e): blocked_count += 1 - logger.info( - f"🚫 Бот заблокирован пользователем с tg_id: {tg_id}" - ) + logger.info(f"🚫 Бот заблокирован пользователем с tg_id: {tg_id}") else: error_count += 1 - logger.error( - f"❌ Ошибка при отправке сообщения пользователю {tg_id}: {e}" - ) + logger.error(f"❌ Ошибка при отправке сообщения пользователю {tg_id}: {e}") await message.answer( f"📊 Результаты рассылки пробных периодов:\n" @@ -99,9 +91,7 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext): f"❌ Ошибок: {error_count}" ) else: - await message.answer( - "📭 Нет пользователей с неиспользованными пробными ключами." - ) + await message.answer("📭 Нет пользователей с неиспользованными пробными ключами.") finally: await conn.close() @@ -111,9 +101,7 @@ async def handle_send_trial_command(message: types.Message, state: FSMContext): @router.message(Command("send_to_all"), IsAdminFilter()) -async def send_message_to_all_clients( - message: types.Message, state: FSMContext, from_panel=False -): +async def send_message_to_all_clients(message: types.Message, state: FSMContext, from_panel=False): try: await message.delete() except Exception: @@ -151,9 +139,7 @@ async def process_message_to_all( success_count += 1 except Exception as e: error_count += 1 - logger.error( - f"❌ Ошибка при отправке сообщения пользователю {tg_id}: {e}" - ) + logger.error(f"❌ Ошибка при отправке сообщения пользователю {tg_id}: {e}") await message.answer( f"📤 Рассылка завершена:\n" diff --git a/handlers/admin/admin_coupons.py b/handlers/admin/admin_coupons.py index 585f3cbc..a706a78d 100644 --- a/handlers/admin/admin_coupons.py +++ b/handlers/admin/admin_coupons.py @@ -17,9 +17,7 @@ router = Router() @router.callback_query(F.data == "coupons_editor", IsAdminFilter()) -async def show_coupon_management_menu( - callback_query: types.CallbackQuery, state: FSMContext -): +async def show_coupon_management_menu(callback_query: types.CallbackQuery, state: FSMContext): try: await callback_query.message.delete() except Exception as e: @@ -28,16 +26,12 @@ async def show_coupon_management_menu( await state.clear() builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="➕ Создать купон", callback_data="create_coupon") - ) + builder.row(InlineKeyboardButton(text="➕ Создать купон", callback_data="create_coupon")) builder.row(InlineKeyboardButton(text="Купоны", callback_data="coupons")) builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="admin")) markup = builder.as_markup() - await callback_query.message.answer( - "🛠 Меню управления купонами:", reply_markup=markup - ) + await callback_query.message.answer("🛠 Меню управления купонами:", reply_markup=markup) await callback_query.answer() @@ -53,14 +47,11 @@ async def show_coupon_list(callback_query: types.CallbackQuery): if not coupons: builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor") - ) + builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor")) markup = builder.as_markup() await callback_query.message.answer( - "❌ На данный момент нет доступных купонов.\n" - "Вы можете вернуться в меню управления.", + "❌ На данный момент нет доступных купонов.\n" "Вы можете вернуться в меню управления.", parse_mode="HTML", reply_markup=markup, ) @@ -85,19 +76,16 @@ async def show_coupon_list(callback_query: types.CallbackQuery): ) ) - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor") - ) + builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor")) markup = builder.as_markup() - await callback_query.message.answer( - coupon_list, parse_mode="HTML", reply_markup=markup - ) + await callback_query.message.answer(coupon_list, parse_mode="HTML", reply_markup=markup) except Exception as e: logger.error(f"Ошибка при получении списка купонов: {e}") await callback_query.message.answer( - f"❌ Произошла ошибка при получении списка купонов: {e}", parse_mode="HTML" + f"❌ Произошла ошибка при получении списка купонов: {e}", + parse_mode="HTML", ) await callback_query.answer() @@ -118,15 +106,14 @@ async def handle_delete_coupon(callback_query: types.CallbackQuery): await show_coupon_list(callback_query) else: await callback_query.message.answer( - f"❌ Купон с кодом {coupon_code} не найден.", parse_mode="HTML" + f"❌ Купон с кодом {coupon_code} не найден.", + parse_mode="HTML", ) await show_coupon_list(callback_query) except Exception as e: logger.error(f"Ошибка при удалении купона: {e}") - await callback_query.message.answer( - f"❌ Произошла ошибка при удалении купона: {e}", parse_mode="HTML" - ) + await callback_query.message.answer(f"❌ Произошла ошибка при удалении купона: {e}", parse_mode="HTML") await callback_query.answer() @@ -178,8 +165,7 @@ async def process_coupon_data(message: types.Message, state: FSMContext): usage_limit = int(parts[2]) except ValueError: await message.answer( - "⚠️ Проверьте правильность введенных данных.\n" - "Сумма должна быть числом, а лимит — целым числом.", + "⚠️ Проверьте правильность введенных данных.\n" "Сумма должна быть числом, а лимит — целым числом.", parse_mode="HTML", reply_markup=markup, ) @@ -195,9 +181,7 @@ async def process_coupon_data(message: types.Message, state: FSMContext): ) builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor") - ) + builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="coupons_editor")) markup = builder.as_markup() try: @@ -211,9 +195,7 @@ async def process_coupon_data(message: types.Message, state: FSMContext): except Exception as e: logger.error(f"Ошибка при создании купона: {e}") - await message.answer( - f"❌ Ошибка при создании купона: {e}", parse_mode="HTML" - ) + await message.answer(f"❌ Ошибка при создании купона: {e}", parse_mode="HTML") @router.callback_query(F.data == "back_to_coupons_menu") diff --git a/handlers/admin/admin_panel.py b/handlers/admin/admin_panel.py index 4393ed9e..3a2004e7 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -1,13 +1,13 @@ -import subprocess from datetime import datetime +import subprocess -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 +import asyncpg from backup import backup_database from bot import bot @@ -21,15 +21,17 @@ router = Router() class UserEditorState(StatesGroup): waiting_for_tg_id = State() displaying_user_info = State() + waiting_for_restart_confirmation = State() @router.callback_query(F.data == "admin", IsAdminFilter()) -async def handle_admin_callback_query(callback_query: CallbackQuery): - await handle_admin_message(callback_query.message) +async def handle_admin_callback_query(callback_query: CallbackQuery, state: FSMContext): + await handle_admin_message(callback_query.message, state) @router.message(Command("admin"), F.data == "admin", IsAdminFilter()) -async def handle_admin_message(message: types.Message): +async def handle_admin_message(message: types.Message, state: FSMContext): + await state.clear() try: await message.delete() @@ -37,35 +39,17 @@ async def handle_admin_message(message: types.Message): pass builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton( - text="📊 Статистика пользователей", callback_data="user_stats" - ) - ) - builder.row( - InlineKeyboardButton( - text="👥 Управление пользователями", callback_data="user_editor" - ) - ) - builder.row( - InlineKeyboardButton( - text="🎟️ Управление купонами", callback_data="coupons_editor" - ) - ) - builder.row( - InlineKeyboardButton(text="📢 Массовая рассылка", callback_data="send_to_alls") - ) - builder.row( - InlineKeyboardButton(text="💾 Создать резервную копию", callback_data="backups") - ) - builder.row( - InlineKeyboardButton(text="🔄 Перезагрузить бота", callback_data="restart_bot") - ) - builder.row( - InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="📊 Статистика пользователей", callback_data="user_stats")) + builder.row(InlineKeyboardButton(text="👥 Управление пользователями", callback_data="user_editor")) + builder.row(InlineKeyboardButton(text="🎟️ Управление купонами", callback_data="coupons_editor")) + builder.row(InlineKeyboardButton(text="📢 Массовая рассылка", callback_data="send_to_alls")) + builder.row(InlineKeyboardButton(text="💾 Создать резервную копию", callback_data="backups")) + builder.row(InlineKeyboardButton(text="🔄 Перезагрузить бота", callback_data="restart_bot")) + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) await bot.send_message( - message.chat.id, "🤖 Панель администратора", reply_markup=builder.as_markup() + message.chat.id, + "🤖 Панель администратора", + reply_markup=builder.as_markup(), ) @@ -83,9 +67,7 @@ async def user_stats_menu(callback_query: CallbackQuery): total_payments_week = await conn.fetchval( "SELECT COALESCE(SUM(amount), 0) FROM payments WHERE created_at >= date_trunc('week', CURRENT_DATE)" ) - total_payments_all_time = await conn.fetchval( - "SELECT COALESCE(SUM(amount), 0) FROM payments" - ) + total_payments_all_time = await conn.fetchval("SELECT COALESCE(SUM(amount), 0) FROM payments") active_keys = await conn.fetchval( "SELECT COUNT(*) FROM keys WHERE expiry_time > $1", @@ -109,19 +91,12 @@ async def user_stats_menu(callback_query: CallbackQuery): ) builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="🔄 Обновить", callback_data="user_stats") - ) - builder.row( - InlineKeyboardButton(text="🔙 Вернуться в меню", callback_data="admin") - ) + builder.row(InlineKeyboardButton(text="🔄 Обновить", callback_data="user_stats")) + builder.row(InlineKeyboardButton(text="🔙 Вернуться в меню", callback_data="admin")) - await callback_query.message.edit_text( - stats_message, reply_markup=builder.as_markup(), parse_mode="HTML" - ) + await callback_query.message.edit_text(stats_message, reply_markup=builder.as_markup(), parse_mode="HTML") finally: await conn.close() - # await callback_query.answer() @router.callback_query(F.data == "send_to_alls", IsAdminFilter()) @@ -134,52 +109,78 @@ async def handle_send_to_all(callback_query: CallbackQuery, state: FSMContext): async def handle_backup(message: Message): await message.answer("💾 Инициализация резервного копирования базы данных...") await backup_database() - await message.answer( - "✅ Резервная копия успешно создана и отправлена администратору." - ) + await message.answer("✅ Резервная копия успешно создана и отправлена администратору.") @router.callback_query(F.data == "restart_bot", IsAdminFilter()) -async def handle_restart(callback_query: CallbackQuery): +async def handle_restart(callback_query: CallbackQuery, state: FSMContext): + await state.set_state(UserEditorState.waiting_for_restart_confirmation) + builder = InlineKeyboardBuilder() + builder.row( + InlineKeyboardButton(text="✅ Да, перезапустить", callback_data="confirm_restart"), + InlineKeyboardButton(text="❌ Нет, отмена", callback_data="admin"), + ) + builder.row(InlineKeyboardButton(text="🔙 Вернуться в меню", callback_data="admin")) + await callback_query.message.edit_text( + "🤔 Вы уверены, что хотите перезапустить бота?", + reply_markup=builder.as_markup(), + ) + + +@router.callback_query( + F.data == "confirm_restart", + UserEditorState.waiting_for_restart_confirmation, + IsAdminFilter(), +) +async def confirm_restart_bot(callback_query: CallbackQuery, state: FSMContext): try: + # Перезапуск службы бота subprocess.run( ["sudo", "systemctl", "restart", "bot.service"], check=True, capture_output=True, text=True, ) - await callback_query.message.answer("🔄 Бот успешно перезапущен.") + + # Очистка состояния + await state.clear() + + # Создание клавиатуры для возврата в меню администратора + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="🔙 Вернуться в меню", callback_data="admin")) + + # Уведомление о успешной перезагрузке + await callback_query.message.edit_text("🔄 Бот успешно перезапущен.", reply_markup=builder.as_markup()) except subprocess.CalledProcessError as e: - await callback_query.message.answer( - f"⚠️ Перезагрузка бота будет выполнена через 30 секунд. Детали: {e.stderr}" + # Обработка ошибок при перезагрузке + builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="🔙 Вернуться в меню", callback_data="admin")) + + await callback_query.message.edit_text( + f"⚠️ Ошибка при перезагрузке бота: {e.stderr}", + reply_markup=builder.as_markup(), ) + await callback_query.answer() + @router.callback_query(F.data == "user_editor", IsAdminFilter()) async def user_editor_menu(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() builder.row( InlineKeyboardButton( - text="🔍 Поиск по названию ключа", callback_data="search_by_key_name" - ) - ) - builder.row( - InlineKeyboardButton( - text="🆔 Поиск по Telegram ID", callback_data="search_by_tg_id" - ) - ) - builder.row( - InlineKeyboardButton( - text="🌐 Поиск по Username", callback_data="search_by_username" + text="🔍 Поиск по названию ключа", + callback_data="search_by_key_name", ) ) + builder.row(InlineKeyboardButton(text="🆔 Поиск по Telegram ID", callback_data="search_by_tg_id")) + builder.row(InlineKeyboardButton(text="🌐 Поиск по Username", callback_data="search_by_username")) builder.row(InlineKeyboardButton(text="🔙 Вернуться назад", callback_data="admin")) await callback_query.message.edit_text( - "👇 Выберите способ поиска пользователя:", reply_markup=builder.as_markup() + "👇 Выберите способ поиска пользователя:", + reply_markup=builder.as_markup(), ) async def handle_error(tg_id, callback_query, message): - await bot.edit_message_text( - message, chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.edit_message_text(message, chat_id=tg_id, message_id=callback_query.message.message_id) diff --git a/handlers/admin/admin_user_editor.py b/handlers/admin/admin_user_editor.py index 5b5495bd..94311599 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -1,12 +1,12 @@ import asyncio from datetime import datetime -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 from aiogram.utils.keyboard import InlineKeyboardBuilder +import asyncpg from bot import bot from config import CLUSTERS, DATABASE_URL, TOTAL_GB @@ -45,9 +45,7 @@ async def handle_username_input(message: types.Message, state: FSMContext): username = message.text.strip() conn = await asyncpg.connect(DATABASE_URL) try: - user_record = await conn.fetchrow( - "SELECT tg_id FROM users WHERE username = $1", username - ) + user_record = await conn.fetchrow("SELECT tg_id FROM users WHERE username = $1", username) if not user_record: await message.reply("🔍 Пользователь с указанным username не найден. 🚫") @@ -55,13 +53,10 @@ async def handle_username_input(message: types.Message, state: FSMContext): return tg_id = user_record["tg_id"] - balance = await conn.fetchval( - "SELECT balance FROM connections WHERE tg_id = $1", tg_id - ) + username = await conn.fetchval("SELECT username FROM users WHERE tg_id = $1", tg_id) + balance = await conn.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) key_records = await conn.fetch("SELECT email FROM keys WHERE tg_id = $1", tg_id) - referral_count = await conn.fetchval( - "SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1", tg_id - ) + referral_count = await conn.fetchval("SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1", tg_id) if balance is None: await message.reply("Пользователь с указанным tg_id не найден.") @@ -71,35 +66,33 @@ async def handle_username_input(message: types.Message, state: FSMContext): builder = InlineKeyboardBuilder() for (email,) in key_records: - builder.row( - InlineKeyboardButton( - text=f"🔑 {email}", callback_data=f"edit_key_{email}" - ) - ) + builder.row(InlineKeyboardButton(text=f"🔑 {email}", callback_data=f"edit_key_{email}")) builder.row( InlineKeyboardButton( - text="📝 Изменить баланс", callback_data=f"change_balance_{tg_id}" + text="📝 Изменить баланс", + callback_data=f"change_balance_{tg_id}", ) ) builder.row( InlineKeyboardButton( - text="🔄 Восстановить пробник", callback_data=f"restore_trial_{tg_id}" + text="🔄 Восстановить пробник", + callback_data=f"restore_trial_{tg_id}", ) ) builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor")) user_info = ( - f"📊 Информация о пользователе:\n" + f"📊 Информация о пользователе:\n\n" + f"🆔 ID пользователя: {tg_id}\n" + f"👤 Логин пользователя: @{username}\n" f"💰 Баланс: {balance}\n" f"👥 Количество рефералов: {referral_count}\n" f"🔑 Ключи (для редактирования нажмите на ключ):" ) - await message.reply( - user_info, reply_markup=builder.as_markup(), parse_mode="HTML" - ) + await message.reply(user_info, reply_markup=builder.as_markup(), parse_mode="HTML") await state.set_state(UserEditorState.displaying_user_info) finally: @@ -112,13 +105,10 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext): conn = await asyncpg.connect(DATABASE_URL) try: - balance = await conn.fetchval( - "SELECT balance FROM connections WHERE tg_id = $1", tg_id - ) + username = await conn.fetchval("SELECT username FROM users WHERE tg_id = $1", tg_id) + balance = await conn.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) key_records = await conn.fetch("SELECT email FROM keys WHERE tg_id = $1", tg_id) - referral_count = await conn.fetchval( - "SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1", tg_id - ) + referral_count = await conn.fetchval("SELECT COUNT(*) FROM referrals WHERE referrer_tg_id = $1", tg_id) if balance is None: await message.reply("❌ Пользователь с указанным tg_id не найден. 🔍") @@ -128,35 +118,33 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext): builder = InlineKeyboardBuilder() for (email,) in key_records: - builder.row( - InlineKeyboardButton( - text=f"🔑 {email}", callback_data=f"edit_key_{email}" - ) - ) + builder.row(InlineKeyboardButton(text=f"🔑 {email}", callback_data=f"edit_key_{email}")) builder.row( InlineKeyboardButton( - text="📝 Изменить баланс", callback_data=f"change_balance_{tg_id}" + text="📝 Изменить баланс", + callback_data=f"change_balance_{tg_id}", ) ) builder.row( InlineKeyboardButton( - text="🔄 Восстановить пробник", callback_data=f"restore_trial_{tg_id}" + text="🔄 Восстановить пробник", + callback_data=f"restore_trial_{tg_id}", ) ) builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="user_editor")) user_info = ( - f"📊 Информация о пользователе:\n" + f"📊 Информация о пользователе:\n\n" + f"🆔 ID пользователя: {tg_id}\n" + f"👤 Логин пользователя: @{username}\n" f"💰 Баланс: {balance}\n" f"👥 Количество рефералов: {referral_count}\n" f"🔑 Ключи (для редактирования нажмите на ключ):" ) - await message.reply( - user_info, reply_markup=builder.as_markup(), parse_mode="HTML" - ) + await message.reply(user_info, reply_markup=builder.as_markup(), parse_mode="HTML") await state.set_state(UserEditorState.displaying_user_info) finally: @@ -170,15 +158,9 @@ async def handle_restore_trial(callback_query: types.CallbackQuery): await restore_trial(tg_id) builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton( - text="🔙 Назад в меню администратора", callback_data="admin" - ) - ) + builder.row(InlineKeyboardButton(text="🔙 Назад в меню администратора", callback_data="admin")) - await callback_query.message.edit_text( - "✅ Триал успешно восстановлен.", reply_markup=builder.as_markup() - ) + await callback_query.message.edit_text("✅ Триал успешно восстановлен.", reply_markup=builder.as_markup()) @router.callback_query(F.data.startswith("change_balance_"), IsAdminFilter()) @@ -194,9 +176,7 @@ async def process_balance_change(callback_query: CallbackQuery, state: FSMContex @router.message(UserEditorState.waiting_for_new_balance, IsAdminFilter()) async def handle_new_balance_input(message: types.Message, state: FSMContext): if not message.text.isdigit() or int(message.text) < 0: - await message.reply( - "❌ Пожалуйста, введите корректную сумму для изменения баланса." - ) + await message.reply("❌ Пожалуйста, введите корректную сумму для изменения баланса.") return new_balance = int(message.text) @@ -206,7 +186,9 @@ async def handle_new_balance_input(message: types.Message, state: FSMContext): conn = await asyncpg.connect(DATABASE_URL) try: await conn.execute( - "UPDATE connections SET balance = $1 WHERE tg_id = $2", new_balance, tg_id + "UPDATE connections SET balance = $1 WHERE tg_id = $2", + new_balance, + tg_id, ) response_message = f"✅ Баланс успешно изменен на {new_balance}." @@ -219,7 +201,9 @@ async def handle_new_balance_input(message: types.Message, state: FSMContext): ) ) await message.reply( - response_message, reply_markup=builder.as_markup(), parse_mode="HTML" + response_message, + reply_markup=builder.as_markup(), + parse_mode="HTML", ) finally: @@ -252,9 +236,7 @@ async def process_key_edit(callback_query: CallbackQuery): for cluster in CLUSTERS.values(): if server_id in cluster: - server_name = cluster[server_id].get( - "name", "Неизвестный сервер" - ) + server_name = cluster[server_id].get("name", "Неизвестный сервер") break expiry_date = datetime.utcfromtimestamp(expiry_time / 1000) @@ -289,18 +271,14 @@ async def process_key_edit(callback_query: CallbackQuery): callback_data=f"delete_key_admin|{email}", ), ) - builder.row( - InlineKeyboardButton(text="🔙 Назад", callback_data="admin") - ) + builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="admin")) await callback_query.message.edit_text( response_message, reply_markup=builder.as_markup(), parse_mode="HTML", ) else: - await callback_query.message.edit_text( - "Информация о ключе не найдена.", parse_mode="HTML" - ) + await callback_query.message.edit_text("Информация о ключе не найдена.", parse_mode="HTML") finally: await conn.close() @@ -364,9 +342,7 @@ async def handle_key_name_input(message: types.Message, state: FSMContext): server_name = cluster[server_id].get("name", "Неизвестный сервер") break - expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime( - "%d %B %Y" - ) + expiry_date = datetime.utcfromtimestamp(expiry_time / 1000).strftime("%d %B %Y") response_messages.append( f"🔑 Ключ:
{key}
\n" @@ -383,7 +359,8 @@ async def handle_key_name_input(message: types.Message, state: FSMContext): ) key_buttons.row( InlineKeyboardButton( - text="❌ Удалить ключ", callback_data=f"delete_key_admin|{email}" + text="❌ Удалить ключ", + callback_data=f"delete_key_admin|{email}", ) ) @@ -424,9 +401,7 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext): try: expiry_time_str = message.text - expiry_time = int( - datetime.strptime(expiry_time_str, "%Y-%m-%d %H:%M:%S").timestamp() * 1000 - ) + expiry_time = int(datetime.strptime(expiry_time_str, "%Y-%m-%d %H:%M:%S").timestamp() * 1000) client_id = await get_client_id_by_email(email) if client_id is None: @@ -436,9 +411,7 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext): conn = await asyncpg.connect(DATABASE_URL) try: - record = await conn.fetchrow( - "SELECT server_id FROM keys WHERE client_id = $1", client_id - ) + record = await conn.fetchrow("SELECT server_id FROM keys WHERE client_id = $1", client_id) if not record: await message.reply("Клиент не найден в базе данных.") await state.clear() @@ -464,12 +437,16 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext): await update_key_expiry(client_id, expiry_time) - response_message = f"✅ Время истечения ключа для клиента {client_id} ({email}) успешно обновлено на всех серверах." + response_message = ( + f"✅ Время истечения ключа для клиента {client_id} ({email}) успешно обновлено на всех серверах." + ) builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="🔙 Назад", callback_data="admin")) await message.reply( - response_message, reply_markup=builder.as_markup(), parse_mode="HTML" + response_message, + reply_markup=builder.as_markup(), + parse_mode="HTML", ) finally: @@ -490,9 +467,7 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery): conn = await asyncpg.connect(DATABASE_URL) try: - client_id = await conn.fetchval( - "SELECT client_id FROM keys WHERE email = $1", email - ) + client_id = await conn.fetchval("SELECT client_id FROM keys WHERE email = $1", email) if client_id is None: await bot.edit_message_text( @@ -505,14 +480,11 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery): builder = InlineKeyboardBuilder() builder.row( types.InlineKeyboardButton( - text="✅ Да, удалить", callback_data=f"confirm_delete_admin|{client_id}" - ) - ) - builder.row( - types.InlineKeyboardButton( - text="❌ Нет, отменить", callback_data="view_keys" + text="✅ Да, удалить", + callback_data=f"confirm_delete_admin|{client_id}", ) ) + builder.row(types.InlineKeyboardButton(text="❌ Нет, отменить", callback_data="view_keys")) await bot.edit_message_text( "❓ Вы уверены, что хотите удалить ключ?", chat_id=tg_id, @@ -534,24 +506,18 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): try: conn = await asyncpg.connect(DATABASE_URL) try: - record = await conn.fetchrow( - "SELECT email FROM keys WHERE client_id = $1", client_id - ) + record = await conn.fetchrow("SELECT email FROM keys WHERE client_id = $1", client_id) if record: email = record["email"] response_message = "✅ Ключ успешно удален." builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="⬅️ Назад", callback_data="view_keys") - ) + builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="view_keys")) async def delete_key_from_servers(email, client_id): tasks = [] for cluster_id in CLUSTERS: - tasks.append( - delete_key_from_cluster(cluster_id, email, client_id) - ) + tasks.append(delete_key_from_cluster(cluster_id, email, client_id)) await asyncio.gather(*tasks) await delete_key_from_servers(email, client_id) @@ -566,9 +532,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): else: response_message = "🚫 Ключ не найден или уже удален." builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="⬅️ Назад", callback_data="view_keys") - ) + builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="view_keys")) await bot.edit_message_text( response_message, chat_id=tg_id, diff --git a/handlers/coupons.py b/handlers/coupons.py index a069fca9..c79b22a4 100644 --- a/handlers/coupons.py +++ b/handlers/coupons.py @@ -1,11 +1,11 @@ from datetime import datetime -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 InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder +import asyncpg from config import DATABASE_URL from database import update_balance @@ -20,18 +20,14 @@ router = Router() @router.callback_query(F.data == "activate_coupon") -async def handle_activate_coupon( - callback_query: types.CallbackQuery, state: FSMContext -): +async def handle_activate_coupon(callback_query: types.CallbackQuery, state: FSMContext): try: await callback_query.message.delete() except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) await callback_query.message.answer( "🎫 Введите код купона:\n\n" @@ -54,13 +50,9 @@ async def process_coupon_code(message: types.Message, state: FSMContext): activation_result = await activate_coupon(message.from_user.id, coupon_code) builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile")) - await message.answer( - activation_result, reply_markup=builder.as_markup(), parse_mode="HTML" - ) + await message.answer(activation_result, reply_markup=builder.as_markup(), parse_mode="HTML") await state.clear() diff --git a/handlers/donate.py b/handlers/donate.py index 0dedc524..6d394abc 100644 --- a/handlers/donate.py +++ b/handlers/donate.py @@ -28,14 +28,11 @@ async def process_donate(callback_query: types.CallbackQuery, state: FSMContext) logger.error(f"Не удалось удалить сообщение: {e}") builder = InlineKeyboardBuilder() + builder.row(InlineKeyboardButton(text="🤖 Бот для покупки звезд", url="https://t.me/PremiumBot")) builder.row( InlineKeyboardButton( - text="🤖 Бот для покупки звезд", url="https://t.me/PremiumBot" - ) - ) - builder.row( - InlineKeyboardButton( - text="💰 Ввести сумму доната", callback_data="enter_custom_donate_amount" + text="💰 Ввести сумму доната", + callback_data="enter_custom_donate_amount", ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="view_profile")) @@ -52,14 +49,10 @@ async def process_donate(callback_query: types.CallbackQuery, state: FSMContext) @router.callback_query(F.data == "enter_custom_donate_amount") -async def process_enter_donate_amount( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_enter_donate_amount(callback_query: types.CallbackQuery, state: FSMContext): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="donate")) - await callback_query.message.edit_text( - f"💸 Введите сумму доната в рублях:", reply_markup=builder.as_markup() - ) + await callback_query.message.edit_text(f"💸 Введите сумму доната в рублях:", reply_markup=builder.as_markup()) await state.set_state(DonateState.entering_donate_amount) await callback_query.answer() @@ -75,9 +68,7 @@ async def process_donate_amount_input(message: types.Message, state: FSMContext) if message.text.isdigit(): amount = int(message.text) if amount // RUB_TO_XTR <= 0: - await message.answer( - f"Сумма доната должна быть больше {RUB_TO_XTR}. Пожалуйста, введите сумму еще раз:" - ) + await message.answer(f"Сумма доната должна быть больше {RUB_TO_XTR}. Пожалуйста, введите сумму еще раз:") return await state.update_data(amount=amount) @@ -122,18 +113,12 @@ async def on_successful_donate(message: types.Message, state: FSMContext): if previous_message_id: try: - await bot.delete_message( - chat_id=user_id, message_id=previous_message_id - ) + await bot.delete_message(chat_id=user_id, message_id=previous_message_id) except Exception as e: logger.error(f"Не удалось удалить сообщение: {e}") builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton( - text="Вернуться в профиль", callback_data="view_profile" - ) - ) + builder.row(InlineKeyboardButton(text="Вернуться в профиль", callback_data="view_profile")) sent_message = await bot.send_message( chat_id=user_id, diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index 0af5a49b..3ac9d143 100644 --- a/handlers/instructions/instructions.py +++ b/handlers/instructions/instructions.py @@ -1,9 +1,9 @@ import os -import asyncpg from aiogram import F, Router, types from aiogram.types import BufferedInputFile, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder +import asyncpg from bot import bot from config import CONNECT_WINDOWS, DATABASE_URL, SUPPORT_CHAT_URL @@ -28,9 +28,7 @@ async def send_instructions(callback_query: types.CallbackQuery): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL)) builder.row( - InlineKeyboardButton( - text="⬅️ Вернуться в профиль", callback_data="view_profile" - ), + InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile"), ) with open(image_path, "rb") as image_from_buffer: @@ -50,9 +48,7 @@ async def process_connect_pc(callback_query: types.CallbackQuery): key_name = callback_query.data.split("|")[1] try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") @@ -86,13 +82,9 @@ async def process_connect_pc(callback_query: types.CallbackQuery): text="💻 Подключить Windows", url=f"{CONNECT_WINDOWS}{key}" ) - support_button = types.InlineKeyboardButton( - text="🆘 Поддержка", url=f"{SUPPORT_CHAT_URL}" - ) + support_button = types.InlineKeyboardButton(text="🆘 Поддержка", url=f"{SUPPORT_CHAT_URL}") - back_button = types.InlineKeyboardButton( - text="🔙 Назад в профиль", callback_data="view_profile" - ) + back_button = types.InlineKeyboardButton(text="🔙 Назад в профиль", callback_data="view_profile") inline_keyboard = [ [connect_windows_button], @@ -102,7 +94,10 @@ async def process_connect_pc(callback_query: types.CallbackQuery): keyboard = types.InlineKeyboardMarkup(inline_keyboard=inline_keyboard) await bot.send_message( - tg_id, instruction_message, reply_markup=keyboard, parse_mode="HTML" + tg_id, + instruction_message, + reply_markup=keyboard, + parse_mode="HTML", ) finally: diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index 835794ce..2233c8d3 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -1,15 +1,23 @@ import asyncio -import uuid from datetime import datetime, timedelta +import uuid -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 +import asyncpg from bot import bot, dp -from config import CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, SUPPORT_CHAT_URL +from config import ( + CONNECT_ANDROID, + CONNECT_IOS, + DATABASE_URL, + DOWNLOAD_ANDROID, + DOWNLOAD_IOS, + PUBLIC_LINK, + SUPPORT_CHAT_URL, +) 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_cluster @@ -33,9 +41,7 @@ async def process_callback_create_key(callback_query: CallbackQuery, state: FSMC tg_id = callback_query.from_user.id try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception: pass @@ -71,18 +77,16 @@ async def select_server(callback_query: CallbackQuery, state: FSMContext): callback_data="confirm_create_new_key", ) ], - [ - InlineKeyboardButton( - text="↩️ Назад", callback_data="cancel_create_key" - ) - ], + [InlineKeyboardButton(text="↩️ Назад", callback_data="cancel_create_key")], ] ), ) await state.update_data(creating_new_key=True) else: await bot.send_message( - chat_id=callback_query.from_user.id, text=KEY_TRIAL, parse_mode="HTML" + chat_id=callback_query.from_user.id, + text=KEY_TRIAL, + parse_mode="HTML", ) await state.set_state(Form.waiting_for_key_name) @@ -97,9 +101,7 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex balance = await get_balance(tg_id) if balance < RENEWAL_PLANS["1"]["price"]: - replenish_button = InlineKeyboardButton( - text="Перейти в профиль", callback_data="view_profile" - ) + 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() @@ -107,9 +109,7 @@ 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) @@ -118,9 +118,7 @@ async def confirm_create_new_key(callback_query: CallbackQuery, state: FSMContex @dp.callback_query(F.data == "cancel_create_key") -async def cancel_create_key( - callback_query: CallbackQuery, state: FSMContext, admin: bool -): +async def cancel_create_key(callback_query: CallbackQuery, state: FSMContext, admin: bool): await process_callback_view_profile(callback_query, state, admin) await callback_query.answer() @@ -133,17 +131,13 @@ async def handle_key_name_input(message: Message, state: FSMContext): logger.info(f"User {tg_id} is attempting to create a key with the name: {key_name}") if not key_name: - await message.bot.send_message( - tg_id, "📝 Пожалуйста, назовите устройство на английском языке." - ) + await message.bot.send_message(tg_id, "📝 Пожалуйста, назовите устройство на английском языке.") logger.warning(f"User {tg_id} entered an invalid key name: {key_name}") return conn = await asyncpg.connect(DATABASE_URL) try: - logger.info( - f"Checking if key name '{key_name}' already exists for user {tg_id} in the database." - ) + logger.info(f"Checking if key name '{key_name}' already exists for user {tg_id} in the database.") existing_key = await conn.fetchrow( "SELECT * FROM keys WHERE email = $1 AND tg_id = $2", key_name.lower(), @@ -168,9 +162,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): conn = await asyncpg.connect(DATABASE_URL) try: logger.info(f"Checking trial status for user {tg_id}.") - existing_connection = await conn.fetchrow( - "SELECT trial FROM connections WHERE tg_id = $1", tg_id - ) + existing_connection = await conn.fetchrow("SELECT trial FROM connections WHERE tg_id = $1", tg_id) finally: await conn.close() @@ -182,9 +174,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): else: balance = await get_balance(tg_id) if balance < RENEWAL_PLANS["1"]["price"]: - replenish_button = InlineKeyboardButton( - text="Перейти в профиль", callback_data="view_profile" - ) + replenish_button = InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile") keyboard = InlineKeyboardMarkup(inline_keyboard=[[replenish_button]]) await message.bot.send_message( tg_id, @@ -206,12 +196,8 @@ async def handle_key_name_input(message: Message, state: FSMContext): button_support = InlineKeyboardButton(text="💬 Поддержка", url=SUPPORT_CHAT_URL) - button_profile = InlineKeyboardButton( - text="👤 Личный кабинет", callback_data="view_profile" - ) - button_iphone = InlineKeyboardButton( - text="🍏 Подключить", url=f"{CONNECT_IOS}{public_link}" - ) + button_profile = InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") + button_iphone = InlineKeyboardButton(text="🍏 Подключить", url=f"{CONNECT_IOS}{public_link}") button_android = InlineKeyboardButton( text="🤖 Подключить", url=f"{CONNECT_ANDROID}{public_link}", @@ -238,9 +224,7 @@ async def handle_key_name_input(message: Message, state: FSMContext): logger.info(f"Sending key message to user {tg_id} with the public link.") - await message.bot.send_message( - tg_id, key_message, parse_mode="HTML", reply_markup=keyboard - ) + await message.bot.send_message(tg_id, key_message, parse_mode="HTML", reply_markup=keyboard) try: least_loaded_cluster = await get_least_loaded_cluster() @@ -263,13 +247,9 @@ async def handle_key_name_input(message: Message, state: FSMContext): conn = await asyncpg.connect(DATABASE_URL) try: logger.info(f"Updating trial status for user {tg_id} in the database.") - existing_connection = await conn.fetchrow( - "SELECT * FROM connections WHERE tg_id = $1", tg_id - ) + existing_connection = await conn.fetchrow("SELECT * FROM connections WHERE tg_id = $1", tg_id) if existing_connection: - await conn.execute( - "UPDATE connections SET trial = 1 WHERE tg_id = $1", tg_id - ) + await conn.execute("UPDATE connections SET trial = 1 WHERE tg_id = $1", tg_id) else: await add_connection(tg_id, 0, 1) finally: @@ -277,7 +257,12 @@ async def handle_key_name_input(message: Message, state: FSMContext): logger.info(f"Storing key for user {tg_id} in the database.") await store_key( - tg_id, client_id, email, expiry_timestamp, public_link, least_loaded_cluster + tg_id, + client_id, + email, + expiry_timestamp, + public_link, + least_loaded_cluster, ) except Exception as e: @@ -293,8 +278,6 @@ async def handle_instructions(callback_query: CallbackQuery): @dp.callback_query(F.data == "back_to_main") -async def handle_back_to_main( - callback_query: CallbackQuery, state: FSMContext, admin: bool -): +async def handle_back_to_main(callback_query: CallbackQuery, state: FSMContext, admin: bool): await process_callback_view_profile(callback_query, state, admin) await callback_query.answer() diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py index 44da28c6..d0a68a18 100644 --- a/handlers/keys/key_utils.py +++ b/handlers/keys/key_utils.py @@ -24,9 +24,7 @@ async def create_key_on_cluster(cluster_id, tg_id, client_id, email, expiry_time ) conn = await asyncpg.connect(DATABASE_URL) - existing_key = await conn.fetchrow( - "SELECT 1 FROM keys WHERE email = $1", email - ) + existing_key = await conn.fetchrow("SELECT 1 FROM keys WHERE email = $1", email) if existing_key: raise ValueError(f"Email {email} уже существует в базе данных.") @@ -72,16 +70,12 @@ async def renew_key_in_cluster(cluster_id, email, client_id, new_expiry_time, to password=ADMIN_PASSWORD, ) - tasks.append( - extend_client_key(xui, email, new_expiry_time, client_id, total_gb) - ) + tasks.append(extend_client_key(xui, email, new_expiry_time, client_id, total_gb)) await asyncio.gather(*tasks) except Exception as e: - logger.error( - f"Не удалось продлить ключ {client_id} в кластере {cluster_id}: {e}" - ) + logger.error(f"Не удалось продлить ключ {client_id} в кластере {cluster_id}: {e}") raise e @@ -117,9 +111,7 @@ async def delete_key_from_cluster(cluster_id, email, client_id): await asyncio.gather(*tasks) except Exception as e: - logger.error( - f"Не удалось удалить ключ {client_id} в кластере {cluster_id}: {e}" - ) + logger.error(f"Не удалось удалить ключ {client_id} в кластере {cluster_id}: {e}") raise e @@ -154,12 +146,8 @@ async def update_key_on_cluster(tg_id, client_id, email, expiry_time, cluster_id await asyncio.gather(*tasks) - logger.info( - f"Ключ успешно обновлен для {client_id} на всех серверах в кластере {cluster_id}" - ) + logger.info(f"Ключ успешно обновлен для {client_id} на всех серверах в кластере {cluster_id}") except Exception as e: - logger.error( - f"Ошибка при обновлении ключа на серверах кластера {cluster_id} для {client_id}: {e}" - ) + logger.error(f"Ошибка при обновлении ключа на серверах кластера {cluster_id} для {client_id}: {e}") raise e diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 11a378b3..bd15afd4 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -1,17 +1,39 @@ import asyncio +from datetime import datetime, timedelta import locale import os -from datetime import datetime, timedelta -import asyncpg from aiogram import F, Router, types from aiogram.types import BufferedInputFile +import asyncpg from bot import bot -from config import CLUSTERS, CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, PUBLIC_LINK, TOTAL_GB +from config import ( + CLUSTERS, + CONNECT_ANDROID, + CONNECT_IOS, + DATABASE_URL, + DOWNLOAD_ANDROID, + DOWNLOAD_IOS, + PUBLIC_LINK, + TOTAL_GB, +) from database import delete_key, get_balance, store_key, update_balance, update_key_expiry -from handlers.keys.key_utils import delete_key_from_cluster, delete_key_from_db, renew_key_in_cluster, update_key_on_cluster -from handlers.texts import INSUFFICIENT_FUNDS_MSG, KEY_NOT_FOUND_MSG, NO_KEYS, PLAN_SELECTION_MSG, RENEWAL_PLANS, SUCCESS_RENEWAL_MSG, key_message +from handlers.keys.key_utils import ( + delete_key_from_cluster, + delete_key_from_db, + renew_key_in_cluster, + update_key_on_cluster, +) +from handlers.texts import ( + INSUFFICIENT_FUNDS_MSG, + KEY_NOT_FOUND_MSG, + NO_KEYS, + PLAN_SELECTION_MSG, + RENEWAL_PLANS, + SUCCESS_RENEWAL_MSG, + key_message, +) from handlers.utils import get_least_loaded_cluster, handle_error from logger import logger @@ -45,22 +67,20 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): buttons.append([button]) - back_button = types.InlineKeyboardButton( - text="🔙 Назад", callback_data="view_profile" - ) + back_button = types.InlineKeyboardButton(text="🔙 Назад", callback_data="view_profile") buttons.append([back_button]) inline_keyboard = types.InlineKeyboardMarkup(inline_keyboard=buttons) response_message = ( - "🔑 Список ваших устройств\n\n" - "👇 Выберите устройство для управления подпиской:" + "🔑 Список ваших устройств\n\n" "👇 Выберите устройство для управления подпиской:" ) image_path = os.path.join("img", "pic_keys.jpg") try: await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id + chat_id=tg_id, + message_id=callback_query.message.message_id, ) except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") @@ -69,9 +89,7 @@ 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, @@ -86,20 +104,15 @@ async def process_callback_view_keys(callback_query: types.CallbackQuery): else: response_message = NO_KEYS - create_key_button = types.InlineKeyboardButton( - text="➕ Создать подписку", callback_data="create_key" - ) - back_button = types.InlineKeyboardButton( - text="🔙 Назад", callback_data="view_profile" - ) + create_key_button = types.InlineKeyboardButton(text="➕ Создать подписку", callback_data="create_key") + back_button = types.InlineKeyboardButton(text="🔙 Назад", callback_data="view_profile") - keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[[create_key_button], [back_button]] - ) + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[create_key_button], [back_button]]) try: await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id + chat_id=tg_id, + message_id=callback_query.message.message_id, ) except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") @@ -110,9 +123,7 @@ 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, @@ -141,9 +152,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): try: try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception: pass @@ -168,9 +177,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): time_left = expiry_date - current_date if time_left.total_seconds() <= 0: - days_left_message = ( - "🕒 Статус подписки:\n🔴 Истекла\nОсталось часов: 0" - ) + days_left_message = "🕒 Статус подписки:\n🔴 Истекла\nОсталось часов: 0" elif time_left.days > 0: days_left_message = f"Осталось дней: {time_left.days}" else: @@ -178,37 +185,22 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): days_left_message = f"Осталось часов: {hours_left}" formatted_expiry_date = expiry_date.strftime("%d %B %Y года") - response_message = key_message( - key, formatted_expiry_date, days_left_message, server_name - ) + response_message = key_message(key, formatted_expiry_date, days_left_message, server_name) - download_android_button = types.InlineKeyboardButton( - text="🤖 Скачать", url=DOWNLOAD_ANDROID - ) - download_iphone_button = types.InlineKeyboardButton( - text="🍏 Скачать", url=DOWNLOAD_IOS - ) + download_android_button = types.InlineKeyboardButton(text="🤖 Скачать", url=DOWNLOAD_ANDROID) + download_iphone_button = types.InlineKeyboardButton(text="🍏 Скачать", url=DOWNLOAD_IOS) - connect_iphone_button = types.InlineKeyboardButton( - text="🍏 Подключить", url=f"{CONNECT_IOS}{key}" - ) - connect_android_button = types.InlineKeyboardButton( - text="🤖 Подключить", url=f"{CONNECT_ANDROID}{key}" - ) + connect_iphone_button = types.InlineKeyboardButton(text="🍏 Подключить", url=f"{CONNECT_IOS}{key}") + connect_android_button = types.InlineKeyboardButton(text="🤖 Подключить", url=f"{CONNECT_ANDROID}{key}") connect_pc_button = types.InlineKeyboardButton( - text="💻 Windows/Linux", callback_data=f"connect_pc|{key_name}" + text="💻 Windows/Linux", + callback_data=f"connect_pc|{key_name}", ) - renew_button = types.InlineKeyboardButton( - text="⏳ Продлить", callback_data=f"renew_key|{key_name}" - ) - delete_button = types.InlineKeyboardButton( - text="❌ Удалить", callback_data=f"delete_key|{key_name}" - ) - back_button = types.InlineKeyboardButton( - text="🔙 Назад в профиль", callback_data="view_profile" - ) + renew_button = types.InlineKeyboardButton(text="⏳ Продлить", callback_data=f"renew_key|{key_name}") + delete_button = types.InlineKeyboardButton(text="❌ Удалить", callback_data=f"delete_key|{key_name}") + back_button = types.InlineKeyboardButton(text="🔙 Назад в профиль", callback_data="view_profile") inline_keyboard = [ [download_iphone_button, download_android_button], @@ -237,9 +229,7 @@ async def process_callback_view_key(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_view.jpg" - ), + photo=BufferedInputFile(image_file.read(), filename="pic_view.jpg"), caption=response_message, reply_markup=keyboard, parse_mode="HTML", @@ -256,14 +246,18 @@ async def process_callback_view_key(callback_query: types.CallbackQuery): except Exception as e: await handle_error( - tg_id, callback_query, f"Ошибка при получении информации о ключе: {e}" + tg_id, + callback_query, + f"Ошибка при получении информации о ключе: {e}", ) await callback_query.answer() @router.callback_query(F.data.startswith("update_subscription|")) -async def process_callback_update_subscription(callback_query: types.CallbackQuery): +async def process_callback_update_subscription( + callback_query: types.CallbackQuery, +): tg_id = callback_query.from_user.id email = callback_query.data.split("|")[1] @@ -296,7 +290,8 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue ) except Exception as delete_error: await bot.send_message( - tg_id, f"Ошибка при удалении старой подписки: {delete_error}" + tg_id, + f"Ошибка при удалении старой подписки: {delete_error}", ) return @@ -305,7 +300,11 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue tasks = [] tasks.append( update_key_on_cluster( - tg_id, client_id, email, expiry_time, least_loaded_cluster_id + tg_id, + client_id, + email, + expiry_time, + least_loaded_cluster_id, ) ) @@ -322,39 +321,42 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue try: await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id + chat_id=tg_id, + message_id=callback_query.message.message_id, ) except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") response_message = f"Ваша подписка {email} обновлена!" - back_button = types.InlineKeyboardButton( - text="🔙 Назад в профиль", callback_data="view_profile" - ) + back_button = types.InlineKeyboardButton(text="🔙 Назад в профиль", callback_data="view_profile") keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await bot.send_message( - tg_id, response_message, reply_markup=keyboard, parse_mode="HTML" + tg_id, + response_message, + reply_markup=keyboard, + parse_mode="HTML", ) else: try: await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id + chat_id=tg_id, + message_id=callback_query.message.message_id, ) except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") await bot.send_message( - tg_id, "Ключ не найден в базе данных.", parse_mode="HTML" + tg_id, + "Ключ не найден в базе данных.", + parse_mode="HTML", ) finally: await conn.close() except Exception as e: - await handle_error( - tg_id, callback_query, f"Ошибка при обновлении подписки: {e}" - ) + await handle_error(tg_id, callback_query, f"Ошибка при обновлении подписки: {e}") await callback_query.answer() @@ -366,9 +368,7 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery): try: try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception: pass @@ -380,11 +380,7 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery): callback_data=f"confirm_delete|{client_id}", ) ], - [ - types.InlineKeyboardButton( - text="❌ Нет, отменить", callback_data="view_keys" - ) - ], + [types.InlineKeyboardButton(text="❌ Нет, отменить", callback_data="view_keys")], ] ) @@ -412,9 +408,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): try: try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception: pass @@ -459,11 +453,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): callback_data=f"renew_plan|12|{client_id}", ) ], - [ - types.InlineKeyboardButton( - text="🔙 Назад", callback_data="view_profile" - ) - ], + [types.InlineKeyboardButton(text="🔙 Назад", callback_data="view_profile")], ] ) @@ -471,9 +461,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): response_message = PLAN_SELECTION_MSG.format( balance=balance, - expiry_date=datetime.utcfromtimestamp(expiry_time / 1000).strftime( - "%Y-%m-%d %H:%M:%S" - ), + expiry_date=datetime.utcfromtimestamp(expiry_time / 1000).strftime("%Y-%m-%d %H:%M:%S"), ) await bot.send_message( @@ -485,9 +473,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery): else: # Если ключ не найден response_message = "Ключ не найден." - await bot.send_message( - chat_id=tg_id, text=response_message, parse_mode="HTML" - ) + await bot.send_message(chat_id=tg_id, text=response_message, parse_mode="HTML") finally: await conn.close() @@ -510,16 +496,12 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): try: conn = await asyncpg.connect(DATABASE_URL) try: - record = await conn.fetchrow( - "SELECT client_id FROM keys WHERE email = $1", email - ) + record = await conn.fetchrow("SELECT client_id FROM keys WHERE email = $1", email) if record: client_id = record["client_id"] response_message = "Ключ успешно удален." - back_button = types.InlineKeyboardButton( - text="Назад", callback_data="view_keys" - ) + back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_keys") keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await delete_key(client_id) @@ -534,9 +516,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): try: tasks = [] for cluster_id, cluster in CLUSTERS.items(): - tasks.append( - delete_key_from_cluster(cluster_id, email, client_id) - ) + tasks.append(delete_key_from_cluster(cluster_id, email, client_id)) await asyncio.gather(*tasks) @@ -549,9 +529,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery): else: response_message = "Ключ не найден или уже удален." - back_button = types.InlineKeyboardButton( - text="Назад", callback_data="view_keys" - ) + back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_keys") keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await bot.edit_message_text( @@ -588,16 +566,15 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): try: try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") conn = await asyncpg.connect(DATABASE_URL) try: record = await conn.fetchrow( - "SELECT email, expiry_time FROM keys WHERE client_id = $1", client_id + "SELECT email, expiry_time FROM keys WHERE client_id = $1", + client_id, ) if record: @@ -606,29 +583,17 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): current_time = datetime.utcnow().timestamp() * 1000 if expiry_time <= current_time: - new_expiry_time = int( - current_time - + timedelta(days=days_to_extend).total_seconds() * 1000 - ) + new_expiry_time = int(current_time + timedelta(days=days_to_extend).total_seconds() * 1000) else: - new_expiry_time = int( - expiry_time - + timedelta(days=days_to_extend).total_seconds() * 1000 - ) + new_expiry_time = int(expiry_time + timedelta(days=days_to_extend).total_seconds() * 1000) cost = RENEWAL_PLANS[plan]["price"] balance = await get_balance(tg_id) if balance < cost: - replenish_button = types.InlineKeyboardButton( - text="Пополнить баланс", callback_data="pay" - ) - view_profile = types.InlineKeyboardButton( - text="👤 Личный кабинет", callback_data="view_profile" - ) - keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[[replenish_button], [view_profile]] - ) + replenish_button = types.InlineKeyboardButton(text="Пополнить баланс", callback_data="pay") + view_profile = types.InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[replenish_button], [view_profile]]) await bot.send_message( tg_id, @@ -638,16 +603,15 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): ) return - response_message = SUCCESS_RENEWAL_MSG.format( - months=RENEWAL_PLANS[plan]["months"] - ) - back_button = types.InlineKeyboardButton( - text="Назад", callback_data="view_profile" - ) + response_message = SUCCESS_RENEWAL_MSG.format(months=RENEWAL_PLANS[plan]["months"]) + back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_profile") keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) await bot.send_message( - tg_id, response_message, reply_markup=keyboard, parse_mode="HTML" + tg_id, + response_message, + reply_markup=keyboard, + parse_mode="HTML", ) async def renew_key_on_servers(): @@ -655,7 +619,11 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): for cluster_id in CLUSTERS: task = asyncio.create_task( renew_key_in_cluster( - cluster_id, email, client_id, new_expiry_time, total_gb + cluster_id, + email, + client_id, + new_expiry_time, + total_gb, ) ) tasks.append(task) @@ -674,8 +642,6 @@ async def process_callback_renew_plan(callback_query: types.CallbackQuery): await conn.close() except Exception as e: - await bot.send_message( - tg_id, f"Ошибка при продлении ключа: {e}", parse_mode="HTML" - ) + await bot.send_message(tg_id, f"Ошибка при продлении ключа: {e}", parse_mode="HTML") await callback_query.answer() diff --git a/handlers/keys/subscriptions.py b/handlers/keys/subscriptions.py index ad04191a..70f2d6da 100644 --- a/handlers/keys/subscriptions.py +++ b/handlers/keys/subscriptions.py @@ -2,8 +2,8 @@ import base64 from datetime import datetime import aiohttp -import asyncpg from aiohttp import web +import asyncpg from config import CLUSTERS, DATABASE_URL, TRANSITION_DATE_STR from logger import logger @@ -19,9 +19,7 @@ async def fetch_url_content(url, tg_id): logger.info(f"Успешно получен контент с {url} для tg_id: {tg_id}") return base64.b64decode(content).decode("utf-8").split("\n") else: - logger.error( - f"Не удалось получить {url} для tg_id: {tg_id}, статус: {response.status}" - ) + logger.error(f"Не удалось получить {url} для tg_id: {tg_id}, статус: {response.status}") return [] except Exception as e: logger.error(f"Ошибка при получении {url} для tg_id: {tg_id}: {e}") @@ -30,9 +28,7 @@ async def fetch_url_content(url, tg_id): async def combine_unique_lines(urls, tg_id, query_string): all_lines = [] - logger.info( - f"Начинаем объединение подписок для tg_id: {tg_id}, запрос: {query_string}" - ) + logger.info(f"Начинаем объединение подписок для tg_id: {tg_id}, запрос: {query_string}") urls_with_query = [f"{url}?{query_string}" for url in urls] logger.info(f"Составлены URL-адреса: {urls_with_query}") @@ -42,9 +38,7 @@ async def combine_unique_lines(urls, tg_id, query_string): all_lines.extend(lines) all_lines = list(set(filter(None, all_lines))) - logger.info( - f"Объединено {len(all_lines)} строк после фильтрации и удаления дубликатов для tg_id: {tg_id}" - ) + logger.info(f"Объединено {len(all_lines)} строк после фильтрации и удаления дубликатов для tg_id: {tg_id}") return all_lines @@ -55,9 +49,7 @@ transition_timestamp_ms = int(transition_date.timestamp() * 1000) transition_timestamp_ms_adjusted = transition_timestamp_ms - (3 * 60 * 60 * 1000) -logger.info( - f"Время перехода (с поправкой на часовой пояс): {transition_timestamp_ms_adjusted}" -) +logger.info(f"Время перехода (с поправкой на часовой пояс): {transition_timestamp_ms_adjusted}") async def handle_old_subscription(request): @@ -74,9 +66,7 @@ async def handle_old_subscription(request): conn = await asyncpg.connect(DATABASE_URL) try: - key_info = await conn.fetchrow( - "SELECT created_at FROM keys WHERE email = $1", email - ) + key_info = await conn.fetchrow("SELECT created_at FROM keys WHERE email = $1", email) if not key_info: logger.warning(f"Клиент с email {email} не найден в базе.") @@ -89,13 +79,9 @@ async def handle_old_subscription(request): logger.info(f"Значение created_at для клиента с email {email}: {created_at_ms}") created_at_datetime = datetime.utcfromtimestamp(created_at_ms / 1000) - logger.info( - f"Время создания клиента в формате datetime (UTC): {created_at_datetime}" - ) + logger.info(f"Время создания клиента в формате datetime (UTC): {created_at_datetime}") - logger.info( - f"Время перехода (с поправкой на часовой пояс): {transition_timestamp_ms_adjusted}" - ) + logger.info(f"Время перехода (с поправкой на часовой пояс): {transition_timestamp_ms_adjusted}") if created_at_ms >= transition_timestamp_ms_adjusted: logger.info(f"Клиент с email {email} является новым.") @@ -112,9 +98,7 @@ async def handle_old_subscription(request): combined_subscriptions = await combine_unique_lines(urls, email, "") - base64_encoded = base64.b64encode( - "\n".join(combined_subscriptions).encode("utf-8") - ).decode("utf-8") + base64_encoded = base64.b64encode("\n".join(combined_subscriptions).encode("utf-8")).decode("utf-8") headers = { "Content-Type": "text/plain; charset=utf-8", @@ -145,9 +129,7 @@ async def handle_new_subscription(request): conn = await asyncpg.connect(DATABASE_URL) try: - client_data = await conn.fetchrow( - "SELECT tg_id FROM keys WHERE email = $1", email - ) + client_data = await conn.fetchrow("SELECT tg_id FROM keys WHERE email = $1", email) if not client_data: logger.warning(f"Клиент с email {email} не найден в базе.") @@ -178,9 +160,7 @@ async def handle_new_subscription(request): combined_subscriptions = await combine_unique_lines(urls, tg_id, query_string) - base64_encoded = base64.b64encode( - "\n".join(combined_subscriptions).encode("utf-8") - ).decode("utf-8") + base64_encoded = base64.b64encode("\n".join(combined_subscriptions).encode("utf-8")).decode("utf-8") headers = { "Content-Type": "text/plain; charset=utf-8", diff --git a/handlers/keys/trial_key.py b/handlers/keys/trial_key.py index 08aca2b2..85f02c33 100644 --- a/handlers/keys/trial_key.py +++ b/handlers/keys/trial_key.py @@ -1,6 +1,6 @@ import asyncio -import uuid from datetime import datetime, timedelta +import uuid import asyncpg from py3xui import AsyncApi @@ -23,9 +23,7 @@ async def create_trial_key(tg_id: int): result = {"key": public_link, "instructions": instructions} - asyncio.create_task( - generate_and_store_keys(tg_id, client_id, email, public_link) - ) + asyncio.create_task(generate_and_store_keys(tg_id, client_id, email, public_link)) return result @@ -33,9 +31,7 @@ async def create_trial_key(tg_id: int): await conn.close() -async def generate_and_store_keys( - tg_id: int, client_id: str, email: str, public_link: str -): +async def generate_and_store_keys(tg_id: int, client_id: str, email: str, public_link: str): conn = await asyncpg.connect(DATABASE_URL) try: current_time = datetime.utcnow() diff --git a/handlers/notifications.py b/handlers/notifications.py index 5c4788d6..7a45072f 100644 --- a/handlers/notifications.py +++ b/handlers/notifications.py @@ -1,8 +1,8 @@ import asyncio from datetime import datetime, timedelta -import asyncpg from aiogram import Bot, Router, types +import asyncpg from py3xui import AsyncApi from client import delete_client @@ -22,9 +22,7 @@ async def notify_expiring_keys(bot: Bot): logger.info("Подключение к базе данных успешно.") current_time = datetime.utcnow().timestamp() * 1000 - threshold_time_10h = ( - datetime.utcnow() + timedelta(hours=10) - ).timestamp() * 1000 + threshold_time_10h = (datetime.utcnow() + timedelta(hours=10)).timestamp() * 1000 threshold_time_24h = (datetime.utcnow() + timedelta(days=1)).timestamp() * 1000 logger.info("Начало обработки уведомлений.") @@ -48,19 +46,18 @@ 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( - f"Статус бота для пользователя {chat_id}: {'заблокирован' if blocked else 'активен'}" - ) + logger.info(f"Статус бота для пользователя {chat_id}: {'заблокирован' if blocked else 'активен'}") return blocked except Exception as e: - logger.warning( - f"Не удалось проверить статус бота для пользователя {chat_id}: {e}" - ) + logger.warning(f"Не удалось проверить статус бота для пользователя {chat_id}: {e}") return False async def notify_10h_keys( - bot: Bot, conn: asyncpg.Connection, current_time: float, threshold_time_10h: float + bot: Bot, + conn: asyncpg.Connection, + current_time: float, + threshold_time_10h: float, ): records = await conn.fetch( """ @@ -114,7 +111,8 @@ async def notify_10h_keys( ], [ types.InlineKeyboardButton( - text="👤 Личный кабинет", callback_data="view_profile" + text="👤 Личный кабинет", + callback_data="view_profile", ) ], ] @@ -122,9 +120,7 @@ async def notify_10h_keys( await bot.send_message(tg_id, message, reply_markup=keyboard) logger.info(f"Уведомление отправлено пользователю {tg_id}.") except Exception as e: - logger.error( - f"Ошибка при отправке уведомления пользователю {tg_id}: {e}" - ) + logger.error(f"Ошибка при отправке уведомления пользователю {tg_id}: {e}") continue await conn.execute( @@ -137,7 +133,10 @@ async def notify_10h_keys( async def notify_24h_keys( - bot: Bot, conn: asyncpg.Connection, current_time: float, threshold_time_24h: float + bot: Bot, + conn: asyncpg.Connection, + current_time: float, + threshold_time_24h: float, ): logger.info("Проверка истекших ключей...") @@ -192,7 +191,8 @@ async def notify_24h_keys( ], [ types.InlineKeyboardButton( - text="👤 Личный кабинет", callback_data="view_profile" + text="👤 Личный кабинет", + callback_data="view_profile", ) ], ] @@ -200,18 +200,14 @@ async def notify_24h_keys( await bot.send_message(tg_id, message_24h, reply_markup=keyboard) logger.info(f"Уведомление за 24 часа отправлено пользователю {tg_id}.") except Exception as e: - logger.error( - f"Ошибка при отправке уведомления за 24 часа пользователю {tg_id}: {e}" - ) + logger.error(f"Ошибка при отправке уведомления за 24 часа пользователю {tg_id}: {e}") continue await conn.execute( "UPDATE keys SET notified_24h = TRUE WHERE client_id = $1", record["client_id"], ) - logger.info( - f"Обновлено поле notified_24h для клиента {record['client_id']}." - ) + logger.info(f"Обновлено поле notified_24h для клиента {record['client_id']}.") await asyncio.sleep(1) @@ -249,30 +245,18 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: "💡 Не откладывайте подключение VPN!" ) keyboard = types.InlineKeyboardMarkup( - inline_keyboard=[ - [ - types.InlineKeyboardButton( - text="👤 Личный кабинет", callback_data="view_profile" - ) - ] - ] + inline_keyboard=[[types.InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile")]] ) try: if balance >= RENEWAL_PLANS["1"]["price"]: await update_balance(tg_id, -RENEWAL_PLANS["1"]["price"]) - new_expiry_time = int( - (datetime.utcnow() + timedelta(days=30)).timestamp() * 1000 - ) + new_expiry_time = int((datetime.utcnow() + timedelta(days=30)).timestamp() * 1000) await update_key_expiry(client_id, new_expiry_time) for cluster_id in CLUSTERS: - await renew_key_in_cluster( - cluster_id, email, client_id, new_expiry_time, TOTAL_GB - ) - logger.info( - f"Ключ для пользователя {tg_id} успешно продлен в кластере {cluster_id}." - ) + await renew_key_in_cluster(cluster_id, email, client_id, new_expiry_time, TOTAL_GB) + logger.info(f"Ключ для пользователя {tg_id} успешно продлен в кластере {cluster_id}.") await conn.execute( """ @@ -282,25 +266,15 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: """, client_id, ) - logger.info( - f"Флаги notified и notified_24 сброшены для клиента с ID {client_id}." - ) + logger.info(f"Флаги notified и notified_24 сброшены для клиента с ID {client_id}.") try: - await bot.send_message( - tg_id, text=KEY_RENEWED, reply_markup=keyboard - ) - logger.info( - f"Уведомление об успешном продлении отправлено клиенту {tg_id}." - ) + await bot.send_message(tg_id, text=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: - await safe_send_message( - bot, tg_id, message_expired, reply_markup=keyboard - ) + await safe_send_message(bot, tg_id, message_expired, reply_markup=keyboard) await delete_key(client_id) for cluster_id, cluster in CLUSTERS.items(): diff --git a/handlers/pay.py b/handlers/pay.py index 91757911..2dc7d5d7 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -49,15 +49,9 @@ async def handle_pay(callback_query: CallbackQuery): callback_data="pay_robokassa", ) ) - builder.row( - InlineKeyboardButton( - text="🎟️ Активировать купон", callback_data="activate_coupon" - ) - ) + builder.row(InlineKeyboardButton(text="🎟️ Активировать купон", callback_data="activate_coupon")) - builder.row( - InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) await callback_query.message.answer( "💸 Выберите удобный способ пополнения баланса:\n\n" diff --git a/handlers/payments/cryprobot_pay.py b/handlers/payments/cryprobot_pay.py index e937f3a2..6ee1416b 100644 --- a/handlers/payments/cryprobot_pay.py +++ b/handlers/payments/cryprobot_pay.py @@ -24,22 +24,16 @@ class ReplenishBalanceState(StatesGroup): entering_custom_amount_crypto = State() -async def send_message_with_deletion( - chat_id, text, reply_markup=None, state=None, message_key="last_message_id" -): +async def send_message_with_deletion(chat_id, text, reply_markup=None, state=None, message_key="last_message_id"): if state: try: state_data = await state.get_data() previous_message_id = state_data.get(message_key) if previous_message_id: - await bot.delete_message( - chat_id=chat_id, message_id=previous_message_id - ) + await bot.delete_message(chat_id=chat_id, message_id=previous_message_id) - sent_message = await bot.send_message( - chat_id=chat_id, text=text, reply_markup=reply_markup - ) + sent_message = await bot.send_message(chat_id=chat_id, text=text, reply_markup=reply_markup) await state.update_data({message_key: sent_message.message_id}) except Exception as e: @@ -50,9 +44,7 @@ async def send_message_with_deletion( @router.callback_query(F.data == "pay_cryptobot") -async def process_callback_pay_cryptobot( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_callback_pay_cryptobot(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id builder = InlineKeyboardBuilder() @@ -78,7 +70,8 @@ async def process_callback_pay_cryptobot( ) builder.row( InlineKeyboardButton( - text="💰 Ввести свою сумму", callback_data="enter_custom_amount_crypto" + text="💰 Ввести свою сумму", + callback_data="enter_custom_amount_crypto", ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_profile")) @@ -91,9 +84,7 @@ async def process_callback_pay_cryptobot( await add_connection(tg_id, balance=0.0, trial=0) try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception as e: logger.error(f"Не удалось удалить сообщение: {e}") @@ -108,9 +99,7 @@ async def process_callback_pay_cryptobot( @router.callback_query(F.data.startswith("crypto_amount|")) -async def process_amount_selection( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext): data = callback_query.data.split("|", 1) if len(data) != 2: @@ -152,9 +141,7 @@ async def process_amount_selection( if hasattr(invoice, "bot_invoice_url"): builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="Пополнить", url=invoice.bot_invoice_url) - ) + builder.row(InlineKeyboardButton(text="Пополнить", url=invoice.bot_invoice_url)) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="pay")) await bot.send_message( chat_id=callback_query.from_user.id, @@ -173,9 +160,7 @@ async def process_amount_selection( async def send_payment_success_notification(user_id: int, amount: float): try: builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile")) await bot.send_message( chat_id=user_id, text=f"Ваш баланс успешно пополнен на {amount} рублей. Спасибо за оплату!", @@ -193,9 +178,7 @@ async def cryptobot_webhook(request): await process_crypto_payment(data["payload"]) return web.Response(status=200) else: - logger.warning( - f"Неподдерживаемый тип обновления: {data.get('update_type')}" - ) + logger.warning(f"Неподдерживаемый тип обновления: {data.get('update_type')}") return web.Response(status=400) except Exception as e: logger.error(f"Ошибка обработки вебхука: {e}") @@ -220,9 +203,7 @@ async def process_crypto_payment(payload): @router.callback_query(F.data == "enter_custom_amount_crypto") -async def process_enter_custom_amount( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_enter_custom_amount(callback_query: types.CallbackQuery, state: FSMContext): await callback_query.message.edit_text(text="Введите сумму пополнения:") await state.set_state(ReplenishBalanceState.entering_custom_amount_crypto) await callback_query.answer() @@ -233,15 +214,11 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) if message.text.isdigit(): amount = int(message.text) if amount // RUB_TO_USDT <= 0: - await message.answer( - f"Сумма должна быть больше {RUB_TO_USDT}. Пожалуйста, введите сумму еще раз:" - ) + await message.answer(f"Сумма должна быть больше {RUB_TO_USDT}. Пожалуйста, введите сумму еще раз:") return await state.update_data(amount=amount) - await state.set_state( - ReplenishBalanceState.waiting_for_payment_confirmation_crypto - ) + await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation_crypto) try: invoice = await crypto.create_invoice( asset="USDT", @@ -252,9 +229,7 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) if hasattr(invoice, "bot_invoice_url"): builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="Пополнить", url=invoice.bot_invoice_url) - ) + builder.row(InlineKeyboardButton(text="Пополнить", url=invoice.bot_invoice_url)) builder.row( InlineKeyboardButton(text="⬅️ Назад", callback_data="pay"), ) @@ -264,7 +239,9 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) ) else: await send_message_with_deletion( - message.from_user.id, "Ошибка при создании платежа.", state=state + message.from_user.id, + "Ошибка при создании платежа.", + state=state, ) except Exception as e: logger.error(f"Ошибка при создании платежа: {e}") diff --git a/handlers/payments/freekassa_pay.py b/handlers/payments/freekassa_pay.py index 52514ecf..3b356b9f 100644 --- a/handlers/payments/freekassa_pay.py +++ b/handlers/payments/freekassa_pay.py @@ -4,13 +4,13 @@ import logging import time import uuid -import requests from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup from aiogram.types import InlineKeyboardButton, InlineKeyboardMarkup from aiogram.utils.keyboard import InlineKeyboardBuilder from aiohttp import web +import requests from bot import bot from config import FREEKASSA_API_KEY, FREEKASSA_SHOP_ID @@ -49,9 +49,7 @@ async def create_payment(user_id, amount, email, ip): params["signature"] = generate_signature(params, FREEKASSA_API_KEY) try: - response = requests.post( - "https://api.freekassa.com/v1/orders/create", json=params - ) + response = requests.post("https://api.freekassa.com/v1/orders/create", json=params) response_data = response.json() logging.debug(f"Ответ от FreeKassa при создании платежа: {response_data}") @@ -95,9 +93,7 @@ async def freekassa_webhook(request): @router.callback_query(lambda c: c.data == "pay_freekassa") -async def process_callback_pay_freekassa( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_callback_pay_freekassa(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id builder = InlineKeyboardBuilder() @@ -122,14 +118,13 @@ async def process_callback_pay_freekassa( ) builder.row( InlineKeyboardButton( - text="💰 Ввести свою сумму", callback_data="enter_custom_amount_freekassa" + text="💰 Ввести свою сумму", + callback_data="enter_custom_amount_freekassa", ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_profile")) - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) await bot.send_message( chat_id=tg_id, @@ -142,9 +137,7 @@ async def process_callback_pay_freekassa( @router.callback_query(F.data.startswith("freekassa_amount|")) -async def process_amount_selection( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext): data = callback_query.data.split("|", 1) amount_str = data[1] try: @@ -155,18 +148,12 @@ async def process_amount_selection( user_email = f"{callback_query.from_user.id}@solo.net" user_ip = callback_query.message.chat.id - payment_url = await create_payment( - callback_query.from_user.id, amount, user_email, user_ip - ) + payment_url = await create_payment(callback_query.from_user.id, amount, user_email, user_ip) if payment_url: confirm_keyboard = InlineKeyboardMarkup( inline_keyboard=[ - [ - InlineKeyboardButton( - text=f"Оплатить {amount} рублей", url=payment_url - ) - ], + [InlineKeyboardButton(text=f"Оплатить {amount} рублей", url=payment_url)], [InlineKeyboardButton(text="⬅️ Назад", callback_data="pay")], ] ) @@ -186,9 +173,7 @@ async def process_amount_selection( @router.callback_query(F.data == "enter_custom_amount_freekassa") -async def process_enter_custom_amount( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_enter_custom_amount(callback_query: types.CallbackQuery, state: FSMContext): await callback_query.message.edit_text(text="Введите сумму пополнения:") await state.set_state(ReplenishBalanceState.entering_custom_amount_freekassa) await callback_query.answer() @@ -199,21 +184,15 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) if message.text.isdigit(): amount = int(message.text) if amount <= 0: - await message.answer( - "Сумма должна быть больше нуля. Пожалуйста, введите сумму еще раз:" - ) + await message.answer("Сумма должна быть больше нуля. Пожалуйста, введите сумму еще раз:") return user_email = f"{message.from_user.id}@solo.net" user_ip = message.chat.id - payment_url = await create_payment( - message.from_user.id, amount, user_email, user_ip - ) + payment_url = await create_payment(message.from_user.id, amount, user_email, user_ip) if payment_url: - keyboard = InlineKeyboardMarkup( - inline_keyboard=[[InlineKeyboardButton("Оплатить", url=payment_url)]] - ) + keyboard = InlineKeyboardMarkup(inline_keyboard=[[InlineKeyboardButton("Оплатить", url=payment_url)]]) await message.answer( f"Вы выбрали оплату на {amount} рублей. Перейдите по ссылке для завершения оплаты:", reply_markup=keyboard, diff --git a/handlers/payments/robokassa_pay.py b/handlers/payments/robokassa_pay.py index 973a6fce..23725876 100644 --- a/handlers/payments/robokassa_pay.py +++ b/handlers/payments/robokassa_pay.py @@ -36,35 +36,28 @@ if ROBOKASSA_ENABLE: def generate_payment_link(amount, inv_id, description, tg_id): """Генерация ссылки на оплату.""" - logger.debug( - f"Generating payment link for amount: {amount}, inv_id: {inv_id}, description: {description}" - ) + logger.debug(f"Generating payment link for amount: {amount}, inv_id: {inv_id}, description: {description}") payment_link = robokassa._payment.link.generate_by_script( - out_sum=amount, inv_id=inv_id, description="пополнение баланса", id=f"{tg_id}" + out_sum=amount, + inv_id=inv_id, + description="пополнение баланса", + id=f"{tg_id}", ) logger.info(f"Generated payment link: {payment_link}") return payment_link -async def send_message_with_deletion( - chat_id, text, reply_markup=None, state=None, message_key="last_message_id" -): +async def send_message_with_deletion(chat_id, text, reply_markup=None, state=None, message_key="last_message_id"): if state: try: state_data = await state.get_data() previous_message_id = state_data.get(message_key) if previous_message_id: - logger.debug( - f"Deleting previous message with ID: {previous_message_id}" - ) - await bot.delete_message( - chat_id=chat_id, message_id=previous_message_id - ) + logger.debug(f"Deleting previous message with ID: {previous_message_id}") + await bot.delete_message(chat_id=chat_id, message_id=previous_message_id) - sent_message = await bot.send_message( - chat_id=chat_id, text=text, reply_markup=reply_markup - ) + sent_message = await bot.send_message(chat_id=chat_id, text=text, reply_markup=reply_markup) await state.update_data({message_key: sent_message.message_id}) logger.debug(f"Sent new message with ID: {sent_message.message_id}") @@ -76,9 +69,7 @@ async def send_message_with_deletion( @router.callback_query(F.data == "pay_robokassa") -async def process_callback_pay_robokassa( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_callback_pay_robokassa(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id logger.info(f"User {tg_id} initiated Robokassa payment.") @@ -104,7 +95,8 @@ async def process_callback_pay_robokassa( ) builder.row( InlineKeyboardButton( - text="💰 Ввести свою сумму", callback_data="enter_custom_amount_robokassa" + text="💰 Ввести свою сумму", + callback_data="enter_custom_amount_robokassa", ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_profile")) @@ -118,9 +110,7 @@ async def process_callback_pay_robokassa( logger.info(f"Created new connection for user {tg_id} with balance 0.0.") try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) logger.debug(f"Deleted message with ID: {callback_query.message.message_id}") except Exception as e: logger.error(f"Не удалось удалить сообщение: {e}") @@ -136,9 +126,7 @@ async def process_callback_pay_robokassa( @router.callback_query(F.data.startswith("robokassa_amount|")) -async def process_amount_selection( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext): logger.info(f"Получены данные callback_data: {callback_query.data}") data = callback_query.data.split("|") @@ -203,9 +191,7 @@ async def robokassa_webhook(request): shp_id = params.get("shp_id") signature_value = params.get("SignatureValue") - logger.info( - f"OutSum: {amount}, InvId: {inv_id}, shp_id: {shp_id}, SignatureValue: {signature_value}" - ) + logger.info(f"OutSum: {amount}, InvId: {inv_id}, shp_id: {shp_id}, SignatureValue: {signature_value}") if not check_payment_signature(params): logger.error("Неверная подпись или данные запроса.") @@ -244,9 +230,7 @@ def check_payment_signature(params): logger.info(f"Signature string before hashing: {signature_string}") - expected_signature = ( - hashlib.md5(signature_string.encode("utf-8")).hexdigest().upper() - ) + expected_signature = hashlib.md5(signature_string.encode("utf-8")).hexdigest().upper() logger.info(f"Expected signature: {expected_signature}") logger.info(f"Received signature: {signature_value}") @@ -256,9 +240,7 @@ def check_payment_signature(params): async def send_payment_success_notification(user_id: int, amount: float): builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile")) await bot.send_message( chat_id=user_id, @@ -269,18 +251,12 @@ async def send_payment_success_notification(user_id: int, amount: float): @router.callback_query(F.data == "enter_custom_amount_robokassa") -async def process_custom_amount_selection( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_custom_amount_selection(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id logger.info(f"User {tg_id} chose to enter a custom amount.") - await callback_query.message.edit_text( - text="Пожалуйста, введите сумму пополнения в рублях (например, 150):" - ) - await state.set_state( - ReplenishBalanceState.waiting_for_payment_confirmation_robokassa - ) + await callback_query.message.edit_text(text="Пожалуйста, введите сумму пополнения в рублях (например, 150):") + await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation_robokassa) await callback_query.answer() @@ -315,6 +291,4 @@ async def handle_custom_amount_input(message: types.Message, state: FSMContext): await state.clear() except ValueError as e: logger.error(f"Некорректная сумма от пользователя {tg_id}: {e}") - await message.answer( - text="Введите корректную сумму в рублях (целое положительное число)." - ) + await message.answer(text="Введите корректную сумму в рублях (целое положительное число).") diff --git a/handlers/payments/stars_pay.py b/handlers/payments/stars_pay.py index 27be8a16..1131079c 100644 --- a/handlers/payments/stars_pay.py +++ b/handlers/payments/stars_pay.py @@ -19,22 +19,16 @@ class ReplenishBalanceState(StatesGroup): entering_custom_amount_stars = State() -async def send_message_with_deletion( - chat_id, text, reply_markup=None, state=None, message_key="last_message_id" -): +async def send_message_with_deletion(chat_id, text, reply_markup=None, state=None, message_key="last_message_id"): if state: try: state_data = await state.get_data() previous_message_id = state_data.get(message_key) if previous_message_id: - await bot.delete_message( - chat_id=chat_id, message_id=previous_message_id - ) + await bot.delete_message(chat_id=chat_id, message_id=previous_message_id) - sent_message = await bot.send_message( - chat_id=chat_id, text=text, reply_markup=reply_markup - ) + sent_message = await bot.send_message(chat_id=chat_id, text=text, reply_markup=reply_markup) await state.update_data({message_key: sent_message.message_id}) except Exception as e: @@ -45,17 +39,11 @@ async def send_message_with_deletion( @router.callback_query(F.data == "pay_stars") -async def process_callback_pay_stars( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_callback_pay_stars(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton( - text="🤖 Бот для покупки звезд", url="https://t.me/PremiumBot" - ) - ) + builder.row(InlineKeyboardButton(text="🤖 Бот для покупки звезд", url="https://t.me/PremiumBot")) for i in range(0, len(PAYMENT_OPTIONS), 2): if i + 1 < len(PAYMENT_OPTIONS): @@ -78,7 +66,8 @@ async def process_callback_pay_stars( ) builder.row( InlineKeyboardButton( - text="💰 Ввести свою сумму", callback_data="enter_custom_amount_stars" + text="💰 Ввести свою сумму", + callback_data="enter_custom_amount_stars", ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="pay")) @@ -106,9 +95,7 @@ async def process_callback_pay_stars( @router.callback_query(F.data.startswith("stars_amount|")) -async def process_amount_selection( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext): data = callback_query.data.split("|", 1) if len(data) != 2: @@ -165,9 +152,7 @@ async def process_amount_selection( async def send_payment_success_notification(user_id: int, amount: float): try: builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile")) await bot.send_message( chat_id=user_id, text=f"Ваш баланс успешно пополнен на {amount} рублей. Спасибо за оплату!", @@ -178,9 +163,7 @@ async def send_payment_success_notification(user_id: int, amount: float): @router.callback_query(F.data == "enter_custom_amount_stars") -async def process_enter_custom_amount( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_enter_custom_amount(callback_query: types.CallbackQuery, state: FSMContext): await callback_query.message.edit_text(text="Введите сумму пополнения:") await state.set_state(ReplenishBalanceState.entering_custom_amount_stars) await callback_query.answer() @@ -191,15 +174,11 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) if message.text.isdigit(): amount = int(message.text) if amount // RUB_TO_XTR <= 0: - await message.answer( - f"Сумма должна быть больше {RUB_TO_XTR}. Пожалуйста, введите сумму еще раз:" - ) + await message.answer(f"Сумма должна быть больше {RUB_TO_XTR}. Пожалуйста, введите сумму еще раз:") return await state.update_data(amount=amount) - await state.set_state( - ReplenishBalanceState.waiting_for_payment_confirmation_stars - ) + await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation_stars) try: builder = InlineKeyboardBuilder() builder.row( diff --git a/handlers/payments/yookassa_pay.py b/handlers/payments/yookassa_pay.py index 440b7eb3..8a89c532 100644 --- a/handlers/payments/yookassa_pay.py +++ b/handlers/payments/yookassa_pay.py @@ -29,22 +29,16 @@ class ReplenishBalanceState(StatesGroup): entering_custom_amount_yookassa = State() -async def send_message_with_deletion( - chat_id, text, reply_markup=None, state=None, message_key="last_message_id" -): +async def send_message_with_deletion(chat_id, text, reply_markup=None, state=None, message_key="last_message_id"): if state: try: state_data = await state.get_data() previous_message_id = state_data.get(message_key) if previous_message_id: - await bot.delete_message( - chat_id=chat_id, message_id=previous_message_id - ) + await bot.delete_message(chat_id=chat_id, message_id=previous_message_id) - sent_message = await bot.send_message( - chat_id=chat_id, text=text, reply_markup=reply_markup - ) + sent_message = await bot.send_message(chat_id=chat_id, text=text, reply_markup=reply_markup) await state.update_data({message_key: sent_message.message_id}) except Exception as e: @@ -55,9 +49,7 @@ async def send_message_with_deletion( @router.callback_query(F.data == "pay_yookassa") -async def process_callback_pay_yookassa( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_callback_pay_yookassa(callback_query: types.CallbackQuery, state: FSMContext): tg_id = callback_query.from_user.id builder = InlineKeyboardBuilder() @@ -83,7 +75,8 @@ async def process_callback_pay_yookassa( ) builder.row( InlineKeyboardButton( - text="💰 Ввести свою сумму", callback_data="enter_custom_amount_yookassa" + text="💰 Ввести свою сумму", + callback_data="enter_custom_amount_yookassa", ) ) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="view_profile")) @@ -96,9 +89,7 @@ async def process_callback_pay_yookassa( await add_connection(tg_id, balance=0.0, trial=0) try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception as e: logger.error(f"Не удалось удалить сообщение: {e}") @@ -113,9 +104,7 @@ async def process_callback_pay_yookassa( @router.callback_query(F.data.startswith("yookassa_amount|")) -async def process_amount_selection( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_amount_selection(callback_query: types.CallbackQuery, state: FSMContext): data = callback_query.data.split("|", 1) if len(data) != 2: @@ -140,9 +129,7 @@ async def process_amount_selection( return await state.update_data(amount=amount) - await state.set_state( - ReplenishBalanceState.waiting_for_payment_confirmation_yookassa - ) + await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation_yookassa) # state_data = await state.get_data() customer_name = callback_query.from_user.full_name @@ -153,7 +140,10 @@ async def process_amount_selection( payment = Payment.create( { "amount": {"value": str(amount), "currency": "RUB"}, - "confirmation": {"type": "redirect", "return_url": "https://pocomacho.ru/"}, + "confirmation": { + "type": "redirect", + "return_url": "https://pocomacho.ru/", + }, "capture": True, "description": "Пополнение баланса", "receipt": { @@ -192,7 +182,9 @@ async def process_amount_selection( ) else: await send_message_with_deletion( - callback_query.from_user.id, "Ошибка при создании платежа.", state=state + callback_query.from_user.id, + "Ошибка при создании платежа.", + state=state, ) await callback_query.answer() @@ -201,9 +193,7 @@ async def process_amount_selection( async def send_payment_success_notification(user_id: int, amount: float): try: builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="Перейти в профиль", callback_data="view_profile")) await bot.send_message( chat_id=user_id, @@ -234,9 +224,7 @@ async def yookassa_webhook(request): @router.callback_query(F.data == "enter_custom_amount_yookassa") -async def process_enter_custom_amount( - callback_query: types.CallbackQuery, state: FSMContext -): +async def process_enter_custom_amount(callback_query: types.CallbackQuery, state: FSMContext): await callback_query.message.edit_text(text="Введите сумму пополнения:") await state.set_state(ReplenishBalanceState.entering_custom_amount_yookassa) await callback_query.answer() @@ -247,15 +235,11 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) if message.text.isdigit(): amount = int(message.text) if amount <= 0: - await message.answer( - "Сумма должна быть больше нуля. Пожалуйста, введите сумму еще раз:" - ) + await message.answer("Сумма должна быть больше нуля. Пожалуйста, введите сумму еще раз:") return await state.update_data(amount=amount) - await state.set_state( - ReplenishBalanceState.waiting_for_payment_confirmation_yookassa - ) + await state.set_state(ReplenishBalanceState.waiting_for_payment_confirmation_yookassa) try: payment = Payment.create( @@ -277,7 +261,10 @@ async def process_custom_amount_input(message: types.Message, state: FSMContext) { "description": "Пополнение баланса", "quantity": "1.00", - "amount": {"value": str(amount), "currency": "RUB"}, + "amount": { + "value": str(amount), + "currency": "RUB", + }, "vat_code": 6, } ], diff --git a/handlers/profile.py b/handlers/profile.py index c152ce58..2b8ffcf9 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -14,9 +14,7 @@ from logger import logger router = Router() -async def process_callback_view_profile( - callback_query: types.CallbackQuery, state: FSMContext, admin: bool -): +async def process_callback_view_profile(callback_query: types.CallbackQuery, state: FSMContext, admin: bool): chat_id = callback_query.from_user.id username = callback_query.from_user.full_name @@ -35,9 +33,7 @@ async def process_callback_view_profile( builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="📢 Наш канал", url=CHANNEL_URL)) - builder.row( - InlineKeyboardButton(text="💡 Тарифы", callback_data="view_tariffs") - ) + builder.row(InlineKeyboardButton(text="💡 Тарифы", callback_data="view_tariffs")) builder.row( InlineKeyboardButton(text="➕ Устройство", callback_data="create_key"), InlineKeyboardButton(text="📱 Мои устройства", callback_data="view_keys"), @@ -52,16 +48,10 @@ async def process_callback_view_profile( InlineKeyboardButton(text="👥 Пригласить друзей", callback_data="invite"), InlineKeyboardButton(text="📘 Инструкции", callback_data="instructions"), ) - builder.row( - InlineKeyboardButton(text="💰 Поддержать проект", callback_data="donate") - ) + builder.row(InlineKeyboardButton(text="💰 Поддержать проект", callback_data="donate")) if admin: - builder.row( - InlineKeyboardButton(text="🔧 Администратор", callback_data="admin") - ) - builder.row( - InlineKeyboardButton(text="⬅️ Главное меню", callback_data="back_to_menu") - ) + builder.row(InlineKeyboardButton(text="🔧 Администратор", callback_data="admin")) + builder.row(InlineKeyboardButton(text="⬅️ Главное меню", callback_data="back_to_menu")) try: await callback_query.message.delete() @@ -87,7 +77,8 @@ async def process_callback_view_profile( except Exception as e: await bot.send_message( - chat_id, f"❗️ Не удалось загрузить профиль. Техническая ошибка: {e}" + chat_id, + f"❗️ Не удалось загрузить профиль. Техническая ошибка: {e}", ) @@ -98,9 +89,7 @@ async def view_tariffs_handler(callback_query: types.CallbackQuery): except Exception as e: logger.error(f"Ошибка при удалении сообщения: {e}") builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) await callback_query.message.answer( "🚀 Доступные тарифы VPN:\n\n" @@ -130,9 +119,7 @@ async def invite_handler(callback_query: types.CallbackQuery): image_path = os.path.join("img", "pic_invite.jpg") builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="⬅️ Вернуться в профиль", callback_data="view_profile")) try: await callback_query.message.delete() @@ -144,9 +131,7 @@ 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(), @@ -170,8 +155,6 @@ async def invite_handler(callback_query: types.CallbackQuery): @router.callback_query(F.data == "view_profile") -async def view_profile_handler( - callback_query: types.CallbackQuery, state: FSMContext, admin: bool = False -): +async def view_profile_handler(callback_query: types.CallbackQuery, state: FSMContext, admin: bool = False): await state.clear() await process_callback_view_profile(callback_query, state, admin) diff --git a/handlers/start.py b/handlers/start.py index 7b296e8f..471d47e2 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,12 +1,20 @@ import os -import asyncpg from aiogram import F, Router from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder +import asyncpg from bot import bot -from config import CHANNEL_URL, CONNECT_ANDROID, CONNECT_IOS, DATABASE_URL, DOWNLOAD_ANDROID, DOWNLOAD_IOS, SUPPORT_CHAT_URL +from config import ( + CHANNEL_URL, + CONNECT_ANDROID, + CONNECT_IOS, + DATABASE_URL, + DOWNLOAD_ANDROID, + DOWNLOAD_IOS, + SUPPORT_CHAT_URL, +) from database import add_connection, add_referral, check_connection_exists, get_trial from handlers.keys.trial_key import create_trial_key from handlers.texts import INSTRUCTIONS_TRIAL, WELCOME_TEXT, get_about_vpn @@ -20,16 +28,10 @@ async def send_welcome_message(chat_id: int, trial_status: int, admin: bool): builder = InlineKeyboardBuilder() if trial_status == 0: - builder.row( - InlineKeyboardButton(text="🔗 Подключить VPN", callback_data="connect_vpn") - ) - builder.row( - InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="🔗 Подключить VPN", callback_data="connect_vpn")) + builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile")) if admin: - builder.row( - InlineKeyboardButton(text="🔧 Администратор", callback_data="admin") - ) + builder.row(InlineKeyboardButton(text="🔧 Администратор", callback_data="admin")) builder.row( InlineKeyboardButton(text="📞 Техническая поддержка", url=SUPPORT_CHAT_URL), ) @@ -58,9 +60,7 @@ async def send_welcome_message(chat_id: int, trial_status: int, admin: bool): async def start_command(message: Message, admin: bool = False): try: - logger.info( - f"Получена команда /start. Текст сообщения: {message.text}, user_id: {message.from_user.id}" - ) + logger.info(f"Получена команда /start. Текст сообщения: {message.text}, user_id: {message.from_user.id}") if "referral_" in message.text: logger.info("Обнаружен реферальный код.") @@ -72,38 +72,24 @@ async def start_command(message: Message, admin: bool = False): return connection_exists = await check_connection_exists(message.from_user.id) - logger.info( - f"Результат проверки подключения для user_id {message.from_user.id}: {connection_exists}" - ) + logger.info(f"Результат проверки подключения для user_id {message.from_user.id}: {connection_exists}") if not connection_exists: - logger.info( - f"Добавляем подключение для пользователя: {message.from_user.id}" - ) + logger.info(f"Добавляем подключение для пользователя: {message.from_user.id}") await add_connection(message.from_user.id) - logger.info( - f"Добавляем реферал для пользователя {message.from_user.id}, приглашённым {referrer_tg_id}" - ) + logger.info(f"Добавляем реферал для пользователя {message.from_user.id}, приглашённым {referrer_tg_id}") await add_referral(message.from_user.id, referrer_tg_id) else: logger.info(f"Пользователь {message.from_user.id} уже зарегистрирован.") - logger.info( - f"Проверяем статус пробного периода для user_id {message.from_user.id}" - ) + logger.info(f"Проверяем статус пробного периода для user_id {message.from_user.id}") trial_status = await get_trial(message.from_user.id) - logger.info( - f"Статус пробного периода для user_id {message.from_user.id}: {trial_status}" - ) + logger.info(f"Статус пробного периода для user_id {message.from_user.id}: {trial_status}") - logger.info( - f"Отправка приветственного сообщения для user_id {message.from_user.id}" - ) + logger.info(f"Отправка приветственного сообщения для user_id {message.from_user.id}") await send_welcome_message(message.chat.id, trial_status, admin) except Exception as e: - logger.error( - f"Ошибка в обработке команды /start для user_id {message.from_user.id}: {e}" - ) + logger.error(f"Ошибка в обработке команды /start для user_id {message.from_user.id}: {e}") await message.answer("Произошла ошибка. Пожалуйста, попробуйте позже.") @@ -133,9 +119,7 @@ async def handle_connect_vpn(callback_query: CallbackQuery): except Exception as e: logger.error(f"Ошибка при обновлении trial: {e}") - await callback_query.message.answer( - "Произошла ошибка при обновлении статуса." - ) + await callback_query.message.answer("Произошла ошибка при обновлении статуса.") key_message = ( f"🔑 Ваш персональный ключ доступа:\n" @@ -144,16 +128,15 @@ async def handle_connect_vpn(callback_query: CallbackQuery): ) builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile") - ) + builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="view_profile")) builder.row( InlineKeyboardButton(text="🍏 Скачать для iOS", url=DOWNLOAD_IOS), InlineKeyboardButton(text="🤖 Скачать для Android", url=DOWNLOAD_ANDROID), ) builder.row( InlineKeyboardButton( - text="🍏 Подключить на iOS", url=f'{CONNECT_IOS}{trial_key_info["key"]}' + text="🍏 Подключить на iOS", + url=f'{CONNECT_IOS}{trial_key_info["key"]}', ), InlineKeyboardButton( text="🤖 Подключить на Android", @@ -161,9 +144,7 @@ async def handle_connect_vpn(callback_query: CallbackQuery): ), ) - await callback_query.message.answer( - key_message, parse_mode="HTML", reply_markup=builder.as_markup() - ) + await callback_query.message.answer(key_message, parse_mode="HTML", reply_markup=builder.as_markup()) await callback_query.answer() @@ -175,14 +156,10 @@ async def handle_about_vpn(callback_query: CallbackQuery): about_vpn_message = get_about_vpn("3.1.1_Stable") builder = InlineKeyboardBuilder() - builder.row( - InlineKeyboardButton(text="💰 Поддержать проект", callback_data="donate") - ) + builder.row(InlineKeyboardButton(text="💰 Поддержать проект", callback_data="donate")) builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="back_to_menu")) - await callback_query.message.answer( - about_vpn_message, parse_mode="HTML", reply_markup=builder.as_markup() - ) + await callback_query.message.answer(about_vpn_message, parse_mode="HTML", reply_markup=builder.as_markup()) await callback_query.answer() diff --git a/handlers/utils.py b/handlers/utils.py index 24cf29ac..355a6e36 100644 --- a/handlers/utils.py +++ b/handlers/utils.py @@ -56,11 +56,7 @@ async def get_least_loaded_cluster() -> str: logger.info(f"Cluster loads: {cluster_loads}") if not cluster_loads: - available_clusters = [ - cluster_id - for cluster_id in CLUSTERS.keys() - if re.match(r"^cluster\d+$", cluster_id) - ] + available_clusters = [cluster_id for cluster_id in CLUSTERS.keys() if re.match(r"^cluster\d+$", cluster_id)] logger.info(f"Available clusters from config: {available_clusters}") @@ -72,18 +68,14 @@ async def get_least_loaded_cluster() -> str: logger.warning("No valid clusters found in config, returning 'cluster1'.") return "cluster1" - least_loaded_cluster = min( - cluster_loads, key=lambda k: (cluster_loads.get(k, 0), k) - ) + least_loaded_cluster = min(cluster_loads, key=lambda k: (cluster_loads.get(k, 0), k)) logger.info(f"Least loaded cluster selected: {least_loaded_cluster}") return least_loaded_cluster -async def handle_error( - tg_id: int, callback_query: Optional[object] = None, message: str = "" -) -> None: +async def handle_error(tg_id: int, callback_query: Optional[object] = None, message: str = "") -> None: """ Обрабатывает ошибку, отправляя сообщение пользователю. @@ -95,9 +87,7 @@ async def handle_error( try: if callback_query and hasattr(callback_query, "message"): try: - await bot.delete_message( - chat_id=tg_id, message_id=callback_query.message.message_id - ) + await bot.delete_message(chat_id=tg_id, message_id=callback_query.message.message_id) except Exception as delete_error: logger.warning(f"Не удалось удалить сообщение: {delete_error}") diff --git a/middlewares/database.py b/middlewares/database.py index 0244a0d8..d529dad4 100644 --- a/middlewares/database.py +++ b/middlewares/database.py @@ -1,8 +1,8 @@ from typing import Any, Awaitable, Callable, Dict -import asyncpg from aiogram import BaseMiddleware from aiogram.types import TelegramObject +import asyncpg from config import DATABASE_URL diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..fb4c1abc --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,27 @@ +[tool.black] +line-length = 120 # Совпадает с flake8 +target-version = ['py39','py310','py311'] # Укажите версию Python вашего проекта +skip-string-normalization = true # Отключает нормализацию кавычек +include = '\.pyi?$' # Включает Python-файлы +exclude = ''' +/( + \.git + | \.hg + | \.mypy_cache + | \.tox + | \.venv + | _build + | buck-out + | build + | dist +)/ +''' # Исключает системные папки + +[tool.isort] +profile = "black" # Устанавливает совместимость с black +line_length = 120 +multi_line_output = 3 # Формат многострочных импортов +include_trailing_comma = true # Совместимость с black +force_sort_within_sections = true # Сортировка внутри секций +sections = ["FUTURE", "STDLIB", "THIRDPARTY", "FIRSTPARTY", "LOCALFOLDER"] +skip_gitignore = true # Учитывать .gitignore