Optimize user activity updates and fix traffic top-up

This commit is contained in:
Vladless
2026-01-23 04:22:59 +03:00
parent af2d6507da
commit 858968665b
8 changed files with 420 additions and 353 deletions
+12 -15
View File
@@ -18,7 +18,6 @@ from database import (
add_payment,
add_user,
check_coupon_usage,
check_user_exists,
create_coupon_usage,
get_coupon_by_code,
get_keys,
@@ -108,20 +107,18 @@ async def activate_coupon(
await state.clear()
return
user_exists = await check_user_exists(session, user_id)
if not user_exists:
if isinstance(user, dict):
await add_user(session=session, **user)
else:
await add_user(
session=session,
tg_id=user.id,
username=getattr(user, "username", None),
first_name=getattr(user, "first_name", None),
last_name=getattr(user, "last_name", None),
language_code=getattr(user, "language_code", None),
is_bot=getattr(user, "is_bot", False),
)
if isinstance(user, dict):
await add_user(session=session, **user)
else:
await add_user(
session=session,
tg_id=user.id,
username=getattr(user, "username", None),
first_name=getattr(user, "first_name", None),
last_name=getattr(user, "last_name", None),
language_code=getattr(user, "language_code", None),
is_bot=getattr(user, "is_bot", False),
)
if coupon.amount > 0:
try:
+11 -13
View File
@@ -20,7 +20,6 @@ from config import (
from core.bootstrap import MODES_CONFIG, NOTIFICATIONS_CONFIG
from database import (
add_user,
check_user_exists,
get_tariffs_for_cluster,
get_trial,
)
@@ -393,18 +392,17 @@ async def create_key(
selected_traffic_gb: int | None = None,
selected_price_rub: int | None = None,
):
if not await check_user_exists(session, tg_id):
from_user = message_or_query.from_user if isinstance(message_or_query, CallbackQuery | Message) else None
if from_user:
await add_user(
tg_id=from_user.id,
username=from_user.username,
first_name=from_user.first_name,
last_name=from_user.last_name,
language_code=from_user.language_code,
is_bot=from_user.is_bot,
session=session,
)
from_user = message_or_query.from_user if isinstance(message_or_query, CallbackQuery | Message) else None
if from_user:
await add_user(
tg_id=from_user.id,
username=from_user.username,
first_name=from_user.first_name,
last_name=from_user.last_name,
language_code=from_user.language_code,
is_bot=from_user.is_bot,
session=session,
)
use_country_selection = bool(MODES_CONFIG.get("COUNTRY_SELECTION_ENABLED", USE_COUNTRY_SELECTION))
+14 -16
View File
@@ -25,7 +25,6 @@ from core.bootstrap import BUTTONS_CONFIG, MODES_CONFIG
from database import (
add_referral,
add_user,
check_user_exists,
get_referral_by_referred_id,
get_referral_stats,
)
@@ -271,23 +270,22 @@ async def handle_referral_link(
await message.answer("❌ Вы уже использовали реферальную ссылку.")
return
user_exists = await check_user_exists(session, user_id)
if user_exists:
if isinstance(user, dict):
inserted = await add_user(session=session, **user)
else:
inserted = await add_user(
session=session,
tg_id=user.id,
username=getattr(user, "username", None),
first_name=getattr(user, "first_name", None),
last_name=getattr(user, "last_name", None),
language_code=getattr(user, "language_code", None),
is_bot=getattr(user, "is_bot", False),
)
if not inserted:
await message.answer("❌ Вы уже зарегистрированы и не можете стать рефералом.")
return
if not user_exists:
if isinstance(user, dict):
await add_user(session=session, **user)
else:
await add_user(
session=session,
tg_id=user.id,
username=getattr(user, "username", None),
first_name=getattr(user, "first_name", None),
last_name=getattr(user, "last_name", None),
language_code=getattr(user, "language_code", None),
is_bot=getattr(user, "is_bot", False),
)
await add_referral(session, user_id, referrer_tg_id)
+250 -226
View File
@@ -5,6 +5,7 @@ from aiogram import F, Router
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
import sqlalchemy as sa
from sqlalchemy.ext.asyncio import AsyncSession
from config import USE_NEW_PAYMENT_FLOW
@@ -17,6 +18,7 @@ from database import (
save_key_config_with_mode,
update_balance,
)
from database.models import User
from handlers.buttons import BACK, CONFIRM_ADDON_BUTTON_TEXT, PAYMENT
from handlers.keys.key_view import render_key_info
from handlers.payments.currency_rates import format_for_user
@@ -613,252 +615,274 @@ async def handle_addons_confirm(callback: CallbackQuery, state: FSMContext, sess
tg_id = callback.from_user.id
data = await state.get_data()
email = data.get("addon_key_email")
tariff_id = data.get("addon_tariff_id")
selected_devices = data.get("addon_selected_device_limit")
selected_traffic_gb = data.get("addon_selected_traffic_gb")
current_devices = data.get("addon_current_device_limit")
current_traffic_gb = data.get("addon_current_traffic_gb")
logger.info(
"[ADDONS] PACK_MODE handle_addons_confirm: "
f"tg_id={tg_id} email={email} tariff_id={tariff_id} "
f"selected_devices={selected_devices} selected_traffic_gb={selected_traffic_gb} "
f"current_devices={current_devices} current_traffic_gb={current_traffic_gb}"
)
if not email or not tariff_id:
await callback.message.answer("❌ Данные для изменения подписки не найдены.")
await state.clear()
if data.get("addon_confirm_in_progress"):
await callback.answer("⏳ Уже обрабатываю...", show_alert=False)
return
record = await get_key_details(session, email)
if not record:
logger.warning(f"[ADDONS] PACK_MODE: подписка {email} не найдена в handle_addons_confirm")
await callback.message.answer("❌ Подписка не найдена.")
await state.clear()
return
await state.update_data(addon_confirm_in_progress=True)
tariff = await get_tariff_by_id(session, int(tariff_id))
if not tariff:
logger.error(f"[ADDONS] PACK_MODE: тариф {tariff_id} не найден в handle_addons_confirm")
await callback.message.answer("❌ Тариф не найден.")
await state.clear()
return
cfg = data.get("addon_tariff_config") or {}
device_options = cfg.get("device_options") or []
traffic_options = cfg.get("traffic_options_gb") or []
pack_devices, pack_traffic, pack_mode = get_pack_flags()
has_device_option = pack_devices and bool(device_options)
has_traffic_option = pack_traffic and bool(traffic_options)
if has_device_option and current_devices is not None and int(current_devices) == 0:
has_device_option = False
selected_devices = None
if has_traffic_option and current_traffic_gb is not None and int(current_traffic_gb) == 0:
has_traffic_option = False
selected_traffic_gb = None
current_devices_for_price = int(current_devices) if current_devices is not None else None
current_traffic_for_price = int(current_traffic_gb) if current_traffic_gb is not None else None
base_price_for_current = calculate_config_price(
tariff=tariff,
selected_device_limit=current_devices_for_price,
selected_traffic_gb=current_traffic_for_price,
)
try:
base_price_for_current_int = int(base_price_for_current) if base_price_for_current is not None else 0
except (TypeError, ValueError):
base_price_for_current_int = 0
recalc_enabled = bool(
MODES_CONFIG.get(
"KEY_ADDONS_RECALC_PRICE",
TARIFFS_CONFIG.get("KEY_ADDONS_RECALC_PRICE", False),
)
)
diff_full = calc_pack_full_price_rub(
tariff=tariff,
has_device_option=has_device_option,
has_traffic_option=has_traffic_option,
selected_devices=int(selected_devices) if selected_devices is not None else None,
selected_traffic_gb=int(selected_traffic_gb) if selected_traffic_gb is not None else None,
)
if recalc_enabled:
remaining_seconds, total_seconds = calc_remaining_ratio_seconds(record.get("expiry_time"), tariff)
extra_price = int((diff_full * remaining_seconds + total_seconds - 1) // total_seconds)
total_price_after_purchase = base_price_for_current_int + extra_price
else:
extra_price = int(diff_full)
total_price_after_purchase = base_price_for_current_int + diff_full
logger.debug(
"[ADDONS] PACK_MODE confirm prices: "
f"base_price_for_current={base_price_for_current_int} diff_full={diff_full} "
f"extra_price={extra_price} recalc_enabled={recalc_enabled} total_price_after_purchase={total_price_after_purchase} "
f"has_device_option={has_device_option} has_traffic_option={has_traffic_option} "
f"pack_mode={pack_mode!r}"
)
if extra_price <= 0:
logger.info(f"[ADDONS] PACK_MODE: extra_price <= 0, доплата не требуется, email={email}")
await state.clear()
await render_key_info(callback.message, session, email, "img/pic_view.jpg")
await callback.answer(ADDONS_NO_EXTRA_PAYMENT_TEXT, show_alert=True)
return
balance = await get_balance(session, tg_id)
logger.debug(f"[ADDONS] PACK_MODE balance check: tg_id={tg_id} balance={balance} extra_price={extra_price}")
if balance < extra_price:
required_amount = ceil(extra_price - balance)
language_code = getattr(callback.from_user, "language_code", None)
required_amount_text = await format_for_user(session, tg_id, float(required_amount), language_code)
email = data.get("addon_key_email")
tariff_id = data.get("addon_tariff_id")
selected_devices = data.get("addon_selected_device_limit")
selected_traffic_gb = data.get("addon_selected_traffic_gb")
current_devices = data.get("addon_current_device_limit")
current_traffic_gb = data.get("addon_current_traffic_gb")
logger.info(
"[ADDONS] PACK_MODE: недостаточно средств "
f"balance={balance} extra_price={extra_price} required_amount={required_amount} "
f"tg_id={tg_id} USE_NEW_PAYMENT_FLOW={USE_NEW_PAYMENT_FLOW}"
"[ADDONS] PACK_MODE handle_addons_confirm: "
f"tg_id={tg_id} email={email} tariff_id={tariff_id} "
f"selected_devices={selected_devices} selected_traffic_gb={selected_traffic_gb} "
f"current_devices={current_devices} current_traffic_gb={current_traffic_gb}"
)
if USE_NEW_PAYMENT_FLOW:
handled = await try_fast_payment_flow(
callback,
session,
state,
tg_id=tg_id,
temp_key="waiting_for_addons_payment",
temp_payload={
"email": email,
"tariff_id": int(tariff_id),
"selected_device_limit": selected_devices,
"selected_traffic_gb": selected_traffic_gb,
"current_device_limit": current_devices,
"current_traffic_gb": current_traffic_gb,
"required_amount": required_amount,
},
required_amount=required_amount,
)
logger.debug(f"[ADDONS] PACK_MODE try_fast_payment_flow handled={handled} tg_id={tg_id} email={email}")
if handled:
return
if not email or not tariff_id:
await callback.message.answer("❌ Данные для изменения подписки не найдены.")
await state.clear()
return
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay"))
await edit_or_send_message(
target_message=callback.message,
text=INSUFFICIENT_FUNDS_RENEWAL_MSG.format(required_amount=required_amount_text),
reply_markup=builder.as_markup(),
record = await get_key_details(session, email)
if not record:
logger.warning(f"[ADDONS] PACK_MODE: подписка {email} не найдена в handle_addons_confirm")
await callback.message.answer("❌ Подписка не найдена.")
await state.clear()
return
tariff = await get_tariff_by_id(session, int(tariff_id))
if not tariff:
logger.error(f"[ADDONS] PACK_MODE: тариф {tariff_id} не найден в handle_addons_confirm")
await callback.message.answer("❌ Тариф не найден.")
await state.clear()
return
cfg = data.get("addon_tariff_config") or {}
device_options = cfg.get("device_options") or []
traffic_options = cfg.get("traffic_options_gb") or []
pack_devices, pack_traffic, pack_mode = get_pack_flags()
has_device_option = pack_devices and bool(device_options)
has_traffic_option = pack_traffic and bool(traffic_options)
if has_device_option and current_devices is not None and int(current_devices) == 0:
has_device_option = False
selected_devices = None
if has_traffic_option and current_traffic_gb is not None and int(current_traffic_gb) == 0:
has_traffic_option = False
selected_traffic_gb = None
current_devices_for_price = int(current_devices) if current_devices is not None else None
current_traffic_for_price = int(current_traffic_gb) if current_traffic_gb is not None else None
base_price_for_current = calculate_config_price(
tariff=tariff,
selected_device_limit=current_devices_for_price,
selected_traffic_gb=current_traffic_for_price,
)
return
try:
expiry_time = record["expiry_time"]
client_id = record["client_id"]
server_id = record["server_id"]
device_limit_effective_current, traffic_limit_bytes_effective_current = await get_effective_limits_for_key(
session=session,
tariff_id=int(tariff_id),
selected_device_limit=int(current_devices) if current_devices is not None else None,
selected_traffic_gb=int(current_traffic_gb) if current_traffic_gb is not None else None,
)
traffic_limit_gb_effective_current = (
int(traffic_limit_bytes_effective_current / GB) if traffic_limit_bytes_effective_current else 0
)
new_device_limit_effective = device_limit_effective_current
new_traffic_limit_gb_effective = traffic_limit_gb_effective_current
if has_device_option and selected_devices is not None:
pack_devices_val = int(selected_devices)
if pack_devices_val <= 0 or (new_device_limit_effective is not None and new_device_limit_effective <= 0):
new_device_limit_effective = 0
else:
if new_device_limit_effective is None:
new_device_limit_effective = pack_devices_val
else:
new_device_limit_effective = new_device_limit_effective + pack_devices_val
if has_traffic_option and selected_traffic_gb is not None:
pack_traffic_val = int(selected_traffic_gb)
if pack_traffic_val <= 0 or new_traffic_limit_gb_effective <= 0:
new_traffic_limit_gb_effective = 0
else:
new_traffic_limit_gb_effective = new_traffic_limit_gb_effective + pack_traffic_val
current_subgroup = None
try:
current_tariff_id = record.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 error:
logger.warning(f"[ADDONS] PACK_MODE: не удалось определить текущую подгруппу: {error}")
base_price_for_current_int = int(base_price_for_current) if base_price_for_current is not None else 0
except (TypeError, ValueError):
base_price_for_current_int = 0
target_subgroup = tariff.get("subgroup_title")
old_subgroup = current_subgroup
recalc_enabled = bool(
MODES_CONFIG.get(
"KEY_ADDONS_RECALC_PRICE",
TARIFFS_CONFIG.get("KEY_ADDONS_RECALC_PRICE", False),
)
)
total_gb = new_traffic_limit_gb_effective
hwid_device_limit_to_set = new_device_limit_effective
diff_full = calc_pack_full_price_rub(
tariff=tariff,
has_device_option=has_device_option,
has_traffic_option=has_traffic_option,
selected_devices=int(selected_devices) if selected_devices is not None else None,
selected_traffic_gb=int(selected_traffic_gb) if selected_traffic_gb is not None else None,
)
if recalc_enabled:
remaining_seconds, total_seconds = calc_remaining_ratio_seconds(record.get("expiry_time"), tariff)
extra_price = int((diff_full * remaining_seconds + total_seconds - 1) // total_seconds)
total_price_after_purchase = base_price_for_current_int + extra_price
else:
extra_price = int(diff_full)
total_price_after_purchase = base_price_for_current_int + diff_full
logger.debug(
"[ADDONS] PACK_MODE renew_key_in_cluster params: "
f"server_id={server_id} email={email} client_id={client_id} total_gb={total_gb} "
f"hwid_device_limit_to_set={hwid_device_limit_to_set} target_subgroup={target_subgroup} "
f"old_subgroup={old_subgroup}"
"[ADDONS] PACK_MODE confirm prices: "
f"base_price_for_current={base_price_for_current_int} diff_full={diff_full} "
f"extra_price={extra_price} recalc_enabled={recalc_enabled} total_price_after_purchase={total_price_after_purchase} "
f"has_device_option={has_device_option} has_traffic_option={has_traffic_option} "
f"pack_mode={pack_mode!r}"
)
await renew_key_in_cluster(
cluster_id=server_id,
email=email,
client_id=client_id,
new_expiry_time=expiry_time,
total_gb=total_gb,
session=session,
hwid_device_limit=hwid_device_limit_to_set,
reset_traffic=False,
target_subgroup=target_subgroup,
old_subgroup=old_subgroup,
plan=int(tariff_id),
)
if extra_price <= 0:
logger.info(f"[ADDONS] PACK_MODE: extra_price <= 0, доплата не требуется, email={email}")
await state.clear()
await render_key_info(callback.message, session, email, "img/pic_view.jpg")
await callback.answer(ADDONS_NO_EXTRA_PAYMENT_TEXT, show_alert=True)
return
await update_balance(session, tg_id, -extra_price)
balance = await get_balance(session, tg_id)
logger.debug(f"[ADDONS] PACK_MODE balance check: tg_id={tg_id} balance={balance} extra_price={extra_price}")
await save_key_config_with_mode(
session=session,
email=email,
selected_devices=new_device_limit_effective,
selected_traffic_gb=new_traffic_limit_gb_effective,
total_price=int(total_price_after_purchase),
has_device_choice=has_device_option,
has_traffic_choice=has_traffic_option,
config_mode="pack",
)
if balance < extra_price:
required_amount = ceil(extra_price - balance)
language_code = getattr(callback.from_user, "language_code", None)
required_amount_text = await format_for_user(session, tg_id, float(required_amount), language_code)
await session.commit()
logger.info(
"[ADDONS] PACK_MODE: недостаточно средств "
f"balance={balance} extra_price={extra_price} required_amount={required_amount} "
f"tg_id={tg_id} USE_NEW_PAYMENT_FLOW={USE_NEW_PAYMENT_FLOW}"
)
logger.info(
"[ADDONS] PACK_MODE успешная покупка пакета: "
f"tg_id={tg_id} email={email} extra_price={extra_price} "
f"new_device_limit_effective={new_device_limit_effective} "
f"new_traffic_limit_gb_effective={new_traffic_limit_gb_effective} "
f"recalc_enabled={recalc_enabled} pack_mode={pack_mode!r}"
)
if USE_NEW_PAYMENT_FLOW:
handled = await try_fast_payment_flow(
callback,
session,
state,
tg_id=tg_id,
temp_key="waiting_for_addons_payment",
temp_payload={
"email": email,
"tariff_id": int(tariff_id),
"selected_device_limit": selected_devices,
"selected_traffic_gb": selected_traffic_gb,
"current_device_limit": current_devices,
"current_traffic_gb": current_traffic_gb,
"required_amount": required_amount,
},
required_amount=required_amount,
)
logger.debug(f"[ADDONS] PACK_MODE try_fast_payment_flow handled={handled} tg_id={tg_id} email={email}")
if handled:
return
await state.clear()
await render_key_info(callback.message, session, email, "img/pic_view.jpg")
await callback.answer(ADDONS_PACK_SUCCESS_TEXT, show_alert=True)
builder = InlineKeyboardBuilder()
builder.row(InlineKeyboardButton(text=PAYMENT, callback_data="pay"))
await edit_or_send_message(
target_message=callback.message,
text=INSUFFICIENT_FUNDS_RENEWAL_MSG.format(required_amount=required_amount_text),
reply_markup=builder.as_markup(),
)
return
except Exception as error:
logger.error(f"[ADDONS] PACK_MODE ошибка при покупке пакета для {email}: {error}")
await callback.message.answer("❌ Ошибка при обновлении подписки. Попробуйте позже.")
await state.clear()
try:
expiry_time = record["expiry_time"]
client_id = record["client_id"]
server_id = record["server_id"]
device_limit_effective_current, traffic_limit_bytes_effective_current = await get_effective_limits_for_key(
session=session,
tariff_id=int(tariff_id),
selected_device_limit=int(current_devices) if current_devices is not None else None,
selected_traffic_gb=int(current_traffic_gb) if current_traffic_gb is not None else None,
)
traffic_limit_gb_effective_current = (
int(traffic_limit_bytes_effective_current / GB) if traffic_limit_bytes_effective_current else 0
)
new_device_limit_effective = device_limit_effective_current
new_traffic_limit_gb_effective = traffic_limit_gb_effective_current
if has_device_option and selected_devices is not None:
pack_devices_val = int(selected_devices)
if pack_devices_val <= 0 or (new_device_limit_effective is not None and new_device_limit_effective <= 0):
new_device_limit_effective = 0
else:
if new_device_limit_effective is None:
new_device_limit_effective = pack_devices_val
else:
new_device_limit_effective = new_device_limit_effective + pack_devices_val
if has_traffic_option and selected_traffic_gb is not None:
pack_traffic_val = int(selected_traffic_gb)
if pack_traffic_val <= 0 or new_traffic_limit_gb_effective <= 0:
new_traffic_limit_gb_effective = 0
else:
new_traffic_limit_gb_effective = new_traffic_limit_gb_effective + pack_traffic_val
current_subgroup = None
try:
current_tariff_id = record.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 error:
logger.warning(f"[ADDONS] PACK_MODE: не удалось определить текущую подгруппу: {error}")
target_subgroup = tariff.get("subgroup_title")
old_subgroup = current_subgroup
total_gb = new_traffic_limit_gb_effective
hwid_device_limit_to_set = new_device_limit_effective
logger.debug(
"[ADDONS] PACK_MODE renew_key_in_cluster params: "
f"server_id={server_id} email={email} client_id={client_id} total_gb={total_gb} "
f"hwid_device_limit_to_set={hwid_device_limit_to_set} target_subgroup={target_subgroup} "
f"old_subgroup={old_subgroup}"
)
await renew_key_in_cluster(
cluster_id=server_id,
email=email,
client_id=client_id,
new_expiry_time=expiry_time,
total_gb=total_gb,
session=session,
hwid_device_limit=hwid_device_limit_to_set,
reset_traffic=False,
target_subgroup=target_subgroup,
old_subgroup=old_subgroup,
plan=int(tariff_id),
)
result = await session.execute(
sa.update(User)
.where(User.tg_id == tg_id, User.balance >= extra_price)
.values(balance=User.balance - extra_price)
)
if (result.rowcount or 0) != 1:
await session.rollback()
await callback.answer("❌ Недостаточно средств.", show_alert=True)
await state.clear()
return
await save_key_config_with_mode(
session=session,
email=email,
selected_devices=new_device_limit_effective,
selected_traffic_gb=new_traffic_limit_gb_effective,
total_price=int(total_price_after_purchase),
has_device_choice=has_device_option,
has_traffic_choice=has_traffic_option,
config_mode="pack",
)
await session.commit()
logger.info(
"[ADDONS] PACK_MODE успешная покупка пакета: "
f"tg_id={tg_id} email={email} extra_price={extra_price} "
f"new_device_limit_effective={new_device_limit_effective} "
f"new_traffic_limit_gb_effective={new_traffic_limit_gb_effective} "
f"recalc_enabled={recalc_enabled} pack_mode={pack_mode!r}"
)
await state.clear()
await render_key_info(callback.message, session, email, "img/pic_view.jpg")
await callback.answer(ADDONS_PACK_SUCCESS_TEXT, show_alert=True)
except Exception as error:
logger.error(f"[ADDONS] PACK_MODE ошибка при покупке пакета для {email}: {error}")
await callback.message.answer("❌ Ошибка при обновлении подписки. Попробуйте позже.")
await state.clear()
finally:
try:
await state.update_data(addon_confirm_in_progress=False)
except Exception:
pass