From 4f2cdef7f90e172517cd9aa486cebe2c60122114 Mon Sep 17 00:00:00 2001 From: Vladless Date: Tue, 1 Apr 2025 02:37:52 +0300 Subject: [PATCH] fixing bugs --- database.py | 21 +++++++++++--- handlers/admin/panel/panel_handler.py | 13 +++++++-- handlers/keys/key_management.py | 28 +++++++++---------- handlers/keys/key_utils.py | 8 +++++- .../notifications/general_notifications.py | 26 ++++++----------- handlers/start.py | 8 ++++++ 6 files changed, 65 insertions(+), 39 deletions(-) diff --git a/database.py b/database.py index 68a30a53..6b9406d5 100644 --- a/database.py +++ b/database.py @@ -549,8 +549,8 @@ async def update_balance( amount: float, session: Any = None, is_admin: bool = False, - skip_referral: bool = False, # <- флаг "пропустить реферальное начисление" - skip_cashback: bool = False, # <- флаг "пропустить кэшбэк" + skip_referral: bool = False, + skip_cashback: bool = False, ): """ Обновляет баланс пользователя в базе данных. @@ -563,7 +563,6 @@ async def update_balance( conn = await asyncpg.connect(DATABASE_URL) session = conn - # Если пополнение не от админа и не сказали пропустить кэшбэк if CASHBACK > 0 and amount > 0 and not is_admin and not skip_cashback: extra = amount * (CASHBACK / 100.0) else: @@ -589,7 +588,6 @@ async def update_balance( f"({'+ кешбэк' if extra > 0 else 'без кешбэка'}), стало: {new_balance}" ) - # Если не админ и не пропустили реферальное начисление — обрабатываем реферальную цепочку if not is_admin and not skip_referral: await handle_referral_on_balance_update(tg_id, int(amount)) @@ -1235,6 +1233,21 @@ async def add_notification(tg_id: int, notification_type: str, session: Any): raise +async def delete_notification(tg_id: int, notification_type: str, session): + """ + Удаляет уведомление пользователя по типу (например: 'email_key_expired'). + """ + try: + await session.execute( + "DELETE FROM notifications WHERE tg_id = $1 AND notification_type = $2", + tg_id, + notification_type, + ) + logger.info(f"🗑 Уведомление '{notification_type}' для пользователя {tg_id} удалено.") + except Exception as e: + logger.error(f"❌ Ошибка при удалении уведомления '{notification_type}' для пользователя {tg_id}: {e}") + + async def check_notification_time(tg_id: int, notification_type: str, hours: int = 12, session: Any = None) -> bool: """ Проверяет, прошло ли указанное количество часов с момента последнего уведомления. diff --git a/handlers/admin/panel/panel_handler.py b/handlers/admin/panel/panel_handler.py index e259254e..1b72867d 100644 --- a/handlers/admin/panel/panel_handler.py +++ b/handlers/admin/panel/panel_handler.py @@ -2,12 +2,15 @@ from aiogram import F, Router from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.types import CallbackQuery, Message +from aiogram.exceptions import TelegramBadRequest from bot import version from filters.admin import IsAdminFilter from .keyboard import AdminPanelCallback, build_panel_kb +from logger import logger + router = Router() @@ -19,12 +22,18 @@ async def handle_admin_callback_query(callback_query: CallbackQuery, state: FSMC await state.clear() if callback_query.message.text: - await callback_query.message.edit_text(text=text, reply_markup=build_panel_kb()) + try: + await callback_query.message.edit_text(text=text, reply_markup=build_panel_kb()) + except TelegramBadRequest as e: + if "message is not modified" in str(e): + logger.warning("🔄 Попытка редактировать сообщение без изменений — пропущено.") + else: + raise else: try: await callback_query.message.delete() except Exception as e: - print(f"Ошибка при удалении сообщения: {e}") + logger.error(f"Ошибка при удалении сообщения: {e}") await callback_query.message.answer(text=text, reply_markup=build_panel_kb()) diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index 477a49cc..61817bce 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -8,7 +8,7 @@ import pytz from aiogram import F, Router from aiogram.fsm.context import FSMContext -from aiogram.types import CallbackQuery, InlineKeyboardButton, Message +from aiogram.types import CallbackQuery, InlineKeyboardButton, Message, InputFile from aiogram.utils.keyboard import InlineKeyboardBuilder from py3xui import AsyncApi @@ -16,11 +16,7 @@ from bot import bot from config import ( ADMIN_PASSWORD, ADMIN_USERNAME, - CONNECT_ANDROID, - CONNECT_IOS, CONNECT_PHONE_BUTTON, - DOWNLOAD_ANDROID, - DOWNLOAD_IOS, NOTIFY_EXTRA_DAYS, PUBLIC_LINK, RENEWAL_PRICES, @@ -45,10 +41,6 @@ from handlers.buttons import ( BACK, CONNECT_DEVICE, CONNECT_PHONE, - DOWNLOAD_ANDROID_BUTTON, - DOWNLOAD_IOS_BUTTON, - IMPORT_ANDROID, - IMPORT_IOS, MAIN_MENU, PAYMENT, PC_BUTTON, @@ -323,17 +315,23 @@ async def create_key( days = remaining_time.days key_message_text = key_message_success(public_link, f"⏳ Осталось дней: {days} 📅") + default_media_path = "img/pic.jpg" + if target_message: await edit_or_send_message( - target_message=target_message, text=key_message_text, reply_markup=builder.as_markup(), media_path=None - ) - else: - await bot.send_message( - chat_id=tg_id, + target_message=target_message, text=key_message_text, reply_markup=builder.as_markup(), + media_path=default_media_path, + ) + else: + photo = InputFile(default_media_path) + await bot.send_photo( + chat_id=tg_id, + photo=photo, + caption=key_message_text, + reply_markup=builder.as_markup(), ) - if state: await state.clear() diff --git a/handlers/keys/key_utils.py b/handlers/keys/key_utils.py index 17fcaafb..887331b4 100644 --- a/handlers/keys/key_utils.py +++ b/handlers/keys/key_utils.py @@ -7,7 +7,7 @@ import asyncpg from py3xui import AsyncApi from config import ADMIN_PASSWORD, ADMIN_USERNAME, DATABASE_URL, LIMIT_IP, PUBLIC_LINK, SUPERNODE, TOTAL_GB -from database import get_servers, store_key +from database import get_servers, store_key, delete_notification from handlers.utils import get_least_loaded_cluster from logger import logger from panels.three_xui import ( @@ -144,6 +144,12 @@ async def renew_key_in_cluster(cluster_id, email, client_id, new_expiry_time, to return False tg_id = tg_id_record["tg_id"] + + notification_prefixes = ["key_24h", "key_10h", "key_expired", "renew"] + for notif in notification_prefixes: + notification_id = f"{email}_{notif}" + await delete_notification(tg_id, notification_id, session=conn) + logger.info(f"🧹 Уведомления для ключа {email} очищены при продлении.") tasks = [] for server_info in cluster: xui = AsyncApi( diff --git a/handlers/notifications/general_notifications.py b/handlers/notifications/general_notifications.py index 28d21aef..c216465e 100644 --- a/handlers/notifications/general_notifications.py +++ b/handlers/notifications/general_notifications.py @@ -29,6 +29,7 @@ from database import ( get_last_notification_time, update_balance, update_key_expiry, + delete_notification ) from handlers.keys.key_utils import delete_key_from_cluster, renew_key_in_cluster from handlers.notifications.notify_kb import build_notification_expired_kb, build_notification_kb @@ -269,22 +270,6 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: delete_immediately = NOTIFY_DELETE_DELAY == 0 delete_after_delay = False - if NOTIFY_DELETE_DELAY > 0 and last_notification_time is not None: - minutes_since = (current_time - last_notification_time) / (1000 * 60) - if minutes_since >= NOTIFY_DELETE_DELAY / 2 and minutes_since < NOTIFY_DELETE_DELAY: - try: - await conn.execute( - "DELETE FROM notifications WHERE tg_id = $1 AND notification_type = $2", - tg_id, - notification_id, - ) - logger.info( - f"⛔ Уведомление {notification_id} для {tg_id} удалено (прошло больше половины задержки)." - ) - except Exception as e: - logger.error(f"Ошибка при удалении уведомления: {e}") - continue - if last_notification_time is not None: delete_after_delay = (current_time - last_notification_time) / (1000 * 60) >= NOTIFY_DELETE_DELAY logger.info( @@ -355,7 +340,13 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: async def process_auto_renew_or_notify( - bot, conn, key: dict, notification_id: str, renewal_period_months: int, standard_photo: str, standard_caption: str + bot, + conn, + key: dict, + notification_id: str, + renewal_period_months: int, + standard_photo: str, + standard_caption: str ): """ Если баланс пользователя позволяет, продлевает ключ на максимальный возможный срок и списывает средства; @@ -405,6 +396,7 @@ async def process_auto_renew_or_notify( await update_key_expiry(client_id, new_expiry_time, conn) await add_notification(tg_id, renew_notification_id, session=conn) + await delete_notification(tg_id, notification_id, session=conn) logger.info( f"✅ Ключ {client_id} продлён на {renewal_period_months} мес. для пользователя {tg_id}. Списано {renewal_cost}." diff --git a/handlers/start.py b/handlers/start.py index ba8cfe2d..c68c2f7c 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -142,6 +142,10 @@ async def process_start_logic( await message.answer("❌ Этот купон уже использован!") return await process_callback_view_profile(message, state, admin) + connection_exists = await check_connection_exists(message.chat.id) + if not connection_exists: + await add_connection(tg_id=message.chat.id, session=session) + await update_balance(message.chat.id, coupon["amount"]) await session.execute( "UPDATE coupons SET usage_count = $1, is_used = $2 WHERE code = $3", @@ -193,6 +197,10 @@ async def process_start_logic( if not existing_referral: await add_referral(message.chat.id, gift_info["sender_tg_id"], session) + connection_exists = await check_connection_exists(message.chat.id) + if not connection_exists: + await add_connection(tg_id=message.chat.id, session=session) + await session.execute("UPDATE connections SET trial = 1 WHERE tg_id = $1", message.chat.id) await create_key(