diff --git a/bot/keyboards/inline/user_keyboards.py b/bot/keyboards/inline/user_keyboards.py index f655310..eaec5a8 100644 --- a/bot/keyboards/inline/user_keyboards.py +++ b/bot/keyboards/inline/user_keyboards.py @@ -169,6 +169,14 @@ def get_back_to_main_menu_markup(lang: str, return builder.as_markup() +def get_subscribe_only_markup(lang: str, i18n_instance) -> InlineKeyboardMarkup: + _ = lambda key, **kwargs: i18n_instance.gettext(lang, key, **kwargs) + builder = InlineKeyboardBuilder() + builder.button(text=_(key="menu_subscribe_inline"), + callback_data="main_action:subscribe") + return builder.as_markup() + + def get_user_banned_keyboard(support_link: Optional[str], lang: str, i18n_instance) -> Optional[InlineKeyboardMarkup]: if not support_link: diff --git a/bot/services/panel_webhook_service.py b/bot/services/panel_webhook_service.py index 15ec4f8..ca5a426 100644 --- a/bot/services/panel_webhook_service.py +++ b/bot/services/panel_webhook_service.py @@ -4,10 +4,12 @@ import hmac import hashlib from aiohttp import web from aiogram import Bot +from aiogram.types import InlineKeyboardMarkup from sqlalchemy.orm import sessionmaker from typing import Optional from config.settings import Settings from bot.middlewares.i18n import JsonI18n +from bot.keyboards.inline.user_keyboards import get_subscribe_only_markup from db.dal import user_dal EVENT_MAP = { @@ -23,10 +25,19 @@ class PanelWebhookService: self.i18n = i18n self.async_session_factory = async_session_factory - async def _send_message(self, user_id: int, lang: str, message_key: str, **kwargs): + async def _send_message( + self, + user_id: int, + lang: str, + message_key: str, + reply_markup: InlineKeyboardMarkup | None = None, + **kwargs, + ): _ = lambda k, **kw: self.i18n.gettext(lang, k, **kw) try: - await self.bot.send_message(user_id, _(message_key, **kwargs)) + await self.bot.send_message( + user_id, _(message_key, **kwargs), reply_markup=reply_markup + ) except Exception as e: logging.error(f"Failed to send notification to {user_id}: {e}") @@ -45,6 +56,8 @@ class PanelWebhookService: lang = db_user.language_code if db_user and db_user.language_code else self.settings.DEFAULT_LANGUAGE first_name = db_user.first_name or f"User {user_id}" if db_user else f"User {user_id}" + markup = get_subscribe_only_markup(lang, self.i18n) + if event_name in EVENT_MAP: days_left, msg_key = EVENT_MAP[event_name] if days_left <= self.settings.SUBSCRIPTION_NOTIFY_DAYS_BEFORE: @@ -52,13 +65,28 @@ class PanelWebhookService: user_id, lang, msg_key, + reply_markup=markup, user_name=first_name, end_date=user_payload.get("expireAt", "")[:10], ) elif event_name == "user.expired" and self.settings.SUBSCRIPTION_NOTIFY_ON_EXPIRE: - await self._send_message(user_id, lang, "subscription_expired_notification") + await self._send_message( + user_id, + lang, + "subscription_expired_notification", + reply_markup=markup, + user_name=first_name, + end_date=user_payload.get("expireAt", "")[:10], + ) elif event_name == "user.expired_24_hours_ago" and self.settings.SUBSCRIPTION_NOTIFY_AFTER_EXPIRE: - await self._send_message(user_id, lang, "subscription_expired_yesterday_notification") + await self._send_message( + user_id, + lang, + "subscription_expired_yesterday_notification", + reply_markup=markup, + user_name=first_name, + end_date=user_payload.get("expireAt", "")[:10], + ) async def handle_webhook(self, raw_body: bytes, signature_header: Optional[str]) -> web.Response: if self.settings.PANEL_WEBHOOK_SECRET: diff --git a/locales/en.json b/locales/en.json index 35ad676..de1a5ad 100644 --- a/locales/en.json +++ b/locales/en.json @@ -211,11 +211,11 @@ "error_displaying_statistics": "Error displaying statistics.", "stub_page_display": "Page", - "subscription_72h_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription ends in 72 hours on {end_date}.\n\nPlease renew it in the main menu.", - "subscription_48h_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription ends in 48 hours on {end_date}.\n\nPlease renew it in the main menu.", - "subscription_24h_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription ends in 24 hours on {end_date}.\n\nPlease renew it in the main menu.", - "subscription_expired_notification": "Your VPN subscription has expired.", - "subscription_expired_yesterday_notification": "Your VPN subscription expired 24 hours ago.", + "subscription_72h_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription expires in 3 days — {end_date}.\n\nPlease renew it using the button below.", + "subscription_48h_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription expires in 2 days — {end_date}.\n\nPlease renew it using the button below.", + "subscription_24h_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription expires in 1 day — {end_date}.\n\nPlease renew it using the button below.", + "subscription_expired_notification": "👋 Hi, {user_name}!\n\n⛔ Your VPN subscription expired on {end_date}.\n\nPlease renew it using the button below.", + "subscription_expired_yesterday_notification": "👋 Hi, {user_name}!\n\n⏳ Your VPN subscription expired yesterday ({end_date}).\n\nPlease renew it using the button below.", "admin_new_trial_notification": "\ud83c\udf21 User {user_id} activated a free trial until {end_date}.", "admin_new_payment_notification": "\ud83d\udcb3 Payment received from user {user_id}: {months} mo. for {amount} {currency}.", diff --git a/locales/ru.json b/locales/ru.json index 85cb240..fac70f8 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -211,11 +211,11 @@ "error_displaying_statistics": "Ошибка отображения статистики.", "stub_page_display": "Страница", - "subscription_72h_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекает через 72 часа ({end_date}).\n\nПожалуйста, продлите её в главном меню бота.", - "subscription_48h_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекает через 48 часов ({end_date}).\n\nПожалуйста, продлите её в главном меню бота.", - "subscription_24h_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекает через 24 часа ({end_date}).\n\nПожалуйста, продлите её в главном меню бота.", - "subscription_expired_notification": "Срок вашей подписки истек.", - "subscription_expired_yesterday_notification": "Ваша подписка истекла сутки назад.", + "subscription_72h_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекает через 3 дня — {end_date}.\n\nПродлите её по кнопке ниже.", + "subscription_48h_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекает через 2 дня — {end_date}.\n\nПродлите её по кнопке ниже.", + "subscription_24h_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекает через 1 день — {end_date}.\n\nПродлите её по кнопке ниже.", + "subscription_expired_notification": "👋 Привет, {user_name}!\n\n⛔ Срок вашей подписки на VPN истек ({end_date}).\n\nПродлите её по кнопке ниже.", + "subscription_expired_yesterday_notification": "👋 Привет, {user_name}!\n\n⏳ Ваша подписка на VPN истекла сутки назад ({end_date}).\n\nПродлите её по кнопке ниже.", "admin_new_trial_notification": "\ud83c\udf21 Пользователь {user_id} активировал пробный период до {end_date}.", "admin_new_payment_notification": "\ud83d\udcb3 Получен платеж от пользователя {user_id}: {months} мес. за {amount} {currency}.",