Merge pull request #168 from Vladless/upstream-168

Changes from upstream
This commit is contained in:
Oleg Kraev
2025-03-25 17:34:54 +03:00
committed by GitHub
9 changed files with 149 additions and 153 deletions
+2 -2
View File
@@ -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(),
}
+10 -6
View File
@@ -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="<b>🎫 Введите код купона:</b>\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 "<b>❌ Купон не найден</b> 🚫 или его использование ограничено. 🔒 Пожалуйста, проверьте код и попробуйте снова. 🔍"
return COUPON_NOT_FOUND_MSG
usage_exists = await check_coupon_usage(coupon_record["id"], user_id, session)
if usage_exists:
return "<b>❌ Вы уже активировали этот купон.</b> 🚫 Купоны могут быть активированы только один раз. 🔒"
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"<b>✅ Купон успешно активирован! 🎉</b>\n\nНа ваш баланс добавлено <b>{coupon_amount} рублей</b> 💰."
return COUPON_ACTIVATED_SUCCESS_MSG.format(coupon_amount=coupon_amount)
+14 -7
View File
@@ -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
+63 -64
View File
@@ -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"• <b>{key_name}</b> ({formatted_date_full})\n"
response_message += "</blockquote>\n"
else:
response_message = (
"<b>🔑 У вас пока нет подписок.</b>\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"
"<b>Вы уверены, что хотите заморозить подписку?</b>"
)
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="<b>Вы уверены, что хотите удалить ключ?</b>", reply_markup=confirmation_keyboard
caption=DELETE_KEY_CONFIRM_MSG,
reply_markup=confirmation_keyboard
)
else:
await callback_query.message.edit_text(
text="<b>Вы уверены, что хотите удалить ключ?</b>", 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,
)
+19 -24
View File
@@ -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)
+10 -21
View File
@@ -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"<b>{display_name}</b>, у нас для тебя подарок! 🎁\n\n"
"<blockquote>"
f"Мы добавили тебе +{NOTIFY_EXTRA_DAYS} дополнительных дня к пробному периоду!\n"
f"Теперь у тебя есть еще шанс протестировать наш VPN целых {NOTIFY_EXTRA_DAYS + TRIAL_TIME} дня!\n"
"</blockquote>"
"Нажми на кнопку ниже, чтобы активировать доступ с бонусом +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"👋 <b>Привет, {display_name}!</b>\n\n"
"<blockquote>"
f"🎉 У тебя есть бесплатный пробный период на {TRIAL_TIME} дней!\n"
"Не упусти возможность попробовать наш VPN прямо сейчас.\n"
"</blockquote>"
"Нажми на кнопку ниже, чтобы активировать пробный доступ! 👇"
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"⚠ <b>Ваша VPN-подписка {email} активна, но трафик не используется.</b>\n\n"
"<blockquote>Если у вас возникли сложности с подключением, "
"нажмите кнопку ниже, чтобы связаться с поддержкой.</blockquote>\n\n"
"🛠 Мы поможем вам разобраться! 💡"
)
message = ZERO_TRAFFIC_MSG.format(email=email)
try:
await bot.send_message(tg_id, message, reply_markup=keyboard)
+2 -11
View File
@@ -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 = (
"💸 <b>Выберите удобный способ пополнения баланса:</b>\n"
"<blockquote>"
"• Быстро и безопасно\n"
"• Поддержка разных платежных систем\n"
"• Моментальное зачисление средств 🚀\n"
"</blockquote>"
)
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,
+5 -5
View File
@@ -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"<b>Управление вашим балансом 💰</b>\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 = "📊 <b>Последние 3 операции с балансом:</b>\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)
+24 -13
View File
@@ -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):