fix: admin balance buttons / show tariff in key / show tariff on buy/renew / backup send modes

This commit is contained in:
Capybara-z
2025-05-30 17:02:37 +03:00
parent 572aa88ee1
commit 31b9d0abdf
5 changed files with 138 additions and 81 deletions
+59 -8
View File
@@ -5,9 +5,13 @@ from pathlib import Path
import aiofiles
from aiogram.types import BufferedInputFile
from aiogram import Bot
from bot import bot
from config import ADMIN_ID, BACK_DIR, DB_NAME, DB_PASSWORD, DB_USER, PG_HOST, PG_PORT
from config import (
ADMIN_ID, BACK_DIR, DB_NAME, DB_PASSWORD, DB_USER, PG_HOST, PG_PORT,
BACKUP_SEND_MODE, BACKUP_CHANNEL_ID, BACKUP_CHANNEL_THREAD_ID, BACKUP_OTHER_BOT_TOKEN, BACKUP_CAPTION
)
from logger import logger
@@ -139,20 +143,67 @@ async def _send_backup_to_admins(backup_file_path: str) -> None:
if not backup_file_path or not os.path.exists(backup_file_path):
raise FileNotFoundError(f"Файл бэкапа не найден: {backup_file_path}")
async def send_default():
for admin_id in ADMIN_ID:
try:
await bot.send_document(
chat_id=admin_id, document=backup_input_file
)
logger.info(f"Бэкап базы данных отправлен админу: {admin_id}")
except Exception as e:
logger.error(f"Не удалось отправить бэкап админу {admin_id}: {e}")
try:
async with aiofiles.open(backup_file_path, "rb") as backup_file:
backup_data = await backup_file.read()
filename = os.path.basename(backup_file_path)
backup_input_file = BufferedInputFile(file=backup_data, filename=filename)
for admin_id in ADMIN_ID:
if BACKUP_SEND_MODE == "default":
await send_default()
elif BACKUP_SEND_MODE == "channel":
channel_id = BACKUP_CHANNEL_ID.strip()
thread_id = BACKUP_CHANNEL_THREAD_ID.strip()
if not channel_id:
logger.error("BACKUP_CHANNEL_ID не задан для режима 'channel', fallback на default")
await send_default()
return
send_kwargs = dict(chat_id=channel_id, document=backup_input_file)
if thread_id:
send_kwargs["message_thread_id"] = int(thread_id)
if BACKUP_CAPTION:
send_kwargs["caption"] = BACKUP_CAPTION
try:
await bot.send_document(
chat_id=admin_id, document=backup_input_file
)
logger.info(f"Бэкап базы данных отправлен админу: {admin_id}")
await bot.send_document(**send_kwargs)
logger.info(f"Бэкап базы данных отправлен в канал: {channel_id} (топик: {thread_id})")
except Exception as e:
logger.error(f"Не удалось отправить бэкап админу {admin_id}: {e}")
logger.error(f"Не удалось отправить бэкап в канал {channel_id}: {e}, fallback на default")
await send_default()
elif BACKUP_SEND_MODE == "bot":
if not BACKUP_OTHER_BOT_TOKEN:
logger.error("BACKUP_OTHER_BOT_TOKEN не задан для режима 'bot', fallback на default")
await send_default()
return
other_bot = Bot(token=BACKUP_OTHER_BOT_TOKEN)
try:
for admin_id in ADMIN_ID:
try:
send_kwargs = dict(chat_id=admin_id, document=backup_input_file)
if BACKUP_CAPTION:
send_kwargs["caption"] = BACKUP_CAPTION
await other_bot.send_document(**send_kwargs)
logger.info(f"Бэкап базы данных отправлен админу через другого бота: {admin_id}")
except Exception as e:
logger.error(f"Не удалось отправить бэкап админу {admin_id} через другого бота: {e}")
await other_bot.session.close()
except Exception as e:
logger.error(f"Ошибка при отправке через другого бота: {e}, fallback на default")
await send_default()
else:
logger.error(f"Неизвестный BACKUP_SEND_MODE: {BACKUP_SEND_MODE}, fallback на default")
await send_default()
except Exception as e:
logger.error(f"Ошибка при отправке бэкапа в Telegram: {e}")
logger.error(f"Ошибка при отправке бэкапа: {e}")
raise
+39 -64
View File
@@ -99,7 +99,7 @@ def build_user_edit_kb(
def build_users_balance_change_kb(tg_id: int) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder()
builder.button(
text=BACK, # todo: fix magic text was set
text=BACK,
callback_data=AdminUserEditorCallback(
action="users_balance_edit", tg_id=tg_id
).pack(),
@@ -112,30 +112,18 @@ async def build_users_balance_kb(
) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder()
result = await session.execute(select(Tariff))
tariffs = result.scalars().all()
unique_prices = set()
for tariff in tariffs:
months = tariff.duration_days // 30
if months < 1:
continue
price = tariff.price_rub
if price in unique_prices:
continue
unique_prices.add(price)
for amount in [100, 250, 500, 1000]:
builder.row(
InlineKeyboardButton(
text=f"+ {price} ({months} мес.)",
text=f"+ {amount}",
callback_data=AdminUserEditorCallback(
action="users_balance_add", tg_id=tg_id, data=price
action="users_balance_add", tg_id=tg_id, data=amount
).pack(),
),
InlineKeyboardButton(
text=f"- {price} ({months} мес.)",
text=f"- {amount}",
callback_data=AdminUserEditorCallback(
action="users_balance_add", tg_id=tg_id, data=-price
action="users_balance_add", tg_id=tg_id, data=-amount
).pack(),
),
)
@@ -172,7 +160,7 @@ async def build_users_balance_kb(
def build_users_key_show_kb(tg_id: int, email: str) -> InlineKeyboardMarkup:
builder = InlineKeyboardBuilder()
builder.button(
text=BACK, # todo: fix magic text was set
text=BACK,
callback_data=AdminUserEditorCallback(
action="users_key_edit", tg_id=tg_id, data=email, edit=True
).pack(),
@@ -187,52 +175,39 @@ async def build_users_key_expiry_kb(
result = await session.execute(select(Key.server_id, Key.tariff_id).where(Key.email == email))
row = result.first()
if not row or not row[0] or not row[1]:
builder.row(
InlineKeyboardButton(
text="⚠️ Сервер не найден",
callback_data=AdminUserEditorCallback(
action="users_key_edit", tg_id=tg_id, data=email
).pack(),
server_id, tariff_id = (row if row else (None, None))
if tariff_id:
result = await session.execute(select(Tariff.group_code).where(Tariff.id == tariff_id))
row = result.first()
if row and row[0]:
group_code = row[0]
result = await session.execute(
select(Tariff)
.where(Tariff.group_code == group_code, Tariff.is_active.is_(True))
)
)
return builder.as_markup()
server_id, tariff_id = row
result = await session.execute(select(Tariff.group_code).where(Tariff.id == tariff_id))
row = result.first()
if not row or not row[0]:
return builder.as_markup()
group_code = row[0]
result = await session.execute(
select(Tariff)
.where(Tariff.group_code == group_code, Tariff.is_active.is_(True))
)
tariffs = result.scalars().all()
unique_durations = set()
for tariff in tariffs:
days = tariff.duration_days
if days < 1 or days in unique_durations:
continue
unique_durations.add(days)
label = format_days(days)
builder.row(
InlineKeyboardButton(
text=f"+ {label}",
callback_data=AdminUserKeyEditorCallback(
action="add", tg_id=tg_id, data=email, month=days
).pack(),
),
InlineKeyboardButton(
text=f"- {label}",
callback_data=AdminUserKeyEditorCallback(
action="add", tg_id=tg_id, data=email, month=-days
).pack(),
),
)
tariffs = result.scalars().all()
unique_durations = set()
for tariff in tariffs:
days = tariff.duration_days
if days < 1 or days in unique_durations:
continue
unique_durations.add(days)
label = format_days(days)
builder.row(
InlineKeyboardButton(
text=f"+ {label}",
callback_data=AdminUserKeyEditorCallback(
action="add", tg_id=tg_id, data=email, month=days
).pack(),
),
InlineKeyboardButton(
text=f"- {label}",
callback_data=AdminUserKeyEditorCallback(
action="add", tg_id=tg_id, data=email, month=-days
).pack(),
),
)
builder.row(
InlineKeyboardButton(
+11 -1
View File
@@ -173,7 +173,17 @@ async def key_cluster_mode(
expiry_time_local = expiry_time.astimezone(moscow_tz)
remaining_time = expiry_time_local - datetime.now(moscow_tz)
days = remaining_time.days
key_message_text = key_message_success(final_link, f"⏳ Осталось дней: {days} 📅")
tariff_info = None
if plan:
tariff_info = await get_tariff_by_id(session, plan)
key_message_text = key_message_success(
final_link,
tariff_name=tariff_info.get("name", ""),
traffic_limit=tariff_info.get("traffic_limit", 0),
device_limit=tariff_info.get("device_limit", 0)
)
default_media_path = "img/pic.jpg"
if safe_to_edit:
+12 -1
View File
@@ -545,7 +545,18 @@ async def finalize_key_creation(
remaining_time = expiry_time - datetime.now(moscow_tz)
days = remaining_time.days
link_to_show = public_link or remnawave_link or "Ссылка не найдена"
key_message_text = key_message_success(link_to_show, f"⏳ Осталось дней: {days} 📅")
tariff_info = None
if tariff_id:
result = await session.execute(select(Tariff).where(Tariff.id == tariff_id))
tariff_info = result.scalar_one_or_none()
key_message_text = key_message_success(
link_to_show,
tariff_name=tariff_info.name if tariff_info else "",
traffic_limit=tariff_info.traffic_limit if tariff_info and tariff_info.traffic_limit is not None else 0,
device_limit=tariff_info.device_limit if tariff_info and tariff_info.device_limit is not None else 0
)
await edit_or_send_message(
target_message=callback_query.message,
@@ -11,7 +11,6 @@ from config import (
NOTIFY_DELETE_KEY,
NOTIFY_HOT_LEADS,
NOTIFY_INACTIVE_TRAFFIC,
NOTIFY_MAXPRICE,
NOTIFY_RENEW,
NOTIFY_RENEW_EXPIRED,
TRIAL_TIME_DISABLE,
@@ -45,10 +44,9 @@ from handlers.texts import (
KEY_EXPIRED_NO_DELAY_MSG,
KEY_EXPIRY_10H,
KEY_EXPIRY_24H,
KEY_RENEWED,
KEY_RENEWED_TEMP_MSG,
get_renewal_message,
)
from handlers.utils import format_hours, format_minutes
from handlers.utils import format_hours, format_minutes, get_russian_month
from logger import logger
from .hot_leads_notifications import notify_hot_leads
@@ -404,7 +402,11 @@ async def handle_expired_keys(
notification_id,
1,
"notify_expired.jpg",
KEY_RENEWED_TEMP_MSG,
get_renewal_message(
tariff_name=tariff.get("name", ""),
traffic_limit=tariff.get("traffic_limit") if tariff.get("traffic_limit") is not None else 0,
device_limit=tariff.get("device_limit") if tariff.get("device_limit") is not None else 0
),
)
continue
except Exception as e:
@@ -598,6 +600,11 @@ async def process_auto_renew_or_notify(
new_expiry_time / 1000, tz=moscow_tz
).strftime("%d %B %Y, %H:%M")
formatted_expiry_date = formatted_expiry_date.replace(
datetime.fromtimestamp(new_expiry_time / 1000, tz=moscow_tz).strftime("%B"),
get_russian_month(datetime.fromtimestamp(new_expiry_time / 1000, tz=moscow_tz))
)
logger.info(
f"Продление подписки {email} на {duration_days} дней для пользователя {tg_id}. Баланс: {balance}, списываем: {renewal_cost}"
)
@@ -617,8 +624,11 @@ async def process_auto_renew_or_notify(
await add_notification(conn, tg_id, renew_notification_id)
await delete_notification(conn, tg_id, notification_id)
renewed_message = KEY_RENEWED.format(
email=email, months=duration_days // 30, expiry_date=formatted_expiry_date
renewed_message = get_renewal_message(
tariff_name=selected_tariff.get("name", ""),
traffic_limit=selected_tariff.get("traffic_limit") if selected_tariff.get("traffic_limit") is not None else 0,
device_limit=selected_tariff.get("device_limit") if selected_tariff.get("device_limit") is not None else 0,
expiry_date=formatted_expiry_date
)
keyboard = build_notification_expired_kb()