mirror of
https://github.com/BEDOLAGA-DEV/remnawave-bedolaga-telegram-bot.git
synced 2026-05-04 20:15:56 +00:00
Update validators.py
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import re
|
||||
from typing import Optional, Union
|
||||
from typing import Optional, Union, Tuple
|
||||
from datetime import datetime
|
||||
import html
|
||||
|
||||
@@ -14,6 +14,11 @@ ALLOWED_HTML_TAGS = {
|
||||
'blockquote'
|
||||
}
|
||||
|
||||
SELF_CLOSING_TAGS = {
|
||||
'br', 'hr', 'img'
|
||||
}
|
||||
|
||||
|
||||
def validate_email(email: str) -> bool:
|
||||
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
return re.match(pattern, email) is not None
|
||||
@@ -158,7 +163,8 @@ def validate_referral_code(code: str) -> bool:
|
||||
|
||||
return validate_promocode(code)
|
||||
|
||||
def validate_html_tags(text: str) -> tuple[bool, str]:
|
||||
|
||||
def validate_html_tags(text: str) -> Tuple[bool, str]:
|
||||
if not text:
|
||||
return True, ""
|
||||
|
||||
@@ -168,21 +174,34 @@ def validate_html_tags(text: str) -> tuple[bool, str]:
|
||||
for is_closing, tag_name in tags:
|
||||
tag_name_lower = tag_name.lower()
|
||||
|
||||
if tag_name_lower not in ALLOWED_HTML_TAGS:
|
||||
if tag_name_lower not in ALLOWED_HTML_TAGS and tag_name_lower not in SELF_CLOSING_TAGS:
|
||||
return False, f"Неподдерживаемый тег: <{tag_name}>"
|
||||
|
||||
return validate_html_structure(text)
|
||||
|
||||
|
||||
def validate_html_structure(text: str) -> Tuple[bool, str]:
|
||||
tag_pattern = r'<(/?)([a-zA-Z][a-zA-Z0-9-]*)[^>]*?/?>'
|
||||
|
||||
matches = re.finditer(tag_pattern, text)
|
||||
tag_stack = []
|
||||
for is_closing, tag_name in tags:
|
||||
tag_name_lower = tag_name.lower()
|
||||
|
||||
for match in matches:
|
||||
full_tag = match.group(0)
|
||||
is_closing = bool(match.group(1))
|
||||
tag_name = match.group(2).lower()
|
||||
|
||||
if not is_closing:
|
||||
tag_stack.append(tag_name_lower)
|
||||
else:
|
||||
if full_tag.endswith('/>') or tag_name in SELF_CLOSING_TAGS:
|
||||
continue
|
||||
|
||||
if not is_closing:
|
||||
tag_stack.append(tag_name)
|
||||
else:
|
||||
if not tag_stack:
|
||||
return False, f"Закрывающий тег без открывающего: </{tag_name}>"
|
||||
|
||||
last_tag = tag_stack.pop()
|
||||
if last_tag != tag_name_lower:
|
||||
if last_tag != tag_name:
|
||||
return False, f"Неправильная вложенность тегов: ожидался </{last_tag}>, найден </{tag_name}>"
|
||||
|
||||
if tag_stack:
|
||||
@@ -191,16 +210,65 @@ def validate_html_tags(text: str) -> tuple[bool, str]:
|
||||
return True, ""
|
||||
|
||||
|
||||
def fix_html_tags(text: str) -> str:
|
||||
if not text:
|
||||
return text
|
||||
|
||||
fixes = [
|
||||
(r'<a href=([^"\s>]+)>', r'<a href="\1">'),
|
||||
(r'<(br|hr|img[^>]*?)>', r'<\1 />'),
|
||||
(r'<<([^>]+)>>', r'<\1>'),
|
||||
(r'<\s+([^>]+)\s+>', r'<\1>'),
|
||||
]
|
||||
|
||||
result = text
|
||||
for pattern, replacement in fixes:
|
||||
result = re.sub(pattern, replacement, result, flags=re.IGNORECASE)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def get_html_help_text() -> str:
|
||||
return """<b>Поддерживаемые HTML теги:</b>
|
||||
|
||||
- <code><b>жирный</b></code> или <code><strong>жирный</strong></code>
|
||||
- <code><i>курсив</i></code> или <code><em>курсив</em></code>
|
||||
- <code><u>подчеркнутый</u></code>
|
||||
- <code><s>зачеркнутый</s></code>
|
||||
- <code><code>моноширинный</code></code>
|
||||
- <code><pre>блок кода</pre></code>
|
||||
- <code><a href="url">ссылка</a></code>
|
||||
- <code><blockquote>цитата</blockquote></code>
|
||||
• <code><b>жирный</b></code> или <code><strong>жирный</strong></code>
|
||||
• <code><i>курсив</i></code> или <code><em>курсив</em></code>
|
||||
• <code><u>подчеркнутый</u></code>
|
||||
• <code><s>зачеркнутый</s></code>
|
||||
• <code><code>моноширинный</code></code>
|
||||
• <code><pre>блок кода</pre></code>
|
||||
• <code><a href="url">ссылка</a></code>
|
||||
• <code><blockquote>цитата</blockquote></code>
|
||||
|
||||
<b>Неподдерживаемые теги:</b> <br>, <p>, <div>, <span>, <spoiler> и другие"""
|
||||
<b>⚠️ Важные правила:</b>
|
||||
• Каждый открывающий тег должен быть закрыт
|
||||
• Теги должны быть правильно вложены
|
||||
• Атрибуты ссылок берите в кавычки
|
||||
|
||||
<b>❌ Неправильно:</b>
|
||||
<code><b>жирный <i>курсив</b></i></code>
|
||||
<code><a href=google.com>ссылка</a></code>
|
||||
|
||||
<b>✅ Правильно:</b>
|
||||
<code><b>жирный <i>курсив</i></b></code>
|
||||
<code><a href="https://google.com">ссылка</a></code>"""
|
||||
|
||||
|
||||
def validate_rules_content(text: str) -> Tuple[bool, str, Optional[str]]:
|
||||
if not text or not text.strip():
|
||||
return False, "Текст правил не может быть пустым", None
|
||||
|
||||
if len(text) > 4000:
|
||||
return False, f"Текст слишком длинный: {len(text)} символов (максимум 4000)", None
|
||||
|
||||
is_valid_html, html_error = validate_html_tags(text)
|
||||
if not is_valid_html:
|
||||
fixed_text = fix_html_tags(text)
|
||||
fixed_is_valid, _ = validate_html_tags(fixed_text)
|
||||
|
||||
if fixed_is_valid and fixed_text != text:
|
||||
return False, html_error, fixed_text
|
||||
else:
|
||||
return False, html_error, None
|
||||
|
||||
return True, "", None
|
||||
|
||||
Reference in New Issue
Block a user