51 lines
2.3 KiB
Python
51 lines
2.3 KiB
Python
"""Copy user messages without losing Telegram entities, including custom emoji."""
|
|
import asyncio
|
|
from functools import wraps
|
|
|
|
from aiogram.types import Message, MessageEntity
|
|
|
|
COPY_TIMEOUT = 15.0
|
|
|
|
|
|
def bounded_copy(func):
|
|
@wraps(func)
|
|
async def wrapped(*args, **kwargs):
|
|
async with asyncio.timeout(COPY_TIMEOUT):
|
|
return await func(*args, **kwargs)
|
|
return wrapped
|
|
|
|
|
|
def utf16_length(text: str) -> int:
|
|
"""Telegram entity offsets count UTF-16 code units, not Python characters."""
|
|
return len(text.encode("utf-16-le")) // 2
|
|
|
|
|
|
@bounded_copy
|
|
async def copy_preserving_entities(message: Message, recipient_id: int, sender_name: str | None = None):
|
|
if sender_name is None:
|
|
# Native copy keeps the original body/caption and its entities unchanged.
|
|
return await message.copy_to(recipient_id)
|
|
|
|
prefix = "📨 "
|
|
header = f"{prefix}{sender_name}:\n\n"
|
|
header_entities = [MessageEntity(type="bold", offset=utf16_length(prefix),
|
|
length=utf16_length(sender_name + ":"))]
|
|
offset = utf16_length(header)
|
|
has_caption = any((message.photo, message.video, message.document,
|
|
message.animation, message.audio, message.voice))
|
|
body = message.text if message.text is not None else (message.caption or "")
|
|
original = message.entities if message.text is not None else message.caption_entities
|
|
entities = header_entities + [entity.model_copy(update={"offset": entity.offset + offset})
|
|
for entity in original or ()]
|
|
limit = 4096 if message.text is not None else 1024
|
|
if (message.text is not None or has_caption) and offset + utf16_length(body) <= limit:
|
|
if message.text is not None:
|
|
return await message.bot.send_message(recipient_id, header + body,
|
|
entities=entities, parse_mode=None)
|
|
return await message.copy_to(recipient_id, caption=header + body,
|
|
caption_entities=entities, parse_mode=None)
|
|
|
|
# Do not trim a long body/caption: send the header separately and copy the original.
|
|
await message.bot.send_message(recipient_id, header, entities=header_entities, parse_mode=None)
|
|
return await message.copy_to(recipient_id)
|