Enhance message parsing and HTML rendering for Telegram posts

This commit is contained in:
vvzvlad
2025-02-01 18:31:14 +03:00
parent 20221c78e1
commit bcac493e4b
2 changed files with 159 additions and 21 deletions
+30 -1
View File
@@ -29,7 +29,36 @@ async def get_post_html(channel: str, post_id: int):
)
return f"""
<html>
<body style="font-family: Arial; margin: 20px;">
<head>
<title>Post {post_id}</title>
<style>
body {{ font-family: Arial; margin: 20px; }}
.poll {{
border: 1px solid #ddd;
padding: 15px;
margin: 20px 0;
border-radius: 8px;
}}
.poll-option {{
margin: 10px 0;
}}
.progress-bar {{
height: 20px;
background: #4CAF50;
border-radius: 4px;
margin: 5px 0;
}}
.stats {{
color: #666;
font-size: 0.9em;
}}
.total {{
margin-top: 15px;
font-weight: bold;
}}
</style>
</head>
<body>
{post['text']}
</body>
</html>
+129 -20
View File
@@ -4,6 +4,7 @@ from pyrogram.types import Message
from config import get_settings
import base64
import os
import re
logger = logging.getLogger(__name__)
settings = get_settings()
@@ -38,19 +39,74 @@ class TelegramClient:
logger.info("Telegram client disconnected")
async def _parse_message(self, message: Message) -> dict:
# Debug raw message structure
print(f"Raw message object: \n{message}")
# Extract text from different sources
text = message.text.html if message.text else "" # Get HTML formatted text
raw_text = message.text.html if message.text else ""
# Handle polls
poll = getattr(message, "poll", None)
if poll:
try:
question = getattr(poll, "question", "No question text")
raw_text += f"\n📊 Poll: {poll.question}\n"
if hasattr(poll, "options") and poll.options:
for i, option in enumerate(poll.options, 1):
raw_text += f"{i}. {getattr(option, 'text', '')}\n"
raw_text += "\n→ Vote in Telegram 🔗\n"
except Exception as e:
logger.error(f"Error parsing poll: {str(e)}")
raw_text += "\n[Error displaying poll]"
# Try to get caption for media messages
if not text and hasattr(message, "caption") and message.caption:
text = message.caption.html
if not raw_text and hasattr(message, "caption") and message.caption:
raw_text = message.caption.html
# Try to get text from other attributes
if not text and hasattr(message, "action"):
text = str(message.action)
elif not text and hasattr(message, "entities"):
if not raw_text and hasattr(message, "action"):
raw_text = str(message.action)
elif not raw_text and hasattr(message, "entities"):
# Extract HTML from entities
text = message.text.html if message.text else ""
raw_text = message.text.html if message.text else ""
# Process text entities for links
text_parts = []
last_offset = 0
entities = getattr(message, "entities", None)
if entities and isinstance(entities, list):
# Sort entities by offset
entities = sorted(entities, key=lambda x: x.offset)
for entity in entities:
if entity.offset > last_offset:
text_parts.append(raw_text[last_offset:entity.offset])
entity_text = raw_text[entity.offset:entity.offset+entity.length]
if entity.type == "text_link":
text_parts.append(f"<a href='{entity.url}'>{entity_text}</a>")
elif entity.type == "url":
text_parts.append(f"<a href='{entity_text}'>{entity_text}</a>")
else:
text_parts.append(entity_text)
last_offset = entity.offset + entity.length
# Add remaining text
if last_offset < len(raw_text):
text_parts.append(raw_text[last_offset:])
processed_text = "".join(text_parts)
else:
processed_text = raw_text
# Replace only URLs not already in <a> tags
processed_text = re.sub(
r'(?<!href=")(https?://\S+)(?!">)',
r'<a href="\1">\1</a>',
processed_text
)
# Parse media and add video markers
media = []
@@ -74,9 +130,9 @@ class TelegramClient:
media.append(parsed_media)
logger.debug(f"Parsed media: {parsed_media}")
video_count = sum(1 for m in media if m.get("type") == "video")
if video_count > 0:
text = f"[{'Video' if video_count == 1 else f'{video_count} Videos'}] {text}" if text else f"[{'Video' if video_count == 1 else f'{video_count} Videos'}]"
#video_count = sum(1 for m in media if m.get("type") == "video")
#if video_count > 0:
# # processed_text = f"[{'Video' if video_count == 1 else f'{video_count} Videos'}] {processed_text}" if processed_text else f"[{'Video' if video_count == 1 else f'{video_count} Videos'}]"
# Always add reactions if present
reactions_text = ""
@@ -85,25 +141,34 @@ class TelegramClient:
f"{r.emoji}({r.count})"
for r in message.reactions.reactions
)
text += reactions_text
processed_text += reactions_text
# Add media previews to text
if media:
previews = []
for m in media:
if m.get('thumbnail'):
previews.append(f"<img src='data:image/jpeg;base64,{m['thumbnail']}' style='max-width: 600px; margin: 10px 0;'>")
media_type = m.get('type', 'media').upper()
overlay = f"<div style='position:absolute; top:5px; left:5px; background:rgba(0,0,0,0.7); color:white; padding:2px 5px; border-radius:3px; font-size:0.8em;'>{media_type}</div>"
previews.append(
f"<div style='position:relative; display:inline-block; margin:5px;'>"
f"<img src='data:image/jpeg;base64,{m['thumbnail']}' style='max-width:600px; max-height:600px; object-fit: contain;'>"
f"{overlay}"
f"</div>"
)
if previews:
text = "<br>".join(previews) + "<br><br>" + text
previews_container = f"<div style='white-space: nowrap; overflow-x: auto;'>{''.join(previews)}</div>"
processed_text = previews_container + processed_text
# Convert newlines to HTML breaks AFTER adding reactions
text = text.replace('\n', '<br>')
processed_text = processed_text.replace('\n', '<br>')
return {
"id": message.id,
"date": message.date.isoformat(),
"text": text,
"views": message.views or 0
"text": processed_text,
"views": message.views or 0,
"media_group_id": message.media_group_id
}
async def _parse_media(self, media_obj) -> dict:
@@ -233,10 +298,54 @@ class TelegramClient:
"details": f"Message {post_id} in {channel} does not exist"
}
# Debug raw message structure
print("Raw message object: %s", message)
return await self._parse_message(message)
# Check for media group
media_group = []
if message.media_group_id:
logger.debug(f"Processing media group {message.media_group_id} for message {post_id}")
current_id = post_id
# Collect previous messages in group
while True:
prev_msg = await self.client.get_messages(
chat_id=channel,
message_ids=current_id - 1
)
if prev_msg and prev_msg.media_group_id == message.media_group_id:
logger.debug(f"Found previous message in group: {prev_msg.id}")
media_group.insert(0, prev_msg)
current_id -= 1
else:
break
# Collect next messages in group
current_id = post_id
while True:
next_msg = await self.client.get_messages(
chat_id=channel,
message_ids=current_id + 1
)
if next_msg and next_msg.media_group_id == message.media_group_id:
logger.debug(f"Found next message in group: {next_msg.id}")
media_group.append(next_msg)
current_id += 1
else:
break
# Process all messages in group
all_messages = media_group + [message]
logger.debug(f"Combined {len(all_messages)} messages in media group")
parsed_messages = [await self._parse_message(msg) for msg in all_messages]
# Combine results
combined = {
"id": post_id,
"date": message.date.isoformat(),
"text": "".join([m["text"] for m in parsed_messages if m["text"]]),
"views": max(m["views"] for m in parsed_messages),
"media_group_id": message.media_group_id
}
logger.debug(f"Combined result: {combined}")
return combined
except errors.RPCError as e:
logger.error(f"Telegram API error: {e.__class__.__name__} - {e}")
raise