608 lines
25 KiB
Python
608 lines
25 KiB
Python
from collections import defaultdict
|
||
from datetime import datetime, timedelta
|
||
from math import ceil
|
||
from typing import Any
|
||
|
||
import pytz
|
||
|
||
from aiogram import F, Router
|
||
from aiogram.fsm.context import FSMContext
|
||
from aiogram.types import CallbackQuery, InlineKeyboardButton
|
||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||
from sqlalchemy import or_, select, update
|
||
from sqlalchemy.ext.asyncio import AsyncSession
|
||
|
||
from bot import bot
|
||
from config import DISCOUNT_ACTIVE_HOURS, RENEW_BUTTON_BEFORE_DAYS, USE_NEW_PAYMENT_FLOW
|
||
from database import (
|
||
check_tariff_exists,
|
||
get_balance,
|
||
get_key_by_server,
|
||
get_key_details,
|
||
get_tariff_by_id,
|
||
get_tariffs,
|
||
update_balance,
|
||
update_key_expiry,
|
||
)
|
||
from database.models import Key, Server
|
||
from database.notifications import check_hot_lead_discount
|
||
from database.tariffs import create_subgroup_hash, find_subgroup_by_hash, get_tariffs
|
||
from handlers.buttons import BACK, MAIN_MENU, MY_SUB, PAYMENT
|
||
from handlers.keys.operations import renew_key_in_cluster
|
||
from handlers.payments.currency_rates import format_for_user
|
||
from handlers.payments.fast_payment_flow import try_fast_payment_flow
|
||
from handlers.texts import (
|
||
DISCOUNT_OFFER_MESSAGE,
|
||
DISCOUNT_OFFER_STEP2,
|
||
DISCOUNT_OFFER_STEP3,
|
||
INSUFFICIENT_FUNDS_RENEWAL_MSG,
|
||
KEY_NOT_FOUND_MSG,
|
||
PLAN_SELECTION_MSG,
|
||
get_renewal_message,
|
||
)
|
||
from handlers.utils import edit_or_send_message, format_discount_time_left, get_russian_month
|
||
from hooks.hook_buttons import insert_hook_buttons
|
||
from hooks.hooks import run_hooks
|
||
from logger import logger
|
||
|
||
|
||
router = Router()
|
||
moscow_tz = pytz.timezone("Europe/Moscow")
|
||
|
||
|
||
@router.callback_query(F.data.startswith("renew_key|"))
|
||
async def process_callback_renew_key(callback_query: CallbackQuery, state: FSMContext, session: AsyncSession):
|
||
tg_id = callback_query.message.chat.id
|
||
key_name = callback_query.data.split("|")[1]
|
||
|
||
try:
|
||
record = await get_key_details(session, key_name)
|
||
if not record:
|
||
await callback_query.message.answer("<b>Ключ не найден.</b>")
|
||
return
|
||
|
||
client_id = record["client_id"]
|
||
expiry_time = record["expiry_time"]
|
||
server_id = record["server_id"]
|
||
tariff_id = record.get("tariff_id")
|
||
|
||
expiry_utc = datetime.utcfromtimestamp(expiry_time / 1000).replace(tzinfo=pytz.UTC)
|
||
available_from_utc = expiry_utc - timedelta(days=RENEW_BUTTON_BEFORE_DAYS)
|
||
now_utc = datetime.utcnow().replace(tzinfo=pytz.UTC)
|
||
|
||
if now_utc < available_from_utc:
|
||
dt_msk = available_from_utc.astimezone(moscow_tz).strftime("%d.%m.%Y %H:%M")
|
||
kb = InlineKeyboardBuilder()
|
||
kb.row(InlineKeyboardButton(text=BACK, callback_data=f"view_key|{key_name}"))
|
||
|
||
try:
|
||
hook_commands = await run_hooks(
|
||
"process_callback_renew_key", callback_query=callback_query, state=state, session=session
|
||
)
|
||
if hook_commands:
|
||
kb = insert_hook_buttons(kb, hook_commands)
|
||
except Exception as e:
|
||
logger.warning(f"[RENEW] Ошибка при применении хуков: {e}")
|
||
|
||
await edit_or_send_message(
|
||
target_message=callback_query.message,
|
||
text=f"Продление доступно с {dt_msk}",
|
||
reply_markup=kb.as_markup(),
|
||
)
|
||
return
|
||
|
||
await state.update_data(renew_key_name=key_name, renew_client_id=client_id)
|
||
|
||
logger.info(f"[RENEW] Получение тарифов для server_id={server_id}")
|
||
|
||
try:
|
||
server_id_int = int(server_id)
|
||
filter_condition = or_(
|
||
Server.id == server_id_int,
|
||
Server.server_name == server_id,
|
||
Server.cluster_name == server_id,
|
||
)
|
||
except ValueError:
|
||
filter_condition = or_(
|
||
Server.server_name == server_id,
|
||
Server.cluster_name == server_id,
|
||
)
|
||
|
||
row = await session.execute(select(Server.tariff_group).where(filter_condition).limit(1))
|
||
row = row.first()
|
||
if not row or not row[0]:
|
||
logger.warning(f"[RENEW] Тарифная группа не найдена для server_id={server_id}")
|
||
await callback_query.message.answer("❌ Не удалось определить тарифную группу.")
|
||
return
|
||
|
||
group_code = row[0]
|
||
|
||
if tariff_id:
|
||
if await check_tariff_exists(session, tariff_id):
|
||
current_tariff = await get_tariff_by_id(session, tariff_id)
|
||
|
||
forbidden_groups = ["discounts", "discounts_max", "gifts", "trial"]
|
||
|
||
try:
|
||
hook_results = await run_hooks(
|
||
"renewal_forbidden_groups", chat_id=tg_id, admin=False, session=session
|
||
)
|
||
for hook_result in hook_results:
|
||
additional_groups = hook_result.get("additional_groups", [])
|
||
forbidden_groups.extend(additional_groups)
|
||
except Exception as e:
|
||
logger.warning(f"[RENEW] Ошибка при получении дополнительных групп: {e}")
|
||
|
||
if current_tariff["group_code"] not in forbidden_groups:
|
||
group_code = current_tariff["group_code"]
|
||
|
||
discount_info = await check_hot_lead_discount(session, tg_id)
|
||
|
||
if discount_info.get("available"):
|
||
group_code = discount_info["tariff_group"]
|
||
|
||
try:
|
||
hook_results = await run_hooks(
|
||
"purchase_tariff_group_override", chat_id=tg_id, admin=False, session=session, original_group=group_code
|
||
)
|
||
for hook_result in hook_results:
|
||
if hook_result.get("override_group"):
|
||
group_code = hook_result["override_group"]
|
||
logger.info(f"[RENEW] Тарифная группа переопределена хуком для продления: {group_code}")
|
||
break
|
||
except Exception as e:
|
||
logger.warning(f"[RENEW] Ошибка при применении хуков переопределения группы: {e}")
|
||
|
||
tariffs_data = await get_tariffs(session, group_code=group_code, with_subgroup_weights=True)
|
||
tariffs = [t for t in tariffs_data["tariffs"] if t.get("is_active")]
|
||
subgroup_weights = tariffs_data["subgroup_weights"]
|
||
|
||
if not tariffs:
|
||
await callback_query.message.answer("❌ Нет доступных тарифов для продления.")
|
||
return
|
||
|
||
grouped_tariffs = defaultdict(list)
|
||
for t in tariffs:
|
||
subgroup = t.get("subgroup_title")
|
||
grouped_tariffs[subgroup].append(t)
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
|
||
language_code = getattr(callback_query.from_user, "language_code", None)
|
||
|
||
for t in grouped_tariffs.get(None, []):
|
||
price_text = await format_for_user(session, tg_id, t["price_rub"], language_code)
|
||
builder.row(
|
||
InlineKeyboardButton(
|
||
text=f"{t['name']} — {price_text}",
|
||
callback_data=f"renew_plan|{t['id']}",
|
||
)
|
||
)
|
||
|
||
sorted_subgroups = sorted([k for k in grouped_tariffs if k], key=lambda x: (subgroup_weights.get(x, 999999), x))
|
||
|
||
for subgroup in sorted_subgroups:
|
||
subgroup_hash = create_subgroup_hash(subgroup, group_code)
|
||
builder.row(
|
||
InlineKeyboardButton(
|
||
text=subgroup,
|
||
callback_data=f"renew_subgroup|{subgroup_hash}",
|
||
)
|
||
)
|
||
|
||
builder.row(InlineKeyboardButton(text=BACK, callback_data=f"view_key|{key_name}"))
|
||
|
||
try:
|
||
hook_builder = InlineKeyboardBuilder()
|
||
hook_builder.attach(builder)
|
||
|
||
hook_commands = await run_hooks("renew_tariffs", chat_id=tg_id, admin=False, session=session)
|
||
if hook_commands:
|
||
hook_builder = insert_hook_buttons(hook_builder, hook_commands)
|
||
|
||
final_markup = hook_builder.as_markup()
|
||
except Exception as e:
|
||
logger.warning(f"[RENEW] Ошибка при применении хуков: {e}")
|
||
final_markup = builder.as_markup()
|
||
|
||
balance_rub = await get_balance(session, tg_id) or 0
|
||
balance = await format_for_user(session, tg_id, balance_rub, language_code)
|
||
|
||
discount_message = ""
|
||
if discount_info.get("available"):
|
||
offer_text = DISCOUNT_OFFER_STEP2 if discount_info["type"] == "hot_lead_step_2" else DISCOUNT_OFFER_STEP3
|
||
expires_at = discount_info["expires_at"]
|
||
time_left = format_discount_time_left(
|
||
expires_at - timedelta(hours=DISCOUNT_ACTIVE_HOURS), DISCOUNT_ACTIVE_HOURS
|
||
)
|
||
discount_message = DISCOUNT_OFFER_MESSAGE.format(offer_text=offer_text, time_left=time_left)
|
||
|
||
response_message = (
|
||
PLAN_SELECTION_MSG.format(
|
||
balance=balance,
|
||
expiry_date=datetime.utcfromtimestamp(expiry_time / 1000).strftime("%Y-%m-%d %H:%M:%S"),
|
||
)
|
||
+ discount_message
|
||
)
|
||
|
||
await edit_or_send_message(
|
||
target_message=callback_query.message,
|
||
text=response_message,
|
||
reply_markup=final_markup,
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"[RENEW] Ошибка в process_callback_renew_key для tg_id={tg_id}: {e}")
|
||
await callback_query.message.answer("❌ Произошла ошибка при обработке. Попробуйте позже.")
|
||
|
||
|
||
@router.callback_query(F.data.startswith("renew_subgroup|"))
|
||
async def show_tariffs_in_renew_subgroup(callback: CallbackQuery, state: FSMContext, session: AsyncSession):
|
||
try:
|
||
subgroup_hash = callback.data.split("|")[1]
|
||
|
||
data = await state.get_data()
|
||
client_id = data.get("renew_client_id")
|
||
key_name = data.get("renew_key_name")
|
||
|
||
if not client_id or not key_name:
|
||
await callback.message.answer("❌ Данные для подгруппы не найдены.")
|
||
return
|
||
|
||
record = await get_key_details(session, key_name)
|
||
if not record:
|
||
await callback.message.answer("❌ Ключ не найден.")
|
||
return
|
||
|
||
server_id = record["server_id"]
|
||
try:
|
||
server_id_int = int(server_id)
|
||
filter_condition = or_(
|
||
Server.id == server_id_int,
|
||
Server.server_name == server_id,
|
||
Server.cluster_name == server_id,
|
||
)
|
||
except ValueError:
|
||
filter_condition = or_(
|
||
Server.server_name == server_id,
|
||
Server.cluster_name == server_id,
|
||
)
|
||
|
||
row = await session.execute(select(Server.tariff_group).where(filter_condition).limit(1))
|
||
row = row.first()
|
||
if not row or not row[0]:
|
||
logger.warning(f"[RENEW_SUBGROUP] Тарифная группа не найдена для server_id={server_id}")
|
||
await callback.message.answer("❌ Не удалось определить тарифную группу.")
|
||
return
|
||
|
||
group_code = row[0]
|
||
|
||
tg_id = callback.from_user.id
|
||
language_code = callback.from_user.language_code
|
||
discount_info = await check_hot_lead_discount(session, tg_id)
|
||
|
||
if discount_info.get("available"):
|
||
group_code = discount_info["tariff_group"]
|
||
|
||
try:
|
||
hook_results = await run_hooks(
|
||
"purchase_tariff_group_override", chat_id=tg_id, admin=False, session=session, original_group=group_code
|
||
)
|
||
for hook_result in hook_results:
|
||
if hook_result.get("override_group"):
|
||
group_code = hook_result["override_group"]
|
||
logger.info(f"[RENEW_SUBGROUP] Тарифная группа переопределена хуком: {group_code}")
|
||
break
|
||
except Exception as e:
|
||
logger.warning(f"[RENEW_SUBGROUP] Ошибка при применении хуков переопределения группы: {e}")
|
||
|
||
subgroup = await find_subgroup_by_hash(session, subgroup_hash, group_code)
|
||
if not subgroup:
|
||
await callback.message.answer("❌ Подгруппа не найдена.")
|
||
return
|
||
|
||
tariffs = await get_tariffs(session, group_code=group_code)
|
||
filtered = [t for t in tariffs if t["subgroup_title"] == subgroup and t["is_active"]]
|
||
|
||
if not filtered:
|
||
await edit_or_send_message(
|
||
target_message=callback.message,
|
||
text="❌ В этой подгруппе пока нет тарифов.",
|
||
reply_markup=None,
|
||
)
|
||
return
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
for t in filtered:
|
||
price_txt = await format_for_user(session, tg_id, t.get("price_rub", 0), language_code)
|
||
builder.row(
|
||
InlineKeyboardButton(
|
||
text=f"{t['name']} — {price_txt}",
|
||
callback_data=f"renew_plan|{t['id']}",
|
||
)
|
||
)
|
||
|
||
builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"renew_key|{key_name}"))
|
||
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
|
||
|
||
try:
|
||
hook_builder = InlineKeyboardBuilder()
|
||
hook_builder.attach(builder)
|
||
|
||
hook_commands = await run_hooks(
|
||
"renew_tariffs", chat_id=callback.from_user.id, admin=False, session=session
|
||
)
|
||
if hook_commands:
|
||
hook_builder = insert_hook_buttons(hook_builder, hook_commands)
|
||
|
||
final_markup = hook_builder.as_markup()
|
||
except Exception as e:
|
||
logger.warning(f"[RENEW_SUBGROUP] Ошибка при применении хуков: {e}")
|
||
final_markup = builder.as_markup()
|
||
|
||
discount_message = ""
|
||
if discount_info.get("available"):
|
||
offer_text = DISCOUNT_OFFER_STEP2 if discount_info["type"] == "hot_lead_step_2" else DISCOUNT_OFFER_STEP3
|
||
expires_at = discount_info["expires_at"]
|
||
time_left = format_discount_time_left(
|
||
expires_at - timedelta(hours=DISCOUNT_ACTIVE_HOURS), DISCOUNT_ACTIVE_HOURS
|
||
)
|
||
discount_message = DISCOUNT_OFFER_MESSAGE.format(offer_text=offer_text, time_left=time_left)
|
||
|
||
await edit_or_send_message(
|
||
target_message=callback.message,
|
||
text=f"<b>{subgroup}</b>\n\nВыберите тариф:{discount_message}",
|
||
reply_markup=final_markup,
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"[RENEW_SUBGROUP] Ошибка при отображении подгруппы: {e}")
|
||
await callback.message.answer("❌ Произошла ошибка при отображении тарифов.")
|
||
|
||
|
||
@router.callback_query(F.data.startswith("renew_plan|"))
|
||
async def process_callback_renew_plan(callback_query: CallbackQuery, state: FSMContext, session: Any):
|
||
tg_id = callback_query.from_user.id
|
||
tariff_id = callback_query.data.split("|")[1]
|
||
tariff_id = int(tariff_id)
|
||
|
||
data = await state.get_data()
|
||
client_id = data.get("renew_client_id")
|
||
key_name = data.get("renew_key_name")
|
||
|
||
if not client_id or not key_name:
|
||
await callback_query.message.answer("❌ Данные для продления не найдены.")
|
||
return
|
||
|
||
try:
|
||
tariff = await get_tariff_by_id(session, tariff_id)
|
||
if not tariff or not tariff["is_active"]:
|
||
await callback_query.message.answer("❌ Тариф не найден или отключён.")
|
||
return
|
||
|
||
discount_info = await check_hot_lead_discount(session, tg_id)
|
||
if tariff.get("group_code") in ["discounts", "discounts_max"]:
|
||
if not discount_info.get("available") or datetime.utcnow() >= discount_info["expires_at"]:
|
||
builder = InlineKeyboardBuilder()
|
||
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
|
||
await callback_query.message.answer(
|
||
"❌ Скидка недоступна или истекла. Пожалуйста, выберите тариф заново.",
|
||
reply_markup=builder.as_markup(),
|
||
)
|
||
return
|
||
|
||
duration_days = tariff["duration_days"]
|
||
cost = tariff["price_rub"]
|
||
total_gb = tariff["traffic_limit"] or 0
|
||
|
||
record = await get_key_by_server(session, tg_id, client_id)
|
||
if not record:
|
||
await callback_query.message.answer(KEY_NOT_FOUND_MSG)
|
||
logger.error(f"[RENEW] Ключ с client_id={client_id} не найден.")
|
||
return
|
||
|
||
email = record["email"]
|
||
expiry_time = record["expiry_time"]
|
||
current_time = datetime.utcnow().timestamp() * 1000
|
||
|
||
if expiry_time <= current_time:
|
||
new_expiry_time = int(current_time + timedelta(days=duration_days).total_seconds() * 1000)
|
||
else:
|
||
new_expiry_time = int(expiry_time + timedelta(days=duration_days).total_seconds() * 1000)
|
||
|
||
balance = round(await get_balance(session, tg_id), 2)
|
||
cost = round(cost, 2)
|
||
|
||
if balance < cost:
|
||
required_amount = ceil(cost - balance)
|
||
|
||
if USE_NEW_PAYMENT_FLOW:
|
||
handled = await try_fast_payment_flow(
|
||
callback_query,
|
||
session,
|
||
state,
|
||
tg_id=tg_id,
|
||
temp_key="waiting_for_renewal_payment",
|
||
temp_payload={
|
||
"tariff_id": tariff_id,
|
||
"client_id": client_id,
|
||
"cost": cost,
|
||
"required_amount": required_amount,
|
||
"new_expiry_time": new_expiry_time,
|
||
"total_gb": total_gb,
|
||
"email": email,
|
||
},
|
||
required_amount=required_amount,
|
||
)
|
||
if handled:
|
||
return
|
||
|
||
language_code = getattr(callback_query.from_user, "language_code", None)
|
||
required_amount_text = await format_for_user(session, tg_id, float(required_amount), language_code)
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay"))
|
||
builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile"))
|
||
await edit_or_send_message(
|
||
target_message=callback_query.message,
|
||
text=INSUFFICIENT_FUNDS_RENEWAL_MSG.format(required_amount=required_amount_text),
|
||
reply_markup=builder.as_markup(),
|
||
)
|
||
return
|
||
|
||
logger.info(f"[RENEW] Продление ключа для пользователя {tg_id} на {duration_days} дней")
|
||
await complete_key_renewal(
|
||
session,
|
||
tg_id,
|
||
client_id,
|
||
email,
|
||
new_expiry_time,
|
||
total_gb,
|
||
cost,
|
||
callback_query,
|
||
tariff_id,
|
||
)
|
||
|
||
except Exception as e:
|
||
logger.error(f"[RENEW] Ошибка при продлении ключа для пользователя {tg_id}: {e}")
|
||
|
||
|
||
async def resolve_cluster_name(session: AsyncSession, server_or_cluster: str) -> str | None:
|
||
result = await session.execute(select(Server).where(Server.cluster_name == server_or_cluster).limit(1))
|
||
server = result.scalars().first()
|
||
if server:
|
||
return server_or_cluster
|
||
|
||
result = await session.execute(select(Server.cluster_name).where(Server.server_name == server_or_cluster).limit(1))
|
||
row = result.scalar()
|
||
return row
|
||
|
||
|
||
async def complete_key_renewal(
|
||
session: AsyncSession,
|
||
tg_id: int,
|
||
client_id: str,
|
||
email: str,
|
||
new_expiry_time: int,
|
||
total_gb: int,
|
||
cost: float,
|
||
callback_query: CallbackQuery | None,
|
||
tariff_id: int,
|
||
):
|
||
try:
|
||
logger.info(f"[Info] Продление ключа {client_id} по тарифу ID={tariff_id} (Start)")
|
||
|
||
waiting_message = None
|
||
wait_text = "⏳ Подождите. Идет продление подписки…"
|
||
|
||
try:
|
||
if callback_query:
|
||
await edit_or_send_message(
|
||
target_message=callback_query.message,
|
||
text=wait_text,
|
||
reply_markup=None,
|
||
)
|
||
else:
|
||
waiting_message = await bot.send_message(tg_id, wait_text)
|
||
except Exception as e:
|
||
logger.warning(f"[Renew] Не удалось показать экран ожидания: {e}")
|
||
|
||
tariff = await get_tariff_by_id(session, tariff_id)
|
||
if not tariff:
|
||
logger.error(f"[Error] Тариф с id={tariff_id} не найден.")
|
||
return
|
||
|
||
formatted_expiry_date = datetime.fromtimestamp(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)),
|
||
)
|
||
|
||
response_message = get_renewal_message(
|
||
tariff_name=tariff["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,
|
||
expiry_date=formatted_expiry_date,
|
||
subgroup_title=tariff.get("subgroup_title", ""),
|
||
)
|
||
|
||
key_info = await get_key_details(session, email)
|
||
if not key_info:
|
||
logger.error(f"[Error] Ключ с client_id={client_id} не найден в БД.")
|
||
return
|
||
|
||
current_subgroup = None
|
||
try:
|
||
current_tariff_id = key_info.get("tariff_id")
|
||
if current_tariff_id:
|
||
current_tariff = await get_tariff_by_id(session, int(current_tariff_id))
|
||
if current_tariff:
|
||
current_subgroup = current_tariff.get("subgroup_title")
|
||
except Exception as e:
|
||
logger.warning(f"[Renew] Не удалось определить текущую подгруппу: {e}")
|
||
|
||
target_subgroup = tariff.get("subgroup_title")
|
||
old_subgroup = current_subgroup
|
||
|
||
server_or_cluster = key_info["server_id"]
|
||
cluster_id = await resolve_cluster_name(session, server_or_cluster)
|
||
if not cluster_id:
|
||
logger.error(f"[Error] Кластер для {server_or_cluster} не найден.")
|
||
return
|
||
|
||
await renew_key_in_cluster(
|
||
cluster_id=cluster_id,
|
||
email=email,
|
||
client_id=client_id,
|
||
new_expiry_time=new_expiry_time,
|
||
total_gb=total_gb,
|
||
session=session,
|
||
hwid_device_limit=tariff.get("device_limit") if tariff.get("device_limit") is not None else 0,
|
||
reset_traffic=True,
|
||
target_subgroup=target_subgroup,
|
||
old_subgroup=old_subgroup,
|
||
plan=tariff_id,
|
||
)
|
||
|
||
key_row = await get_key_details(session, email)
|
||
effective_client_id = key_row["client_id"] if key_row else client_id
|
||
|
||
await update_key_expiry(session, effective_client_id, new_expiry_time)
|
||
await session.execute(update(Key).where(Key.email == email).values(tariff_id=tariff_id))
|
||
await update_balance(session, tg_id, -cost)
|
||
|
||
builder = InlineKeyboardBuilder()
|
||
builder.row(InlineKeyboardButton(text=MY_SUB, callback_data=f"view_key|{email}"))
|
||
try:
|
||
hook_commands = await run_hooks(
|
||
"renewal_complete", chat_id=tg_id, admin=False, session=session, email=email, client_id=client_id
|
||
)
|
||
if hook_commands:
|
||
builder = insert_hook_buttons(builder, hook_commands)
|
||
except Exception as e:
|
||
logger.warning(f"[RENEWAL_COMPLETE] Ошибка при применении хуков: {e}")
|
||
|
||
try:
|
||
if callback_query:
|
||
await edit_or_send_message(
|
||
target_message=callback_query.message,
|
||
text=response_message,
|
||
reply_markup=builder.as_markup(),
|
||
)
|
||
elif waiting_message:
|
||
await edit_or_send_message(
|
||
target_message=waiting_message,
|
||
text=response_message,
|
||
reply_markup=builder.as_markup(),
|
||
)
|
||
else:
|
||
await bot.send_message(tg_id, response_message, reply_markup=builder.as_markup())
|
||
except Exception as e:
|
||
logger.error(f"[Error] Ошибка при выводе финального сообщения: {e}")
|
||
await bot.send_message(tg_id, response_message, reply_markup=builder.as_markup())
|
||
|
||
logger.info(f"[Info] Продление ключа {client_id} завершено успешно (User: {tg_id})")
|
||
|
||
except Exception as e:
|
||
logger.error(f"[Error] Ошибка в complete_key_renewal: {e}")
|