import html import re def to_html(obj): return html.escape(str(obj)) def format_llm_answer_html(text: str | None) -> str: if text is None: return "" escaped = html.escape(str(text).replace("\r\n", "\n").strip()) normalized_lines = [] for line in escaped.split("\n"): normalized_line = re.sub(r"^\s*[-*]\s+", "• ", line.rstrip()) normalized_lines.append(normalized_line) formatted = "\n".join(normalized_lines) formatted = re.sub(r"\*\*(.+?)\*\*", r"\1", formatted) return formatted def split_plain_text_chunks(text: str | None, limit: int = 3500) -> list[str]: if text is None: return [""] normalized = str(text).replace("\r\n", "\n").strip() if not normalized: return [""] paragraphs = normalized.split("\n\n") chunks: list[str] = [] current = "" for paragraph in paragraphs: paragraph = paragraph.strip() if not paragraph: continue candidate = paragraph if not current else f"{current}\n\n{paragraph}" if len(candidate) <= limit: current = candidate continue if current: chunks.append(current) current = "" if len(paragraph) <= limit: current = paragraph continue lines = paragraph.split("\n") line_buffer = "" for line in lines: line = line.rstrip() line_candidate = line if not line_buffer else f"{line_buffer}\n{line}" if len(line_candidate) <= limit: line_buffer = line_candidate continue if line_buffer: chunks.append(line_buffer) line_buffer = "" while len(line) > limit: chunks.append(line[:limit].rstrip()) line = line[limit:].lstrip() line_buffer = line if line_buffer: current = line_buffer if current: chunks.append(current) return chunks or [normalized[:limit]] def parse_links_to_inline_markup(message: str) -> list: """ Парсит сообщение с форматированными ссылками и возвращает список рядов кнопок. Формат входного сообщения: - [Текст кнопки + Ссылка] для одной кнопки. - [Кнопка1 + Ссылка1][Кнопка2 + Ссылка2] для нескольких кнопок в одном ряду. - Каждая строка представляет отдельный ряд кнопок. Пример: [Кнопка1 + https://example.com] [Кнопка2 + https://example.org][Кнопка3 + https://example.net] :param message: Строка с отформатированными ссылками. :return: Список рядов кнопок, где каждый ряд — это список кортежей (Текст, Ссылка). """ # Исправленное регулярное выражение для поиска [Текст + Ссылка] pattern = re.compile(r"\[([^\[\]+]+)\s*\+\s*(https?://[^\[\]]+)\]") # Инициализируем список рядов кнопок keyboard_rows = [] # Разбиваем сообщение на строки lines = message.strip().split("\n") for line in lines: # Находим все совпадения в строке matches = pattern.findall(line) if matches: row = [] for text, url in matches: button = (text.strip(), url.strip()) row.append(button) keyboard_rows.append(row) return keyboard_rows