Merge pull request #346 from Fr1ngg/bedolaga/fix-subscription-link-generation-issue-xh6pew

Handle Happ crypto links in subscription flows
This commit is contained in:
Egor
2025-09-25 11:01:09 +03:00
committed by GitHub
4 changed files with 169 additions and 25 deletions

View File

@@ -567,14 +567,20 @@ async def show_subscription_info(
subscription_link = get_display_subscription_link(subscription)
if subscription_link:
if actual_status in ['trial_active', 'paid_active'] and not settings.HIDE_SUBSCRIPTION_LINK:
message += "\n\n" + texts.t(
"SUBSCRIPTION_CONNECT_LINK_SECTION",
"🔗 <b>Ссылка для подключения:</b>\n<code>{subscription_url}</code>",
).format(subscription_url=subscription_link)
message += "\n\n" + texts.t(
"SUBSCRIPTION_CONNECT_LINK_PROMPT",
"📱 Скопируйте ссылку и добавьте в ваше VPN приложение",
)
if settings.is_happ_cryptolink_mode():
message += "\n\n" + texts.t(
'HAPP_CRYPTO_LINK_BUTTON_PROMPT',
'🔐 <b>Подключение через Happ</b>\nНажмите кнопку «Подключиться» ниже, чтобы получить защищённую ссылку.'
)
else:
message += "\n\n" + texts.t(
'SUBSCRIPTION_CONNECT_LINK_SECTION',
'🔗 <b>Ссылка для подключения:</b>\n<code>{subscription_url}</code>'
).format(subscription_url=subscription_link)
message += "\n\n" + texts.t(
'SUBSCRIPTION_CONNECT_LINK_PROMPT',
'📱 Скопируйте ссылку и добавьте в ваше VPN приложение'
)
await callback.message.edit_text(
message,
@@ -839,10 +845,16 @@ async def activate_trial(
subscription_link = get_display_subscription_link(subscription)
if remnawave_user and subscription_link:
subscription_import_link = texts.t(
"SUBSCRIPTION_IMPORT_LINK_SECTION",
"🔗 <b>Ваша ссылка для импорта в VPN приложение:</b>\\n<code>{subscription_url}</code>",
).format(subscription_url=subscription_link)
if settings.is_happ_cryptolink_mode():
subscription_import_link = texts.t(
"HAPP_CRYPTO_LINK_BUTTON_PROMPT",
"🔐 <b>Подключение через Happ</b>\\nНажмите кнопку «Подключиться» ниже, чтобы получить защищённую ссылку.",
)
else:
subscription_import_link = texts.t(
"SUBSCRIPTION_IMPORT_LINK_SECTION",
"🔗 <b>Ваша ссылка для импорта в VPN приложение:</b>\\n<code>{subscription_url}</code>",
).format(subscription_url=subscription_link)
trial_success_text = (
f"{texts.TRIAL_ACTIVATED}\n\n"
@@ -882,7 +894,7 @@ async def activate_trial(
],
[InlineKeyboardButton(text=texts.t("BACK_TO_MAIN_MENU_BUTTON", "⬅️ В главное меню"), callback_data="back_to_menu")],
])
elif connect_mode in {"link", "happ_cryptolink"}:
elif connect_mode == "link":
rows = [
[InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), url=subscription_link)]
]
@@ -896,6 +908,25 @@ async def activate_trial(
)
])
connect_keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
elif connect_mode == "happ_cryptolink":
rows = [
[
InlineKeyboardButton(
text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"),
callback_data="open_happ_subscription_link"
)
]
]
happ_row = get_happ_download_button_row(texts)
if happ_row:
rows.append(happ_row)
rows.append([
InlineKeyboardButton(
text=texts.t("BACK_TO_MAIN_MENU_BUTTON", "⬅️ В главное меню"),
callback_data="back_to_menu"
)
])
connect_keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
else:
connect_keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), callback_data="subscription_connect")],
@@ -2693,8 +2724,18 @@ async def get_subscription_info_text(subscription, texts, db_user, db: AsyncSess
info_text += f"\n💰 <b>Стоимость подписки в месяц:</b> {texts.format_price(subscription_cost)}"
if subscription_url and subscription_url != "Генерируется...":
info_text += f"\n\n🔗 <b>Ваша ссылка для импорта в VPN приложениe:</b>\n<code>{subscription_url}</code>"
if settings.is_happ_cryptolink_mode():
info_text += (
"\n\n"
+ texts.t(
'HAPP_CRYPTO_LINK_BUTTON_PROMPT',
'🔐 <b>Подключение через Happ</b>\nНажмите кнопку «Подключиться» ниже, чтобы получить защищённую ссылку.'
)
)
else:
info_text += (
f"\n\n🔗 <b>Ваша ссылка для импорта в VPN приложениe:</b>\n<code>{subscription_url}</code>"
)
return info_text
def format_traffic_display(traffic_gb: int, is_fixed_mode: bool = None) -> str:
@@ -3295,14 +3336,24 @@ async def confirm_purchase(
subscription_link = get_display_subscription_link(subscription)
if remnawave_user and subscription_link:
import_link_section = texts.t(
"SUBSCRIPTION_IMPORT_LINK_SECTION",
"🔗 <b>Ваша ссылка для импорта в VPN приложение:</b>\\n<code>{subscription_url}</code>",
).format(subscription_url=subscription_link)
if settings.is_happ_cryptolink_mode():
import_link_section = texts.t(
'HAPP_CRYPTO_LINK_BUTTON_PROMPT',
'🔐 <b>Подключение через Happ</b>\nНажмите кнопку «Подключиться» ниже, чтобы получить защищённую ссылку.'
)
else:
import_link_section = texts.t(
'SUBSCRIPTION_IMPORT_LINK_SECTION',
'🔗 <b>Ваша ссылка для импорта в VPN приложение:</b>\n<code>{subscription_url}</code>'
).format(subscription_url=subscription_link)
success_text = (
f"{texts.SUBSCRIPTION_PURCHASED}\n\n"
f"{import_link_section}\n\n"
f"{texts.SUBSCRIPTION_PURCHASED}
"
f"{import_link_section}
"
f"{texts.t('SUBSCRIPTION_IMPORT_INSTRUCTION_PROMPT', '📱 Нажмите кнопку ниже, чтобы получить инструкцию по настройке VPN на вашем устройстве')}"
)
@@ -3338,7 +3389,7 @@ async def confirm_purchase(
],
[InlineKeyboardButton(text=texts.t("BACK_TO_MAIN_MENU_BUTTON", "⬅️ В главное меню"), callback_data="back_to_menu")],
])
elif connect_mode in {"link", "happ_cryptolink"}:
elif connect_mode == "link":
rows = [
[InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), url=subscription_link)]
]
@@ -3347,6 +3398,15 @@ async def confirm_purchase(
rows.append(happ_row)
rows.append([InlineKeyboardButton(text=texts.t("BACK_TO_MAIN_MENU_BUTTON", "⬅️ В главное меню"), callback_data="back_to_menu")])
connect_keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
elif connect_mode == "happ_cryptolink":
rows = [
[InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), callback_data="open_happ_subscription_link")]
]
happ_row = get_happ_download_button_row(texts)
if happ_row:
rows.append(happ_row)
rows.append([InlineKeyboardButton(text=texts.t("BACK_TO_MAIN_MENU_BUTTON", "⬅️ В главное меню"), callback_data="back_to_menu")])
connect_keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
else:
connect_keyboard = InlineKeyboardMarkup(inline_keyboard=[
[InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), callback_data="subscription_connect")],
@@ -4185,7 +4245,7 @@ async def handle_connect_subscription(
parse_mode="HTML"
)
elif connect_mode in {"link", "happ_cryptolink"}:
elif connect_mode == "link":
rows = [
[
InlineKeyboardButton(
@@ -4213,6 +4273,32 @@ async def handle_connect_subscription(
reply_markup=keyboard,
parse_mode="HTML"
)
elif connect_mode == "happ_cryptolink":
rows = [
[
InlineKeyboardButton(
text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"),
callback_data="open_happ_subscription_link"
)
]
]
happ_row = get_happ_download_button_row(texts)
if happ_row:
rows.append(happ_row)
rows.append([
InlineKeyboardButton(text=texts.BACK, callback_data="menu_subscription")
])
keyboard = InlineKeyboardMarkup(inline_keyboard=rows)
await callback.message.edit_text(
texts.t(
"HAPP_CRYPTO_LINK_BUTTON_PROMPT",
"🔐 <b>Подключение через Happ</b>\nНажмите кнопку «Подключиться» ниже, чтобы получить защищённую ссылку.",
),
reply_markup=keyboard,
parse_mode="HTML"
)
else:
device_text = texts.t(
@@ -4498,6 +4584,36 @@ async def handle_no_traffic_packages(
)
async def handle_open_happ_subscription_link(
callback: types.CallbackQuery,
db_user: User,
db: AsyncSession
):
texts = get_texts(db_user.language)
subscription = db_user.subscription
subscription_link = get_display_subscription_link(subscription)
if not subscription_link:
await callback.answer(
texts.t("SUBSCRIPTION_LINK_UNAVAILABLE", "❌ Ссылка подписки недоступна"),
show_alert=True,
)
return
message_text = texts.t(
"HAPP_CRYPTO_LINK_MESSAGE",
"🔐 <b>Ссылка для Happ</b>\n\nНажмите на ссылку ниже, чтобы открыть Happ, или скопируйте её вручную:\n\n<code>{subscription_link}</code>\n\nЕсли приложение не открылось автоматически, откройте Happ и вставьте ссылку вручную.",
).format(subscription_link=subscription_link)
await callback.answer(texts.t("HAPP_CRYPTO_LINK_SENT", "🔐 Ссылка отправлена ниже."))
await callback.message.answer(
message_text,
parse_mode="HTML",
disable_web_page_preview=True,
)
async def handle_open_subscription_link(
callback: types.CallbackQuery,
db_user: User,
@@ -5260,6 +5376,11 @@ def register_handlers(dp: Dispatcher):
F.data == "open_subscription_link"
)
dp.callback_query.register(
handle_open_happ_subscription_link,
F.data == "open_happ_subscription_link"
)
dp.callback_query.register(
handle_subscription_settings,
F.data == "subscription_settings"

View File

@@ -113,7 +113,7 @@ def get_main_menu_keyboard(
web_app=types.WebAppInfo(url=settings.MINIAPP_CUSTOM_URL)
)
])
elif connect_mode in {"link", "happ_cryptolink"}:
elif connect_mode == "link":
if subscription_link:
keyboard.append([
InlineKeyboardButton(
@@ -123,6 +123,16 @@ def get_main_menu_keyboard(
])
else:
keyboard.append([_fallback_connect_button()])
elif connect_mode == "happ_cryptolink":
if subscription_link:
keyboard.append([
InlineKeyboardButton(
text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"),
callback_data="open_happ_subscription_link"
)
])
else:
keyboard.append([_fallback_connect_button()])
else:
keyboard.append([_fallback_connect_button()])
@@ -385,10 +395,17 @@ def get_subscription_keyboard(
keyboard.append([
InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), callback_data="subscription_connect")
])
elif connect_mode in {"link", "happ_cryptolink"}:
elif connect_mode == "link":
keyboard.append([
InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), url=subscription_link)
])
elif connect_mode == "happ_cryptolink":
keyboard.append([
InlineKeyboardButton(
text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"),
callback_data="open_happ_subscription_link"
)
])
else:
keyboard.append([
InlineKeyboardButton(text=texts.t("CONNECT_BUTTON", "🔗 Подключиться"), callback_data="subscription_connect")

View File

@@ -28,6 +28,9 @@
"HAPP_DOWNLOAD_LINK_MESSAGE": "⬇️ Download Happ for {platform}:",
"HAPP_DOWNLOAD_LINK_NOT_SET": "❌ Download link for this device is not configured",
"HAPP_DOWNLOAD_OPEN_LINK": "🔗 Open link",
"HAPP_CRYPTO_LINK_BUTTON_PROMPT": "🔐 <b>Connect via Happ</b>\nTap the “Connect” button below to reveal your secure link.",
"HAPP_CRYPTO_LINK_MESSAGE": "🔐 <b>Happ link</b>\n\nTap the link below to open Happ or copy it manually:\n\n<code>{subscription_link}</code>\n\nIf the app doesn't open automatically, launch Happ and paste the link manually.",
"HAPP_CRYPTO_LINK_SENT": "🔐 The link has been sent below.",
"CONTINUE": "➡️ Continue",
"CONTINUE_BUTTON": "➡️ Continue",
"COPY_SUBSCRIPTION_LINK": "📋 Copy subscription link",

View File

@@ -191,6 +191,9 @@
"HAPP_DOWNLOAD_LINK_MESSAGE": "⬇️ Скачайте Happ для {platform}:",
"HAPP_DOWNLOAD_LINK_NOT_SET": "❌ Ссылка для этого устройства не настроена",
"HAPP_DOWNLOAD_OPEN_LINK": "🔗 Открыть ссылку",
"HAPP_CRYPTO_LINK_BUTTON_PROMPT": "🔐 <b>Подключение через Happ</b>\nНажмите кнопку «Подключиться» ниже, чтобы получить защищённую ссылку.",
"HAPP_CRYPTO_LINK_MESSAGE": "🔐 <b>Ссылка для Happ</b>\n\nНажмите на ссылку ниже, чтобы открыть Happ, или скопируйте её вручную:\n\n<code>{subscription_link}</code>\n\nЕсли приложение не открылось автоматически, откройте Happ и вставьте ссылку вручную.",
"HAPP_CRYPTO_LINK_SENT": "🔐 Ссылка отправлена ниже.",
"CONTACT_SUPPORT": "💬 Написать в поддержку",
"CONTINUE": "➡️ Продолжить",
"CONTINUE_BUTTON": "✅ Продолжить",