fixing bugs
This commit is contained in:
+17
-4
@@ -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:
|
||||
"""
|
||||
Проверяет, прошло ли указанное количество часов с момента последнего уведомления.
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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}."
|
||||
|
||||
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user