@@ -10,6 +10,7 @@ async def send_file(
1010 chat_id : Union [int , str ],
1111 file_path : Union [str , List [str ]],
1212 caption : str = None ,
13+ topic_id : Optional [int ] = None ,
1314 ctx : Optional [Context ] = None ,
1415 account : str = None ,
1516) -> str :
@@ -20,13 +21,16 @@ async def send_file(
2021 file_path: Absolute or relative path to the file under allowed roots.
2122 Pass a list of 2-10 paths to send them as one Telegram media group.
2223 caption: Optional caption for the file or media group.
24+ topic_id: Optional forum topic ID (from list_topics). Sends into that topic
25+ in a forum-enabled community/supergroup. Also works as reply_to for a message.
2326 """
2427 try :
2528 if isinstance (file_path , list ):
2629 return await _send_album (
2730 chat_id = chat_id ,
2831 file_paths = file_path ,
2932 caption = caption ,
33+ topic_id = topic_id ,
3034 ctx = ctx ,
3135 account = account ,
3236 )
@@ -40,18 +44,24 @@ async def send_file(
4044 if path_error :
4145 return path_error
4246 entity = await resolve_entity (chat_id , cl )
43- await cl .send_file (entity , str (safe_path ), caption = caption )
47+ await cl .send_file (entity , str (safe_path ), caption = caption , reply_to = topic_id )
4448 return f"File sent to chat { chat_id } from { safe_path } ."
4549 except Exception as e :
4650 return log_and_format_error (
47- "send_file" , e , chat_id = chat_id , file_path = file_path , caption = caption
51+ "send_file" ,
52+ e ,
53+ chat_id = chat_id ,
54+ file_path = file_path ,
55+ caption = caption ,
56+ topic_id = topic_id ,
4857 )
4958
5059
5160async def _send_album (
5261 chat_id : Union [int , str ],
5362 file_paths : List [str ],
5463 caption : str = None ,
64+ topic_id : Optional [int ] = None ,
5565 ctx : Optional [Context ] = None ,
5666 account : str = None ,
5767) -> str :
@@ -71,7 +81,7 @@ async def _send_album(
7181 safe_paths .append (str (safe_path ))
7282
7383 entity = await resolve_entity (chat_id , cl )
74- await cl .send_file (entity , safe_paths , caption = caption )
84+ await cl .send_file (entity , safe_paths , caption = caption , reply_to = topic_id )
7585 return f"Album sent to chat { chat_id } with { len (safe_paths )} files."
7686
7787
@@ -84,6 +94,7 @@ async def send_album(
8494 chat_id : Union [int , str ],
8595 file_paths : List [str ],
8696 caption : str = None ,
97+ topic_id : Optional [int ] = None ,
8798 ctx : Optional [Context ] = None ,
8899 account : str = None ,
89100) -> str :
@@ -94,6 +105,8 @@ async def send_album(
94105 chat_id: The chat ID or username.
95106 file_paths: 2-10 absolute or relative file paths under allowed roots.
96107 caption: Optional caption for the album. Telegram displays it on the first item.
108+ topic_id: Optional forum topic ID (from list_topics). Sends into that topic
109+ in a forum-enabled community/supergroup. Also works as reply_to for a message.
97110 """
98111 try :
99112 if not isinstance (file_paths , list ):
@@ -102,12 +115,18 @@ async def send_album(
102115 chat_id = chat_id ,
103116 file_paths = file_paths ,
104117 caption = caption ,
118+ topic_id = topic_id ,
105119 ctx = ctx ,
106120 account = account ,
107121 )
108122 except Exception as e :
109123 return log_and_format_error (
110- "send_album" , e , chat_id = chat_id , file_paths = file_paths , caption = caption
124+ "send_album" ,
125+ e ,
126+ chat_id = chat_id ,
127+ file_paths = file_paths ,
128+ caption = caption ,
129+ topic_id = topic_id ,
111130 )
112131
113132
@@ -183,6 +202,7 @@ async def download_media(
183202async def send_voice (
184203 chat_id : Union [int , str ],
185204 file_path : str ,
205+ topic_id : Optional [int ] = None ,
186206 ctx : Optional [Context ] = None ,
187207 account : str = None ,
188208) -> str :
@@ -192,6 +212,8 @@ async def send_voice(
192212 Args:
193213 chat_id: The chat ID or username.
194214 file_path: Absolute or relative path under allowed roots to the OGG/OPUS file.
215+ topic_id: Optional forum topic ID (from list_topics). Sends into that topic
216+ in a forum-enabled community/supergroup. Also works as reply_to for a message.
195217 """
196218 try :
197219 cl = get_client (account )
@@ -215,10 +237,12 @@ async def send_voice(
215237 return "Voice file must be .ogg or .opus format."
216238
217239 entity = await resolve_entity (chat_id , cl )
218- await cl .send_file (entity , str (safe_path ), voice_note = True )
240+ await cl .send_file (entity , str (safe_path ), voice_note = True , reply_to = topic_id )
219241 return f"Voice message sent to chat { chat_id } from { safe_path } ."
220242 except Exception as e :
221- return log_and_format_error ("send_voice" , e , chat_id = chat_id , file_path = file_path )
243+ return log_and_format_error (
244+ "send_voice" , e , chat_id = chat_id , file_path = file_path , topic_id = topic_id
245+ )
222246
223247
224248@mcp .tool (
@@ -308,6 +332,7 @@ async def get_sticker_sets(account: str = None) -> str:
308332async def send_sticker (
309333 chat_id : Union [int , str ],
310334 file_path : str ,
335+ topic_id : Optional [int ] = None ,
311336 ctx : Optional [Context ] = None ,
312337 account : str = None ,
313338) -> str :
@@ -317,6 +342,8 @@ async def send_sticker(
317342 Args:
318343 chat_id: The chat ID or username.
319344 file_path: Absolute or relative path under allowed roots to the .webp sticker file.
345+ topic_id: Optional forum topic ID (from list_topics). Sends into that topic
346+ in a forum-enabled community/supergroup. Also works as reply_to for a message.
320347 """
321348 try :
322349 cl = get_client (account )
@@ -329,10 +356,12 @@ async def send_sticker(
329356 return path_error
330357
331358 entity = await resolve_entity (chat_id , cl )
332- await cl .send_file (entity , str (safe_path ), force_document = False )
359+ await cl .send_file (entity , str (safe_path ), force_document = False , reply_to = topic_id )
333360 return f"Sticker sent to chat { chat_id } from { safe_path } ."
334361 except Exception as e :
335- return log_and_format_error ("send_sticker" , e , chat_id = chat_id , file_path = file_path )
362+ return log_and_format_error (
363+ "send_sticker" , e , chat_id = chat_id , file_path = file_path , topic_id = topic_id
364+ )
336365
337366
338367@mcp .tool (
@@ -399,23 +428,32 @@ async def get_gif_search(query: str, limit: int = 10, account: str = None) -> st
399428@mcp .tool (annotations = ToolAnnotations (title = "Send Gif" , openWorldHint = True , destructiveHint = True ))
400429@with_account (readonly = False )
401430@validate_id ("chat_id" )
402- async def send_gif (chat_id : Union [int , str ], gif_id : int , account : str = None ) -> str :
431+ async def send_gif (
432+ chat_id : Union [int , str ],
433+ gif_id : int ,
434+ topic_id : Optional [int ] = None ,
435+ account : str = None ,
436+ ) -> str :
403437 """
404438 Send a GIF to a chat by Telegram GIF document ID (not a file path).
405439
406440 Args:
407441 chat_id: The chat ID or username.
408442 gif_id: Telegram document ID for the GIF (from get_gif_search).
443+ topic_id: Optional forum topic ID (from list_topics). Sends into that topic
444+ in a forum-enabled community/supergroup. Also works as reply_to for a message.
409445 """
410446 try :
411447 cl = get_client (account )
412448 if not isinstance (gif_id , int ):
413449 return "gif_id must be a Telegram document ID (integer), not a file path. Use get_gif_search to find IDs."
414450 entity = await resolve_entity (chat_id , cl )
415- await cl .send_file (entity , gif_id )
451+ await cl .send_file (entity , gif_id , reply_to = topic_id )
416452 return f"GIF sent to chat { chat_id } ."
417453 except Exception as e :
418- return log_and_format_error ("send_gif" , e , chat_id = chat_id , gif_id = gif_id )
454+ return log_and_format_error (
455+ "send_gif" , e , chat_id = chat_id , gif_id = gif_id , topic_id = topic_id
456+ )
419457
420458
421459__all__ = [
0 commit comments