diff --git a/app/handlers/subscription/__init__.py b/app/handlers/subscription/__init__.py index 72d95127..c005e8a0 100644 --- a/app/handlers/subscription/__init__.py +++ b/app/handlers/subscription/__init__.py @@ -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', diff --git a/app/handlers/subscription/common.py b/app/handlers/subscription/common.py index a98870bd..471b42f6 100644 --- a/app/handlers/subscription/common.py +++ b/app/handlers/subscription/common.py @@ -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'Шаг {step_num}' + if title_text: + step += f' - {title_text}' + step += ':' + 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 diff --git a/app/handlers/subscription/devices.py b/app/handlers/subscription/devices.py index a598ff8f..0d5ff1c2 100644 --- a/app/handlers/subscription/devices.py +++ b/app/handlers/subscription/devices.py @@ -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Шаг {step_num}' - if title_text: - guide_text += f' - {title_text}' - guide_text += ':' - 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'Шаг {step_num}' - if title_text: - guide_text += f' - {title_text}' - guide_text += ':' - 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) diff --git a/app/keyboards/inline.py b/app/keyboards/inline.py index d59be2d0..6b83fc01 100644 --- a/app/keyboards/inline.py +++ b/app/keyboards/inline.py @@ -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: