telegram-mcp/telegram-mcp-server/main.py
2025-04-04 19:19:39 +08:00

206 lines
No EOL
6.6 KiB
Python

"""
Telegram MCP Server
The server uses FastMCP to expose Telegram functionality in a standardized way,
with each tool implemented as a decorated function. This allows Claude to:
1. Search contacts and chats
2. Retrieve message history with optional context
3. Send messages to individuals or groups
The server connects to the local SQLite database maintained by the Telegram Bridge,
and also communicates with the Bridge's HTTP API for sending messages.
"""
from typing import List, Dict, Any, Optional, Tuple
from mcp.server.fastmcp import FastMCP
from datetime import datetime
from telegram import (
search_contacts as telegram_search_contacts,
list_messages as telegram_list_messages,
list_chats as telegram_list_chats,
get_chat as telegram_get_chat,
get_direct_chat_by_contact as telegram_get_direct_chat_by_contact,
get_contact_chats as telegram_get_contact_chats,
get_last_interaction as telegram_get_last_interaction,
get_message_context as telegram_get_message_context,
send_message as telegram_send_message
)
# Initialize FastMCP server
mcp = FastMCP("telegram")
@mcp.tool()
def search_contacts(query: str) -> List[Dict[str, Any]]:
"""Search Telegram contacts by name or username.
Args:
query: Search term to match against contact names or usernames
"""
contacts = telegram_search_contacts(query)
return contacts
@mcp.tool()
def list_messages(
date_range: Optional[Tuple[datetime, datetime]] = None,
sender_id: Optional[int] = None,
chat_id: Optional[int] = None,
query: Optional[str] = None,
limit: int = 20,
page: int = 0,
include_context: bool = True,
context_before: int = 1,
context_after: int = 1
) -> List[Dict[str, Any]]:
"""Get Telegram messages matching specified criteria with optional context.
Args:
date_range: Optional tuple of (start_date, end_date) to filter messages by date
sender_id: Optional sender ID to filter messages by sender
chat_id: Optional chat ID to filter messages by chat
query: Optional search term to filter messages by content
limit: Maximum number of messages to return (default 20)
page: Page number for pagination (default 0)
include_context: Whether to include messages before and after matches (default True)
context_before: Number of messages to include before each match (default 1)
context_after: Number of messages to include after each match (default 1)
"""
messages = telegram_list_messages(
date_range=date_range,
sender_id=sender_id,
chat_id=chat_id,
query=query,
limit=limit,
page=page,
include_context=include_context,
context_before=context_before,
context_after=context_after
)
return messages
@mcp.tool()
def list_chats(
query: Optional[str] = None,
limit: int = 20,
page: int = 0,
chat_type: Optional[str] = None,
sort_by: str = "last_active"
) -> List[Dict[str, Any]]:
"""Get Telegram chats matching specified criteria.
Args:
query: Optional search term to filter chats by name or username
limit: Maximum number of chats to return (default 20)
page: Page number for pagination (default 0)
chat_type: Optional chat type filter ("user", "group", "channel", or "supergroup")
sort_by: Field to sort results by, either "last_active" or "title" (default "last_active")
"""
chats = telegram_list_chats(
query=query,
limit=limit,
page=page,
chat_type=chat_type,
sort_by=sort_by
)
return chats
@mcp.tool()
def get_chat(chat_id: int) -> Dict[str, Any]:
"""Get Telegram chat metadata by ID.
Args:
chat_id: The ID of the chat to retrieve
"""
chat = telegram_get_chat(chat_id)
return chat
@mcp.tool()
def get_direct_chat_by_contact(contact_id: int) -> Dict[str, Any]:
"""Get Telegram chat metadata by contact ID.
Args:
contact_id: The contact ID to search for
"""
chat = telegram_get_direct_chat_by_contact(contact_id)
return chat
@mcp.tool()
def get_contact_chats(contact_id: int, limit: int = 20, page: int = 0) -> List[Dict[str, Any]]:
"""Get all Telegram chats involving the contact.
Args:
contact_id: The contact's ID to search for
limit: Maximum number of chats to return (default 20)
page: Page number for pagination (default 0)
"""
chats = telegram_get_contact_chats(contact_id, limit, page)
return chats
@mcp.tool()
def get_last_interaction(contact_id: int) -> Dict[str, Any]:
"""Get most recent Telegram message involving the contact.
Args:
contact_id: The ID of the contact to search for
"""
message = telegram_get_last_interaction(contact_id)
return message
@mcp.tool()
def get_message_context(
message_id: int,
chat_id: int,
before: int = 5,
after: int = 5
) -> Dict[str, Any]:
"""Get context around a specific Telegram message.
Args:
message_id: The ID of the message to get context for
chat_id: The ID of the chat containing the message
before: Number of messages to include before the target message (default 5)
after: Number of messages to include after the target message (default 5)
"""
context = telegram_get_message_context(message_id, chat_id, before, after)
return context
@mcp.tool()
def send_message(
recipient: str,
message: str
) -> Dict[str, Any]:
"""Send a Telegram message to a person or group.
Args:
recipient: The recipient - either a username (with or without @) or a chat ID
message: The message text to send
Returns:
A dictionary containing success status and a status message
"""
# Validate input
if not recipient:
return {
"success": False,
"message": "Recipient must be provided"
}
# Call the telegram_send_message function
success, status_message = telegram_send_message(recipient, message)
return {
"success": success,
"message": status_message
}
if __name__ == "__main__":
# Redirect stdout/stderr to suppress initial output that might confuse Claude
import sys
import os
# Create logs directory
os.makedirs(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs'), exist_ok=True)
# Redirect stderr to log file
sys.stderr = open(os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), 'logs', 'mcp_error.log'), 'w')
# Initialize and run the server
mcp.run(transport='stdio')