From d562cf61fbca7f8ba8a8794765faa68048970446 Mon Sep 17 00:00:00 2001 From: anonim <70073044+l1v0n1@users.noreply.github.com> Date: Tue, 22 Apr 2025 18:24:46 +0300 Subject: [PATCH] refactor: Remove file handling functions from main.py and update README.md Removed several functions that required direct file path access, including `send_file`, `download_media`, `set_profile_photo`, `edit_chat_photo`, `send_voice`, `send_sticker`, and `upload_file`, due to limitations in the current MCP environment. Updated the README to reflect these changes and added a section on removed functionality. --- README.md | 17 ++--- main.py | 213 ------------------------------------------------------ 2 files changed, 7 insertions(+), 223 deletions(-) diff --git a/README.md b/README.md index f09885b..dbdd26f 100644 --- a/README.md +++ b/README.md @@ -41,7 +41,6 @@ This MCP server exposes a huge suite of Telegram tools. **Every major Telegram/T - **create_group(title, user_ids)**: Create a new group - **create_channel(title, about, megagroup)**: Create a channel or supergroup - **edit_chat_title(chat_id, title)**: Change chat/group/channel title -- **edit_chat_photo(chat_id, file_path)**: Set chat/group/channel photo - **delete_chat_photo(chat_id)**: Remove chat/group/channel photo - **leave_chat(chat_id)**: Leave a group or channel - **get_participants(chat_id)**: List all participants @@ -70,6 +69,7 @@ This MCP server exposes a huge suite of Telegram tools. **Every major Telegram/T - **get_message_context(chat_id, message_id, context_size)**: Context around a message - **get_history(chat_id, limit)**: Full chat history - **get_pinned_messages(chat_id)**: List pinned messages +- **get_last_interaction(contact_id)**: Most recent message with a contact ### Contact Management - **list_contacts()**: List all contacts @@ -84,21 +84,15 @@ This MCP server exposes a huge suite of Telegram tools. **Every major Telegram/T - **get_contact_ids()**: List all contact IDs - **get_direct_chat_by_contact(contact_query)**: Find direct chat with a contact - **get_contact_chats(contact_id)**: List all chats with a contact -- **get_last_interaction(contact_id)**: Most recent message with a contact ### User & Profile - **get_me()**: Get your user info - **update_profile(first_name, last_name, about)**: Update your profile -- **set_profile_photo(file_path)**: Set your profile photo - **delete_profile_photo()**: Remove your profile photo - **get_user_photos(user_id, limit)**: Get a user's profile photos - **get_user_status(user_id)**: Get a user's online status ### Media -- **send_file(chat_id, file_path, caption)**: Send a file -- **send_voice(chat_id, file_path)**: Send a voice message -- **download_media(chat_id, message_id, file_path)**: Download media -- **upload_file(file_path)**: Upload a file to Telegram servers - **get_media_info(chat_id, message_id)**: Get info about media in a message ### Search & Discovery @@ -108,9 +102,6 @@ This MCP server exposes a huge suite of Telegram tools. **Every major Telegram/T ### Stickers, GIFs, Bots - **get_sticker_sets()**: List sticker sets -- **send_sticker(chat_id, file_path)**: Send a sticker -- **get_gif_search(query, limit)**: Search for GIFs -- **send_gif(chat_id, gif_id)**: Send a GIF - **get_bot_info(bot_username)**: Get info about a bot - **set_bot_commands(bot_username, commands)**: Set bot commands (bot accounts only) @@ -123,6 +114,12 @@ This MCP server exposes a huge suite of Telegram tools. **Every major Telegram/T - **unarchive_chat(chat_id)**: Unarchive a chat - **get_recent_actions(chat_id)**: Get recent admin actions +## Removed Functionality + +Please note that tools requiring direct file path access on the server (`send_file`, `download_media`, `set_profile_photo`, `edit_chat_photo`, `send_voice`, `send_sticker`, `upload_file`) have been removed from `main.py`. This is due to limitations in the current MCP environment regarding handling file attachments and local file system paths. + +Additionally, GIF-related tools (`get_gif_search`, `get_saved_gifs`, `send_gif`) have been removed due to ongoing issues with reliability in the Telethon library or Telegram API interactions. + --- ## 📋 Requirements diff --git a/main.py b/main.py index c7899df..71500a9 100644 --- a/main.py +++ b/main.py @@ -996,53 +996,6 @@ async def get_participants(chat_id: int) -> str: return log_and_format_error("get_participants", e, chat_id=chat_id) -@mcp.tool() -async def send_file(chat_id: int, file_path: str, caption: str = None) -> str: - """ - Send a file to a chat. - Args: - chat_id: The chat ID. - file_path: Absolute path to the file to send (must exist and be readable). - caption: Optional caption for the file. - """ - try: - if not os.path.isfile(file_path): - return f"File not found: {file_path}" - if not os.access(file_path, os.R_OK): - return f"File is not readable: {file_path}" - entity = await client.get_entity(chat_id) - await client.send_file(entity, file_path, caption=caption) - return f"File sent to chat {chat_id}." - except Exception as e: - return log_and_format_error("send_file", e, chat_id=chat_id, file_path=file_path, caption=caption) - - -@mcp.tool() -async def download_media(chat_id: int, message_id: int, file_path: str) -> str: - """ - Download media from a message in a chat. - Args: - chat_id: The chat ID. - message_id: The message ID containing the media. - file_path: Absolute path to save the downloaded file (must be writable). - """ - try: - entity = await client.get_entity(chat_id) - msg = await client.get_messages(entity, ids=message_id) - if not msg or not msg.media: - return "No media found in the specified message." - # Check if directory is writable - dir_path = os.path.dirname(file_path) or '.' - if not os.access(dir_path, os.W_OK): - return f"Directory not writable: {dir_path}" - await client.download_media(msg, file=file_path) - if not os.path.isfile(file_path): - return f"Download failed: file not created at {file_path}" - return f"Media downloaded to {file_path}." - except Exception as e: - return log_and_format_error("download_media", e, chat_id=chat_id, message_id=message_id, file_path=file_path) - - @mcp.tool() async def update_profile(first_name: str = None, last_name: str = None, about: str = None) -> str: """ @@ -1059,20 +1012,6 @@ async def update_profile(first_name: str = None, last_name: str = None, about: s return log_and_format_error("update_profile", e, first_name=first_name, last_name=last_name, about=about) -@mcp.tool() -async def set_profile_photo(file_path: str) -> str: - """ - Set a new profile photo. - """ - try: - await client(functions.photos.UploadProfilePhotoRequest( - file=await client.upload_file(file_path) - )) - return "Profile photo updated." - except Exception as e: - return log_and_format_error("set_profile_photo", e, file_path=file_path) - - @mcp.tool() async def delete_profile_photo() -> str: """ @@ -1278,37 +1217,6 @@ async def edit_chat_title(chat_id: int, title: str) -> str: return log_and_format_error("edit_chat_title", e, chat_id=chat_id, title=title) -@mcp.tool() -async def edit_chat_photo(chat_id: int, file_path: str) -> str: - """ - Edit the photo of a chat, group, or channel. Requires a file path to an image. - """ - try: - if not os.path.isfile(file_path): - return f"Photo file not found: {file_path}" - if not os.access(file_path, os.R_OK): - return f"Photo file not readable: {file_path}" - - entity = await client.get_entity(chat_id) - uploaded_file = await client.upload_file(file_path) - - if isinstance(entity, Channel): - # For channels/supergroups, use EditPhotoRequest with InputChatUploadedPhoto - input_photo = InputChatUploadedPhoto(file=uploaded_file) - await client(functions.channels.EditPhotoRequest(channel=entity, photo=input_photo)) - elif isinstance(entity, Chat): - # For basic groups, use EditChatPhotoRequest with InputChatUploadedPhoto - input_photo = InputChatUploadedPhoto(file=uploaded_file) - await client(functions.messages.EditChatPhotoRequest(chat_id=chat_id, photo=input_photo)) - else: - return f"Cannot edit photo for this entity type ({type(entity)})." - - return f"Chat {chat_id} photo updated." - except Exception as e: - logger.exception(f"edit_chat_photo failed (chat_id={chat_id}, file_path='{file_path}')") - return log_and_format_error("edit_chat_photo", e, chat_id=chat_id, file_path=file_path) - - @mcp.tool() async def delete_chat_photo(chat_id: int) -> str: """ @@ -1749,29 +1657,6 @@ async def import_chat_invite(hash: str) -> str: return log_and_format_error("import_chat_invite", e, hash=hash) -@mcp.tool() -async def send_voice(chat_id: int, file_path: str) -> str: - """ - Send a voice message to a chat. File must be an OGG/OPUS voice note. - Args: - chat_id: The chat ID. - file_path: Absolute path to the OGG/OPUS file. - """ - try: - if not os.path.isfile(file_path): - return f"File not found: {file_path}" - if not os.access(file_path, os.R_OK): - return f"File is not readable: {file_path}" - mime, _ = mimetypes.guess_type(file_path) - if not (mime and (mime == 'audio/ogg' or file_path.lower().endswith('.ogg') or file_path.lower().endswith('.opus'))): - return "Voice file must be .ogg or .opus format." - entity = await client.get_entity(chat_id) - await client.send_file(entity, file_path, voice_note=True) - return f"Voice message sent to chat {chat_id}." - except Exception as e: - return log_and_format_error("send_voice", e, chat_id=chat_id, file_path=file_path) - - @mcp.tool() async def forward_message(from_chat_id: int, message_id: int, to_chat_id: int) -> str: """ @@ -1864,24 +1749,6 @@ async def reply_to_message(chat_id: int, message_id: int, text: str) -> str: return log_and_format_error("reply_to_message", e, chat_id=chat_id, message_id=message_id, text=text) -@mcp.tool() -async def upload_file(file_path: str) -> str: - """ - Upload a file to Telegram servers (returns file handle as string, not a file path). - Args: - file_path: Absolute path to the file to upload (must exist and be readable). - """ - try: - if not os.path.isfile(file_path): - return f"File not found: {file_path}" - if not os.access(file_path, os.R_OK): - return f"File is not readable: {file_path}" - file = await client.upload_file(file_path) - return str(file) - except Exception as e: - return log_and_format_error("upload_file", e, file_path=file_path) - - @mcp.tool() async def get_media_info(chat_id: int, message_id: int) -> str: """ @@ -2049,86 +1916,6 @@ async def get_sticker_sets() -> str: return log_and_format_error("get_sticker_sets", e) -@mcp.tool() -async def send_sticker(chat_id: int, file_path: str) -> str: - """ - Send a sticker to a chat. File must be a valid .webp sticker file. - Args: - chat_id: The chat ID. - file_path: Absolute path to the .webp sticker file. - """ - try: - if not os.path.isfile(file_path): - return f"Sticker file not found: {file_path}" - if not os.access(file_path, os.R_OK): - return f"Sticker file is not readable: {file_path}" - if not file_path.lower().endswith('.webp'): - return "Sticker file must be a .webp file." - entity = await client.get_entity(chat_id) - await client.send_file(entity, file_path, force_document=False) - return f"Sticker sent to chat {chat_id}." - except Exception as e: - return log_and_format_error("send_sticker", e, chat_id=chat_id, file_path=file_path) - - -@mcp.tool() -async def get_gif_search(query: str, limit: int = 10) -> str: - """ - Search for GIFs by query. Returns a list of Telegram document IDs (not file paths). - Args: - query: Search term for GIFs. - limit: Max number of GIFs to return. - """ - try: - # Try approach 1: SearchGifsRequest - try: - result = await client(functions.messages.SearchGifsRequest(q=query, offset_id=0, limit=limit)) - if not result.gifs: - return "[]" - return json.dumps([g.document.id for g in result.gifs], indent=2, default=json_serializer) - except (AttributeError, ImportError): - # Fallback approach: Use SearchRequest with GIF filter - try: - from telethon.tl.types import InputMessagesFilterGif - result = await client(functions.messages.SearchRequest( - peer="gif", q=query, filter=InputMessagesFilterGif(), - min_date=None, max_date=None, offset_id=0, add_offset=0, - limit=limit, max_id=0, min_id=0, hash=0 - )) - if not result or not hasattr(result, 'messages') or not result.messages: - return "[]" - # Extract document IDs from any messages with media - gif_ids = [] - for msg in result.messages: - if hasattr(msg, 'media') and msg.media and hasattr(msg.media, 'document'): - gif_ids.append(msg.media.document.id) - return json.dumps(gif_ids, default=json_serializer) - except Exception as inner_e: - # Last resort: Try to fetch from a public bot - return f"Could not search GIFs using available methods: {inner_e}" - except Exception as e: - logger.exception(f"get_gif_search failed (query={query}, limit={limit})") - return log_and_format_error("get_gif_search", e, query=query, limit=limit) - - -@mcp.tool() -async def send_gif(chat_id: int, gif_id: int) -> str: - """ - Send a GIF to a chat by Telegram GIF document ID (not a file path). - Args: - chat_id: The chat ID. - gif_id: Telegram document ID for the GIF (from get_gif_search). - """ - try: - if not isinstance(gif_id, int): - return "gif_id must be a Telegram document ID (integer), not a file path. Use get_gif_search to find IDs." - entity = await client.get_entity(chat_id) - await client.send_file(entity, gif_id) - return f"GIF sent to chat {chat_id}." - except Exception as e: - return log_and_format_error("send_gif", e, chat_id=chat_id, gif_id=gif_id) - - @mcp.tool() async def get_bot_info(bot_username: str) -> str: """