mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-02-21 03:40:55 +00:00
Merge pull request #1957 from BEDOLAGA-DEV/bedolaga/remove-unnecessary-chat-messages-after-balance-refill
Clean up Telegram Stars top-up messages
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import logging
|
||||
from aiogram import types
|
||||
from aiogram.fsm.context import FSMContext
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.config import settings
|
||||
from app.database.models import User
|
||||
@@ -22,11 +21,11 @@ async def start_stars_payment(
|
||||
state: FSMContext
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
|
||||
|
||||
if not settings.TELEGRAM_STARS_ENABLED:
|
||||
await callback.answer("❌ Пополнение через Stars временно недоступно", show_alert=True)
|
||||
return
|
||||
|
||||
|
||||
# Формируем текст сообщения в зависимости от настройки
|
||||
if settings.YOOKASSA_QUICK_AMOUNT_SELECTION_ENABLED and not settings.DISABLE_TOPUP_BUTTONS:
|
||||
message_text = (
|
||||
@@ -35,10 +34,10 @@ async def start_stars_payment(
|
||||
)
|
||||
else:
|
||||
message_text = texts.TOP_UP_AMOUNT
|
||||
|
||||
|
||||
# Создаем клавиатуру
|
||||
keyboard = get_back_keyboard(db_user.language)
|
||||
|
||||
|
||||
# Если включен быстрый выбор суммы и не отключены кнопки, добавляем кнопки
|
||||
if settings.YOOKASSA_QUICK_AMOUNT_SELECTION_ENABLED and not settings.DISABLE_TOPUP_BUTTONS:
|
||||
from .main import get_quick_amount_buttons
|
||||
@@ -46,14 +45,17 @@ async def start_stars_payment(
|
||||
if quick_amount_buttons:
|
||||
# Вставляем кнопки быстрого выбора перед кнопкой "Назад"
|
||||
keyboard.inline_keyboard = quick_amount_buttons + keyboard.inline_keyboard
|
||||
|
||||
|
||||
await callback.message.edit_text(
|
||||
message_text,
|
||||
reply_markup=keyboard
|
||||
)
|
||||
|
||||
|
||||
await state.set_state(BalanceStates.waiting_for_amount)
|
||||
await state.update_data(payment_method="stars")
|
||||
await state.update_data(
|
||||
payment_method="stars",
|
||||
prompt_message_id=callback.message.message_id,
|
||||
)
|
||||
await callback.answer()
|
||||
|
||||
|
||||
@@ -65,40 +67,75 @@ async def process_stars_payment_amount(
|
||||
state: FSMContext
|
||||
):
|
||||
texts = get_texts(db_user.language)
|
||||
|
||||
|
||||
if not settings.TELEGRAM_STARS_ENABLED:
|
||||
await message.answer("⚠️ Оплата Stars временно недоступна")
|
||||
return
|
||||
|
||||
|
||||
try:
|
||||
amount_rubles = amount_kopeks / 100
|
||||
stars_amount = TelegramStarsService.calculate_stars_from_rubles(amount_rubles)
|
||||
stars_rate = settings.get_stars_rate()
|
||||
|
||||
stars_rate = settings.get_stars_rate()
|
||||
|
||||
payment_service = PaymentService(message.bot)
|
||||
invoice_link = await payment_service.create_stars_invoice(
|
||||
amount_kopeks=amount_kopeks,
|
||||
description=f"Пополнение баланса на {texts.format_price(amount_kopeks)}",
|
||||
payload=f"balance_{db_user.id}_{amount_kopeks}"
|
||||
)
|
||||
|
||||
|
||||
keyboard = types.InlineKeyboardMarkup(inline_keyboard=[
|
||||
[types.InlineKeyboardButton(text="⭐ Оплатить", url=invoice_link)],
|
||||
[types.InlineKeyboardButton(text=texts.BACK, callback_data="balance_topup")]
|
||||
])
|
||||
|
||||
await message.answer(
|
||||
f"⭐ <b>Оплата через Telegram Stars</b>\n\n"
|
||||
f"💰 Сумма: {texts.format_price(amount_kopeks)}\n"
|
||||
f"⭐ К оплате: {stars_amount} звезд\n"
|
||||
f"📊 Курс: {stars_rate}₽ за звезду\n\n"
|
||||
|
||||
prompt_message_id = (await state.get_data()).get("prompt_message_id")
|
||||
|
||||
if prompt_message_id:
|
||||
try:
|
||||
await message.bot.delete_message(
|
||||
chat_id=message.chat.id,
|
||||
message_id=prompt_message_id,
|
||||
)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Не удалось удалить сообщение с вводом суммы Stars",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
try:
|
||||
await message.delete()
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Не удалось удалить сообщение пользователя с суммой",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
invoice_message = await message.answer(
|
||||
f"⭐ <b>Оплата через Telegram Stars</b>\n\n",
|
||||
f"💰 Сумма: {texts.format_price(amount_kopeks)}\n",
|
||||
f"⭐ К оплате: {stars_amount} звезд\n",
|
||||
f"📊 Курс: {stars_rate}₽ за звезду\n\n",
|
||||
f"Нажмите кнопку ниже для оплаты:",
|
||||
reply_markup=keyboard,
|
||||
parse_mode="HTML"
|
||||
)
|
||||
|
||||
|
||||
try:
|
||||
from app.utils.stars_message_cleanup import register_stars_payment_messages
|
||||
|
||||
await register_stars_payment_messages(
|
||||
user_id=message.from_user.id,
|
||||
message_ids=[invoice_message.message_id],
|
||||
)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Не удалось зарегистрировать сообщения Stars для очистки",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
await state.clear()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Ошибка создания Stars invoice: {e}")
|
||||
await message.answer("⚠️ Ошибка создания платежа")
|
||||
await message.answer("⚠️ Ошибка создания платежа")
|
||||
|
||||
@@ -10,6 +10,7 @@ from app.external.telegram_stars import TelegramStarsService
|
||||
from app.database.crud.user import get_user_by_telegram_id
|
||||
from app.localization.loader import DEFAULT_LANGUAGE
|
||||
from app.localization.texts import get_texts
|
||||
from app.utils.stars_message_cleanup import consume_stars_payment_messages
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -113,8 +114,30 @@ async def handle_successful_payment(
|
||||
payload=payment.invoice_payload,
|
||||
telegram_payment_charge_id=payment.telegram_payment_charge_id
|
||||
)
|
||||
|
||||
|
||||
if success:
|
||||
cleanup_message_ids = set()
|
||||
|
||||
try:
|
||||
cleanup_message_ids = await consume_stars_payment_messages(user_id)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Не удалось получить сообщения Stars для удаления",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
for message_id in cleanup_message_ids:
|
||||
try:
|
||||
await message.bot.delete_message(
|
||||
chat_id=message.chat.id,
|
||||
message_id=message_id,
|
||||
)
|
||||
except Exception:
|
||||
logger.warning(
|
||||
"Не удалось удалить промежуточное сообщение Stars",
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
rubles_amount = TelegramStarsService.calculate_rubles_from_stars(payment.total_amount)
|
||||
amount_kopeks = int((rubles_amount * Decimal(100)).to_integral_value(rounding=ROUND_HALF_UP))
|
||||
amount_text = settings.format_price(amount_kopeks).replace(" ₽", "")
|
||||
|
||||
@@ -515,36 +515,6 @@ class TelegramStarsMixin:
|
||||
exc_info=True,
|
||||
)
|
||||
|
||||
if getattr(self, "bot", None):
|
||||
try:
|
||||
keyboard = await self.build_topup_success_keyboard(user)
|
||||
|
||||
charge_id_short = (telegram_payment_charge_id or getattr(transaction, "external_id", ""))[:8]
|
||||
|
||||
await self.bot.send_message(
|
||||
user.telegram_id,
|
||||
(
|
||||
"✅ <b>Пополнение успешно!</b>\n\n"
|
||||
f"⭐ Звезд: {stars_amount}\n"
|
||||
f"💰 Сумма: {settings.format_price(amount_kopeks)}\n"
|
||||
"🦊 Способ: Telegram Stars\n"
|
||||
f"🆔 Транзакция: {charge_id_short}...\n\n"
|
||||
"Баланс пополнен автоматически!"
|
||||
),
|
||||
parse_mode="HTML",
|
||||
reply_markup=keyboard,
|
||||
)
|
||||
logger.info(
|
||||
"✅ Отправлено уведомление пользователю %s о пополнении на %s",
|
||||
user.telegram_id,
|
||||
settings.format_price(amount_kopeks),
|
||||
)
|
||||
except Exception as error: # pragma: no cover - диагностический лог
|
||||
logger.error(
|
||||
"Ошибка отправки уведомления о пополнении Stars: %s",
|
||||
error,
|
||||
)
|
||||
|
||||
# Проверяем наличие сохраненной корзины для возврата к оформлению подписки
|
||||
try:
|
||||
from aiogram import types
|
||||
|
||||
35
app/utils/stars_message_cleanup.py
Normal file
35
app/utils/stars_message_cleanup.py
Normal file
@@ -0,0 +1,35 @@
|
||||
"""Вспомогательные функции для очистки служебных сообщений Telegram Stars."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from typing import Iterable, Set
|
||||
|
||||
__all__ = [
|
||||
"register_stars_payment_messages",
|
||||
"consume_stars_payment_messages",
|
||||
]
|
||||
|
||||
|
||||
_messages_lock = asyncio.Lock()
|
||||
_messages_to_cleanup: dict[int, set[int]] = {}
|
||||
|
||||
|
||||
async def register_stars_payment_messages(user_id: int, message_ids: Iterable[int]) -> None:
|
||||
"""Сохраняет идентификаторы сообщений для последующего удаления.
|
||||
|
||||
Args:
|
||||
user_id: Telegram ID пользователя.
|
||||
message_ids: Сообщения, которые нужно удалить после успешной оплаты.
|
||||
"""
|
||||
|
||||
async with _messages_lock:
|
||||
existing = _messages_to_cleanup.setdefault(user_id, set())
|
||||
existing.update(message_ids)
|
||||
|
||||
|
||||
async def consume_stars_payment_messages(user_id: int) -> Set[int]:
|
||||
"""Возвращает и удаляет накопленные сообщения для пользователя."""
|
||||
|
||||
async with _messages_lock:
|
||||
return _messages_to_cleanup.pop(user_id, set())
|
||||
Reference in New Issue
Block a user