mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-03-06 22:14:04 +00:00
fix: eliminate double panel API call on tariff change, harden cart notification
Bug 1 improvement: Replaced double API call pattern (sync + update_remnawave_user)
with single _sync_subscription_to_panel call that accepts reset_traffic parameter.
This prevents TRIAL status being overwritten to EXPIRED by the second call's
different status computation logic.
Bug 2 improvement: Moved keyboard construction inside try block to prevent
AttributeError crash if locale keys are missing. Switched button text from
attribute access (texts.KEY) to defensive texts.get('KEY', fallback).
Added empty template guard to prevent sending empty messages to Telegram API.
This commit is contained in:
@@ -206,10 +206,17 @@ async def _build_subscription_info_async(db: AsyncSession, subscription: Subscri
|
||||
return info
|
||||
|
||||
|
||||
async def _sync_subscription_to_panel(db: AsyncSession, user: User, subscription: Subscription) -> dict:
|
||||
async def _sync_subscription_to_panel(
|
||||
db: AsyncSession,
|
||||
user: User,
|
||||
subscription: Subscription,
|
||||
reset_traffic: bool = False,
|
||||
reset_traffic_reason: str | None = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Sync user subscription to Remnawave panel.
|
||||
Creates user if not exists, updates if exists.
|
||||
Optionally resets traffic after sync.
|
||||
Returns dict with changes/errors.
|
||||
"""
|
||||
try:
|
||||
@@ -335,6 +342,16 @@ async def _sync_subscription_to_panel(db: AsyncSession, user: User, subscription
|
||||
changes['panel_uuid'] = new_panel_user.uuid
|
||||
logger.info('Created user in Remnawave panel', user_id=user.id, uuid=new_panel_user.uuid)
|
||||
|
||||
# Reset traffic on panel if requested
|
||||
if reset_traffic and user.remnawave_uuid:
|
||||
try:
|
||||
await api.reset_user_traffic(user.remnawave_uuid)
|
||||
changes['traffic_reset'] = True
|
||||
reason_text = f' ({reset_traffic_reason})' if reset_traffic_reason else ''
|
||||
logger.info('Reset RemnaWave traffic for user', user_id=user.id, reason=reason_text)
|
||||
except Exception as reset_exc:
|
||||
logger.warning('Failed to reset RemnaWave traffic', user_id=user.id, error=reset_exc)
|
||||
|
||||
user.last_remnawave_sync = datetime.now(UTC)
|
||||
await db.commit()
|
||||
|
||||
@@ -1075,17 +1092,11 @@ async def update_user_subscription(
|
||||
|
||||
# Синхронизируем с RemnaWave (discovery/create + сброс трафика по админ-настройке)
|
||||
try:
|
||||
result = await _sync_subscription_to_panel(db, user, subscription)
|
||||
if settings.RESET_TRAFFIC_ON_TARIFF_SWITCH and result.get('action') in ('updated', 'created'):
|
||||
from app.services.subscription_service import SubscriptionService
|
||||
|
||||
subscription_service = SubscriptionService()
|
||||
await subscription_service.update_remnawave_user(
|
||||
db,
|
||||
subscription,
|
||||
reset_traffic=True,
|
||||
reset_reason='смена тарифа (cabinet admin)',
|
||||
)
|
||||
await _sync_subscription_to_panel(
|
||||
db, user, subscription,
|
||||
reset_traffic=settings.RESET_TRAFFIC_ON_TARIFF_SWITCH,
|
||||
reset_traffic_reason='смена тарифа (cabinet admin)',
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error('Failed to sync tariff switch with RemnaWave', error=e)
|
||||
|
||||
|
||||
@@ -352,21 +352,30 @@ async def send_cart_notification_after_topup(
|
||||
amount=fmt(amount_kopeks), balance=fmt(balance), cart_total=fmt(cart_total), missing=fmt(missing),
|
||||
)
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=texts.RETURN_TO_SUBSCRIPTION_CHECKOUT,
|
||||
callback_data='return_to_saved_cart',
|
||||
)
|
||||
],
|
||||
[types.InlineKeyboardButton(text=texts.MY_BALANCE_BUTTON, callback_data='menu_balance')],
|
||||
[types.InlineKeyboardButton(text=texts.MAIN_MENU_BUTTON, callback_data='back_to_menu')],
|
||||
]
|
||||
)
|
||||
if not message_text:
|
||||
logger.warning('Missing cart notification template', language=getattr(user, 'language', 'ru'))
|
||||
return False
|
||||
|
||||
sent = False
|
||||
try:
|
||||
keyboard = types.InlineKeyboardMarkup(
|
||||
inline_keyboard=[
|
||||
[
|
||||
types.InlineKeyboardButton(
|
||||
text=texts.get('RETURN_TO_SUBSCRIPTION_CHECKOUT', '⬅️ Checkout'),
|
||||
callback_data='return_to_saved_cart',
|
||||
)
|
||||
],
|
||||
[types.InlineKeyboardButton(
|
||||
text=texts.get('MY_BALANCE_BUTTON', '💰 Balance'),
|
||||
callback_data='menu_balance',
|
||||
)],
|
||||
[types.InlineKeyboardButton(
|
||||
text=texts.get('MAIN_MENU_BUTTON', '🏠 Menu'),
|
||||
callback_data='back_to_menu',
|
||||
)],
|
||||
]
|
||||
)
|
||||
await bot.send_message(
|
||||
chat_id=user.telegram_id,
|
||||
text=message_text,
|
||||
@@ -374,10 +383,10 @@ async def send_cart_notification_after_topup(
|
||||
parse_mode='HTML',
|
||||
)
|
||||
sent = True
|
||||
logger.info('Отправлено уведомление с кнопкой возврата к оформлению подписки пользователю', user_id=user.id)
|
||||
logger.info('Sent cart notification to user', user_id=user.id)
|
||||
except Exception as send_error:
|
||||
logger.error(
|
||||
'Ошибка отправки уведомления о корзине пользователю',
|
||||
'Failed to send cart notification to user',
|
||||
user_id=user.id,
|
||||
error=send_error,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user