| """ |
| Telegram Bot接口定义 |
| 定义抽象接口以方便单元测试 |
| """ |
| |
| from abc import ABC, abstractmethod |
| from typing import Dict, List, Any, Optional, Callable, Union |
| from telegram import Update, Message |
| from telegram.ext import ContextTypes |
| |
| |
| class ITelegramClient(ABC): |
| """Telegram客户端接口 - 便于单元测试时Mock""" |
| |
| @abstractmethod |
| async def send_message( |
| self, |
| chat_id: Union[int, str], |
| text: str, |
| parse_mode: Optional[str] = None, |
| reply_to_message_id: Optional[int] = None |
| ) -> Message: |
| """发送消息""" |
| pass |
| |
| @abstractmethod |
| async def edit_message_text( |
| self, |
| text: str, |
| chat_id: Union[int, str], |
| message_id: int, |
| parse_mode: Optional[str] = None |
| ) -> Union[Message, bool]: |
| """编辑消息文本""" |
| pass |
| |
| @abstractmethod |
| async def get_file(self, file_id: str): |
| """获取文件对象""" |
| pass |
| |
| @abstractmethod |
| async def send_photo( |
| self, |
| chat_id: Union[int, str], |
| photo: Union[str, bytes], |
| caption: Optional[str] = None |
| ) -> Message: |
| """发送图片""" |
| pass |
| |
| @abstractmethod |
| async def send_document( |
| self, |
| chat_id: Union[int, str], |
| document: Union[str, bytes], |
| caption: Optional[str] = None |
| ) -> Message: |
| """发送文档""" |
| pass |
| |
| |
| class IContextManager(ABC): |
| """上下文管理器接口""" |
| |
| @abstractmethod |
| def add_message(self, chat_id: Union[int, str], user_id: int, message: str, is_bot: bool = False, user_info: dict = None): |
| """ |
| 添加消息到上下文 |
| |
| Args: |
| chat_id: 聊天ID |
| user_id: 用户ID |
| message: 消息内容 |
| is_bot: 是否为机器人消息 |
| user_info: 用户信息字典,包含username, first_name, last_name等 |
| """ |
| pass |
| |
| @abstractmethod |
| def get_context(self, chat_id: Union[int, str], limit: int = 10) -> List[Dict[str, Any]]: |
| """获取聊天上下文""" |
| pass |
| |
| @abstractmethod |
| def clear_context(self, chat_id: Union[int, str]): |
| """清空指定聊天的上下文""" |
| pass |
| |
| |
| class IMessageHandler(ABC): |
| """消息处理器接口""" |
| |
| @abstractmethod |
| async def handle_text_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE): |
| """处理文本消息""" |
| pass |
| |
| @abstractmethod |
| async def handle_photo_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE): |
| """处理图片消息""" |
| pass |
| |
| @abstractmethod |
| async def handle_document_message(self, update: Update, context: ContextTypes.DEFAULT_TYPE): |
| """处理文档消息""" |
| pass |
| |
| |
| class IFileHandler(ABC): |
| """文件处理器接口""" |
| |
| @abstractmethod |
| async def process_image(self, file_path: str) -> str: |
| """处理图片文件,返回描述文本""" |
| pass |
| |
| @abstractmethod |
| async def process_document(self, file_path: str) -> str: |
| """处理文档文件,返回内容摘要""" |
| pass |
| |
| @abstractmethod |
| async def generate_image(self, prompt: str) -> bytes: |
| """生成图片,返回图片数据""" |
| pass |
| |
| |
| class IClaudeAgent(ABC): |
| """Claude Agent接口 - 用于处理用户请求""" |
| |
| @abstractmethod |
| async def process_message( |
| self, |
| message: str, |
| context: List[Dict[str, Any]], |
| user_info: Dict[str, Any] |
| ) -> str: |
| """处理用户消息,返回回复""" |
| pass |
| |
| @abstractmethod |
| async def process_with_image( |
| self, |
| message: str, |
| image_path: str, |
| context: List[Dict[str, Any]], |
| user_info: Dict[str, Any] |
| ) -> str: |
| """处理包含图片的消息""" |
| pass |
| |
| @abstractmethod |
| async def process_with_document( |
| self, |
| message: str, |
| document_path: str, |
| context: List[Dict[str, Any]], |
| user_info: Dict[str, Any] |
| ) -> str: |
| """处理包含文档的消息""" |
| pass |
| |
| |
| class IStreamMessageSender(ABC): |
| """流式消息发送器接口""" |
| |
| @abstractmethod |
| async def send_streaming_message( |
| self, |
| chat_id: Union[int, str], |
| message_generator: Callable, |
| initial_text: str = "⌨️ User is typing...", |
| reply_to_message_id: Optional[int] = None |
| ) -> Message: |
| """发送流式更新的消息""" |
| pass |