From 258593d9f7fd601c1409fb4b5434206c0b4998c2 Mon Sep 17 00:00:00 2001 From: superyan Date: Wed, 18 Dec 2024 21:27:00 +0800 Subject: [PATCH] Wed Dec 18 21:27:00 CST 2024 inscode --- .env | 3 + .inscode | 2 +- README.md | 50 ++++++ __pycache__/api_client.cpython-39.pyc | Bin 0 -> 5826 bytes __pycache__/config.cpython-39.pyc | Bin 0 -> 416 bytes api_client.py | 132 ++++++++++++++ bot.py | 247 ++++++++++++++++++++++++++ config.py | 14 ++ requirements.txt | 6 + test_bot.py | 86 +++++++++ 10 files changed, 539 insertions(+), 1 deletion(-) create mode 100644 .env create mode 100644 README.md create mode 100644 __pycache__/api_client.cpython-39.pyc create mode 100644 __pycache__/config.cpython-39.pyc create mode 100644 api_client.py create mode 100644 bot.py create mode 100644 config.py create mode 100644 test_bot.py diff --git a/.env b/.env new file mode 100644 index 0000000..dc01ca2 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +API_TOKEN=sk-uztyxtwbxdjhccafmwytabtyzadmoycjnanmctstduyatlnf +TELEGRAM_BOT_TOKEN=7390701971:AAFZ9hfl7HbTdBkm1WB8nTVNmzPdcxTPpoU +BOT_NAME=aiagent10_bot diff --git a/.inscode b/.inscode index 3a001eb..66c97eb 100644 --- a/.inscode +++ b/.inscode @@ -1,4 +1,4 @@ -run = "pip install -r requirements.txt;python main.py" +run = "pip install -r requirements.txt;python bot.py" language = "python" [packager] diff --git a/README.md b/README.md new file mode 100644 index 0000000..9a8700c --- /dev/null +++ b/README.md @@ -0,0 +1,50 @@ +# Telegram AI 助手机器人 + +这是一个基于 Streamlit 的 Telegram 机器人应用,可以在私聊和群组中与用户进行对话。 + +## 功能特点 + +- 支持私聊和群组对话 +- 在群组中通过 @ 机器人触发对话 +- 支持基本命令(/start, /help) +- 实时显示对话历史 +- 使用 Streamlit 提供友好的 Web 界面 + +## 安装说明 + +1. 克隆项目到本地 +2. 安装依赖: + ```bash + pip install -r requirements.txt + ``` + +3. 创建 `.env` 文件,添加以下配置: + ``` + API_TOKEN=你的API密钥 + TELEGRAM_BOT_TOKEN=你的Telegram机器人Token + BOT_NAME=你的机器人名称 + ``` + +## 使用方法 + +1. 运行应用: + ```bash + streamlit run bot.py + ``` + +2. 在浏览器中打开显示的地址 +3. 点击"开始监听 Telegram"按钮启动机器人 + +### 在 Telegram 中使用 + +- 私聊:直接发送消息给机器人 +- 群组:在消息中 @ 机器人(例如:@你的机器人 你好) +- 命令: + - /start:开始对话 + - /help:显示帮助信息 + +## 注意事项 + +- 请确保已正确配置所有环境变量 +- 确保机器人已被添加到目标群组 +- 机器人需要有足够的群组权限 diff --git a/__pycache__/api_client.cpython-39.pyc b/__pycache__/api_client.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0d0bcec5247789bea42045137206ee6aa3259b2a GIT binary patch literal 5826 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 Jx!Kpe=6_wRd1jX!Fu;%iSDX@o zk>pAc1kxjb0L!i%fL8@8E9jPR$yE!ca#k*?GS*g-t6_KxYLy?Z1$s4@ZxQO@5vDAN z=Sy?SSdyI}c;Ah+Wq0?S+0q)XLaqF#tm7Wujlpm(@U+A zOEh!UjjdGTEw?OY-Q763Ithf?R2H{-w|`+fg+eM8m6kZHK`PV=_}xP(r3?)gY)a$N q(Q&KMY&7{V;&C`JlSQtbMikFTaK?8JvK Dict[str, str]: + return {"role": self.role, "content": self.content} + +class Conversation: + def __init__(self, max_history: int = 10): + self.messages: List[Message] = [] + self.max_history = max_history + + def add_message(self, role: str, content: str): + self.messages.append(Message(role, content)) + # 保持历史记录在限制范围内,但保留系统消息 + if len(self.messages) > self.max_history + 1: # +1 是为了系统消息 + # 保留第一条(系统)消息 + self.messages = [self.messages[0]] + self.messages[-(self.max_history-1):] + + def get_messages(self) -> List[Dict[str, str]]: + return [msg.to_dict() for msg in self.messages] + +class APIClient: + def __init__(self): + self.headers = { + "Authorization": f"Bearer {API_TOKEN}", + "Content-Type": "application/json" + } + self.session: Optional[aiohttp.ClientSession] = None + self.conversations: Dict[int, Conversation] = {} # chat_id -> Conversation + + def get_or_create_conversation(self, chat_id: int) -> Conversation: + if chat_id not in self.conversations: + conv = Conversation() + # 添加系统提示和知识库 + conv.add_message("system", + "你是一个友好、专业的 AI 助手,专门负责解答关于 AGI Open Network (AON) 的问题。\n" + "以下是你的知识库:\n\n" + "1. AI Agent 定义:\n" + "AI Agent 是由人工智能驱动的软件实体,能够自主执行任务、做出决策,并与用户或其他系统交互以实现特定目标。\n\n" + "2. AI Agent 为什么需要加密货币:\n" + "- 提供去中心化和安全的基础设施\n" + "- 支持智能合约执行任务\n" + "- 实现安全的微交易\n" + "- 促进共享所有权\n" + "- 实现透明的金融交互\n\n" + "3. AON 愿景和使命:\n" + "愿景:让 AI agents 成为有价值的资产,在各种消费应用中产生收益。\n" + "使命:创建开放平台,让所有人都能开发、部署和变现 AI Agents 和 AI Models。\n\n" + "4. AON 产品模块:\n" + "- AI Model API 平台(3000+ 开源模型)\n" + "- AI 算力聚合器\n" + "- AI Agent 发行平台(IAO)\n" + "- AI Model 发行平台(IMO)\n" + "- Web3 SDK(MPC, PayFi)\n\n" + "5. 用户类型:\n" + "专业开发者:可使用平台 AI 模型和算力资源\n" + "非专业开发者:可使用无代码开发模板\n\n" + "6. 变现模式:\n" + "- IAO(Initial Agent Offering):AI Agent 代币发行\n" + "- IMO(Initial Model Offering):AI 模型代币发行\n\n" + "请根据以上知识,准确、简洁地回答用户问题。如果遇到知识库之外的问题,请诚实地表示不确定。" + ) + self.conversations[chat_id] = conv + return self.conversations[chat_id] + + async def ensure_session(self): + """确保 aiohttp session 已创建""" + if self.session is None or self.session.closed: + self.session = aiohttp.ClientSession() + + async def close(self): + """关闭 session""" + if self.session and not self.session.closed: + await self.session.close() + + def clear_history(self, chat_id: int): + """清除特定对话的历史记录""" + if chat_id in self.conversations: + del self.conversations[chat_id] + + async def get_ai_response(self, chat_id: int, user_message: str, retry_count: int = 0) -> str: + """获取 AI 回复""" + try: + await self.ensure_session() + + # 获取或创建对话 + conversation = self.get_or_create_conversation(chat_id) + + # 添加用户消息 + conversation.add_message("user", user_message) + + payload = { + "model": MODEL_NAME, + "messages": conversation.get_messages() + } + + logger.debug(f"发送请求到 API,payload: {payload}") + + async with self.session.post(API_URL, json=payload, headers=self.headers) as response: + response.raise_for_status() + data = await response.json() + + if "choices" in data and data["choices"]: + ai_response = data["choices"][0]["message"]["content"] + # 添加 AI 回复到历史记录 + conversation.add_message("assistant", ai_response) + return ai_response + else: + logger.error(f"API 返回的数据格式不正确: {data}") + return "抱歉,我现在无法正确理解和回复。" + + except aiohttp.ClientError as e: + if retry_count < 2: # 最多重试2次 + logger.warning(f"API 请求失败,正在重试 ({retry_count + 1}/2): {str(e)}") + return await self.get_ai_response(chat_id, user_message, retry_count + 1) + else: + logger.error(f"API 请求失败,已达到最大重试次数: {str(e)}") + return "抱歉,我暂时无法访问 AI 服务,请稍后再试。" + + except Exception as e: + logger.error(f"调用 API 时发生错误: {str(e)}", exc_info=True) + return "抱歉,处理你的消息时出现了问题,请稍后再试。" diff --git a/bot.py b/bot.py new file mode 100644 index 0000000..b802cab --- /dev/null +++ b/bot.py @@ -0,0 +1,247 @@ +import os +import logging +import asyncio +from telegram import Bot, Update +from telegram.ext import Application, CommandHandler, MessageHandler, filters, ContextTypes +from telegram.error import TelegramError, NetworkError, TimedOut +from config import TELEGRAM_BOT_TOKEN +from api_client import APIClient + +# 设置日志记录 +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +# 创建 API 客户端 +api_client = APIClient() + +async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + """处理 /start 命令""" + try: + logger.info(f"收到 /start 命令,来自用户: {update.message.from_user.username}") + await update.message.reply_text( + "👋 你好!我是一个智能助手。\n\n" + "🤖 我可以:\n" + "1. 回答你的问题\n" + "2. 帮你完成任务\n" + "3. 陪你聊天\n\n" + "💡 直接发消息给我就可以开始对话!\n" + "🔄 使用 /clear 可以清除对话历史\n" + "❓ 使用 /help 查看更多帮助" + ) + except Exception as e: + logger.error(f"处理 /start 命令时出错: {str(e)}", exc_info=True) + await update.message.reply_text("抱歉,处理命令时出现错误,请稍后再试。") + +async def help_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + """处理 /help 命令""" + try: + logger.info(f"收到 /help 命令,来自用户: {update.message.from_user.username}") + await update.message.reply_text( + "🔍 帮助信息:\n\n" + "命令列表:\n" + "/start - 开始对话\n" + "/help - 显示帮助信息\n" + "/clear - 清除对话历史\n" + "/check - 检查机器人权限\n\n" + "使用提示:\n" + "1. 直接发送消息即可与我对话\n" + "2. 我会记住对话内容,保持上下文连贯\n" + "3. 如果想开始新话题,可以用 /clear 清除历史\n" + "4. 我会用emoji让对话更生动\n" + "5. 如果遇到问题,请尝试重新发送消息" + ) + except Exception as e: + logger.error(f"处理 /help 命令时出错: {str(e)}", exc_info=True) + await update.message.reply_text("抱歉,处理命令时出现错误,请稍后再试。") + +async def clear_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + """处理 /clear 命令""" + try: + chat_id = update.message.chat_id + logger.info(f"收到 /clear 命令,来自用户: {update.message.from_user.username}") + + # 清除对话历史 + api_client.clear_history(chat_id) + + await update.message.reply_text( + "🧹 已清除对话历史!\n" + "现在我们可以开始新的对话了。" + ) + except Exception as e: + logger.error(f"处理 /clear 命令时出错: {str(e)}", exc_info=True) + await update.message.reply_text("抱歉,清除历史记录时出现错误,请稍后再试。") + +async def check_permissions_command(update: Update, context: ContextTypes.DEFAULT_TYPE): + """检查机器人权限""" + try: + chat = update.message.chat + logger.info(f"正在检查权限 - 聊天ID: {chat.id}, 类型: {chat.type}") + + # 获取机器人信息 + bot_info = await context.bot.get_me() + logger.info(f"机器人信息 - ID: {bot_info.id}, 用户名: @{bot_info.username}") + + # 获取机器人成员信息 + try: + bot_member = await chat.get_member(context.bot.id) + logger.info(f"机器人成员信息 - 状态: {bot_member.status}, 权限: {vars(bot_member)}") + except Exception as e: + logger.error(f"获取机器人成员信息失败: {str(e)}") + bot_member = None + + # 获取发送者信息 + try: + sender = await chat.get_member(update.message.from_user.id) + logger.info(f"发送者信息 - 状态: {sender.status}, 权限: {vars(sender)}") + except Exception as e: + logger.error(f"获取发送者信息失败: {str(e)}") + sender = None + + permissions_info = [ + f"🤖 机器人信息:", + f"- 用户名:@{bot_info.username}", + f"- ID:{bot_info.id}", + f"- 是否为机器人:{'是' if bot_info.is_bot else '否'}", + f"\n📱 当前聊天信息:", + f"- 类型:{chat.type}", + f"- ID:{chat.id}", + f"- 标题:{chat.title if chat.type != 'private' else '私聊'}", + f"\n👤 发送者信息:", + f"- 用户名:@{update.message.from_user.username}", + f"- ID:{update.message.from_user.id}", + f"- 状态:{sender.status if sender else '未知'}" + ] + + if bot_member: + permissions_info.extend([ + f"\n🔑 机器人权限:", + f"- 成员状态:{bot_member.status}", + f"- 是否为管理员:{'是' if bot_member.status in ['administrator', 'creator'] else '否'}", + f"- 是否可以发送消息:{'是' if getattr(bot_member, 'can_send_messages', None) is not False else '否'}", + f"- 是否可以发送媒体消息:{'是' if getattr(bot_member, 'can_send_media_messages', None) is not False else '否'}", + f"- 是否可以添加网页预览:{'是' if getattr(bot_member, 'can_add_web_page_previews', None) is not False else '否'}", + f"- 是否可以发送其他消息:{'是' if getattr(bot_member, 'can_send_other_messages', None) is not False else '否'}", + f"- 原始权限数据:{vars(bot_member)}" + ]) + else: + permissions_info.append("\n❌ 无法获取机器人权限信息") + + await update.message.reply_text("\n".join(permissions_info)) + + except Exception as e: + error_msg = f"检查权限时出错: {str(e)}" + logger.error(error_msg, exc_info=True) + await update.message.reply_text(f"抱歉,检查权限时出现错误:\n{error_msg}") + +async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE, retry_count=0): + """处理用户消息""" + try: + # 获取用户信息和消息 + user = update.message.from_user + message_text = update.message.text + chat_id = update.message.chat_id + bot_username = context.bot.username + + # 检查是否是群组消息 + is_group = update.message.chat.type in ["group", "supergroup"] + logger.info(f"消息类型: {'群组消息' if is_group else '私聊消息'}") + logger.info(f"机器人用户名: {bot_username}") + logger.info(f"原始消息: {message_text}") + + # 检查是否提到了机器人(包括回复和@) + mentioned = False + if is_group: + # 检查是否是回复机器人的消息 + if update.message.reply_to_message and update.message.reply_to_message.from_user.id == context.bot.id: + mentioned = True + # 检查是否@机器人(支持消息中的任何位置) + elif bot_username and f"@{bot_username}" in message_text: + mentioned = True + # 移除@username部分 + message_text = message_text.replace(f"@{bot_username}", "").strip() + + logger.info(f"是否提到机器人: {mentioned}") + + # 如果没有提到机器人,则忽略消息 + if not mentioned: + logger.info("未提到机器人,忽略消息") + return + + logger.info(f"处理消息 [{chat_id}] 来自 {user.username}: {message_text}") + + # 发送"正在输入"状态 + await update.message.chat.send_action(action="typing") + + # 调用 AI API 获取回复 + response = await api_client.get_ai_response(chat_id, message_text) + + # 发送回复 + await update.message.reply_text(response) + logger.info(f"已回复消息 [{chat_id}]: {response[:100]}...") + + except (NetworkError, TimedOut) as e: + if retry_count < 3: + logger.warning(f"发送消息失败,正在重试 ({retry_count + 1}/3): {str(e)}") + await asyncio.sleep(1) + return await handle_message(update, context, retry_count + 1) + else: + logger.error(f"发送消息失败,已达到最大重试次数: {str(e)}") + await update.message.reply_text("抱歉,发送消息时出现错误,请稍后再试。") + except Exception as e: + logger.error(f"处理消息时出错: {str(e)}", exc_info=True) + await update.message.reply_text("抱歉,处理消息时出现错误,请稍后再试。") + +async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE) -> None: + """处理错误""" + logger.error("发生异常:", exc_info=context.error) + +async def cleanup(): + """清理资源""" + await api_client.close() + +def run_bot(): + """运行机器人""" + try: + # 创建应用 + application = Application.builder().token(TELEGRAM_BOT_TOKEN).build() + + # 添加处理器 + application.add_handler(CommandHandler("start", start_command)) + application.add_handler(CommandHandler("help", help_command)) + application.add_handler(CommandHandler("clear", clear_command)) + application.add_handler(CommandHandler("check", check_permissions_command)) + + # 添加消息处理器,处理提及和私聊消息 + application.add_handler(MessageHandler( + (filters.TEXT & ~filters.COMMAND) & + (filters.ChatType.GROUPS | filters.ChatType.PRIVATE) & + (filters.Entity("mention") | filters.ChatType.PRIVATE), + handle_message + )) + + # 添加处理器,处理回复机器人的消息 + application.add_handler(MessageHandler( + (filters.TEXT & ~filters.COMMAND) & + filters.REPLY, + handle_message + )) + + # 添加错误处理器 + application.add_error_handler(error_handler) + + # 启动机器人 + logger.info("正在启动机器人...") + application.run_polling(drop_pending_updates=True) + + except Exception as e: + logger.error(f"启动机器人时出错: {str(e)}", exc_info=True) + raise + finally: + # 确保清理资源 + asyncio.run(cleanup()) + +if __name__ == "__main__": + run_bot() diff --git a/config.py b/config.py new file mode 100644 index 0000000..9702168 --- /dev/null +++ b/config.py @@ -0,0 +1,14 @@ +import os +from dotenv import load_dotenv + +# 加载环境变量 +load_dotenv() + +# API 配置 +API_URL = "https://api.siliconflow.cn/v1/chat/completions" +API_TOKEN = os.getenv("API_TOKEN") # 从环境变量获取 +MODEL_NAME = "meta-llama/Llama-3.3-70B-Instruct" + +# Telegram Bot 配置 +TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN") # 从环境变量获取 +BOT_NAME = os.getenv("BOT_NAME", "AI助手") # 默认名称为"AI助手" diff --git a/requirements.txt b/requirements.txt index e69de29..79ae48f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -0,0 +1,6 @@ +python-telegram-bot>=20.7 +requests==2.31.0 +streamlit==1.29.0 +python-dotenv==1.0.0 +asyncio>=3.4.3 +aiohttp==3.9.1 diff --git a/test_bot.py b/test_bot.py new file mode 100644 index 0000000..a129717 --- /dev/null +++ b/test_bot.py @@ -0,0 +1,86 @@ +import os +import logging +import asyncio +from telegram import Bot +from telegram.error import TelegramError, NetworkError, TimedOut +from config import TELEGRAM_BOT_TOKEN + +# 设置日志记录 +logging.basicConfig( + level=logging.DEBUG, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s' +) +logger = logging.getLogger(__name__) + +async def handle_message(bot: Bot, chat_id: int, text: str, retry_count=0): + """处理接收到的消息,带重试机制""" + try: + if text == '/start': + await bot.send_message(chat_id=chat_id, text="你好!我是测试机器人。") + else: + await bot.send_message(chat_id=chat_id, text=f"你说: {text}") + except (NetworkError, TimedOut) as e: + if retry_count < 3: + logger.warning(f"发送消息失败,正在重试 ({retry_count + 1}/3): {str(e)}") + await asyncio.sleep(1) + return await handle_message(bot, chat_id, text, retry_count + 1) + else: + logger.error(f"发送消息失败,已达到最大重试次数: {str(e)}") + raise + +async def main(): + """持续运行的主函数""" + retry_count = 0 + while True: + try: + logger.info(f"使用 token: {TELEGRAM_BOT_TOKEN}") + bot = Bot(token=TELEGRAM_BOT_TOKEN) + + # 获取机器人信息 + me = await bot.get_me() + logger.info(f"机器人信息: {me.to_dict()}") + + # 记录最后处理的更新ID + last_update_id = 0 + + while True: + try: + # 获取新的更新 + updates = await bot.get_updates( + offset=last_update_id + 1, + timeout=30, + allowed_updates=['message'] + ) + + for update in updates: + if update.message and update.message.text: + chat_id = update.message.chat_id + text = update.message.text + logger.info(f"收到消息 [{chat_id}]: {text}") + await handle_message(bot, chat_id, text) + + # 更新最后处理的更新ID + last_update_id = update.update_id + + except (NetworkError, TimedOut) as e: + logger.warning(f"网络错误,等待后重试: {str(e)}") + await asyncio.sleep(5) + continue + except Exception as e: + logger.error(f"处理消息时发生错误: {str(e)}", exc_info=True) + continue + + # 短暂休息,避免过于频繁的请求 + await asyncio.sleep(1) + + except Exception as e: + retry_count += 1 + if retry_count > 5: + logger.error(f"发生严重错误,程序退出: {str(e)}", exc_info=True) + raise + + logger.error(f"发生错误,{retry_count}/5 次重试: {str(e)}", exc_info=True) + await asyncio.sleep(5) + +if __name__ == '__main__': + asyncio.run(main()) -- GitLab