fix: переписан split_message с отслеживанием состояния блока кода
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
parent
ba13eb2a1a
commit
4b69ee0310
|
|
@ -69,18 +69,16 @@ def split_message(text: str, max_length: int = MAX_MESSAGE_LENGTH) -> List[Tuple
|
||||||
- code_closed: True если блок кода ЗАКРЫТ в этом сообщении (есть закрывающий ```)
|
- code_closed: True если блок кода ЗАКРЫТ в этом сообщении (есть закрывающий ```)
|
||||||
|
|
||||||
Алгоритм:
|
Алгоритм:
|
||||||
1. Находим все блоки кода
|
1. Разбиваем текст на строки
|
||||||
2. Стараемся не разрывать блоки кода
|
2. Накапливаем строки до достижения лимита
|
||||||
3. Если блок кода не влезает — отправляем отдельным сообщением без Markdown
|
3. Отслеживаем состояние блока кода (внутри/снаружи)
|
||||||
"""
|
"""
|
||||||
def calc_code_flags(txt: str) -> Tuple[bool, bool, bool]:
|
def calc_code_flags(txt: str) -> Tuple[bool, bool, bool]:
|
||||||
"""Вычислить флаги code для данного текста."""
|
"""Вычислить флаги code для данного текста."""
|
||||||
has_code = '```' in txt
|
has_code = '```' in txt
|
||||||
backtick_count = txt.count('```')
|
backtick_count = txt.count('```')
|
||||||
# code_opened: есть хотя бы один ```
|
|
||||||
code_opened = backtick_count >= 1
|
code_opened = backtick_count >= 1
|
||||||
# code_closed: есть хотя бы 2 ``` (пара) или нечётное количество (открыт и закрыт в одном)
|
code_closed = backtick_count >= 2 or (backtick_count == 1 and txt.rstrip().endswith('```'))
|
||||||
code_closed = backtick_count >= 2
|
|
||||||
return has_code, code_opened, code_closed
|
return has_code, code_opened, code_closed
|
||||||
|
|
||||||
if len(text) <= max_length:
|
if len(text) <= max_length:
|
||||||
|
|
@ -88,95 +86,63 @@ def split_message(text: str, max_length: int = MAX_MESSAGE_LENGTH) -> List[Tuple
|
||||||
return [(text, has_code, code_opened, code_closed)]
|
return [(text, has_code, code_opened, code_closed)]
|
||||||
|
|
||||||
parts = []
|
parts = []
|
||||||
code_blocks = find_code_blocks(text)
|
lines = text.split('\n')
|
||||||
|
|
||||||
# Текущая позиция в тексте
|
|
||||||
pos = 0
|
|
||||||
current = ""
|
current = ""
|
||||||
|
in_code_block = False # Состояние: внутри блока кода или нет
|
||||||
|
code_block_started_in_current = False # Блок кода был открыт в текущей части
|
||||||
|
|
||||||
for block_start, block_end in code_blocks:
|
for line in lines:
|
||||||
# Текст до блока кода
|
# Проверяем, содержит ли строка ```
|
||||||
before_code = text[pos:block_start]
|
backticks_in_line = line.count('```')
|
||||||
|
|
||||||
# Сам блок кода (включая ```)
|
# Если строка содержит нечётное количество ```, она меняет состояние
|
||||||
code_block = text[block_start:block_end]
|
if backticks_in_line % 2 == 1:
|
||||||
|
# Эта строка содержит ``` который меняет состояние
|
||||||
# Обрабатываем текст до блока
|
if in_code_block:
|
||||||
for line in before_code.split('\n'):
|
# Были внутри блока — эта строка закрывает его
|
||||||
if len(current) + len(line) + 1 > max_length - RESERVED_FOR_HEADER:
|
test_line = current + ('\n' if current else '') + line
|
||||||
if current:
|
if len(test_line) > max_length - RESERVED_FOR_HEADER:
|
||||||
has_code, code_opened, code_closed = calc_code_flags(current)
|
# Сначала отправляем текущее (блок не закрыт в этой части!)
|
||||||
parts.append((current, has_code, code_opened, code_closed))
|
if current:
|
||||||
current = line
|
has_code, code_opened, _ = calc_code_flags(current)
|
||||||
|
# code_closed=False потому что блок продолжится
|
||||||
|
parts.append((current, has_code, code_opened, False))
|
||||||
|
current = line
|
||||||
|
code_block_started_in_current = False
|
||||||
|
else:
|
||||||
|
current = test_line
|
||||||
|
code_block_started_in_current = True
|
||||||
|
in_code_block = False
|
||||||
else:
|
else:
|
||||||
current += ('\n' if current else '') + line
|
# Были снаружи — эта строка открывает блок
|
||||||
|
test_line = current + ('\n' if current else '') + line
|
||||||
# Проверяем влезет ли блок кода
|
if len(test_line) > max_length - RESERVED_FOR_HEADER:
|
||||||
if len(current) + len(code_block) + 1 > max_length - RESERVED_FOR_HEADER:
|
if current:
|
||||||
# Отправляем что накопилось
|
has_code, code_opened, code_closed = calc_code_flags(current)
|
||||||
if current:
|
parts.append((current, has_code, code_opened, code_closed))
|
||||||
has_code, code_opened, code_closed = calc_code_flags(current)
|
current = line
|
||||||
parts.append((current, has_code, code_opened, code_closed))
|
code_block_started_in_current = True
|
||||||
|
else:
|
||||||
# Если блок кода слишком длинный — режем его на части
|
current = test_line
|
||||||
if len(code_block) > max_length - RESERVED_FOR_HEADER:
|
code_block_started_in_current = True
|
||||||
# Блок кода включает ```, нужно резать правильно
|
in_code_block = True
|
||||||
# block_start и block_end включают оба ```
|
|
||||||
# Находим позицию открывающего и закрывающего ```
|
|
||||||
open_end = code_block.find('```', 3) + 3 # Конец открывающего ```
|
|
||||||
close_start = code_block.rfind('```') # Начало закрывающего ```
|
|
||||||
|
|
||||||
# Содержимое блока без ```
|
|
||||||
code_content = code_block[3:close_start] # Только содержимое
|
|
||||||
|
|
||||||
# Режем содержимое на части
|
|
||||||
content_max_len = max_length - RESERVED_FOR_HEADER - 3 # -3 для ```
|
|
||||||
chunks = []
|
|
||||||
for i in range(0, len(code_content), content_max_len):
|
|
||||||
chunks.append(code_content[i:i + content_max_len])
|
|
||||||
|
|
||||||
# Создаём части с правильными ```
|
|
||||||
for i, chunk in enumerate(chunks):
|
|
||||||
if i == 0 and len(chunks) == 1:
|
|
||||||
# Один чанк — полный блок
|
|
||||||
full_block = f"```{chunk}```"
|
|
||||||
parts.append((full_block, True, True, True))
|
|
||||||
elif i == 0:
|
|
||||||
# Первая часть — только открывающий ```
|
|
||||||
first_part = f"```{chunk}"
|
|
||||||
parts.append((first_part, True, True, False))
|
|
||||||
elif i == len(chunks) - 1:
|
|
||||||
# Последняя часть — только закрывающий ```
|
|
||||||
last_part = f"{chunk}```"
|
|
||||||
parts.append((last_part, True, False, True))
|
|
||||||
else:
|
|
||||||
# Средняя часть — без ```
|
|
||||||
parts.append((chunk, False, False, False))
|
|
||||||
|
|
||||||
current = ""
|
|
||||||
else:
|
|
||||||
# Блок влезает в следующее сообщение
|
|
||||||
current = code_block
|
|
||||||
else:
|
else:
|
||||||
# Блок кода влезает в текущее сообщение
|
# Строка не меняет состояние
|
||||||
current += ('\n' if current else '') + code_block
|
test_line = current + ('\n' if current else '') + line
|
||||||
|
if len(test_line) > max_length - RESERVED_FOR_HEADER:
|
||||||
pos = block_end
|
|
||||||
|
|
||||||
# Обрабатываем остаток текста после последнего блока
|
|
||||||
if pos < len(text):
|
|
||||||
remaining = text[pos:]
|
|
||||||
for line in remaining.split('\n'):
|
|
||||||
if len(current) + len(line) + 1 > max_length - RESERVED_FOR_HEADER:
|
|
||||||
if current:
|
if current:
|
||||||
has_code, code_opened, code_closed = calc_code_flags(current)
|
has_code, code_opened, _ = calc_code_flags(current)
|
||||||
|
# Если мы внутри блока кода — block не закрыт
|
||||||
|
code_closed = not in_code_block
|
||||||
parts.append((current, has_code, code_opened, code_closed))
|
parts.append((current, has_code, code_opened, code_closed))
|
||||||
current = line
|
current = line
|
||||||
|
code_block_started_in_current = in_code_block
|
||||||
else:
|
else:
|
||||||
current += ('\n' if current else '') + line
|
current = test_line
|
||||||
|
|
||||||
if current:
|
if current:
|
||||||
has_code, code_opened, code_closed = calc_code_flags(current)
|
has_code, code_opened, _ = calc_code_flags(current)
|
||||||
|
code_closed = not in_code_block
|
||||||
parts.append((current, has_code, code_opened, code_closed))
|
parts.append((current, has_code, code_opened, code_closed))
|
||||||
|
|
||||||
return parts
|
return parts
|
||||||
|
|
@ -208,31 +174,21 @@ async def send_long_message(update: Update, text: str, parse_mode: str = None, p
|
||||||
|
|
||||||
for i, (part, has_code, code_opened, code_closed) in enumerate(parts):
|
for i, (part, has_code, code_opened, code_closed) in enumerate(parts):
|
||||||
# Определяем parse_mode для этого сообщения
|
# Определяем parse_mode для этого сообщения
|
||||||
actual_parse_mode = parse_mode if parse_mode and (has_code or code_opened or code_closed) else None
|
actual_parse_mode = parse_mode if parse_mode and has_code else None
|
||||||
|
|
||||||
# Логика работы с блоками кода между сообщениями:
|
# Логика работы с блоками кода между сообщениями:
|
||||||
need_prepend = False
|
# Если предыдущее сообщение не закрыло блок — нужно открыть в этом
|
||||||
need_append = False
|
# Если текущее сообщение не закрыло блок — нужно закрыть в следующем
|
||||||
|
|
||||||
if total > 1 and actual_parse_mode:
|
if total > 1 and actual_parse_mode:
|
||||||
# Проверяем нужно ли открыть блок в начале этого сообщения
|
# Проверяем нужно ли открыть блок в начале этого сообщения
|
||||||
if i > 0 and not parts[i-1][3]: # предыдущее не закрыло блок (parts[i-1][3] = code_closed)
|
if i > 0 and not parts[i-1][3]: # предыдущее не закрыло блок
|
||||||
need_prepend = True
|
part = "```\n" + part
|
||||||
|
|
||||||
# Проверяем нужно ли закрыть блок в конце этого сообщения
|
# Проверяем нужно ли закрыть блок в конце этого сообщения
|
||||||
# Если текущее не закрыло блок и следующее не имеет кода/не закроет
|
|
||||||
if i < total - 1 and not code_closed:
|
if i < total - 1 and not code_closed:
|
||||||
next_has = parts[i+1][1]
|
# Следующее сообщение не начинается с ``` — закрываем блок
|
||||||
next_opened = parts[i+1][2]
|
if not parts[i+1][2]: # следующее не открывает блок
|
||||||
# Закрываем если следующее не имеет кода или не открывает свой блок
|
part = part + "\n```"
|
||||||
if not next_has:
|
|
||||||
need_append = True
|
|
||||||
|
|
||||||
# Применяем модификации
|
|
||||||
if need_prepend:
|
|
||||||
part = "```\n" + part
|
|
||||||
if need_append:
|
|
||||||
part = part + "\n```"
|
|
||||||
|
|
||||||
# Добавляем номер части ПОСЛЕ работы с блоками кода
|
# Добавляем номер части ПОСЛЕ работы с блоками кода
|
||||||
if total > 1:
|
if total > 1:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue