From b04bc4dca58201e6d5c75a64da84d3e43641916d Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 31 Jan 2025 00:14:56 +0300 Subject: [PATCH 1/8] Remove user router and related blocking/unblocking logic - Deleted handlers/user.py file - Removed user_router from main handlers router - Eliminated user blocking/unblocking event handlers --- handlers/__init__.py | 4 +--- handlers/user.py | 22 ---------------------- 2 files changed, 1 insertion(+), 25 deletions(-) delete mode 100644 handlers/user.py diff --git a/handlers/__init__.py b/handlers/__init__.py index 2ac2e777..4670223a 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -13,7 +13,6 @@ from .pay import router as pay_router from .payments import router as payments_router from .profile import router as profile_router from .start import router as start_router -from .user import router as user_router router = Router(name="handlers_main_router") @@ -28,6 +27,5 @@ router.include_routers( payments_router, keys_router, instructions_router, - admin_router, - user_router, + admin_router ) diff --git a/handlers/user.py b/handlers/user.py deleted file mode 100644 index 44a190ec..00000000 --- a/handlers/user.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Any - -from aiogram import Router -from aiogram.filters.chat_member_updated import KICKED, MEMBER, ChatMemberUpdatedFilter -from aiogram.types import ChatMemberUpdated - -from database import create_blocked_user, delete_blocked_user -from logger import logger - -router = Router() - - -@router.my_chat_member(ChatMemberUpdatedFilter(member_status_changed=KICKED)) -async def user_blocked_bot(event: ChatMemberUpdated, session: Any): - logger.info(f"User {event.from_user.id} blocked the bot.") - await create_blocked_user(event.from_user.id, session) - - -@router.my_chat_member(ChatMemberUpdatedFilter(member_status_changed=MEMBER)) -async def user_unblocked_bot(event: ChatMemberUpdated, session: Any): - logger.info(f"User {event.from_user.id} unblocked the bot.") - await delete_blocked_user(event.from_user.id, session) From 1d19d8b7444f32eec76c5c2550f9012c24d936d8 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Thu, 30 Jan 2025 21:15:14 +0000 Subject: [PATCH 2/8] Auto-format code with Ruff using pyproject.toml --- handlers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/__init__.py b/handlers/__init__.py index 4670223a..c2cd5125 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -27,5 +27,5 @@ router.include_routers( payments_router, keys_router, instructions_router, - admin_router + admin_router, ) From c41b77d365e54bc938085e9bcbfe1060513c1c6d Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 31 Jan 2025 02:41:40 +0300 Subject: [PATCH 3/8] Update database query and key handling in admin user editor - Modify get_keys function to return all columns in keys table - Update admin user editor to handle full key record instead of just email - Cast tg_id to integer in user record processing - Adjust key display and callback data generation to use full key record --- database.py | 2 +- handlers/admin/admin_user_editor.py | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/database.py b/database.py index ea1b5cab..693ab291 100644 --- a/database.py +++ b/database.py @@ -433,7 +433,7 @@ async def get_keys(tg_id: int, session: Any): try: records = await session.fetch( """ - SELECT client_id, email, created_at, key + SELECT * FROM keys WHERE tg_id = $1 """, diff --git a/handlers/admin/admin_user_editor.py b/handlers/admin/admin_user_editor.py index 4d4ff573..8c367dc9 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -72,7 +72,7 @@ async def handle_username_input(message: types.Message, state: FSMContext, sessi await state.clear() return - tg_id = user_record["tg_id"] + tg_id = int(user_record["tg_id"]) username = await session.fetchval("SELECT username FROM users WHERE tg_id = $1", tg_id) balance = await session.fetchval("SELECT balance FROM connections WHERE tg_id = $1", tg_id) key_records = await get_keys(tg_id, session) @@ -90,8 +90,8 @@ async def handle_username_input(message: types.Message, state: FSMContext, sessi builder = InlineKeyboardBuilder() - for (email,) in key_records: - builder.row(InlineKeyboardButton(text=f"🔑 {email}", callback_data=f"edit_key_{email}")) + for key in key_records: + builder.row(InlineKeyboardButton(text=f"🔑 {key['email']}", callback_data=f"edit_key_{key['email']}")) builder.row( InlineKeyboardButton( @@ -170,8 +170,8 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext, session: builder = InlineKeyboardBuilder() - for (email,) in key_records: - builder.row(InlineKeyboardButton(text=f"🔑 {email}", callback_data=f"edit_key_{email}")) + for key in key_records: + builder.row(InlineKeyboardButton(text=f"🔑 {key['email']}", callback_data=f"edit_key_{key['email']}")) builder.row( InlineKeyboardButton( @@ -518,8 +518,8 @@ async def handle_user_info(callback_query: types.CallbackQuery, state: FSMContex builder = InlineKeyboardBuilder() - for (email,) in key_records: - builder.row(InlineKeyboardButton(text=f"🔑 {email}", callback_data=f"edit_key_{email}")) + for key in key_records: + builder.row(InlineKeyboardButton(text=f"🔑 {key['email']}", callback_data=f"edit_key_{key['email']}")) builder.row(InlineKeyboardButton(text="📝 Изменить баланс", callback_data=f"change_balance_{tg_id}")) builder.row(InlineKeyboardButton(text="🔄 Восстановить пробник", callback_data=f"restore_trial_{tg_id}")) From 37fbd35e8f2d05ca5b7c64e028b08e8a66fca581 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 31 Jan 2025 02:44:48 +0300 Subject: [PATCH 4/8] Add trailing comma in handlers router inclusion - Minor formatting adjustment in router configuration - Ensure consistent code style with trailing comma --- handlers/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/__init__.py b/handlers/__init__.py index 4670223a..c2cd5125 100644 --- a/handlers/__init__.py +++ b/handlers/__init__.py @@ -27,5 +27,5 @@ router.include_routers( payments_router, keys_router, instructions_router, - admin_router + admin_router, ) From 3741e4f3bfc8472cf76609ecf2a17fe1ff5d529c Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 31 Jan 2025 03:01:46 +0300 Subject: [PATCH 5/8] Standardize import order in handler modules - Reorder imports in multiple handler files to follow a consistent pattern - Move type-specific imports like Message and CallbackQuery to be more organized - Ensure clean and uniform import statements across admin and handler modules --- handlers/admin/admin_coupons.py | 7 +++---- handlers/admin/admin_servers.py | 11 ++++------- handlers/admin/admin_users.py | 8 ++------ handlers/coupons.py | 2 +- handlers/donate.py | 2 +- handlers/instructions/instructions.py | 2 +- handlers/keys/keys.py | 6 ++---- handlers/profile.py | 2 +- 8 files changed, 15 insertions(+), 25 deletions(-) diff --git a/handlers/admin/admin_coupons.py b/handlers/admin/admin_coupons.py index 9d4f0b4f..559f336e 100644 --- a/handlers/admin/admin_coupons.py +++ b/handlers/admin/admin_coupons.py @@ -3,7 +3,8 @@ from typing import Any from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import Message, CallbackQuery +from aiogram.types import CallbackQuery, Message + from database import create_coupon, delete_coupon, get_all_coupons from filters.admin import IsAdminFilter from keyboards.admin.coupons_kb import AdminCouponDeleteCallback, build_coupons_kb, build_coupons_list_kb @@ -136,9 +137,7 @@ async def handle_coupons_list(callback_query: CallbackQuery, session: Any): AdminCouponDeleteCallback.filter(), IsAdminFilter(), ) -async def handle_coupon_delete( - callback_query: CallbackQuery, callback_data: AdminCouponDeleteCallback, session: Any -): +async def handle_coupon_delete(callback_query: CallbackQuery, callback_data: AdminCouponDeleteCallback, session: Any): coupon_code = callback_data.coupon_code try: diff --git a/handlers/admin/admin_servers.py b/handlers/admin/admin_servers.py index 80d26c00..516fa779 100644 --- a/handlers/admin/admin_servers.py +++ b/handlers/admin/admin_servers.py @@ -4,8 +4,9 @@ import asyncpg from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from py3xui import AsyncApi from aiogram.types import CallbackQuery, Message +from py3xui import AsyncApi + from backup import create_backup_and_send_to_admins from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL from database import check_unique_server_name, delete_server, get_servers @@ -228,9 +229,7 @@ async def handle_inbound_id_input(message: Message, state: FSMContext): @router.callback_query(AdminServerEditorCallback.filter(F.action == "clusters_manage"), IsAdminFilter()) -async def handle_clusters_manage( - callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, session: Any -): +async def handle_clusters_manage(callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, session: Any): cluster_name = callback_data.data servers = await get_servers(session) @@ -354,9 +353,7 @@ async def handle_servers_add( @router.callback_query(AdminServerEditorCallback.filter(F.action == "clusters_backup"), IsAdminFilter()) -async def handle_clusters_backup( - callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, session: Any -): +async def handle_clusters_backup(callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, session: Any): cluster_name = callback_data.data servers = await get_servers(session) diff --git a/handlers/admin/admin_users.py b/handlers/admin/admin_users.py index a1f8af4c..9cf672dc 100644 --- a/handlers/admin/admin_users.py +++ b/handlers/admin/admin_users.py @@ -131,9 +131,7 @@ async def handle_key_name_input(message: Message, state: FSMContext, session: An AdminUserEditorCallback.filter(F.action == "users_send_message"), IsAdminFilter(), ) -async def handle_send_message( - callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, state: FSMContext -): +async def handle_send_message(callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, state: FSMContext): tg_id = callback_data.tg_id await callback_query.message.edit_text( @@ -162,9 +160,7 @@ async def handle_message_text_input(message: Message, state: FSMContext): AdminUserEditorCallback.filter(F.action == "users_trial_restore"), IsAdminFilter(), ) -async def handle_trial_restore( - callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, session: Any -): +async def handle_trial_restore(callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, session: Any): tg_id = callback_data.tg_id await update_trial(tg_id, 0, session) diff --git a/handlers/coupons.py b/handlers/coupons.py index 38f992bb..0976d8dc 100644 --- a/handlers/coupons.py +++ b/handlers/coupons.py @@ -3,7 +3,7 @@ from typing import Any from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import InlineKeyboardButton, CallbackQuery, Message +from aiogram.types import CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from database import ( diff --git a/handlers/donate.py b/handlers/donate.py index 4e2bbc13..552c4b8d 100644 --- a/handlers/donate.py +++ b/handlers/donate.py @@ -1,7 +1,7 @@ from aiogram import F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import InlineKeyboardButton, LabeledPrice, PreCheckoutQuery, Message, CallbackQuery +from aiogram.types import CallbackQuery, InlineKeyboardButton, LabeledPrice, Message, PreCheckoutQuery from aiogram.utils.keyboard import InlineKeyboardBuilder from config import RUB_TO_XTR diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index 2e5ff47d..db8ec324 100644 --- a/handlers/instructions/instructions.py +++ b/handlers/instructions/instructions.py @@ -3,7 +3,7 @@ from typing import Any import aiofiles from aiogram import F, Router, types -from aiogram.types import BufferedInputFile, InlineKeyboardButton, Message, CallbackQuery +from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from config import CONNECT_MACOS, CONNECT_WINDOWS, SUPPORT_CHAT_URL diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 9d38e657..c15d3ff0 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -8,7 +8,7 @@ import aiofiles import asyncpg import pytz from aiogram import F, Router, types -from aiogram.types import BufferedInputFile, InlineKeyboardButton, Message, CallbackQuery +from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from bot import bot @@ -70,9 +70,7 @@ router = Router() @router.callback_query(F.data == "view_keys") @router.message(F.text == "/subs") -async def process_callback_or_message_view_keys( - callback_query_or_message: Message | CallbackQuery, session: Any -): +async def process_callback_or_message_view_keys(callback_query_or_message: Message | CallbackQuery, session: Any): if isinstance(callback_query_or_message, CallbackQuery): chat_id = callback_query_or_message.message.chat.id send_message = callback_query_or_message.message.answer diff --git a/handlers/profile.py b/handlers/profile.py index 86afb467..7f50cb9b 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -5,7 +5,7 @@ import aiofiles import asyncpg from aiogram import F, Router, types from aiogram.fsm.context import FSMContext -from aiogram.types import BufferedInputFile, InlineKeyboardButton, CallbackQuery, Message +from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from config import DATABASE_URL, NEWS_MESSAGE, RENEWAL_PLANS From c379011fd2ae8f3641ca35ead7a6db88a34784c1 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 31 Jan 2025 06:44:36 +0300 Subject: [PATCH 6/8] Fix key renewal and expiry update function signatures - Update `change_expiry_time` to pass session to `update_key_expiry` - Adjust `complete_key_renewal` connection closing order - Minor code structure improvements in key handling functions --- handlers/admin/admin_users.py | 2 +- handlers/keys/keys.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/handlers/admin/admin_users.py b/handlers/admin/admin_users.py index 9cf672dc..e73b3fb2 100644 --- a/handlers/admin/admin_users.py +++ b/handlers/admin/admin_users.py @@ -633,7 +633,7 @@ async def change_expiry_time(expiry_time: int, email: str, session: Any) -> Exce await asyncio.gather(*tasks) await update_key_on_all_servers() - await update_key_expiry(client_id, expiry_time) + await update_key_expiry(client_id, expiry_time, session) async def get_user_balance(tg_id: int, session: Any) -> float: diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index c15d3ff0..3cdc893c 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -484,8 +484,6 @@ async def complete_key_renewal(tg_id, client_id, email, new_expiry_time, total_g else: cluster_id = server_id - await conn.close() - logger.info(f"[RENEW] Запуск продления ключа для пользователя {tg_id} на {plan} мес. в кластере {cluster_id}.") async def renew_key_on_cluster(): @@ -500,5 +498,7 @@ async def complete_key_renewal(tg_id, client_id, email, new_expiry_time, total_g await update_key_expiry(client_id, new_expiry_time, conn) await update_balance(tg_id, -cost, conn) logger.info(f"[RENEW] Ключ {client_id} успешно продлён на {plan} мес. для пользователя {tg_id}.") + + await conn.close() await renew_key_on_cluster() From 3d6acf51f62f8b2887e0ccbb3cf38cf90c2395d2 Mon Sep 17 00:00:00 2001 From: GitHub Actions Date: Fri, 31 Jan 2025 03:45:45 +0000 Subject: [PATCH 7/8] Auto-format code with Ruff using pyproject.toml --- handlers/keys/keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 3cdc893c..149c1733 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -498,7 +498,7 @@ async def complete_key_renewal(tg_id, client_id, email, new_expiry_time, total_g await update_key_expiry(client_id, new_expiry_time, conn) await update_balance(tg_id, -cost, conn) logger.info(f"[RENEW] Ключ {client_id} успешно продлён на {plan} мес. для пользователя {tg_id}.") - + await conn.close() await renew_key_on_cluster() From cc429001429fb0fb3735a6739206c80f99841a06 Mon Sep 17 00:00:00 2001 From: Zakhar Izmaylov Date: Fri, 31 Jan 2025 10:54:42 +0300 Subject: [PATCH 8/8] Update server name input handler with session parameter - Add session parameter to server name input handler - Modify server name uniqueness check to use session and cluster name - Improve data retrieval and validation in server creation flow --- handlers/admin/admin_servers.py | 11 ++++++----- handlers/keys/keys.py | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/handlers/admin/admin_servers.py b/handlers/admin/admin_servers.py index 516fa779..792bc81f 100644 --- a/handlers/admin/admin_servers.py +++ b/handlers/admin/admin_servers.py @@ -94,7 +94,7 @@ async def handle_cluster_name_input(message: Message, state: FSMContext): @router.message(AdminServersEditor.waiting_for_server_name, IsAdminFilter()) -async def handle_server_name_input(message: Message, state: FSMContext): +async def handle_server_name_input(message: Message, state: FSMContext, session: Any): if not message.text: await message.answer( text="❌ Имя сервера не может быть пустым. Попробуйте снова.", reply_markup=build_admin_back_kb("servers") @@ -103,15 +103,16 @@ async def handle_server_name_input(message: Message, state: FSMContext): server_name = message.text.strip() - if not await check_unique_server_name(server_name): + user_data = await state.get_data() + cluster_name = user_data.get("cluster_name") + + if not await check_unique_server_name(server_name, session, cluster_name): await message.answer( text="❌ Сервер с таким именем уже существует. Пожалуйста, выберите другое имя.", reply_markup=build_admin_back_kb("servers"), ) return - user_data = await state.get_data() - cluster_name = user_data.get("cluster_name") await state.update_data(server_name=server_name) text = ( @@ -130,7 +131,7 @@ async def handle_server_name_input(message: Message, state: FSMContext): @router.message(AdminServersEditor.waiting_for_api_url, IsAdminFilter()) -async def handle_api_url_input(message: Message, state: FSMContext): +async def handle_api_url_input(message: Message, state: FSMContext, session: Any): if not message.text or not message.text.strip().startswith("https://"): await message.answer( text="❌ API URL должен начинаться с https://. Попробуйте снова.", diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 3cdc893c..149c1733 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -498,7 +498,7 @@ async def complete_key_renewal(tg_id, client_id, email, new_expiry_time, total_g await update_key_expiry(client_id, new_expiry_time, conn) await update_balance(tg_id, -cost, conn) logger.info(f"[RENEW] Ключ {client_id} успешно продлён на {plan} мес. для пользователя {tg_id}.") - + await conn.close() await renew_key_on_cluster()