ba0f43169c
bot.py разгружен: чистые функции вынесены без изменения поведения: - validators.py — регэкспы, normalize_phone, format_phone_display, is_valid_name - keyboards.py — все get_*_keyboard Поведение проверено регрессионным прогоном (полный флоу заявки, админка, меню). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
45 lines
2.2 KiB
Python
45 lines
2.2 KiB
Python
"""Валидация и форматирование пользовательского ввода."""
|
||
import re
|
||
from typing import Optional
|
||
|
||
|
||
# Регулярные выражения для валидации
|
||
# Российский номер для нормализации 8XXX / 7XXX -> +7XXX
|
||
RU_PHONE_REGEX = re.compile(r"^\+?[78]\d{10}$")
|
||
# Международный формат (E.164): + и 10–15 цифр. Принимаем и иностранные номера.
|
||
INTL_PHONE_REGEX = re.compile(r"^\+?\d{10,15}$")
|
||
# Имя: буквы (кириллица/латиница), пробел, дефис, апостроф; длина 2–50
|
||
NAME_REGEX = re.compile(r"^[A-Za-zА-Яа-яЁё][A-Za-zА-Яа-яЁё \-']{1,49}$")
|
||
|
||
|
||
def normalize_phone(text: str) -> Optional[str]:
|
||
"""Очищает и нормализует телефон. Возвращает нормализованный номер или None, если формат неверен."""
|
||
phone_cleaned = re.sub(r"[\s\-\(\)]", "", text)
|
||
|
||
# Российские номера приводим к виду +7XXXXXXXXXX
|
||
if phone_cleaned.startswith("8") and len(phone_cleaned) == 11:
|
||
phone_cleaned = "+7" + phone_cleaned[1:]
|
||
elif phone_cleaned.startswith("7") and len(phone_cleaned) == 11:
|
||
phone_cleaned = "+" + phone_cleaned
|
||
|
||
# Принимаем как российский, так и любой международный номер в формате E.164
|
||
if RU_PHONE_REGEX.match(phone_cleaned) or INTL_PHONE_REGEX.match(phone_cleaned):
|
||
return phone_cleaned
|
||
return None
|
||
|
||
|
||
def format_phone_display(phone: str) -> str:
|
||
"""Красивое отображение номера: +7 (999) 123-45-67 для РФ, иначе как есть."""
|
||
if not phone:
|
||
return phone
|
||
digits = re.sub(r"\D", "", phone)
|
||
if len(digits) == 11 and digits[0] in ("7", "8"):
|
||
d = digits
|
||
return f"+7 ({d[1:4]}) {d[4:7]}-{d[7:9]}-{d[9:11]}"
|
||
return phone
|
||
|
||
|
||
def is_valid_name(text: str) -> bool:
|
||
"""Имя должно состоять только из букв, пробела, дефиса или апострофа (без цифр и спецсимволов)."""
|
||
return bool(NAME_REGEX.match(text.strip()))
|