Исправить лендинг: Telegram из интеграции telegram_bot, нормализация URL.

- Добавлен content_bot/landing_utils.py: получение ссылок MAX и Telegram из lk_integration по slug партнёра, нормализация URL и автоисправление записей в БД.
- landing_page больше не берёт Telegram из настроек max_bot; удалён устаревший fallback на url_telegram.
- Добавлены тесты нормализации URL и формирования кнопок лендинга.
main
pilot 4 weeks ago
parent e5a823c99f
commit a88aeba01a

@ -0,0 +1,123 @@
import json
from typing import Callable, Optional, Tuple
from max_bot.models import Client, Integration
def normalize_telegram_url(url: Optional[str]) -> Optional[str]:
"""Приводит ссылку на Telegram-бота к виду https://t.me/bot_name."""
if not url or not str(url).strip():
return None
value = str(url).strip()
if value.startswith('@'):
value = value[1:]
if value.startswith('https://'):
return value
if value.startswith('http://'):
return f'https://{value[7:]}'
if value.startswith('t.me/'):
return f'https://{value}'
return f'https://t.me/{value.lstrip("/")}'
def normalize_max_bot_url(url: Optional[str]) -> Optional[str]:
"""Приводит ссылку на MAX-бота к виду https://max.ru/bot_id."""
if not url or not str(url).strip():
return None
value = str(url).strip()
if value.startswith('https://max.ru/'):
return value
if value.startswith('http://max.ru/'):
return f'https://max.ru/{value[len("http://max.ru/"):]}'
if value.startswith('max.ru/'):
return f'https://{value}'
return f'https://max.ru/{value.lstrip("/")}'
def _load_settings(integration: Integration) -> dict:
if not integration.setting:
return {}
try:
settings = json.loads(integration.setting)
except json.JSONDecodeError:
return {}
return settings if isinstance(settings, dict) else {}
def _save_settings(integration: Integration, settings: dict) -> None:
integration.setting = json.dumps(settings, ensure_ascii=False)
integration.save(update_fields=['setting', 'updated_at'])
def get_integration_url(
client: Client,
partner_slug: str,
setting_key: str = 'url',
normalizer: Optional[Callable[[Optional[str]], Optional[str]]] = None,
persist_fix: bool = True,
) -> Optional[str]:
"""
Возвращает URL из lk_integration по slug партнёра.
При необходимости нормализует и сохраняет исправленное значение в БД.
"""
integration = Integration.objects.filter(
client=client,
partner__slug=partner_slug,
status=True,
).select_related('partner').first()
if not integration:
return None
settings = _load_settings(integration)
raw_url = settings.get(setting_key)
if not raw_url:
return None
normalized = normalizer(raw_url) if normalizer else raw_url
if persist_fix and normalized and normalized != raw_url:
settings[setting_key] = normalized
_save_settings(integration, settings)
return normalized
def cleanup_max_bot_integration(client: Client) -> None:
"""Удаляет устаревший ключ url_telegram из настроек max_bot."""
integration = Integration.objects.filter(
client=client,
partner__slug='max_bot',
status=True,
).first()
if not integration:
return
settings = _load_settings(integration)
if 'url_telegram' not in settings:
return
del settings['url_telegram']
_save_settings(integration, settings)
def get_landing_bot_urls(client: Client) -> Tuple[Optional[str], Optional[str]]:
"""Возвращает (max_bot_url, telegram_url) для лендинга."""
cleanup_max_bot_integration(client)
max_bot_url = get_integration_url(
client=client,
partner_slug='max_bot',
setting_key='url',
normalizer=normalize_max_bot_url,
)
telegram_url = get_integration_url(
client=client,
partner_slug='telegram_bot',
setting_key='url',
normalizer=normalize_telegram_url,
)
return max_bot_url, telegram_url

