fix: address code review issues in guide mode rework

- Add fallback else branch for subscriptionLink in blocks format
  (prevents silent button drop when deep link resolution fails)
- Extract render_guide_blocks() helper to eliminate duplicated
  block-rendering logic between handle_device_guide and
  handle_specific_app_guide
- Add HTML escaping for admin-controlled config text in guide blocks
- Remove unused get_localized_value import from devices.py
This commit is contained in:
Fringg
2026-02-24 05:18:25 +03:00
parent 5a269b249e
commit fae6f71def
4 changed files with 46 additions and 38 deletions

View File

@@ -26,6 +26,7 @@ from .common import (
load_app_config,
load_app_config_async,
normalize_app,
render_guide_blocks,
resolve_button_url,
update_traffic_prices,
validate_traffic_price,
@@ -190,6 +191,7 @@ __all__ = [
'normalize_app',
'refresh_traffic_config',
'register_handlers',
'render_guide_blocks',
'resolve_button_url',
'resume_subscription_checkout',
'return_to_saved_cart',

View File

@@ -1,5 +1,6 @@
import asyncio
import base64
import html as html_mod
import json
import time
from datetime import datetime
@@ -241,6 +242,31 @@ def format_additional_section(additional: Any, texts, language: str) -> str:
return '\n'.join(parts)
def render_guide_blocks(blocks: list[dict], language: str) -> str:
"""Render block-format guide steps to HTML text."""
parts: list[str] = []
step_num = 1
for block in blocks:
if not isinstance(block, dict):
continue
title = block.get('title', {})
desc = block.get('description', {})
title_text = html_mod.escape(
get_localized_value(title, language) if isinstance(title, dict) else str(title or '')
)
desc_text = html_mod.escape(get_localized_value(desc, language) if isinstance(desc, dict) else str(desc or ''))
if title_text or desc_text:
step = f'<b>Шаг {step_num}'
if title_text:
step += f' - {title_text}'
step += ':</b>'
if desc_text:
step += f'\n{desc_text}'
parts.append(step)
step_num += 1
return '\n\n'.join(parts)
def build_redirect_link(target_link: str | None, template: str | None) -> str | None:
if not target_link or not template:
return None

View File

@@ -40,9 +40,9 @@ from .common import (
get_apps_for_device,
get_apps_for_platform_async,
get_device_name,
get_localized_value,
get_step_description,
logger,
render_guide_blocks,
)
from .countries import _get_available_countries
@@ -1345,24 +1345,9 @@ async def handle_device_guide(callback: types.CallbackQuery, db_user: User, db:
)
if is_blocks_format:
# Build guide text from blocks
step_num = 1
for block in featured_app.get('blocks', []):
if not isinstance(block, dict):
continue
title = block.get('title', {})
desc = block.get('description', {})
title_text = get_localized_value(title, db_user.language) if isinstance(title, dict) else str(title or '')
desc_text = get_localized_value(desc, db_user.language) if isinstance(desc, dict) else str(desc or '')
if title_text or desc_text:
guide_text += f'\n\n<b>Шаг {step_num}'
if title_text:
guide_text += f' - {title_text}'
guide_text += ':</b>'
if desc_text:
guide_text += f'\n{desc_text}'
step_num += 1
blocks_text = render_guide_blocks(featured_app.get('blocks', []), db_user.language)
if blocks_text:
guide_text += '\n\n' + blocks_text
else:
# Legacy steps
installation_description = get_step_description(
@@ -1546,25 +1531,9 @@ async def handle_specific_app_guide(callback: types.CallbackQuery, db_user: User
)
if is_blocks_format:
# Build guide from blocks
step_num = 1
for block in app.get('blocks', []):
if not isinstance(block, dict):
continue
title = block.get('title', {})
desc = block.get('description', {})
title_text = get_localized_value(title, db_user.language) if isinstance(title, dict) else str(title or '')
desc_text = get_localized_value(desc, db_user.language) if isinstance(desc, dict) else str(desc or '')
if title_text or desc_text:
guide_text += f'<b>Шаг {step_num}'
if title_text:
guide_text += f' - {title_text}'
guide_text += ':</b>'
if desc_text:
guide_text += f'\n{desc_text}'
guide_text += '\n\n'
step_num += 1
blocks_text = render_guide_blocks(app.get('blocks', []), db_user.language)
if blocks_text:
guide_text += blocks_text + '\n\n'
else:
raw_app = app.get('_raw', app)
installation_description = get_step_description(raw_app, 'installationStep', db_user.language)

View File

@@ -2437,6 +2437,17 @@ def get_connection_guide_keyboard(
)
]
)
else:
# Fallback: use raw subscription URL
keyboard.append(
[
InlineKeyboardButton(
text=texts.t('CONNECT_BUTTON', '🔗 Подключиться'),
url=subscription_url,
style='success',
)
]
)
elif btn_type == 'copyButton':
url = resolved_url or resolve_button_url(btn_url, subscription_url)
if url: