mirror of
https://github.com/FerraSoft/bottohelp.git
synced 2026-08-06 21:55:03 +00:00
Подготовка к релизу
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
"""
|
||||
Система валидации данных для телеграм-бота.
|
||||
Обеспечивает безопасность и корректность входящих данных.
|
||||
"""
|
||||
|
||||
import re
|
||||
from typing import Optional, Union, List
|
||||
|
||||
|
||||
class ValidationError(Exception):
|
||||
"""Исключение валидации данных"""
|
||||
def __init__(self, message: str, field: str = None):
|
||||
self.message = message
|
||||
self.field = field
|
||||
super().__init__(message)
|
||||
|
||||
|
||||
class Validator:
|
||||
"""Базовый класс для валидаторов"""
|
||||
|
||||
@staticmethod
|
||||
def validate_string_length(text: str, min_length: int = 0, max_length: int = 1000) -> bool:
|
||||
"""Валидация длины строки"""
|
||||
if not isinstance(text, str):
|
||||
return False
|
||||
return min_length <= len(text) <= max_length
|
||||
|
||||
@staticmethod
|
||||
def validate_numeric_range(value: Union[int, float], min_val: Union[int, float], max_val: Union[int, float]) -> bool:
|
||||
"""Валидация числового диапазона"""
|
||||
try:
|
||||
numeric_value = float(value)
|
||||
return min_val <= numeric_value <= max_val
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def validate_user_id(user_id: Union[int, str]) -> bool:
|
||||
"""Валидация ID пользователя Telegram"""
|
||||
try:
|
||||
uid = int(user_id)
|
||||
return 1 <= uid <= 2147483647 # Диапазон Telegram user ID
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def validate_chat_id(chat_id: Union[int, str]) -> bool:
|
||||
"""Валидация ID чата Telegram"""
|
||||
try:
|
||||
cid = int(chat_id)
|
||||
return -2147483648 <= cid <= 2147483647 # Диапазон Telegram chat ID
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
|
||||
class InputValidator:
|
||||
"""Валидатор пользовательского ввода"""
|
||||
|
||||
# Регулярные выражения для валидации
|
||||
EMAIL_PATTERN = re.compile(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$|^[a-zA-Z0-9._%+-]+@localhost$')
|
||||
PHONE_PATTERN = re.compile(r'^\+?[\d\s\-\(\)]{10,}$')
|
||||
URL_PATTERN = re.compile(r'^https?://[^\s/$.?#].[^\s]*$')
|
||||
|
||||
@classmethod
|
||||
def validate_email(cls, email: str) -> bool:
|
||||
"""Валидация email адреса"""
|
||||
return bool(cls.EMAIL_PATTERN.match(email)) if email else False
|
||||
|
||||
@classmethod
|
||||
def validate_phone(cls, phone: str) -> bool:
|
||||
"""Валидация номера телефона"""
|
||||
return bool(cls.PHONE_PATTERN.match(phone)) if phone else False
|
||||
|
||||
@classmethod
|
||||
def validate_url(cls, url: str) -> bool:
|
||||
"""Валидация URL"""
|
||||
return bool(cls.URL_PATTERN.match(url)) if url else False
|
||||
|
||||
@classmethod
|
||||
def validate_city_name(cls, city: str, max_length: int = 50) -> bool:
|
||||
"""Валидация названия города"""
|
||||
if not city or not isinstance(city, str):
|
||||
return False
|
||||
|
||||
# Проверка длины
|
||||
if len(city) < 2 or len(city) > max_length:
|
||||
return False
|
||||
|
||||
# Проверка на наличие только допустимых символов (буквы, пробелы, дефисы)
|
||||
return bool(re.match(r'^[a-zA-Zа-яА-Я\s\-]+$', city))
|
||||
|
||||
@classmethod
|
||||
def validate_username(cls, username: str) -> bool:
|
||||
"""Валидация username Telegram"""
|
||||
if not username or not isinstance(username, str):
|
||||
return False
|
||||
|
||||
# Username должен начинаться с буквы, содержать только буквы, цифры и подчеркивания
|
||||
# Длина от 5 до 32 символов
|
||||
if not (5 <= len(username) <= 32):
|
||||
return False
|
||||
|
||||
return bool(re.match(r'^[a-zA-Z][a-zA-Z0-9_]*$', username))
|
||||
|
||||
@classmethod
|
||||
def validate_text_content(cls, text: str, max_length: int = 4000) -> bool:
|
||||
"""Валидация текстового контента"""
|
||||
if not text or not isinstance(text, str):
|
||||
return False
|
||||
|
||||
# Проверка длины
|
||||
if len(text) > max_length:
|
||||
return False
|
||||
|
||||
# Проверка на наличие потенциально опасных символов
|
||||
dangerous_chars = ['<', '>', '"', "'", '&']
|
||||
return not any(char in text for char in dangerous_chars)
|
||||
|
||||
@classmethod
|
||||
def validate_donation_amount(cls, amount: Union[str, float]) -> bool:
|
||||
"""Валидация суммы доната"""
|
||||
try:
|
||||
amount_float = float(amount)
|
||||
return 1.0 <= amount_float <= 100000.0 # От 1 рубля до 100k рублей
|
||||
except (ValueError, TypeError):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def validate_time_format(cls, time_str: str) -> bool:
|
||||
"""Валидация формата времени"""
|
||||
if not time_str or not isinstance(time_str, str):
|
||||
return False
|
||||
|
||||
# Проверяем форматы: YYYY-MM-DD HH:MM:SS, YYYY-MM-DD HH:MM, +30m, +2h, +1d
|
||||
patterns = [
|
||||
r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}(?::\d{2})?$', # Абсолютное время
|
||||
r'^\+(\d+)[mhd]$' # Относительное время
|
||||
]
|
||||
|
||||
return any(re.match(pattern, time_str) for pattern in patterns)
|
||||
|
||||
@classmethod
|
||||
def sanitize_filename(cls, filename: str) -> str:
|
||||
"""Очистка имени файла от опасных символов"""
|
||||
if not filename:
|
||||
return "unnamed_file"
|
||||
|
||||
# Оставляем только безопасные символы
|
||||
safe_chars = re.sub(r'[^\w\.\-\s]', '', filename)
|
||||
return safe_chars.strip() or "unnamed_file"
|
||||
|
||||
@classmethod
|
||||
def validate_csv_filename(cls, filename: str) -> bool:
|
||||
"""Валидация имени CSV файла"""
|
||||
if not filename or not isinstance(filename, str):
|
||||
return False
|
||||
|
||||
# Проверка длины
|
||||
if len(filename) > 255:
|
||||
return False
|
||||
|
||||
# Проверка на наличие только безопасных символов
|
||||
return bool(re.match(r'^[a-zA-Z0-9._\-/\s]+$', filename))
|
||||
|
||||
@classmethod
|
||||
def validate_error_type(cls, error_type: str) -> bool:
|
||||
"""Валидация типа ошибки"""
|
||||
if not error_type or not isinstance(error_type, str):
|
||||
return False
|
||||
valid_types = ['bug', 'feature', 'crash', 'ui', 'security', 'improvement', 'other']
|
||||
return error_type.lower() in valid_types
|
||||
|
||||
@classmethod
|
||||
def validate_priority(cls, priority: str) -> bool:
|
||||
"""Валидация приоритета"""
|
||||
valid_priorities = ['low', 'medium', 'high', 'critical']
|
||||
return priority.lower() in valid_priorities
|
||||
Reference in New Issue
Block a user