Как правильно посчитать длину текста с эмодзи в Python?

Столкнулся с проблемой при написании функции для телеграм-бота, который обрабатывает текст и добавляет новые entities (свойства текста). Иногда он это делает со смещением. Как показала практика, это связано с подсчётом длины сообщения из-за наличия в них эмодзи.

Многие эмодзи на самом деле занимают два символа, хоть отображаются как одна "картинка". Естественно и длина этих эмодзи определяется как 1 символ.

Пример. Делаем строку с таким эмодзи.
spoiler
6398acf2d2295461007957.jpeg

Но на самом деле эмодзи из двух символов:
spoiler
6398ad1a50f02949242399.jpeg

Пайтон видит длину так:
spoiler
6398ad468c1fe021786449.jpeg

В итоге в Телеграм сообщение имеем такое смещение при добавлении текстовой ссылки:
spoiler
6398ad69c6178280730154.jpeg


Как можно исправить этот косяк? Чем и как считать длину? По идее же, это UTF-8 показывает эмодзи как один символ. Может как-то менять кодировку при подсчёте длины?
  • Вопрос задан
  • 252 просмотра
Решения вопроса 1
EntireMusic
@EntireMusic Автор вопроса
(:
Готовый редактор постов канала, некорректно работает только если есть группа медиа и описание написано не к первому медиа)) В таком случае ссылки добавятся к первому медиа, но в следующем тоже будет описание, в итоге Телеграм не покажет ни одно. Но это исключение, лень фиксить.

from aiogram import Router, types, F
from aiogram.utils import formatting as fmt


# Редактор постов канала в отдельном роутере
router = Router()

# Это понадобится, чтобы в медиагруппе редактировалось только описание первого медиа
check_mg = set()

#Фильтры: проверим что пост не переслан и что это медиа имеющие caption
@router.channel_post(~F.forward_from_chat, ~F.forward_from, (F.text | F.photo | F.video | F.animation | F.document))
async def redactor(message: types.Message):
    # Собираем текстовые данные из поста
    text = message.text or message.caption or ''

    # Собираем те entities которые уже есть в посте
    ent = message.entities or message.caption_entities or []

    # Удаляем встроенные ссылки если они есть
    ent = [e for e in ent if e.type != 'text_link']

   # Через инструмент formatting создаём внешний вид будущего поста
    content = fmt.Text(
        # Старый текст
        text,
        '\n\n',
        # В моём случае я добавляю три ссылки на канал, чат и админа с эмодзи-разделителем
        fmt.as_line(
            fmt.Bold(fmt.TextLink("Канал", url='https://t.me/1')),
            fmt.Bold(fmt.TextLink("Чат", url='https://t.me/2')),
            fmt.Bold(fmt.TextLink("Админ", url='https://t.me/3')),
            # Тут эмодзи который будет между ссылками
            sep='  '
        )
    )
    # Собираем новый текст и новые entities в кучу
    text, new_ent = content.render()

    # Тут расписывать не буду, разные проверки и разные варианты отправки отредактированного сообщения
    if message.text is not None:
        if len(text) <= 4096:
            await message.edit_text(text, entities=ent + new_ent, disable_web_page_preview=True)
        else:
            print("Can't edit Text, length exceeded.")
    elif message.media_group_id is None:
        if len(text) <= 1024:
            await message.edit_caption(caption=text, caption_entities=ent + new_ent, disable_web_page_preview=True)
        else:
            print("Can't edit Caption, length exceeded.")
    elif message.media_group_id not in check_mg:
        check_mg.add(message.media_group_id)
        if len(text) <= 1024:
            await message.edit_caption(caption=text, caption_entities=ent + new_ent, disable_web_page_preview=True)
            await asyncio.sleep(1)
            check_mg.remove(message.media_group_id)
        else:
            print("Can't edit Caption, length exceeded.")
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
yummy1gay
@yummy1gay
Проблема тут в API Telegram, я тоже наткнулся на неё, и с радостью поделюсь с вами её решением (хоть и поздновато).

Нам понадобится библиотека emoji из pip, чтобы посчитать количество эмодзи в тексте, нужно вызвать функцию emoji.emoji_count.

Вот пример кода с фиксом этой проблемы:

import emoji
from telethon.tl.types import MessageEntityBold

message_text = "Hi!"
emojis = emoji.emoji_count(message_text)

text_with_emoji = len(message_text) + emojis

entities = [MessageEntityBold(offset=0, length=text_with_emoji)]


Так же хочу отметить что лучше использовать опцию parse_mode=HTML, но в некоторых функциях API Telegram ее попросту нет, например в той что нужна была мне, а именно EditInlineBotMessage
Ответ написан
Vapaamies
@Vapaamies
Психанул и снес свои ответы козлам, не отмечающим…
Проблема наверняка в веб-редакторе, режущем символ цвета. В самом Питоне проблемы нет. Если скопировать цветной эмодзи в нормальный редактор для программистов, отображающий кодировку и коды символов, всё работает.
python emo.py
2
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы