Files
Kenik ba0f43169c Рефакторинг (этап 1): валидаторы и клавиатуры вынесены в модули
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>
2026-06-24 21:41:21 +03:00

45 lines
2.2 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Валидация и форматирование пользовательского ввода."""
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()))