diff --git a/handlers/captcha.py b/handlers/captcha.py index f6408f88..8f294895 100644 --- a/handlers/captcha.py +++ b/handlers/captcha.py @@ -8,7 +8,7 @@ from aiogram.fsm.context import FSMContext from aiogram.types import CallbackQuery, Message from aiogram.utils.keyboard import InlineKeyboardBuilder from config import CAPTCHA_EMOJIS - +from handlers.texts import CAPTCHA_PROMPT_MSG from logger import logger from .utils import edit_or_send_message @@ -37,7 +37,7 @@ async def generate_captcha(message: Message, state: FSMContext): builder.adjust(2, 2) return { - "text": f"🔒 Для подтверждения, что вы не робот, выберите кнопку с {correct_text}", + "text": CAPTCHA_PROMPT_MSG.format(correct_text=correct_text), "markup": builder.as_markup(), } diff --git a/handlers/coupons.py b/handlers/coupons.py index fc230c49..807aa2f2 100644 --- a/handlers/coupons.py +++ b/handlers/coupons.py @@ -13,7 +13,12 @@ from database import ( update_balance, update_coupon_usage_count, ) - +from handlers.texts import ( + COUPON_INPUT_PROMPT, + COUPON_NOT_FOUND_MSG, + COUPON_ALREADY_USED_MSG, + COUPON_ACTIVATED_SUCCESS_MSG, +) from .utils import edit_or_send_message @@ -37,8 +42,7 @@ async def handle_activate_coupon(callback_query_or_message: Message | CallbackQu await edit_or_send_message( target_message=target_message, - text="🎫 Введите код купона:\n\n" - "📝 Пожалуйста, введите действующий код купона, который вы хотите активировать. 🔑", + text=COUPON_INPUT_PROMPT, reply_markup=builder.as_markup(), media_path=None, ) @@ -61,12 +65,12 @@ async def activate_coupon(user_id: int, coupon_code: str, session: Any): coupon_record = await get_coupon_by_code(coupon_code, session) if not coupon_record: - return "❌ Купон не найден 🚫 или его использование ограничено. 🔒 Пожалуйста, проверьте код и попробуйте снова. 🔍" + return COUPON_NOT_FOUND_MSG usage_exists = await check_coupon_usage(coupon_record["id"], user_id, session) if usage_exists: - return "❌ Вы уже активировали этот купон. 🚫 Купоны могут быть активированы только один раз. 🔒" + return COUPON_ALREADY_USED_MSG coupon_amount = coupon_record["amount"] @@ -74,4 +78,4 @@ async def activate_coupon(user_id: int, coupon_code: str, session: Any): await create_coupon_usage(coupon_record["id"], user_id, session) await update_balance(user_id, coupon_amount, session) - return f"✅ Купон успешно активирован! 🎉\n\nНа ваш баланс добавлено {coupon_amount} рублей 💰." + return COUPON_ACTIVATED_SUCCESS_MSG.format(coupon_amount=coupon_amount) diff --git a/handlers/keys/key_management.py b/handlers/keys/key_management.py index ed8344e7..9debef3e 100644 --- a/handlers/keys/key_management.py +++ b/handlers/keys/key_management.py @@ -53,7 +53,14 @@ from handlers.buttons.add_subscribe import ( from handlers.keys.key_utils import create_client_on_server, create_key_on_cluster from handlers.payments.robokassa_pay import handle_custom_amount_input from handlers.payments.yookassa_pay import process_custom_amount_input -from handlers.texts import DISCOUNTS, key_message_success +from handlers.texts import ( + DISCOUNTS, + key_message_success, + CREATING_CONNECTION_MSG, + SELECT_TARIFF_PLAN_MSG, + INSUFFICIENT_FUNDS_MSG, + SELECT_COUNTRY_MSG, +) from handlers.utils import edit_or_send_message, generate_random_email, get_least_loaded_cluster from logger import logger @@ -97,7 +104,7 @@ async def handle_key_creation( target_message=message_or_query if isinstance(message_or_query, Message) else message_or_query.message, - text="⏳ Пожалуйста, подождите, создаем вам подключение...", + text=CREATING_CONNECTION_MSG, reply_markup=None, ) @@ -129,7 +136,7 @@ async def handle_key_creation( await edit_or_send_message( target_message=target_message, - text="💳 Выберите тарифный план для создания нового ключа:", + text=SELECT_TARIFF_PLAN_MSG, reply_markup=builder.as_markup(), media_path=None, ) @@ -171,7 +178,7 @@ async def select_tariff_plan(callback_query: CallbackQuery, session: Any): builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) await edit_or_send_message( target_message=callback_query.message, - text=f"💳 Недостаточно средств. Для продолжения необходимо пополнить баланс на {required_amount}₽.", + text=INSUFFICIENT_FUNDS_MSG.format(required_amount=required_amount), reply_markup=builder.as_markup(), media_path=None, ) @@ -181,7 +188,7 @@ async def select_tariff_plan(callback_query: CallbackQuery, session: Any): await edit_or_send_message( target_message=callback_query.message, - text="⏳ Подождите, создаем вам подключение...", + text=CREATING_CONNECTION_MSG, reply_markup=builder.as_markup(), ) @@ -232,14 +239,14 @@ async def create_key( if target_message: await edit_or_send_message( target_message=target_message, - text="🌍 Пожалуйста, выберите страну для вашего ключа:", + text=SELECT_COUNTRY_MSG, reply_markup=builder.as_markup(), media_path=None, ) else: await bot.send_message( chat_id=tg_id, - text="🌍 Пожалуйста, выберите страну для вашего ключа:", + text=SELECT_COUNTRY_MSG, reply_markup=builder.as_markup(), ) return diff --git a/handlers/keys/keys.py b/handlers/keys/keys.py index 1c603573..28eee02a 100644 --- a/handlers/keys/keys.py +++ b/handlers/keys/keys.py @@ -65,6 +65,15 @@ from handlers.texts import ( SUBSCRIPTION_DESCRIPTION, SUCCESS_RENEWAL_MSG, key_message, + NO_SUBSCRIPTIONS_MSG, + FROZEN_SUBSCRIPTION_MSG, + UNFREEZE_SUBSCRIPTION_CONFIRM_MSG, + SUBSCRIPTION_UNFROZEN_MSG, + FREEZE_SUBSCRIPTION_CONFIRM_MSG, + SUBSCRIPTION_FROZEN_MSG, + DELETE_KEY_CONFIRM_MSG, + KEY_DELETED_MSG_SIMPLE, + INSUFFICIENT_FUNDS_RENEWAL_MSG, ) from handlers.utils import edit_or_send_message, handle_error from logger import logger @@ -123,11 +132,8 @@ def build_keys_response(records): response_message += f"• {key_name} ({formatted_date_full})\n" response_message += "\n" - else: - response_message = ( - "🔑 У вас пока нет подписок.\n\nВы можете создать новую подписку для подключения устройств." - ) + response_message = NO_SUBSCRIPTIONS_MSG builder.row(InlineKeyboardButton(text="➕ Добавить подписку", callback_data="create_key")) builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) @@ -146,7 +152,7 @@ async def process_callback_view_key(callback_query: CallbackQuery, session: Any) is_frozen = record["is_frozen"] if is_frozen: - response_message = "Подписка заморожена.\nДата истечения будет обновлена после разморозки." + response_message = FROZEN_SUBSCRIPTION_MSG builder = InlineKeyboardBuilder() builder.row( @@ -283,9 +289,7 @@ async def process_callback_view_key(callback_query: CallbackQuery, session: Any) @router.callback_query(F.data.startswith("unfreeze_subscription|")) async def process_callback_unfreeze_subscription(callback_query: CallbackQuery, session: Any): key_name = callback_query.data.split("|")[1] - confirm_text = ( - "Хотите включить (разморозить) подписку?\n\nПосле включения доступа трафик и время снова начнут расходоваться." - ) + confirm_text = UNFREEZE_SUBSCRIPTION_CONFIRM_MSG builder = InlineKeyboardBuilder() builder.row( @@ -350,9 +354,9 @@ async def process_callback_unfreeze_subscription_confirm(callback_query: Callbac email=email, client_id=client_id, new_expiry_time=new_expiry_time, - total_gb=TOTAL_GB, + total_gb=TOTAL_GB ) - text_ok = "✅ Подписка успешно включена.\n\nТеперь трафик и время подписки будут расходоваться." + text_ok = SUBSCRIPTION_UNFROZEN_MSG builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}")) await edit_or_send_message( @@ -384,11 +388,7 @@ async def process_callback_freeze_subscription(callback_query: CallbackQuery, se tg_id = callback_query.message.chat.id key_name = callback_query.data.split("|")[1] - confirm_text = ( - "Вы можете заморозить (отключить) свою подписку на любой удобный срок, если временно не будете " - "пользоваться VPN. Включить обратно можно будет в этом же меню.\n\n" - "Вы уверены, что хотите заморозить подписку?" - ) + confirm_text = FREEZE_SUBSCRIPTION_CONFIRM_MSG builder = InlineKeyboardBuilder() builder.row( @@ -448,10 +448,7 @@ async def process_callback_freeze_subscription_confirm(callback_query: CallbackQ client_id, ) - text_ok = ( - "✅ Подписка успешно заморожена.\n\n" - "Чтобы включить обратно, зайдите в меню ключа и нажмите «Включить подписку»." - ) + text_ok = SUBSCRIPTION_FROZEN_MSG builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="⬅️ Назад", callback_data=f"view_key|{key_name}")) await edit_or_send_message( @@ -552,17 +549,61 @@ async def process_callback_delete_key(callback_query: CallbackQuery): if callback_query.message.caption: await callback_query.message.edit_caption( - caption="Вы уверены, что хотите удалить ключ?", reply_markup=confirmation_keyboard + caption=DELETE_KEY_CONFIRM_MSG, + reply_markup=confirmation_keyboard ) else: await callback_query.message.edit_text( - text="Вы уверены, что хотите удалить ключ?", reply_markup=confirmation_keyboard + text=DELETE_KEY_CONFIRM_MSG, + reply_markup=confirmation_keyboard ) except Exception as e: logger.error(f"Ошибка при обработке запроса на удаление ключа {client_id}: {e}") +@router.callback_query(F.data.startswith("confirm_delete|")) +async def process_callback_confirm_delete(callback_query: CallbackQuery, session: Any): + email = callback_query.data.split("|")[1] + try: + record = await get_key_details(email, session) + if record: + client_id = record["client_id"] + response_message = KEY_DELETED_MSG_SIMPLE + back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_keys") + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + + await delete_key(client_id, session) + + await edit_or_send_message( + target_message=callback_query.message, text=response_message, reply_markup=keyboard, media_path=None + ) + + servers = await get_servers(session) + + async def delete_key_from_servers(): + try: # lol + tasks = [] + for cluster_id, _cluster in servers.items(): + tasks.append(delete_key_from_cluster(cluster_id, email, client_id)) + await asyncio.gather(*tasks, return_exceptions=True) + except Exception as e: + logger.error(f"Ошибка при удалении ключа {client_id}: {e}") + + asyncio.create_task(delete_key_from_servers()) + + await delete_key(client_id, session) + else: + response_message = "Ключ не найден или уже удален." + back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_keys") + keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) + await edit_or_send_message( + target_message=callback_query.message, text=response_message, reply_markup=keyboard, media_path=None + ) + except Exception as e: + logger.error(e) + + @router.callback_query(F.data.startswith("renew_key|")) async def process_callback_renew_key(callback_query: CallbackQuery, session: Any): tg_id = callback_query.message.chat.id @@ -610,48 +651,6 @@ async def process_callback_renew_key(callback_query: CallbackQuery, session: Any logger.error(e) -@router.callback_query(F.data.startswith("confirm_delete|")) -async def process_callback_confirm_delete(callback_query: CallbackQuery, session: Any): - email = callback_query.data.split("|")[1] - try: - record = await get_key_details(email, session) - if record: - client_id = record["client_id"] - response_message = "Ключ успешно удален." - back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_keys") - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) - - await delete_key(client_id, session) - - await edit_or_send_message( - target_message=callback_query.message, text=response_message, reply_markup=keyboard, media_path=None - ) - - servers = await get_servers(session) - - async def delete_key_from_servers(): - try: # lol - tasks = [] - for cluster_id, _cluster in servers.items(): - tasks.append(delete_key_from_cluster(cluster_id, email, client_id)) - await asyncio.gather(*tasks, return_exceptions=True) - except Exception as e: - logger.error(f"Ошибка при удалении ключа {client_id}: {e}") - - asyncio.create_task(delete_key_from_servers()) - - await delete_key(client_id, session) - else: - response_message = "Ключ не найден или уже удален." - back_button = types.InlineKeyboardButton(text="Назад", callback_data="view_keys") - keyboard = types.InlineKeyboardMarkup(inline_keyboard=[[back_button]]) - await edit_or_send_message( - target_message=callback_query.message, text=response_message, reply_markup=keyboard, media_path=None - ) - except Exception as e: - logger.error(e) - - @router.callback_query(F.data.startswith("renew_plan|")) async def process_callback_renew_plan(callback_query: CallbackQuery, session: Any): tg_id = callback_query.message.chat.id @@ -713,7 +712,7 @@ async def process_callback_renew_plan(callback_query: CallbackQuery, session: An await edit_or_send_message( target_message=callback_query.message, - text=f"💳 Недостаточно средств. Пополните баланс на {required_amount}₽.", + text=INSUFFICIENT_FUNDS_RENEWAL_MSG.format(required_amount=required_amount), reply_markup=builder.as_markup(), media_path=None, ) diff --git a/handlers/notifications/general_notifications.py b/handlers/notifications/general_notifications.py index 2895f305..d11f6695 100644 --- a/handlers/notifications/general_notifications.py +++ b/handlers/notifications/general_notifications.py @@ -1,7 +1,5 @@ import asyncio - from datetime import datetime, timedelta - import asyncpg import pytz @@ -19,7 +17,6 @@ from config import ( TRIAL_TIME_DISABLE, NOTIFY_INACTIVE_TRAFFIC, ) - from database import ( add_notification, check_notification_time, @@ -31,19 +28,26 @@ from database import ( update_key_expiry, ) from handlers.keys.key_utils import delete_key_from_cluster, renew_key_in_cluster -from handlers.texts import KEY_EXPIRY_10H, KEY_EXPIRY_24H, KEY_RENEWED +from handlers.texts import ( + KEY_EXPIRY_10H, + KEY_EXPIRY_24H, + KEY_RENEWED, + KEY_RENEWED_TEMP_MSG, + KEY_DELETED_MSG, + KEY_EXPIRED_DELAY_HOURS_MINUTES_MSG, + KEY_EXPIRED_DELAY_HOURS_MSG, + KEY_EXPIRED_DELAY_MINUTES_MSG, + KEY_EXPIRED_NO_DELAY_MSG, +) from keyboards.notifications.notify_kb import build_notification_expired_kb, build_notification_kb from logger import logger - from .notify_utils import send_notification from .special_notifications import notify_inactive_trial_users, notify_users_no_traffic - router = Router() moscow_tz = pytz.timezone("Europe/Moscow") - async def periodic_notifications(bot: Bot): """ Обработчик, который: @@ -94,7 +98,6 @@ async def periodic_notifications(bot: Bot): await asyncio.sleep(NOTIFICATION_TIME) - async def notify_24h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, threshold_time_24h: int, keys: list): logger.info("Начало проверки подписок, истекающих через 24 часа.") @@ -146,7 +149,6 @@ async def notify_24h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, logger.info("✅ Обработка всех уведомлений за 24 часа завершена.") await asyncio.sleep(1) - async def notify_10h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, threshold_time_10h: int, keys: list): """ Отправляет уведомления пользователям о том, что их подписка истекает через 10 часов. @@ -206,7 +208,6 @@ async def notify_10h_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, logger.info("✅ Обработка всех уведомлений за 10 часов завершена.") await asyncio.sleep(1) - async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: int, keys: list): """ Обрабатывает истекшие ключи, проверяя продление или удаление. @@ -242,7 +243,7 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: if balance >= renewal_cost: try: await process_auto_renew_or_notify( - bot, conn, key, notification_id, 1, "notify_expired.jpg", "Ваш ключ продлён!" + bot, conn, key, notification_id, 1, "notify_expired.jpg", KEY_RENEWED_TEMP_MSG ) except Exception as e: logger.error(f"Ошибка авто-продления для пользователя {tg_id}: {e}") @@ -271,10 +272,7 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: bot, tg_id, "notify_expired.jpg", - ( - f"Ваша подписка {email} была удалена, так как вы не продлили её действие.\n\n" - "Перейдите в личный кабинет и получите новую!" - ), + KEY_DELETED_MSG.format(email=email), keyboard, ) logger.info(f"📢 Отправлено уведомление об удалении подписки {email} пользователю {tg_id}.") @@ -293,14 +291,15 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: if hours > 0: if minutes > 0: - delay_message = f"⚠️ Ваша подписка {email} истекла.\n\nЕсли вы не продлите её, она будет удалена через {hours} час{'а' if hours == 1 else 'ов'} и {minutes} минут." + delay_message = KEY_EXPIRED_DELAY_HOURS_MINUTES_MSG.format( + email=email, hours=hours, minutes=minutes + ) else: - delay_message = f"⚠️ Ваша подписка {email} истекла.\n\nЕсли вы не продлите её, она будет удалена через {hours} час{'а' if hours == 1 else 'ов'}." + delay_message = KEY_EXPIRED_DELAY_HOURS_MSG.format(email=email, hours=hours) else: - delay_message = f"⚠️ Ваша подписка {email} истекла.\n\nЕсли вы не продлите её, она будет удалена через {NOTIFY_DELETE_DELAY} минут." - + delay_message = KEY_EXPIRED_DELAY_MINUTES_MSG.format(email=email, minutes=NOTIFY_DELETE_DELAY) else: - delay_message = f"⚠ Ваша подписка {email} истекла!\n\nПродлите доступ, чтобы возобновить услуги." + delay_message = KEY_EXPIRED_NO_DELAY_MSG.format(email=email) try: await send_notification( @@ -320,7 +319,6 @@ async def handle_expired_keys(bot: Bot, conn: asyncpg.Connection, current_time: logger.info("✅ Обработка истекших ключей завершена.") await asyncio.sleep(1) - async def process_auto_renew_or_notify( bot, conn, key: dict, notification_id: str, renewal_period_months: int, standard_photo: str, standard_caption: str ): @@ -341,7 +339,6 @@ async def process_auto_renew_or_notify( return balance = await get_balance(tg_id) - except Exception as e: logger.error(f"Ошибка получения данных для пользователя {tg_id}: {e}") return @@ -384,12 +381,10 @@ async def process_auto_renew_or_notify( keyboard = build_notification_expired_kb() await send_notification(bot, tg_id, "notify_expired.jpg", renewed_message, keyboard) - except KeyError as e: logger.error(f"❌ Ошибка форматирования сообщения KEY_RENEWED: отсутствует ключ {e}") except Exception as e: logger.error(f"❌ Ошибка при продлении ключа {client_id} для пользователя {tg_id}: {e}") - else: keyboard = build_notification_kb(email) await send_notification(bot, tg_id, standard_photo, standard_caption, keyboard) diff --git a/handlers/notifications/special_notifications.py b/handlers/notifications/special_notifications.py index c4a57d7f..9ca81461 100644 --- a/handlers/notifications/special_notifications.py +++ b/handlers/notifications/special_notifications.py @@ -15,6 +15,7 @@ from database import ( check_notification_time, create_blocked_user, ) +from handlers.texts import TRIAL_INACTIVE_FIRST_MSG, TRIAL_INACTIVE_BONUS_MSG, ZERO_TRAFFIC_MSG from handlers.keys.key_utils import get_user_traffic from logger import logger @@ -81,24 +82,17 @@ async def notify_inactive_trial_users(bot: Bot, conn: asyncpg.Connection): ) if trial_extended: - message = ( - f"{display_name}, у нас для тебя подарок! 🎁\n\n" - "
" - f"Мы добавили тебе +{NOTIFY_EXTRA_DAYS} дополнительных дня к пробному периоду!\n" - f"Теперь у тебя есть еще шанс протестировать наш VPN целых {NOTIFY_EXTRA_DAYS + TRIAL_TIME} дня!\n" - "" - "Нажми на кнопку ниже, чтобы активировать доступ с бонусом +2 дня! 👇" + total_days = NOTIFY_EXTRA_DAYS + TRIAL_TIME + message = TRIAL_INACTIVE_BONUS_MSG.format( + display_name=display_name, + NOTIFY_EXTRA_DAYS=NOTIFY_EXTRA_DAYS, + total_days=total_days ) - await conn.execute("UPDATE connections SET trial = -1 WHERE tg_id = $1", tg_id) else: - message = ( - f"👋 Привет, {display_name}!\n\n" - "
" - f"🎉 У тебя есть бесплатный пробный период на {TRIAL_TIME} дней!\n" - "Не упусти возможность попробовать наш VPN прямо сейчас.\n" - "" - "Нажми на кнопку ниже, чтобы активировать пробный доступ! 👇" + message = TRIAL_INACTIVE_FIRST_MSG.format( + display_name=display_name, + TRIAL_TIME=TRIAL_TIME ) try: @@ -182,12 +176,7 @@ async def notify_users_no_traffic(bot: Bot, conn: asyncpg.Connection, current_ti builder.row(types.InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) keyboard = builder.as_markup() - message = ( - f"⚠ Ваша VPN-подписка {email} активна, но трафик не используется.\n\n" - "
Если у вас возникли сложности с подключением, " - "нажмите кнопку ниже, чтобы связаться с поддержкой.\n\n" - "🛠 Мы поможем вам разобраться! 💡" - ) + message = ZERO_TRAFFIC_MSG.format(email=email) try: await bot.send_message(tg_id, message, reply_markup=keyboard) diff --git a/handlers/pay.py b/handlers/pay.py index fb5e5e7e..036df442 100644 --- a/handlers/pay.py +++ b/handlers/pay.py @@ -9,7 +9,7 @@ from config import ( YOOKASSA_ENABLE, YOOMONEY_ENABLE, ) - +from handlers.texts import PAYMENT_METHODS_MSG from .utils import edit_or_send_message @@ -59,18 +59,9 @@ async def handle_pay(callback_query: CallbackQuery): builder.row(InlineKeyboardButton(text="💰 Поддержать проект", callback_data="donate")) builder.row(InlineKeyboardButton(text="👤 Личный кабинет", callback_data="profile")) - payment_text = ( - "💸 Выберите удобный способ пополнения баланса:\n" - "
" - "• Быстро и безопасно\n" - "• Поддержка разных платежных систем\n" - "• Моментальное зачисление средств 🚀\n" - "" - ) - await edit_or_send_message( target_message=callback_query.message, - text=payment_text, + text=PAYMENT_METHODS_MSG, reply_markup=builder.as_markup(), media_path=None, disable_web_page_preview=False, diff --git a/handlers/profile.py b/handlers/profile.py index a76a4328..043d3e52 100644 --- a/handlers/profile.py +++ b/handlers/profile.py @@ -40,8 +40,7 @@ from handlers.buttons.profile import ( MY_SUBS, PAYMENT, ) -from handlers.texts import get_referral_link, invite_message_send, profile_message_send -from ..panel.keyboard import AdminPanelCallback +from keyboards.admin.panel_kb import AdminPanelCallback from logger import logger from .utils import edit_or_send_message @@ -130,7 +129,8 @@ async def balance_handler(callback_query: CallbackQuery, session: Any): builder.row(InlineKeyboardButton(text="🎟️ Активировать купон", callback_data="activate_coupon")) builder.row(InlineKeyboardButton(text=MAIN_MENU, callback_data="profile")) - text = f"Управление вашим балансом 💰\n\nВаш баланс: {balance}" + text = BALANCE_MANAGEMENT_TEXT.format(balance=balance) + await edit_or_send_message( target_message=callback_query.message, text=text, @@ -149,7 +149,7 @@ async def balance_history_handler(callback_query: CallbackQuery, session: Any): records = await get_last_payments(callback_query.from_user.id, session) if records: - history_text = "📊 Последние 3 операции с балансом:\n\n" + history_text = BALANCE_HISTORY_HEADER for record in records: amount = record["amount"] payment_system = record["payment_system"] @@ -216,7 +216,7 @@ async def invite_handler(callback_query_or_message: Message | CallbackQuery): if INLINE_MODE: builder.button(text="👥 Пригласить друга", switch_inline_query="invite") else: - invite_text = f"\nПриглашаю тебя пользоваться действительно быстрым VPN вместе:\n\n{referral_link}" + invite_text = INVITE_TEXT_NON_INLINE.format(referral_link=referral_link) builder.button(text="👥 Пригласить друга", switch_inline_query=invite_text) builder.button(text="👤 Личный кабинет", callback_data="profile") builder.adjust(1) diff --git a/handlers/start.py b/handlers/start.py index 7dd91283..10a8667e 100644 --- a/handlers/start.py +++ b/handlers/start.py @@ -1,5 +1,4 @@ import os - from typing import Any from aiogram import F, Router @@ -32,8 +31,19 @@ from database import ( ) from handlers.captcha import generate_captcha from handlers.keys.key_management import create_key -from handlers.texts import WELCOME_TEXT, get_about_vpn -from ..panel.keyboard import AdminPanelCallback +from handlers.texts import ( + WELCOME_TEXT, + get_about_vpn, + SUBSCRIPTION_REQUIRED_MSG, + NOT_SUBSCRIBED_YET_MSG, + SUBSCRIPTION_CONFIRMED_MSG, + SUBSCRIPTION_CHECK_ERROR_MSG, + GIFT_ALREADY_USED_OR_NOT_EXISTS_MSG, + REFERRAL_SUCCESS_MSG, + NEW_REFERRAL_NOTIFICATION, + COUPON_SUCCESS_MSG, +) +from keyboards.admin.panel_kb import AdminPanelCallback from logger import logger from .utils import edit_or_send_message @@ -75,7 +85,7 @@ async def start_command(message: Message, state: FSMContext, session: Any, admin builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data="check_subscription")) await edit_or_send_message( target_message=message, - text=f"Для использования бота, пожалуйста, подпишитесь на наш канал: {CHANNEL_URL}", + text=SUBSCRIPTION_REQUIRED_MSG, reply_markup=builder.as_markup(), ) return @@ -90,7 +100,7 @@ async def start_command(message: Message, state: FSMContext, session: Any, admin builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data="check_subscription")) await edit_or_send_message( target_message=message, - text=f"Пожалуйста, подпишитесь на наш канал: {CHANNEL_URL}", + text=SUBSCRIPTION_REQUIRED_MSG, reply_markup=builder.as_markup(), ) return @@ -161,7 +171,7 @@ async def process_start_logic( logger.info( f"Купон {coupon_code} успешно использован пользователем {message.chat.id}, начислено {coupon['amount']} RUB." ) - await message.answer(f"🎉 Ваш баланс пополнен на {coupon['amount']} RUB по купону!") + await message.answer(COUPON_SUCCESS_MSG.format(amount=coupon["amount"])) return await show_start_menu(message, admin, session) if "gift_" in text: @@ -184,7 +194,7 @@ async def process_start_logic( if gift_info is None: logger.warning(f"Подарок с ID {gift_id} уже был использован или не существует.") - await message.answer("Этот подарок уже был использован или не существует.") + await message.answer(GIFT_ALREADY_USED_OR_NOT_EXISTS_MSG) return await show_start_menu(message, admin, session) if gift_info["is_used"]: @@ -261,10 +271,11 @@ async def process_start_logic( await add_referral(message.chat.id, referrer_tg_id, session) logger.info(f"Реферал {message.chat.id} использовал ссылку от пользователя {referrer_tg_id}") - await message.answer(f"Вы стали рефералом пользователя с ID {referrer_tg_id}") + await message.answer(REFERRAL_SUCCESS_MSG.format(referrer_tg_id=referrer_tg_id)) try: await bot.send_message( - referrer_tg_id, f"🎉 Ваш реферал {message.chat.id} успешно зарегистрировался!" + referrer_tg_id, + NEW_REFERRAL_NOTIFICATION.format(referred_id=message.chat.id) ) logger.info( f"Уведомление отправлено пользователю {referrer_tg_id} о новом реферале {message.chat.id}" @@ -299,15 +310,15 @@ async def check_subscription_callback(callback_query: CallbackQuery, state: FSMC logger.info(f"[CALLBACK] Статус подписки пользователя {user_id}: {member.status}") if member.status not in ["member", "administrator", "creator"]: - await callback_query.answer("Вы еще не подписаны на канал!", show_alert=True) + await callback_query.answer(NOT_SUBSCRIBED_YET_MSG, show_alert=True) builder = InlineKeyboardBuilder() builder.row(InlineKeyboardButton(text="✅ Я подписался", callback_data="check_subscription")) await callback_query.message.edit_text( - f"Для использования бота, пожалуйста, подпишитесь на наш канал: {CHANNEL_URL}", + SUBSCRIPTION_REQUIRED_MSG, reply_markup=builder.as_markup(), ) else: - await callback_query.answer("Подписка подтверждена!") + await callback_query.answer(SUBSCRIPTION_CONFIRMED_MSG) data = await state.get_data() original_text = data.get("original_text") if not original_text: @@ -316,7 +327,7 @@ async def check_subscription_callback(callback_query: CallbackQuery, state: FSMC logger.info(f"[CALLBACK] Завершен вызов process_start_logic для пользователя {user_id}") except Exception as e: logger.error(f"[CALLBACK] Ошибка проверки подписки для пользователя {user_id}: {e}", exc_info=True) - await callback_query.answer("Ошибка проверки подписки, повторите попытку", show_alert=True) + await callback_query.answer(SUBSCRIPTION_CHECK_ERROR_MSG, show_alert=True) async def show_start_menu(message: Message, admin: bool, session: Any):