Merge pull request #168 from Vladless/upstream-168
Changes from upstream
This commit is contained in:
+2
-2
@@ -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
@@ -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)
|
||||
|
||||
@@ -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
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user