File "d:\Visual Studio\project\GPTTgBOT\main.py", line 168
    list_items = ''.join([f'<li>{re.sub(r'^\d+\.\s', "", item.strip())}</li>' for item in 
items])
                                          ^
SyntaxError: f-string: unmatched '('

def markdown_to_telegram_html(text):
    # Функция для экранирования специальных символов HTML
    def escape_html(text):
        html_escape_table = {
            "&": "&amp;",
            '"': "&quot;",
            "'": "&#x27;",
            ">": "&gt;",
            "<": "&lt;",
        }
        return "".join(html_escape_table.get(c, c) for c in text)

    # Экранирование специальных символов
    text = escape_html(text)
    
    # Жирный курсивный текст (***) 
    text = re.sub(r'\*\*\*(.*?)\*\*\*', r'<b><i></i></b>', text)
    
    # Жирный текст (**)
    text = re.sub(r'\*\*(.*?)\*\*', r'<b></b>', text)

    # Курсивный текст (*)
    text = re.sub(r'\*(.*?)\*', r'<i></i>', text)

    # Зачёркнутый текст (~~)
    text = re.sub(r'~~(.*?)~~', r'

', text)

    # Горизонтальная линия (--- или ***)
    text = re.sub(r'^---$', r'――――――――――', text, flags=re.MULTILINE)
    text = re.sub(r'^\*\*\*$', r'――――――――――', text, flags=re.MULTILINE)

    # Ссылки [текст](URL)
    # Используем функцию замены, чтобы избежать ошибок с вложенными скобками
    def replace_links(match):
        text = match.group(1)
        url = match.group(2)
        # Экранируем URL
        url = url.replace('"', '&quot;')
        return f'<a href="{url}">{text}</a>'

    text = re.sub(r'\[([^\]]+)\]\(([^)\s]+)\)', replace_links, text)

    # Блоки кода (многострочные)
    # Используем re.DOTALL для захвата символов новой строки
    text = re.sub(r'```(?:\w+)?
([\s\S]+?)```', r'<pre></pre>', text)

    # Однострочный код (`код`)
    text = re.sub(r'`([^`]+?)`', r'<code></code>', text)

    # Цитаты (> )
    text = re.sub(r'^> (.+)$', r'<blockquote></blockquote>', text, flags=re.MULTILINE)  

    # Списки
    # Необходимо обрабатывать маркерованные и нумерованные списки
    # Маркерованные списки (-, *, +)
    def replace_unordered_lists(match):
        items = match.group(0).strip().split('
')
        list_items = ''.join([f'<li>{item.strip()[2:]}</li>' for item in items])
        return f'<ul>{list_items}</ul>'

    text = re.sub(r'((?:^\s*[-\*\+]\s.+
?)+)', replace_unordered_lists, text, flags=re.MULTILINE)

    # Нумерованные списки
    def replace_ordered_lists(match):
        items = match.group(0).strip().split('
')
        list_items = ''.join([f'<li>{re.sub(r'^\d+\.\s', "", item.strip())}</li>' for item in items])
        return f'<ol>{list_items}</ol>'

    text = re.sub(r'((?:^\s*\d+\.\s.+
?)+)', replace_ordered_lists, text, flags=re.MULTILINE)

    # Удаление неподдерживаемых HTML-тегов
    def remove_unsupported_html_tags(text):
        allowed_tags = ['a', 'b', 'strong', 'i', 'em', 'u', 'ins', 's', 
                        'strike', 'del', 'code', 'pre', 'blockquote', 'ul', 'ol', 'li']    
        # Правильное регулярное выражение для удаления неразрешённых тегов
        pattern = r'</?(?!(' + '|'.join(allowed_tags) + r'))\w+(?:\s[^>]*)?>'
        text = re.sub(pattern, '', text)
        return text

    text = remove_unsupported_html_tags(text)

    return text.strip()