@ -1,3 +1,99 @@
from django.test import TestCase
from django.test import TestCase, Client as DjangoClient
from unittest.mock import patch, MagicMock
# Create your tests here.
from content_bot.landing_utils import (
normalize_telegram_url,
normalize_max_bot_url,
get_landing_bot_urls,
)
class LandingUrlNormalizationTests(TestCase):
def test_normalize_telegram_full_url(self):
self.assertEqual(
normalize_telegram_url('https://t.me/cheguevaraclub_bot'),
'https://t.me/cheguevaraclub_bot',
)
def test_normalize_telegram_without_scheme(self):
self.assertEqual(
normalize_telegram_url('t.me/celentanouu_bot'),
'https://t.me/celentanouu_bot',
)
def test_normalize_telegram_bot_name(self):
self.assertEqual(
normalize_telegram_url('barLeto_bot'),
'https://t.me/barLeto_bot',
)
def test_normalize_max_bot_full_url(self):
self.assertEqual(
normalize_max_bot_url('https://max.ru/id0323343968_bot'),
'https://max.ru/id0323343968_bot',
)
def test_normalize_max_bot_short_id(self):
self.assertEqual(
normalize_max_bot_url('d0323350531_bot'),
'https://max.ru/d0323350531_bot',
)
class LandingBotUrlsTests(TestCase):
@patch('content_bot.landing_utils.Integration')
def test_get_landing_bot_urls_uses_separate_integrations(self, mock_integration):
client = MagicMock()
max_integration = MagicMock()
max_integration.setting = '{"url":"d0323350531_bot"}'
telegram_integration = MagicMock()
telegram_integration.setting = '{"url":"https://t.me/cheguevaraclub_bot"}'
def filter_side_effect(**kwargs):
qs = MagicMock()
slug = kwargs.get('partner__slug')
if slug == 'max_bot':
qs.first.return_value = max_integration
elif slug == 'telegram_bot':
qs.first.return_value = telegram_integration
else:
qs.first.return_value = None
return qs
mock_integration.objects.filter.side_effect = filter_side_effect
with patch('content_bot.landing_utils._save_settings') as mock_save:
max_url, telegram_url = get_landing_bot_urls(client)
self.assertEqual(max_url, 'https://max.ru/d0323350531_bot')
self.assertEqual(telegram_url, 'https://t.me/cheguevaraclub_bot')
self.assertEqual(mock_save.call_count, 1)
class LandingPageViewTests(TestCase):
@patch('content_bot.views.get_landing_bot_urls')
@patch('content_bot.views.Qr')
@patch('content_bot.views.get_object_or_404')
def test_landing_page_passes_urls_to_template(self, mock_get_object, mock_qr, mock_get_urls):
client_obj = MagicMock()
client_obj.id = 1089
client_obj.brand = 'Test Brand'
client_obj.logo = ''
mock_get_object.return_value = client_obj
qr_obj = MagicMock()
qr_obj.brand = 'QR Brand'
qr_obj.logo = 'logo.png'
qr_obj.description = 'Description'
mock_qr.objects.filter.return_value.first.return_value = qr_obj
mock_get_urls.return_value = (
'https://max.ru/d0323350531_bot',
'https://t.me/cheguevaraclub_bot',
)
response = DjangoClient().get('/content/landing/1089/')
self.assertEqual(response.status_code, 200)
self.assertContains(response, 'https://max.ru/d0323350531_bot')
self.assertContains(response, 'https://t.me/cheguevaraclub_bot')

@ -2,8 +2,8 @@ from django.http import JsonResponse
from django.core.handlers.wsgi import WSGIRequest
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render, get_object_or_404
from max_bot.models import Client, Qr, Integration, Partner
import json
from max_bot.models import Client, Qr
from content_bot.landing_utils import get_landing_bot_urls
# Create your views here.
@ -22,75 +22,20 @@ def landing_page(request, client_id):
"""
Минилендинг для мобильного телефона.
Отображает логотип, название, описание заведения и две кнопки:
- Max Bot
- Telegram Bot
- Max Bot (из lk_integration, partner slug=max_bot)
- Telegram Bot (из lk_integration, partner slug=telegram_bot)
"""
# Получаем клиента
client = get_object_or_404(Client, id=client_id, status=True)
# Получаем данные из lk_qr
qr = Qr.objects.filter(client=client).first()
max_bot_url, telegram_url = get_landing_bot_urls(client)
# Данные для шаблона
context = {
'client_id': client.id,
'brand': qr.brand if qr and qr.brand else client.brand or 'Ресторан',
'logo': qr.logo if qr and qr.logo else client.logo or '',
'description': qr.description if qr and qr.description else '',
'max_bot_url': max_bot_url,
'telegram_url': telegram_url,
}
# Ищем интеграции
max_bot_url = None
telegram_url = None
integrations = Integration.objects.filter(client=client, status=True)
for integration in integrations:
if not integration.setting:
continue
try:
settings = json.loads(integration.setting)
# Max Bot
if integration.partner.slug == 'max_bot':
if 'url' in settings:
max_bot_url = settings['url']
# Telegram
if integration.partner.slug == 'telegram':
if 'url_telegram' in settings:
telegram_url = settings['url_telegram']
except json.JSONDecodeError:
continue
# Если не нашли по партнеру, пробуем по ключам
if not max_bot_url:
for integration in integrations:
if not integration.setting:
continue
try:
settings = json.loads(integration.setting)
if 'url' in settings and 'max' in settings['url'].lower():
max_bot_url = settings['url']
break
except json.JSONDecodeError:
continue
if not telegram_url:
for integration in integrations:
if not integration.setting:
continue
try:
settings = json.loads(integration.setting)
if 'url_telegram' in settings:
telegram_url = settings['url_telegram']
break
except json.JSONDecodeError:
continue
context['max_bot_url'] = max_bot_url
context['telegram_url'] = telegram_url
return render(request, 'max_bot/landing.html', context)

Loading…
Cancel
Save