@@ -13,7 +13,13 @@ def get_media_label(msg) -> str:
1313 msg.message but the media stays in msg.media).
1414 """
1515 try :
16- # стикер/голос/видео/аудио/гиф — это тоже document, поэтому проверяем их РАНЬШЕ document
16+ # Link web preview is NOT an attachment. Check it FIRST: for a message with a
17+ # link, Telethon returns the preview image via msg.photo; otherwise it would
18+ # be incorrectly classified as a "photo".
19+ if getattr (msg , "web_preview" , None ) is not None :
20+ return ""
21+ # Sticker/voice/video/audio/GIF are also represented as documents, so check
22+ # them BEFORE the generic document handler.
1723 sticker = getattr (msg , "sticker" , None )
1824 if sticker is not None :
1925 alt = ""
@@ -47,16 +53,168 @@ def get_media_label(msg) -> str:
4753 return "geo"
4854 if getattr (msg , "poll" , None ) is not None :
4955 return "poll"
50- # web-превью ссылки — это не вложение, не флагуем
51- if getattr (msg , "web_preview" , None ) is not None :
52- return ""
5356 if getattr (msg , "media" , None ) is not None :
5457 return "media"
5558 return ""
5659 except Exception :
5760 return ""
5861
5962
63+ def _inline_button_texts (msg ):
64+ """Inline button texts of the message (flat list), [] if none."""
65+ out = []
66+ try :
67+ for row in getattr (msg , "buttons" , None ) or []:
68+ for b in row :
69+ t = getattr (b , "text" , None )
70+ if t :
71+ out .append (t )
72+ except Exception :
73+ pass
74+ return out
75+
76+
77+ def _link_urls (msg ):
78+ """Explicit URLs from entities (links hidden behind text), [] if none."""
79+ out = []
80+ try :
81+ for e in getattr (msg , "entities" , None ) or []:
82+ u = getattr (e , "url" , None )
83+ if u :
84+ out .append (u )
85+ except Exception :
86+ pass
87+ return out
88+
89+
90+ def message_to_dict (msg ) -> dict :
91+ """API-complete but compact Telethon message view (omit empty fields).
92+
93+ The goal is for the MCP output to match the API object in completeness, rather
94+ than losing data such as media, albums, forwards, edits, buttons, reactions,
95+ and so on. All these fields are already present in the message object returned
96+ by the same get_messages request.
97+ """
98+ d = {"id" : msg .id , "sender" : get_sender_name (msg ), "date" : msg .date }
99+
100+ sender_id = getattr (msg , "sender_id" , None )
101+ if sender_id is not None :
102+ d ["sender_id" ] = sender_id
103+ if getattr (msg , "out" , False ):
104+ d ["out" ] = True
105+
106+ text = sanitize_user_content (msg .message ) if getattr (msg , "message" , None ) else ""
107+ if text :
108+ d ["text" ] = text
109+
110+ media_label = get_media_label (msg )
111+ if media_label :
112+ d ["media" ] = media_label
113+
114+ grouped_id = getattr (msg , "grouped_id" , None )
115+ if grouped_id :
116+ d ["grouped_id" ] = grouped_id # album: messages sharing one grouped_id form a single group
117+
118+ reply_to_id = (
119+ getattr (msg .reply_to , "reply_to_msg_id" , None ) if getattr (msg , "reply_to" , None ) else None
120+ )
121+ if reply_to_id :
122+ d ["reply_to" ] = reply_to_id
123+
124+ fwd = getattr (msg , "fwd_from" , None )
125+ if fwd is not None :
126+ finfo = {}
127+ fdate = getattr (fwd , "date" , None )
128+ if fdate :
129+ finfo ["date" ] = fdate
130+ fname = getattr (fwd , "from_name" , None )
131+ if fname :
132+ finfo ["from_name" ] = sanitize_name (fname )
133+ d ["forwarded" ] = finfo or True
134+
135+ via_bot_id = getattr (msg , "via_bot_id" , None )
136+ if via_bot_id :
137+ d ["via_bot_id" ] = via_bot_id
138+
139+ edit_date = getattr (msg , "edit_date" , None )
140+ if edit_date :
141+ d ["edited" ] = edit_date
142+
143+ if getattr (msg , "pinned" , False ):
144+ d ["pinned" ] = True
145+
146+ engagement = get_engagement_dict (msg )
147+ if engagement :
148+ d ["engagement" ] = engagement
149+
150+ replies = getattr (msg , "replies" , None )
151+ if replies is not None :
152+ cnt = getattr (replies , "replies" , None )
153+ if cnt is not None :
154+ d ["comments" ] = cnt
155+
156+ buttons = _inline_button_texts (msg )
157+ if buttons :
158+ d ["buttons" ] = buttons
159+
160+ urls = _link_urls (msg )
161+ if urls :
162+ d ["link_urls" ] = urls
163+
164+ action = getattr (msg , "action" , None )
165+ if action is not None :
166+ d ["action" ] = type (action ).__name__ # service message (joined/pinned/…)
167+
168+ ttl = getattr (msg , "ttl_period" , None )
169+ if ttl :
170+ d ["ttl_period" ] = ttl
171+
172+ return d
173+
174+
175+ def format_message_line (msg ) -> str :
176+ """Single-line human-readable message representation with ALL key flags."""
177+ parts = [f"ID: { msg .id } " , get_sender_name (msg ), f"Date: { msg .date } " ]
178+
179+ reply_to_id = (
180+ getattr (msg .reply_to , "reply_to_msg_id" , None ) if getattr (msg , "reply_to" , None ) else None
181+ )
182+ if reply_to_id :
183+ parts .append (f"reply to { reply_to_id } " )
184+
185+ flags = []
186+ media_label = get_media_label (msg )
187+ if media_label :
188+ flags .append (f"📎 { media_label } " )
189+ grouped_id = getattr (msg , "grouped_id" , None )
190+ if grouped_id :
191+ flags .append (f"album:{ grouped_id } " )
192+ if getattr (msg , "fwd_from" , None ) is not None :
193+ flags .append ("forwarded" )
194+ if getattr (msg , "edit_date" , None ):
195+ flags .append ("edited" )
196+ if getattr (msg , "via_bot_id" , None ):
197+ flags .append ("via_bot" )
198+ if getattr (msg , "pinned" , False ):
199+ flags .append ("pinned" )
200+ btn = _inline_button_texts (msg )
201+ if btn :
202+ flags .append (f"buttons:{ len (btn )} " )
203+ action = getattr (msg , "action" , None )
204+ if action is not None :
205+ flags .append (f"service:{ type (action ).__name__ } " )
206+ if flags :
207+ parts .append (", " .join (flags ))
208+
209+ engagement_info = get_engagement_info (msg ).lstrip (" |" ).strip ()
210+ if engagement_info :
211+ parts .append (engagement_info )
212+
213+ raw = sanitize_user_content (msg .message ) if getattr (msg , "message" , None ) else ""
214+ safe_text = raw .replace ("\n " , "\\ n" ) if raw else "[empty]"
215+ return " | " .join (parts ) + f" | Message: { safe_text } "
216+
217+
60218@mcp .tool (annotations = ToolAnnotations (title = "Get Messages" , openWorldHint = True , readOnlyHint = True ))
61219@with_account (readonly = True )
62220@validate_id ("chat_id" )
@@ -79,21 +237,7 @@ async def get_messages(
79237 messages = await cl .get_messages (entity , limit = page_size , add_offset = offset )
80238 if not messages :
81239 return "No messages found for this page."
82- lines = []
83- for msg in messages :
84- sender_name = get_sender_name (msg )
85- reply_info = ""
86- if msg .reply_to and msg .reply_to .reply_to_msg_id :
87- reply_info = f" | reply to { msg .reply_to .reply_to_msg_id } "
88-
89- engagement_info = get_engagement_info (msg )
90- safe_text = sanitize_user_content (msg .message ).replace ("\n " , "\\ n" )
91- media_label = get_media_label (msg )
92- media_info = f" | 📎 { media_label } " if media_label else ""
93-
94- lines .append (
95- f"ID: { msg .id } | { sender_name } | Date: { msg .date } { reply_info } { engagement_info } { media_info } | Message: { safe_text } "
96- )
240+ lines = [format_message_line (msg ) for msg in messages ]
97241 return "\n " .join (lines )
98242 except Exception as e :
99243 return log_and_format_error (
@@ -1237,21 +1381,7 @@ async def get_history(chat_id: Union[int, str], limit: int = 100, account: str =
12371381 entity = await resolve_entity (chat_id , cl )
12381382 messages = await cl .get_messages (entity , limit = limit )
12391383
1240- records = []
1241- for msg in messages :
1242- record = {
1243- "id" : msg .id ,
1244- "sender" : get_sender_name (msg ),
1245- "date" : msg .date ,
1246- "text" : sanitize_user_content (msg .message ),
1247- }
1248- reply_to_id = getattr (msg .reply_to , "reply_to_msg_id" , None ) if msg .reply_to else None
1249- if reply_to_id :
1250- record ["reply_to" ] = reply_to_id
1251- media_label = get_media_label (msg )
1252- if media_label :
1253- record ["media" ] = media_label
1254- records .append (record )
1384+ records = [message_to_dict (msg ) for msg in messages ]
12551385 return format_tool_result (records )
12561386 except Exception as e :
12571387 return log_and_format_error ("get_history" , e , chat_id = chat_id , limit = limit )
0 commit comments