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/__init__.py b/handlers/__init__.py index 2ac2e777..c2cd5125 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") @@ -29,5 +28,4 @@ router.include_routers( keys_router, instructions_router, admin_router, - user_router, ) diff --git a/handlers/admin/admin_coupons.py b/handlers/admin/admin_coupons.py index 52c10a4e..559f336e 100644 --- a/handlers/admin/admin_coupons.py +++ b/handlers/admin/admin_coupons.py @@ -3,6 +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 CallbackQuery, Message from database import create_coupon, delete_coupon, get_all_coupons from filters.admin import IsAdminFilter @@ -22,7 +23,7 @@ class AdminCouponsState(StatesGroup): IsAdminFilter(), ) async def handle_coupons( - callback_query: types.CallbackQuery, + callback_query: CallbackQuery, ): await callback_query.message.edit_text(text="🛠 Меню управления купонами:", reply_markup=build_coupons_kb()) @@ -31,7 +32,7 @@ async def handle_coupons( AdminPanelCallback.filter(F.action == "coupons_create"), IsAdminFilter(), ) -async def handle_coupons_create(callback_query: types.CallbackQuery, state: FSMContext): +async def handle_coupons_create(callback_query: CallbackQuery, state: FSMContext): text = ( "🎫 Введите данные для создания купона в формате:\n\n" "📝 код 💰 сумма 🔢 лимит\n\n" @@ -46,7 +47,7 @@ async def handle_coupons_create(callback_query: types.CallbackQuery, state: FSMC @router.message(AdminCouponsState.waiting_for_coupon_data, IsAdminFilter()) -async def handle_coupon_data_input(message: types.Message, state: FSMContext, session: Any): +async def handle_coupon_data_input(message: Message, state: FSMContext, session: Any): text = message.text.strip() parts = text.split() @@ -98,7 +99,7 @@ async def handle_coupon_data_input(message: types.Message, state: FSMContext, se AdminPanelCallback.filter(F.action == "coupons_list"), IsAdminFilter(), ) -async def handle_coupons_list(callback_query: types.CallbackQuery, session: Any): +async def handle_coupons_list(callback_query: CallbackQuery, session: Any): try: page = int(callback_query.data.split(":")[1]) if ":" in callback_query.data else 1 per_page = 10 @@ -136,9 +137,7 @@ async def handle_coupons_list(callback_query: types.CallbackQuery, session: Any) AdminCouponDeleteCallback.filter(), IsAdminFilter(), ) -async def handle_coupon_delete( - callback_query: types.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_panel.py b/handlers/admin/admin_panel.py index f3d76f87..a2fb540b 100644 --- a/handlers/admin/admin_panel.py +++ b/handlers/admin/admin_panel.py @@ -1,7 +1,7 @@ from aiogram import F, Router, types from aiogram.filters import Command from aiogram.fsm.context import FSMContext -from aiogram.types import CallbackQuery +from aiogram.types import CallbackQuery, Message from bot import version from filters.admin import IsAdminFilter @@ -24,7 +24,7 @@ async def handle_admin_callback_query(callback_query: CallbackQuery, state: FSMC @router.message(Command("admin"), IsAdminFilter()) -async def handle_admin_message(message: types.Message, state: FSMContext): +async def handle_admin_message(message: Message, state: FSMContext): text = f"🤖 Панель администратора\n📌 Версия бота: {version}" await state.clear() diff --git a/handlers/admin/admin_sender.py b/handlers/admin/admin_sender.py index 4e8a92cf..af0c7535 100644 --- a/handlers/admin/admin_sender.py +++ b/handlers/admin/admin_sender.py @@ -4,7 +4,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 CallbackQuery +from aiogram.types import CallbackQuery, Message from filters.admin import IsAdminFilter from keyboards.admin.panel_kb import AdminPanelCallback, build_admin_back_kb @@ -46,7 +46,7 @@ async def handle_sender_callback(callback_query: CallbackQuery, callback_data: A AdminSender.waiting_for_message, IsAdminFilter(), ) -async def handle_message_input(message: types.Message, state: FSMContext, session: Any): +async def handle_message_input(message: Message, state: FSMContext, session: Any): text_message = message.text try: diff --git a/handlers/admin/admin_servers.py b/handlers/admin/admin_servers.py index 9632b424..792bc81f 100644 --- a/handlers/admin/admin_servers.py +++ b/handlers/admin/admin_servers.py @@ -4,6 +4,7 @@ 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, Message from py3xui import AsyncApi from backup import create_backup_and_send_to_admins @@ -34,7 +35,7 @@ class AdminServersEditor(StatesGroup): AdminPanelCallback.filter(F.action == "servers"), IsAdminFilter(), ) -async def handle_servers(callback_query: types.CallbackQuery): +async def handle_servers(callback_query: CallbackQuery): servers = await get_servers() text = ( @@ -55,7 +56,7 @@ async def handle_servers(callback_query: types.CallbackQuery): AdminPanelCallback.filter(F.action == "clusters_add"), IsAdminFilter(), ) -async def handle_clusters_add(callback_query: types.CallbackQuery, state: FSMContext): +async def handle_clusters_add(callback_query: CallbackQuery, state: FSMContext): text = ( "🔧 Введите имя нового кластера:\n\n" "Имя кластера должно быть уникальным!\n" @@ -68,7 +69,7 @@ async def handle_clusters_add(callback_query: types.CallbackQuery, state: FSMCon @router.message(AdminServersEditor.waiting_for_cluster_name, IsAdminFilter()) -async def handle_cluster_name_input(message: types.Message, state: FSMContext): +async def handle_cluster_name_input(message: Message, state: FSMContext): if not message.text: await message.answer( text="❌ Имя кластера не может быть пустым. Попробуйте снова.", reply_markup=build_admin_back_kb("servers") @@ -93,7 +94,7 @@ async def handle_cluster_name_input(message: types.Message, state: FSMContext): @router.message(AdminServersEditor.waiting_for_server_name, IsAdminFilter()) -async def handle_server_name_input(message: types.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") @@ -102,15 +103,16 @@ async def handle_server_name_input(message: types.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 = ( @@ -129,7 +131,7 @@ async def handle_server_name_input(message: types.Message, state: FSMContext): @router.message(AdminServersEditor.waiting_for_api_url, IsAdminFilter()) -async def handle_api_url_input(message: types.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://. Попробуйте снова.", @@ -161,7 +163,7 @@ async def handle_api_url_input(message: types.Message, state: FSMContext): @router.message(AdminServersEditor.waiting_for_subscription_url, IsAdminFilter()) -async def handle_subscription_url_input(message: types.Message, state: FSMContext): +async def handle_subscription_url_input(message: Message, state: FSMContext): if not message.text or not message.text.strip().startswith("https://"): await message.answer( text="❌ subscription_url должен начинаться с https://. Попробуйте снова.", @@ -189,7 +191,7 @@ async def handle_subscription_url_input(message: types.Message, state: FSMContex @router.message(AdminServersEditor.waiting_for_inbound_id, IsAdminFilter()) -async def handle_inbound_id_input(message: types.Message, state: FSMContext): +async def handle_inbound_id_input(message: Message, state: FSMContext): inbound_id = message.text.strip() if not inbound_id.isdigit(): @@ -228,9 +230,7 @@ async def handle_inbound_id_input(message: types.Message, state: FSMContext): @router.callback_query(AdminServerEditorCallback.filter(F.action == "clusters_manage"), IsAdminFilter()) -async def handle_clusters_manage( - callback_query: types.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) @@ -244,7 +244,7 @@ async def handle_clusters_manage( @router.callback_query(AdminServerEditorCallback.filter(F.action == "servers_availability"), IsAdminFilter()) async def handle_servers_availability( - callback_query: types.CallbackQuery, callback_data: AdminServerEditorCallback, session: Any + callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, session: Any ): cluster_name = callback_data.data @@ -280,7 +280,7 @@ async def handle_servers_availability( @router.callback_query(AdminServerEditorCallback.filter(F.action == "servers_manage"), IsAdminFilter()) -async def handle_servers_manage(callback_query: types.CallbackQuery, callback_data: AdminServerEditorCallback): +async def handle_servers_manage(callback_query: CallbackQuery, callback_data: AdminServerEditorCallback): server_name = callback_data.data servers = await get_servers() @@ -309,7 +309,7 @@ async def handle_servers_manage(callback_query: types.CallbackQuery, callback_da @router.callback_query(AdminServerEditorCallback.filter(F.action == "servers_delete"), IsAdminFilter()) -async def handle_servers_delete(callback_query: types.CallbackQuery, callback_data: AdminServerEditorCallback): +async def handle_servers_delete(callback_query: CallbackQuery, callback_data: AdminServerEditorCallback): server_name = callback_data.data await callback_query.message.edit_text( @@ -320,7 +320,7 @@ async def handle_servers_delete(callback_query: types.CallbackQuery, callback_da @router.callback_query(AdminServerEditorCallback.filter(F.action == "servers_delete_confirm"), IsAdminFilter()) async def handle_servers_delete_confirm( - callback_query: types.CallbackQuery, callback_data: AdminServerEditorCallback, session: Any + callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, session: Any ): server_name = callback_data.data @@ -333,7 +333,7 @@ async def handle_servers_delete_confirm( @router.callback_query(AdminServerEditorCallback.filter(F.action == "servers_add"), IsAdminFilter()) async def handle_servers_add( - callback_query: types.CallbackQuery, callback_data: AdminServerEditorCallback, state: FSMContext + callback_query: CallbackQuery, callback_data: AdminServerEditorCallback, state: FSMContext ): cluster_name = callback_data.data @@ -354,9 +354,7 @@ async def handle_servers_add( @router.callback_query(AdminServerEditorCallback.filter(F.action == "clusters_backup"), IsAdminFilter()) -async def handle_clusters_backup( - callback_query: types.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_user_editor.py b/handlers/admin/admin_user_editor.py index 4d4ff573..37026123 100644 --- a/handlers/admin/admin_user_editor.py +++ b/handlers/admin/admin_user_editor.py @@ -6,7 +6,7 @@ import pytz from aiogram import Bot, F, Router, types from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup +from aiogram.types import CallbackQuery, InlineKeyboardButton, InlineKeyboardMarkup, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from config import TOTAL_GB @@ -58,7 +58,7 @@ async def prompt_username(callback_query: CallbackQuery, state: FSMContext): @router.message(UserEditorState.waiting_for_username, IsAdminFilter()) -async def handle_username_input(message: types.Message, state: FSMContext, session: Any): +async def handle_username_input(message: Message, state: FSMContext, session: Any): username = message.text.strip().lstrip("@").replace("https://t.me/", "") user_record = await session.fetchrow("SELECT tg_id FROM users WHERE username = $1", username) @@ -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( @@ -124,7 +124,7 @@ async def handle_username_input(message: types.Message, state: FSMContext, sessi @router.callback_query(F.data.startswith("send_message_")) -async def handle_send_message(callback_query: types.CallbackQuery, state: FSMContext): +async def handle_send_message(callback_query: CallbackQuery, state: FSMContext): tg_id = callback_query.data.split("_")[2] await state.update_data(target_tg_id=tg_id) await callback_query.message.answer("✉️ Введите текст сообщения, которое вы хотите отправить пользователю.") @@ -132,7 +132,7 @@ async def handle_send_message(callback_query: types.CallbackQuery, state: FSMCon @router.message(UserEditorState.waiting_for_message_text, IsAdminFilter()) -async def process_send_message(message: types.Message, state: FSMContext, bot: Bot): +async def process_send_message(message: Message, state: FSMContext, bot: Bot): data = await state.get_data() target_tg_id = data.get("target_tg_id") @@ -151,7 +151,7 @@ async def process_send_message(message: types.Message, state: FSMContext, bot: B @router.message(UserEditorState.waiting_for_tg_id, F.text.isdigit(), IsAdminFilter()) -async def handle_tg_id_input(message: types.Message, state: FSMContext, session: Any): +async def handle_tg_id_input(message: Message, state: FSMContext, session: Any): tg_id = int(message.text) 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) @@ -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( @@ -204,7 +204,7 @@ async def handle_tg_id_input(message: types.Message, state: FSMContext, session: @router.callback_query(F.data.startswith("restore_trial_"), IsAdminFilter()) -async def handle_restore_trial(callback_query: types.CallbackQuery, session: Any): +async def handle_restore_trial(callback_query: CallbackQuery, session: Any): tg_id = int(callback_query.data.split("_")[2]) await update_trial(tg_id, 0, session) @@ -226,7 +226,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, session: Any): +async def handle_new_balance_input(message: Message, state: FSMContext, session: Any): if not message.text.isdigit() or int(message.text) < 0: builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="user_editor")) @@ -313,7 +313,7 @@ async def prompt_key_name(callback_query: CallbackQuery, state: FSMContext): @router.message(UserEditorState.waiting_for_key_name, IsAdminFilter()) -async def handle_key_name_input(message: types.Message, state: FSMContext, session: Any): +async def handle_key_name_input(message: Message, state: FSMContext, session: Any): key_name = sanitize_key_name(message.text) key_details = await get_key_details(key_name, session) @@ -370,7 +370,7 @@ async def prompt_expiry_change(callback_query: CallbackQuery, state: FSMContext) @router.message(UserEditorState.waiting_for_expiry_time, IsAdminFilter()) -async def handle_expiry_time_input(message: types.Message, state: FSMContext, session: Any): +async def handle_expiry_time_input(message: Message, state: FSMContext, session: Any): user_data = await state.get_data() email = user_data.get("email") @@ -453,7 +453,7 @@ async def handle_expiry_time_input(message: types.Message, state: FSMContext, se @router.callback_query(F.data.startswith("delete_key_admin|"), IsAdminFilter()) -async def process_callback_delete_key(callback_query: types.CallbackQuery, session: Any): +async def process_callback_delete_key(callback_query: CallbackQuery, session: Any): email = callback_query.data.split("|")[1] key_details = await get_key_details(email, session) @@ -478,7 +478,7 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery, sessi @router.callback_query(F.data.startswith("confirm_delete_admin|"), IsAdminFilter()) -async def process_callback_confirm_delete(callback_query: types.CallbackQuery, session: Any): +async def process_callback_confirm_delete(callback_query: CallbackQuery, session: Any): client_id = callback_query.data.split("|")[1] record = await session.fetchrow("SELECT email FROM keys WHERE client_id = $1", client_id) @@ -509,7 +509,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery, s @router.callback_query(F.data.startswith("user_info|"), IsAdminFilter()) -async def handle_user_info(callback_query: types.CallbackQuery, state: FSMContext, session: Any): +async def handle_user_info(callback_query: CallbackQuery, state: FSMContext, session: Any): tg_id = int(callback_query.data.split("|")[1]) 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) @@ -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}")) @@ -542,7 +542,7 @@ async def handle_user_info(callback_query: types.CallbackQuery, state: FSMContex @router.callback_query(F.data.startswith("confirm_delete_user_"), IsAdminFilter()) -async def confirm_delete_user(callback_query: types.CallbackQuery, state: FSMContext, session: Any): +async def confirm_delete_user(callback_query: CallbackQuery, state: FSMContext, session: Any): tg_id = int(callback_query.data.split("_")[3]) confirmation_markup = InlineKeyboardMarkup( @@ -560,7 +560,7 @@ async def confirm_delete_user(callback_query: types.CallbackQuery, state: FSMCon @router.callback_query(F.data.startswith("delete_user_"), IsAdminFilter()) -async def delete_user(callback_query: types.CallbackQuery, session: Any): +async def delete_user(callback_query: CallbackQuery, session: Any): tg_id = int(callback_query.data.split("_")[2]) key_records = await session.fetch("SELECT email, client_id FROM keys WHERE tg_id = $1", tg_id) diff --git a/handlers/admin/admin_users.py b/handlers/admin/admin_users.py index 8ad42bea..e73b3fb2 100644 --- a/handlers/admin/admin_users.py +++ b/handlers/admin/admin_users.py @@ -7,7 +7,7 @@ from aiogram.exceptions import TelegramBadRequest from aiogram.filters.callback_data import CallbackData from aiogram.fsm.context import FSMContext from aiogram.fsm.state import State, StatesGroup -from aiogram.types import CallbackQuery +from aiogram.types import CallbackQuery, Message from config import TOTAL_GB from database import delete_key, delete_user_data, get_client_id_by_email, get_servers, update_key_expiry, update_trial @@ -75,7 +75,7 @@ async def handle_search_key(callback_query: CallbackQuery, state: FSMContext): @router.message(UserEditorState.waiting_for_user_data, IsAdminFilter()) -async def handle_user_data_input(message: types.Message, state: FSMContext, session: Any): +async def handle_user_data_input(message: Message, state: FSMContext, session: Any): kb = build_admin_back_kb() if message.forward_from: @@ -110,7 +110,7 @@ async def handle_user_data_input(message: types.Message, state: FSMContext, sess @router.message(UserEditorState.waiting_for_key_name, IsAdminFilter()) -async def handle_key_name_input(message: types.Message, state: FSMContext, session: Any): +async def handle_key_name_input(message: Message, state: FSMContext, session: Any): kb = build_admin_back_kb() if not message.text: @@ -131,9 +131,7 @@ async def handle_key_name_input(message: types.Message, state: FSMContext, sessi AdminUserEditorCallback.filter(F.action == "users_send_message"), IsAdminFilter(), ) -async def handle_send_message( - callback_query: types.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( @@ -145,7 +143,7 @@ async def handle_send_message( @router.message(UserEditorState.waiting_for_message_text, IsAdminFilter()) -async def handle_message_text_input(message: types.Message, state: FSMContext): +async def handle_message_text_input(message: Message, state: FSMContext): data = await state.get_data() tg_id = data.get("tg_id") @@ -162,9 +160,7 @@ async def handle_message_text_input(message: types.Message, state: FSMContext): AdminUserEditorCallback.filter(F.action == "users_trial_restore"), IsAdminFilter(), ) -async def handle_trial_restore( - callback_query: types.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) @@ -260,7 +256,7 @@ async def handle_balance_set(callback_query: CallbackQuery, callback_data: Admin @router.message(UserEditorState.waiting_for_balance, IsAdminFilter()) -async def handle_balance_input(message: types.Message, state: FSMContext, session: Any): +async def handle_balance_input(message: Message, state: FSMContext, session: Any): data = await state.get_data() tg_id = data.get("tg_id") op_type = data.get("op_type") @@ -391,7 +387,7 @@ async def handle_expiry_set( @router.message(UserEditorState.waiting_for_expiry_time, IsAdminFilter()) -async def handle_expiry_time_input(message: types.Message, state: FSMContext, session: Any): +async def handle_expiry_time_input(message: Message, state: FSMContext, session: Any): data = await state.get_data() tg_id = data.get("tg_id") email = data.get("email") @@ -452,7 +448,7 @@ async def handle_update_key(callback_query: CallbackQuery, callback_data: AdminU @router.callback_query(AdminUserEditorCallback.filter(F.action == "users_delete_key"), IsAdminFilter()) -async def handle_delete_key(callback_query: types.CallbackQuery, callback_data: AdminUserEditorCallback, session: Any): +async def handle_delete_key(callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, session: Any): email = callback_data.data client_id = await session.fetchval("SELECT client_id FROM keys WHERE email = $1", email) @@ -469,7 +465,7 @@ async def handle_delete_key(callback_query: types.CallbackQuery, callback_data: @router.callback_query(AdminUserEditorCallback.filter(F.action == "users_delete_key_confirm"), IsAdminFilter()) async def handle_delete_key_confirm( - callback_query: types.CallbackQuery, callback_data: AdminUserEditorCallback, session: Any + callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, session: Any ): email = callback_data.data record = await session.fetchrow("SELECT client_id FROM keys WHERE email = $1", email) @@ -496,7 +492,7 @@ async def handle_delete_key_confirm( @router.callback_query(AdminUserEditorCallback.filter(F.action == "users_delete_user"), IsAdminFilter()) -async def handle_delete_user(callback_query: types.CallbackQuery, callback_data: AdminUserEditorCallback): +async def handle_delete_user(callback_query: CallbackQuery, callback_data: AdminUserEditorCallback): tg_id = callback_data.tg_id await callback_query.message.edit_text( text=f"❗️ Вы уверены, что хотите удалить пользователя с ID {tg_id}?", reply_markup=build_user_delete_kb(tg_id) @@ -505,7 +501,7 @@ async def handle_delete_user(callback_query: types.CallbackQuery, callback_data: @router.callback_query(AdminUserEditorCallback.filter(F.action == "users_delete_user_confirm"), IsAdminFilter()) async def handle_delete_user_confirm( - callback_query: types.CallbackQuery, callback_data: AdminUserEditorCallback, session: Any + callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, session: Any ): tg_id = callback_data.tg_id key_records = await session.fetch("SELECT email, client_id FROM keys WHERE tg_id = $1", tg_id) @@ -537,13 +533,13 @@ async def handle_delete_user_confirm( @router.callback_query(AdminUserEditorCallback.filter(F.action == "users_editor"), IsAdminFilter()) async def handle_editor( - callback_query: types.CallbackQuery, callback_data: AdminUserEditorCallback, state: FSMContext, session: Any + callback_query: CallbackQuery, callback_data: AdminUserEditorCallback, state: FSMContext, session: Any ): await process_user_search(callback_query.message, state, session, callback_data.tg_id, callback_data.edit) async def process_user_search( - message: types.Message, state: FSMContext, session: Any, tg_id: int, edit: bool = False + message: Message, state: FSMContext, session: Any, tg_id: int, edit: bool = False ) -> None: await state.clear() @@ -637,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/coupons.py b/handlers/coupons.py index 0a125da2..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 +from aiogram.types import CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from database import ( @@ -23,7 +23,7 @@ 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: CallbackQuery, state: FSMContext): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) @@ -36,7 +36,7 @@ async def handle_activate_coupon(callback_query: types.CallbackQuery, state: FSM @router.message(CouponActivationState.waiting_for_coupon_code) -async def process_coupon_code(message: types.Message, state: FSMContext, session: Any): +async def process_coupon_code(message: Message, state: FSMContext, session: Any): coupon_code = message.text.strip() activation_result = await activate_coupon(message.chat.id, coupon_code, session) diff --git a/handlers/donate.py b/handlers/donate.py index 3de7d323..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 +from aiogram.types import CallbackQuery, InlineKeyboardButton, LabeledPrice, Message, PreCheckoutQuery from aiogram.utils.keyboard import InlineKeyboardBuilder from config import RUB_TO_XTR @@ -18,7 +18,7 @@ router = Router() @router.callback_query(F.data == "donate") -async def process_donate(callback_query: types.CallbackQuery, state: FSMContext): +async def process_donate(callback_query: CallbackQuery, state: FSMContext): await state.clear() builder = InlineKeyboardBuilder() @@ -40,7 +40,7 @@ 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: CallbackQuery, state: FSMContext): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data="donate")) await callback_query.message.answer("💸 Введите сумму доната в рублях:", reply_markup=builder.as_markup()) @@ -48,7 +48,7 @@ async def process_enter_donate_amount(callback_query: types.CallbackQuery, state @router.message(DonateState.entering_donate_amount) -async def process_donate_amount_input(message: types.Message, state: FSMContext): +async def process_donate_amount_input(message: Message, state: FSMContext): if message.text.isdigit(): amount = int(message.text) if amount // RUB_TO_XTR <= 0: @@ -85,7 +85,7 @@ async def on_pre_checkout_query(pre_checkout_query: PreCheckoutQuery): @router.message(F.successful_payment, DonateState.waiting_for_donate_payment) -async def on_successful_donate(message: types.Message, state: FSMContext): +async def on_successful_donate(message: Message, state: FSMContext): try: amount = float(message.successful_payment.invoice_payload.split("_")[0]) builder = InlineKeyboardBuilder() diff --git a/handlers/instructions/instructions.py b/handlers/instructions/instructions.py index 89053024..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 +from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from config import CONNECT_MACOS, CONNECT_WINDOWS, SUPPORT_CHAT_URL @@ -23,13 +23,13 @@ router = Router() @router.callback_query(F.data == "instructions") @router.message(F.text == "/instructions") async def send_instructions( - callback_query_or_message: types.CallbackQuery | types.Message, + callback_query_or_message: CallbackQuery | Message, ): instructions_message = INSTRUCTIONS image_path = os.path.join("img", "instructions.jpg") if not os.path.isfile(image_path): - if isinstance(callback_query_or_message, types.CallbackQuery): + if isinstance(callback_query_or_message, CallbackQuery): await callback_query_or_message.message.answer("Файл изображения не найден.") else: await callback_query_or_message.answer("Файл изображения не найден.") @@ -41,7 +41,7 @@ async def send_instructions( InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile"), ) - if isinstance(callback_query_or_message, types.CallbackQuery): + if isinstance(callback_query_or_message, CallbackQuery): send_photo = callback_query_or_message.message.answer_photo else: send_photo = callback_query_or_message.answer_photo @@ -56,7 +56,7 @@ async def send_instructions( @router.callback_query(F.data.startswith("connect_pc|")) -async def process_connect_pc(callback_query: types.CallbackQuery, session: Any): +async def process_connect_pc(callback_query: CallbackQuery, session: Any): key_name = callback_query.data.split("|")[1] record = await get_key_details(key_name, session) @@ -83,7 +83,7 @@ async def process_connect_pc(callback_query: types.CallbackQuery, session: Any): @router.callback_query(F.data.startswith("connect_tv|")) -async def process_connect_tv(callback_query: types.CallbackQuery): +async def process_connect_tv(callback_query: CallbackQuery): key_name = callback_query.data.split("|")[1] builder = InlineKeyboardBuilder() @@ -98,7 +98,7 @@ async def process_connect_tv(callback_query: types.CallbackQuery): @router.callback_query(F.data.startswith("continue_tv|")) -async def process_continue_tv(callback_query: types.CallbackQuery, session: Any): +async def process_continue_tv(callback_query: CallbackQuery, session: Any): key_name = callback_query.data.split("|")[1] tg_id = callback_query.from_user.id diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 018d3f1e..149c1733 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 +from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from bot import bot @@ -70,10 +70,8 @@ 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: types.Message | types.CallbackQuery, session: Any -): - if isinstance(callback_query_or_message, types.CallbackQuery): +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 send_photo = callback_query_or_message.message.answer_photo @@ -154,7 +152,7 @@ async def send_with_optional_image(send_message, send_photo, image_path, text, k @router.callback_query(F.data.startswith("view_key|")) -async def process_callback_view_key(callback_query: types.CallbackQuery, session: Any): +async def process_callback_view_key(callback_query: CallbackQuery, session: Any): tg_id = callback_query.message.chat.id key_name = callback_query.data.split("|")[1] try: @@ -247,7 +245,7 @@ async def process_callback_view_key(callback_query: types.CallbackQuery, session @router.callback_query(F.data.startswith("update_subscription|")) -async def process_callback_update_subscription(callback_query: types.CallbackQuery, session: Any): +async def process_callback_update_subscription(callback_query: CallbackQuery, session: Any): tg_id = callback_query.message.chat.id email = callback_query.data.split("|")[1] @@ -260,7 +258,7 @@ async def process_callback_update_subscription(callback_query: types.CallbackQue @router.callback_query(F.data.startswith("delete_key|")) -async def process_callback_delete_key(callback_query: types.CallbackQuery): +async def process_callback_delete_key(callback_query: CallbackQuery): client_id = callback_query.data.split("|")[1] try: confirmation_keyboard = types.InlineKeyboardMarkup( @@ -285,7 +283,7 @@ async def process_callback_delete_key(callback_query: types.CallbackQuery): @router.callback_query(F.data.startswith("renew_key|")) -async def process_callback_renew_key(callback_query: types.CallbackQuery, session: Any): +async def process_callback_renew_key(callback_query: CallbackQuery, session: Any): tg_id = callback_query.message.chat.id key_name = callback_query.data.split("|")[1] try: @@ -331,7 +329,7 @@ async def process_callback_renew_key(callback_query: types.CallbackQuery, sessio @router.callback_query(F.data.startswith("confirm_delete|")) -async def process_callback_confirm_delete(callback_query: types.CallbackQuery, session: Any): +async def process_callback_confirm_delete(callback_query: CallbackQuery, session: Any): email = callback_query.data.split("|")[1] try: record = await get_key_details(email, session) @@ -379,7 +377,7 @@ async def process_callback_confirm_delete(callback_query: types.CallbackQuery, s @router.callback_query(F.data.startswith("renew_plan|")) -async def process_callback_renew_plan(callback_query: types.CallbackQuery, session: Any): +async def process_callback_renew_plan(callback_query: CallbackQuery, session: Any): tg_id = callback_query.message.chat.id plan, client_id = callback_query.data.split("|")[1], callback_query.data.split("|")[2] days_to_extend = 30 * int(plan) @@ -486,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(): @@ -503,4 +499,6 @@ async def complete_key_renewal(tg_id, client_id, email, new_expiry_time, total_g await update_balance(tg_id, -cost, conn) logger.info(f"[RENEW] Ключ {client_id} успешно продлён на {plan} мес. для пользователя {tg_id}.") + await conn.close() + await renew_key_on_cluster() diff --git a/handlers/profile.py b/handlers/profile.py index 64bbe141..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 +from aiogram.types import BufferedInputFile, CallbackQuery, InlineKeyboardButton, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from config import DATABASE_URL, NEWS_MESSAGE, RENEWAL_PLANS @@ -29,15 +29,15 @@ router = Router() @router.callback_query(F.data == "profile") @router.message(F.text == "/profile") async def process_callback_view_profile( - callback_query_or_message: types.Message | types.CallbackQuery, + callback_query_or_message: Message | CallbackQuery, state: FSMContext, admin: bool, ): - if isinstance(callback_query_or_message, types.CallbackQuery): + if isinstance(callback_query_or_message, CallbackQuery): chat_id = callback_query_or_message.message.chat.id username = callback_query_or_message.from_user.full_name is_callback = True - elif isinstance(callback_query_or_message, types.Message): + elif isinstance(callback_query_or_message, Message): chat_id = callback_query_or_message.chat.id username = callback_query_or_message.from_user.full_name is_callback = False @@ -114,7 +114,7 @@ async def process_callback_view_profile( @router.callback_query(F.data == "balance") -async def balance_handler(callback_query: types.CallbackQuery): +async def balance_handler(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay")) builder.row(InlineKeyboardButton(text=BALANCE_HISTORY, callback_data="balance_history")) @@ -124,7 +124,7 @@ async def balance_handler(callback_query: types.CallbackQuery): @router.callback_query(F.data == "balance_history") -async def balance_history_handler(callback_query: types.CallbackQuery, session: Any): +async def balance_history_handler(callback_query: CallbackQuery, session: Any): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay")) builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) @@ -152,7 +152,7 @@ async def balance_history_handler(callback_query: types.CallbackQuery, session: @router.message(F.text == "/tariffs") @router.callback_query(F.data == "view_tariffs") -async def view_tariffs_handler(callback_query: types.CallbackQuery): +async def view_tariffs_handler(callback_query: CallbackQuery): builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) @@ -183,7 +183,7 @@ async def view_tariffs_handler(callback_query: types.CallbackQuery): @router.callback_query(F.data == "invite") -async def invite_handler(callback_query: types.CallbackQuery): +async def invite_handler(callback_query: CallbackQuery): chat_id = callback_query.message.chat.id referral_link = get_referral_link(chat_id) 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)