fix: HTML parse fallback, email change race condition, username length limit

- start.py: retry welcome message with parse_mode=None on TelegramBadRequest HTML parse error
- auth.py: handle IntegrityError race condition on email change, wrap email sending in try-except
- config.py: truncate RemnaWave username to 36 chars (API limit) instead of 64
This commit is contained in:
Fringg
2026-02-11 20:51:50 +03:00
parent fcaa9dfb27
commit d05ff678ab
3 changed files with 51 additions and 12 deletions

View File

@@ -7,6 +7,7 @@ from datetime import UTC, datetime
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy import select
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
from app.config import settings
@@ -1010,7 +1011,14 @@ async def request_email_change(
user.email_verification_token = verification_token
user.email_verification_expires = verification_expires
await db.commit()
try:
await db.commit()
except IntegrityError:
await db.rollback()
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail='This email is already registered',
)
if settings.is_cabinet_email_verification_enabled() and email_service.is_configured():
cabinet_url = settings.CABINET_URL
@@ -1031,16 +1039,19 @@ async def request_email_change(
)
custom_subject, custom_body = override if override else (None, None)
await asyncio.to_thread(
email_service.send_verification_email,
to_email=request.new_email,
verification_token=verification_token,
verification_url=verification_url,
username=user.first_name,
language=lang,
custom_subject=custom_subject,
custom_body_html=custom_body,
)
try:
await asyncio.to_thread(
email_service.send_verification_email,
to_email=request.new_email,
verification_token=verification_token,
verification_url=verification_url,
username=user.first_name,
language=lang,
custom_subject=custom_subject,
custom_body_html=custom_body,
)
except Exception as e:
logger.error(f'Failed to send verification email to {request.new_email} for user {user.id}: {e}')
logger.info(f'Unverified email replaced for user {user.id}: {old_email} -> {request.new_email}')

View File

@@ -1057,7 +1057,7 @@ class Settings(BaseSettings):
if not sanitized_username:
sanitized_username = f'user_{identifier}'
return sanitized_username[:64]
return sanitized_username[:36]
@staticmethod
def parse_daily_time_list(raw_value: str | None) -> list[time]:

View File

@@ -1225,6 +1225,20 @@ async def complete_registration_from_callback(callback: types.CallbackQuery, sta
)
logger.info(f'✅ Приветственное сообщение отправлено пользователю {user.telegram_id}')
await _send_pinned_message(callback.bot, db, user)
except TelegramBadRequest as e:
if 'parse entities' in str(e).lower() or "can't parse" in str(e).lower():
logger.warning(f'HTML parse error в приветственном сообщении, повтор без parse_mode: {e}')
try:
await callback.message.answer(
offer_text,
reply_markup=get_post_registration_keyboard(user.language),
parse_mode=None,
)
await _send_pinned_message(callback.bot, db, user)
except Exception as fallback_err:
logger.error(f'Ошибка при повторной отправке приветственного сообщения: {fallback_err}')
else:
logger.error(f'Ошибка при отправке приветственного сообщения: {e}')
except Exception as e:
logger.error(f'Ошибка при отправке приветственного сообщения: {e}')
else:
@@ -1504,6 +1518,20 @@ async def complete_registration(message: types.Message, state: FSMContext, db: A
)
logger.info(f'✅ Приветственное сообщение отправлено пользователю {user.telegram_id}')
await _send_pinned_message(message.bot, db, user)
except TelegramBadRequest as e:
if 'parse entities' in str(e).lower() or "can't parse" in str(e).lower():
logger.warning(f'HTML parse error в приветственном сообщении, повтор без parse_mode: {e}')
try:
await message.answer(
offer_text,
reply_markup=keyboard,
parse_mode=None,
)
await _send_pinned_message(message.bot, db, user)
except Exception as fallback_err:
logger.error(f'Ошибка при повторной отправке приветственного сообщения: {fallback_err}')
else:
logger.error(f'Ошибка при отправке приветственного сообщения: {e}')
except Exception as e:
logger.error(f'Ошибка при отправке приветственного сообщения: {e}')
else: