Разбивка сообщения для 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 знаков?
Можно ли сделать красивее и/или эффективнее?


Ответы (0 шт):