Разбивка сообщения для Telegram с заголовком для каждой части
Задача:
Реализовать функцию, принимающую заголовок сообщения и его тело.
Функция должна вернуть массив сообщений (строк), каждое из которых не должно превышать 4096 символов (ограничение Telegram на длину одного сообщения).
Сообщение должно начинаться с заголовка, далее пустая строка, а затем тело.
Если данная конструкция превышает 4096 символов, должно быть сформировано несколько сообщений, и в таком случае к заголовку нужно дописать " (part x/y)"
Где x - номер текущей части, а y - количество всех частей.
То есть либо функция собирает строку header+'\n'+body и отправляет её, либо, если одним сообщением не получается, разбивает body на несколько частей и формирует сообщения с заголовком, содержащим номер текущей части и общее их количество.
После заголовка должно следовать максимально возможное количество символов из body, чтобы все сообщения были по 4096 знаков, а последнее <= 4096 знаков.
Реализация:
У меня получилось следующее:
TELEGRAM_MESSAGE_LENGTH = 4096 # Максимально возможная длина сообщения в Telegram
def get_secure_telegram_messages(header, body):
# Если все данные помещаются в одно сообщение, просто собрать сообщение и поместить в список
if len(header)+len(body)+1<=TELEGRAM_MESSAGE_LENGTH:
messages = [header+'\n'+body]
else:
# Сначала нужно подсчитать общее количество частей.
# То есть выяснить, на сколько частей необходимо разбить сообщение, чтобы не превысить лимит в 4096 знаков.
# В заголовке должно быть указано общее количество частей, оно будет вычислено позже.
total_parts = 0
offset = 0
last_part_length = 0
# В текущем блоке кода точно известно, что части будет как минимум две, а значит заголовок будет выглядить так:
# header (part current_part/total_parts)
# Длина произвольного заголовка - len(header), " (part /)" - 9 символов и "\n" 1 символ.
# В итоге длина заголовка будет не меньше len(header)+10 знаков
fixed_part_length = len(header)+10
while True:
# Длина текущей части складывается из фиксированной длины, вычисленной ранее,
# А также длины номера текущей части. Так как он порядковый, он будет равен количеству частей + 1.
part_length = fixed_part_length+len(str(total_parts+1))
# При старте цикла смещение равно нулю, оно используется для отбрасывания символов от начала тела.
# Значение new_offset используется в качестве второго компонента для получения среза.
# Для его вычисления нужно сложить offset и максимально возможную длину сообщения,
# А также вычесть длину уже вычесленного фрагмента части, так как заголовок и новая строка тоже входят в сообщение.
# Сдесь предполагается, что ранее посчитанный фрагмент сообщения будет короче 4096 символов.
new_offset = offset+TELEGRAM_MESSAGE_LENGTH-part_length
# Теперь, используя вычесленные значения для получения среза, нужно получить длину этого среза.
temp = len(body[offset:new_offset])
# Если результат пустой, значит предпоследняя итерация вычислила последнюю часть, и можно прервать цикл.
if temp==0:
break
# сохранить длину последней вычисленной части (будет использовано ниже), изменить offset и total_parts
last_part_length = part_length+temp
offset = new_offset
total_parts += 1
# Вычесление количество свободных символов.
# Длина последней части может быть равна 4096 символов, но может быть и меньше.
free = TELEGRAM_MESSAGE_LENGTH-last_part_length
while True:
# Для вычисления количества символов, требуемого для дописывания общего количества частей в каждую из них,
# нужно найти длину числа их количества и умножить на количество частей.
additional = len(str(total_parts))*total_parts
# Если число дополнительных символов входит в свободное число символов, значит при фактической генерации списка всё тело получится распределить по total_parts частям.
if additional<=free:
break
# Если не входит, значит нужна ещё одна часть.
total_parts += 1
# При добавлении части (сообщения) появляется 4096 свободных символов, но часть будет занята заголовком,
# Который вычисляется способом, аналогичным способу в предыдущем цикле.
free += TELEGRAM_MESSAGE_LENGTH-fixed_part_length+len(str(total_parts+1))
# После всех вычислений можно формировать сообщения.
messages = []
offset = 0
for i in range(total_parts):
message = f'{header} (part {i+1}/{total_parts})\n'
new_offset = offset+TELEGRAM_MESSAGE_LENGTH-len(message)
message += body[offset:new_offset]
offset = new_offset
messages.append(message)
return messages
Вопросы:
корректен ли данный алгоритм, при условии, что заголовок не сможет достичь 4096 знаков?
Можно ли сделать красивее и/или эффективнее?