From dd5999b51f5d5c7d90b72fb2d79ca7bd7e537d98 Mon Sep 17 00:00:00 2001 From: vvzvlad Date: Sun, 17 May 2026 23:12:11 +0300 Subject: [PATCH] fix: guard against missing messages and reaction issues Added a guard in `api_server.download_media_file` to detect `None` or empty messages and return a 404 error, preventing downstream failures. Enhanced `PostParser._extract_reactions` to handle paid reactions, custom emojis, and unknown types, aggregating counts correctly. Updated `_extract_flags` to skip paid and custom emoji reactions, ensuring flag detection works reliably. --- api_server.py | 7 ++++++- post_parser.py | 23 ++++++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/api_server.py b/api_server.py index c595995..67f3324 100644 --- a/api_server.py +++ b/api_server.py @@ -360,7 +360,12 @@ async def download_media_file(channel: Union[str, int], post_id: int, file_uniqu except asyncio.TimeoutError: logger.error(f"Timeout getting messages for {channel}/{post_id}") raise HTTPException(status_code=504, detail="Request timeout") - + + # Guard: message may be None (deleted) or an empty stub returned by Pyrogram + if not message or getattr(message, 'empty', False): + logger.warning(f"Message {post_id} not found or empty in channel {channel}") + raise HTTPException(status_code=404, detail="Post not found or deleted") + if message.media == MessageMediaType.POLL: return None, False diff --git a/post_parser.py b/post_parser.py index 8dbae7d..2ce408d 100644 --- a/post_parser.py +++ b/post_parser.py @@ -309,7 +309,20 @@ class PostParser: def _extract_reactions(self, message: Message) -> Union[Dict[str, int], None]: if reactions := getattr(message, 'reactions', None): - return {r.emoji: r.count for r in reactions.reactions} + result: Dict[str, int] = {} + for r in reactions.reactions: + # Resolve emoji key using the same logic as _reactions_views_links() + if getattr(r, "is_paid", False): + emoji = "⭐" + elif hasattr(r, "emoji") and r.emoji: + emoji = r.emoji + elif hasattr(r, "custom_emoji_id"): + emoji = "❓" # custom emoji — no text representation available + else: + emoji = "❓" # unknown reaction type + # Accumulate counts in case multiple reactions resolve to the same key + result[emoji] = result.get(emoji, 0) + r.count + return result return None def _extract_flags(self, message: Message) -> List[str]: #Tests: tests/postparser_extract_flags.py @@ -365,10 +378,14 @@ class PostParser: # Check if the post's reactions contain more clown emojis (🤡) or poo emojis (💩). if reactions := getattr(message, "reactions", None): for reaction in getattr(reactions, "reactions", []): - if reaction.emoji == "🤡" and reaction.count >= 30: + # Skip paid reactions and custom emoji — they have no .emoji attribute + if getattr(reaction, "is_paid", False) or not hasattr(reaction, "emoji"): + continue + emoji = reaction.emoji # attribute existence is guaranteed by hasattr guard above + if emoji == "🤡" and reaction.count >= 30: flags.append("clownpoo") break - if reaction.emoji == "💩" and reaction.count >= 30: + if emoji == "💩" and reaction.count >= 30: flags.append("clownpoo") break