From 4d9e1bf355200824f1ab55d53fb970ddfd46f1d7 Mon Sep 17 00:00:00 2001 From: oldpear <1453599706@qq.com> Date: Wed, 24 Sep 2025 17:55:33 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E7=89=88=E6=9C=AC=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E4=BA=86ai=E6=A8=A1=E5=9E=8B=E6=8F=90=E4=BE=9B=E5=95=86?= =?UTF-8?q?=E9=80=BB=E8=BE=91=EF=BC=8C=E6=96=B0=E5=A2=9EMCUNC=EF=BC=8C?= =?UTF-8?q?=E9=98=BF=E9=87=8C=E4=BA=91=E7=99=BE=E7=82=BCai=E6=8F=90?= =?UTF-8?q?=E4=BE=9B=E5=95=86=EF=BC=8C=E6=94=AF=E6=8C=81=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E5=8F=AF=E9=85=8D=E7=BD=AE=EF=BC=8C=E4=BC=98=E5=8C=96=E6=97=A5?= =?UTF-8?q?=E5=BF=97=E8=BE=93=E5=87=BA=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.toml | 42 +++++++++-- control/group.py | 45 +++++++----- control/notice.py | 6 +- control/private.py | 45 +++++++----- control/request.py | 6 +- main.py | 21 +++--- model/AiCat.py | 144 -------------------------------------- model/Clear.py | 39 +++++++---- model/ai_model.py | 35 +++++++++ model/ai_models/aliyun.py | 57 +++++++++++++++ model/ai_models/dify.py | 77 ++++++++++++++++++++ model/ai_models/mcunc.py | 75 ++++++++++++++++++++ model/ai_models/xyit.py | 76 ++++++++++++++++++++ model/logger.py | 60 ++++++++++++++++ model/sql_tools.py | 42 +++++++++++ 15 files changed, 563 insertions(+), 207 deletions(-) delete mode 100644 model/AiCat.py create mode 100644 model/ai_model.py create mode 100644 model/ai_models/aliyun.py create mode 100644 model/ai_models/dify.py create mode 100644 model/ai_models/mcunc.py create mode 100644 model/ai_models/xyit.py create mode 100644 model/logger.py create mode 100644 model/sql_tools.py diff --git a/config.toml b/config.toml index 1f412ec..5e1e483 100644 --- a/config.toml +++ b/config.toml @@ -15,16 +15,48 @@ ai_service = "xyit" # ai平台 支持 “dify” “xyit" friend_auto = false # 好友自动同意 group_auto = true group_welcome = false # 入群欢迎 -group_welcome_message = "!at 欢迎加入本群,使用@bot /help获取此bot帮助" # 入群消息 !at 为@加群用户 group_leave = false # 退群提醒 + +# 语言配置 +group_welcome_message = "!at 欢迎加入本群,使用@bot /help获取此bot帮助" # 入群消息 !at 为@加群用户 group_leave_message = "用户{userid}退群了" # 退群消息 ,{userid}为退群用户id +error_message = "服务器繁忙,请稍后再逝" +permission_denied_message = "此bot未在该群启用" +status_message = "---猫娘 QBOT---\n Q bot 运行正常 \n 版本: 2.0 pre \n © 无尽创意MCUNC" +help_message = "直接输入聊天内容即可 \n /help -- 获取帮助 \n /clear [群号 / private:Q号] (all 为全部,不填为本 群/用户) \n /status -- 查看bot状态" +command_isnone_message = "你似乎没有提供想和我聊的内容喵~ \n 直接输入聊天内容即可" +command_notfound_message = "指令不存在,输入/help查看帮助" +chatmessage_isnone_message = "你似乎没有提供想和我聊的内容喵~ \n 格式:/cat <提问内容>" +clean_all_success_message = "✅ 已清空所有群组数据" +clean_one_success_message = "✅ 已成功删除 group_id = {group_id} 的数据" +clean_one_notfound_message = "⚠️ 没有找到 group_id = {group_id} 的数据" +clean_nopermissoin_message = "你不是管理员哦喵~" +clean_fail_message = "❌ 数据库操作失败" + + +# 用户标识配置 + # 此项用户在消息开头添加用户qid,以便于ai识别用户(需在ai模型中添加提示词) + # 如使用xyit平台,请勿修改 +enable_qid = true # 是否启用 +qid_prefix = "" # 前缀 +qid_suffix = "" # 后缀 # dify配置 ai_service选择dify时需配置 -dify_ip= "" # ip:端口 +dify_ip= "" # https://ip:port dify_token = "" # token -# xyit配置 ai_service选择xyit时需配置 -xyit_ip = "ai.openapi.xyit.net" # ip 此项一般不需用修改 +# mcunc配置 ai_service选择mcunc时需配置 +xyit_ip = "https://ai.openapi.mcunc.cn" # ip 此项一般不需用修改 xyit_appID = "" # appID xyit_appKEY = "" # appKEY -xyit_model = "maoniang" # 模型名称 \ No newline at end of file +xyit_model = "maoniang" # 模型名称 + +# xyit配置 ai_service选择xyit时需配置 +xyit_ip = "https://ai.openapi.xyit.net" # ip 此项一般不需用修改 +xyit_appID = "" # appID +xyit_appKEY = "" # appKEY +xyit_model = "maoniang" # 模型名称 + +# 阿里云百炼配置 ai_service选择aliyun时需配置 +aliyun_app_id = "" +aliyun_api_key = "" \ No newline at end of file diff --git a/control/group.py b/control/group.py index 98fea49..0347725 100644 --- a/control/group.py +++ b/control/group.py @@ -1,8 +1,10 @@ import logging -from model.AiCat import AiCat +from model import ai_model from model.Clear import Clear import toml +from model.logger import setup_logger +logger = setup_logger() class group: def __init__(self, msg): self.user_id = msg.user_id @@ -14,6 +16,18 @@ class group: self.message = msg.message self.self_id = msg.self_id self.time = msg.time + try: + with open("config.toml", "r", encoding="utf-8") as f: + config = toml.load(f) + self.permission_denied_message = config.get("permission_denied_message") + self.error_message = config.get("error_message") + self.help_message = config.get("help_message") + self.command_isnone_message = config.get("command_none_message") + self.command_notfound_message = config.get("command_notfound_message") + self.status_message = config.get("status_message") + self.chatmessage_isnone_message = config.get("chatmessage_isnone_message") + except Exception as e: + logger.error(f"读取配置文件出现错误{e}") def main(self): is_at = self.is_at() @@ -22,11 +36,11 @@ class group: else: permission = self.check_permission() if permission is None: - return "服务器繁忙,请稍后再逝" + return self.error_message elif permission: return self.menu(is_at) else: - return "此bot未在该群启用" + return self.permission_denied_message def is_at(self): for seg in self.message: @@ -43,7 +57,7 @@ class group: config = toml.load(f) allowed_groups = config.get("allowed_groups", []) except Exception as e: - logging.error(str(e)) + logger.error(str(e)) return None # 检查当前群是否在允许列表中 @@ -54,22 +68,22 @@ class group: else: return False + def menu(self,command): if command.startswith("/help"): - return " 直接输入聊天内容即可 \n /help -- 获取帮助 \n /clear [群号 / private:Q号] (all 为全部,不填为本 群/用户) \n /status -- 查看bot状态 " - elif command.startswith("/cat"):# 留此指令接口为了方便通过审核 + return self.help_message + elif command.startswith("/cat"):# 留此指令接口为了方便qq官bot通过审核 # 排除 " /cat "" /cat"未传参情况 parts = command.split("/cat ", 1) if len(parts) > 1: chat_content = parts[1].strip() if chat_content: - cat = AiCat(chat_content, self.user_id,self.group_id) - answer = AiCat.main(cat) + answer = ai_model.main(chat_content, self.user_id,self.group_id) return answer else: - return "你似乎没有提供想和我聊的内容喵~ \n 格式:/cat <提问内容>" + return self.chatmessage_isnone_message else: - return "你似乎没有提供想和我聊的内容喵~ \n 格式:/cat <提问内容>" + return self.chatmessage_isnone_message elif command.startswith("/clear"): parts = command.split("/clear ", 1) if len(parts) > 1: @@ -84,14 +98,13 @@ class group: clear = Clear(self.user_id, self.group_id) return clear.main() elif command.startswith("/status"): - return "---猫娘 QBOT---\n Q bot 运行正常 \n 版本: 2.0 pre \n © 融玩文化 | 无尽创意MCUNC" + return self.status_message elif command.startswith("/"): - return "指令不存在,输入/help查看帮助" + return self.command_notfound_message elif command == "": - return "你似乎没有提供想和我聊的内容喵~ \n 直接输入聊天内容即可" + return self.command_isnone_message elif command is None: - return "你似乎没有提供想和我聊的内容喵~ \n 直接输入聊天内容即可" + return self.command_isnone_message else: - cat = AiCat(command, self.user_id,self.group_id) - answer = AiCat.main(cat) + answer = ai_model.main(command, self.user_id, self.group_id) return answer \ No newline at end of file diff --git a/control/notice.py b/control/notice.py index 687d7f2..200ad80 100644 --- a/control/notice.py +++ b/control/notice.py @@ -1,6 +1,8 @@ import toml import logging +from model.logger import setup_logger +logger = setup_logger() class notice: def __init__(self,msg): print(msg) @@ -41,7 +43,7 @@ class notice: else: return None except Exception as e: - logging.error(f"读取配置文件错误:{e}") + logger.error(f"读取配置文件错误:{e}") return None def group_decrease(self): @@ -59,5 +61,5 @@ class notice: else: return None except Exception as e: - logging.error(f"读取配置文件错误:{e}") + logger.error(f"读取配置文件错误:{e}") return None \ No newline at end of file diff --git a/control/private.py b/control/private.py index 746dd10..55fead6 100644 --- a/control/private.py +++ b/control/private.py @@ -1,8 +1,11 @@ -import logging -from model.AiCat import AiCat +from model.logger import setup_logger from model.Clear import Clear +from model import ai_model import toml + + +logger = setup_logger() class private: def __init__(self, msg): self.user_id = msg.user_id @@ -13,17 +16,29 @@ class private: self.message = msg.message self.self_id = msg.self_id self.time = msg.time + try: + with open("config.toml", "r", encoding="utf-8") as f: + config = toml.load(f) + self.permission_denied_message = config.get("permission_denied_message") + self.error_message = config.get("error_message") + self.help_message = config.get("help_message") + self.command_isnone_message = config.get("command_none_message") + self.command_notfound_message = config.get("command_notfound_message") + self.status_message = config.get("status_message") + self.chatmessage_isnone_message = config.get("chatmessage_isnone_message") + except Exception as e: + logger.error(f"读取配置文件出现错误{e}") def main(self): texts = [seg['data']['text'].strip() for seg in self.message if seg['type'] == 'text'] full_text = ' '.join(texts).strip() permission = self.check_permission() if permission is None: - return "服务器繁忙,请稍后再逝" + return self.error_message elif permission: return self.menu(full_text) else: - return "此bot未在该群启用" + return self.permission_denied_message def check_permission(self): try: @@ -31,7 +46,7 @@ class private: config = toml.load(f) allowed_users = config.get("allowed_users", []) except Exception as e: - logging.error(str(e)) + logger.error(str(e)) return None # 检查当前群是否在允许列表中 @@ -44,20 +59,19 @@ class private: def menu(self,command): if command.startswith("/help"): - return " 直接输入聊天内容即可 \n /help -- 获取帮助 \n /clear [群号 / private:Q号] (all 为全部,不填为本 群/用户) \n /status -- 查看bot状态 " + return self.help_message elif command.startswith(" /cat"):# 留此指令接口为了方便通过审核 # 排除 " /cat "" /cat"未传参情况 parts = command.split(" /cat ", 1) if len(parts) > 1: chat_content = parts[1].strip() if chat_content: - cat = AiCat(chat_content,self.user_id , f"private:{self.user_id}") - answer = AiCat.main(cat) + answer = ai_model.main(chat_content, self.user_id, f"private:{self.user_id}") return answer else: - return "你似乎没有提供想和我聊的内容喵~ \n 格式:/cat <提问内容>" + return self.chatmessage_isnone_message else: - return "你似乎没有提供想和我聊的内容喵~ \n 格式:/cat <提问内容>" + return self.chatmessage_isnone_message elif command.startswith("/clear"): parts = command.split("/clear ", 1) if len(parts) > 1: @@ -72,14 +86,13 @@ class private: clear = Clear(self.user_id, f"private:{self.user_id}") return clear.main() elif command.startswith(" /status"): - return "---猫娘 QBOT---\n Q bot 运行正常 \n 版本: 2.0 pre \n © 融玩文化 | 无尽创意MCUNC" + return self.status_message elif command.startswith(" /"): - return "指令不存在,输入/help查看帮助" + return self.command_notfound_message elif command == "": - return "你似乎没有提供想和我聊的内容喵~ \n 直接输入聊天内容即可" + return self.command_isnone_message elif command is None: - return "你似乎没有提供想和我聊的内容喵~ \n 直接输入聊天内容即可" + return self.command_isnone_message else: - cat = AiCat(command, self.user_id,f"private:{self.user_id}") - answer = AiCat.main(cat) + answer = ai_model.main(command, self.user_id, f"private:{self.user_id}") return answer \ No newline at end of file diff --git a/control/request.py b/control/request.py index 0a8608f..5ce2124 100644 --- a/control/request.py +++ b/control/request.py @@ -1,6 +1,8 @@ import logging import toml + +logger = setup_logger() class request: def __init__(self,msg): self.time = msg.time @@ -28,7 +30,7 @@ class request: friend_auto = config.get("friend_auto") return friend_auto except Exception as e: - logging.error(f"读取配置文件错误:{e}") + logger.error(f"读取配置文件错误:{e}") return False def get_allow_group(self): @@ -38,5 +40,5 @@ class request: allow_group = config.get("allowed_groups") return allow_group except Exception as e: - logging.error(f"读取配置文件错误:{e}") + logger.error(f"读取配置文件错误:{e}") return [] diff --git a/main.py b/main.py index 25bff22..47cab8c 100644 --- a/main.py +++ b/main.py @@ -1,6 +1,6 @@ from ncatbot.core.notice import NoticeMessage from ncatbot.core import BotClient, Request -import logging +from model.logger import setup_logger import asyncio from control.group import group from control.private import private @@ -9,6 +9,7 @@ from control.notice import notice import toml +logger = setup_logger() with open("./config.toml", "r", encoding="utf-8") as f: config = toml.load(f) bt_uin = config.get("bot_qq") @@ -25,7 +26,7 @@ api = bot.run_blocking(bt_uin=bt_uin, root=root, ws_uri=ws_uri, web_uri=web_uri, @bot.group_event() async def on_group_message(msg): - logging.info(f"收到消息:{msg.raw_message},来自{msg.group_id}群聊{msg.user_id}用户") + logger.info(f"收到消息:{msg.raw_message},来自{msg.group_id}群聊{msg.user_id}用户") if msg.user_id == 2854196310: # qq管家,防止刷屏。bot大战请看 pass else: @@ -34,12 +35,12 @@ async def on_group_message(msg): if return_message is None: return else: - logging.info(f"返回消息:{return_message}") + logger.info(f"返回消息:{return_message}") await bot.api.post_group_msg(group_id=msg.group_id, text=return_message, reply=msg.message_id) @bot.private_event() async def on_private_message(msg): - logging.info(f"收到消息:{msg.raw_message},来自{msg.user_id}用户") + logger.info(f"收到消息:{msg.raw_message},来自{msg.user_id}用户") if msg.user_id == 2854196310: # qq管家,防止刷屏。 pass else: @@ -48,33 +49,33 @@ async def on_private_message(msg): if return_message is None: return else: - logging.info(f"返回消息:{return_message}") + logger.info(f"返回消息:{return_message}") await bot.api.post_private_msg(user_id=msg.user_id, text=return_message, reply=msg.message_id) @bot.request_event() async def on_request_event(msg: Request): - logging.info(f"收到request事件:{msg.request_type},来自{msg.group_id}群聊,{msg.user_id}用户,验证消息:{msg.comment}") + logger.info(f"收到request事件:{msg.request_type},来自{msg.group_id}群聊,{msg.user_id}用户,验证消息:{msg.comment}") ctrl = request(msg) accept_friend_application = ctrl.main() if accept_friend_application is True: await msg.reply(True, comment="请求已通过") - logging.info("请求已通过") + logger.info("请求已通过") else: await msg.reply(False, comment="请求被拒绝") - logging.info("请求被拒绝") + logger.info("请求被拒绝") @bot.notice_event() async def on_notice_event(msg: NoticeMessage): - logging.info(f"收到notice事件:{msg['notice_type']},来自{msg['user_id']}用户") + logger.info(f"收到notice事件:{msg['notice_type']},来自{msg['user_id']}用户") ctrl = notice(msg) return_message = ctrl.main() if return_message is None: return else: - logging.info(f"返回消息:{return_message}") + logger.info(f"返回消息:{return_message}") await bot.api.post_group_msg(group_id=msg["group_id"], text=return_message) diff --git a/model/AiCat.py b/model/AiCat.py deleted file mode 100644 index eb93c1e..0000000 --- a/model/AiCat.py +++ /dev/null @@ -1,144 +0,0 @@ -import requests -import json -import logging -import toml -import sqlite3 - -class AiCat: - def __init__(self,message,qid,group_openid): - self.message = message - self.qid = qid - self.group_openid = group_openid - self.query = f"{self.qid}{self.message}" - - - - def main(self): - with open('./config.toml', 'r', encoding='utf-8') as f: - config = toml.load(f) - - # 获取所需字段 - ai_service = config.get("ai_service") - # dify - dify_ip = config.get("dify_ip") - dify_token = config.get("dify_token") - # xyit - xyit_ip = config.get("xyit_ip") - xyit_appID = config.get("xyit_appID") - xyit_appKEY = config.get("xyit_appKEY") - xyit_model = config.get("xyit_model") - - self.init_db() - - uuid = self.get_uuid() - - if uuid == "": - logging.info("未找到 UUID") - else: - logging.info(f"找到 UUID: {uuid}") - - if ai_service == "dify": - # API URL - url = f"https://{dify_ip}/v1/chat-messages" # 替换为实际的 API 地址 - - # 请求头 - headers = { - "Content-Type": "application/json", - "Authorization": dify_token # 替换为你的 API 密钥 - } - - # 请求体 - payload = { - "query": self.query, # 用户输入/提问内容 - "inputs": {}, # App 定义的变量值(默认为空) - "response_mode": "blocking", # 流式模式或阻塞模式 - "user": "QBotAPI", # 用户标识,需保证唯一性 - "conversation_id": uuid, # (选填)会话 ID,继续对话时需要传入 - "files": [], # 文件列表(选填),适用于文件结合文本理解 - "auto_generate_name": True # (选填)自动生成标题,默认为 True - } - - logging.info("请求平台:dify") - - elif ai_service == "xyit": - url = f"https://{xyit_ip}/models/{xyit_model}/" # 替换为实际的 API 地址 - - # 请求头 - headers = { - "Content-Type": "application/json", - "appID": xyit_appID , # 替换为你的 API 密钥 - "appKEY": xyit_appKEY - } - - # 请求体 - payload = { - "query": self.query, # 用户输入/提问内容 - "conversation_id": uuid, # (选填)会话 ID,继续对话时需要传入 - } - - logging.info("请求平台:xyit") - - else: - logging.error("未配置ai平台") - return "服务器繁忙,请稍后再逝" - - # 发送 POST 请求 - try: - response = requests.post(url, headers=headers, data=json.dumps(payload)) - - # 检查响应状态码 - if response.status_code == 200: - logging.info("请求成功!返回结果:") - response_data = response.json() - print(response_data) # test - # 存储uuid - if uuid == "": - try: - with sqlite3.connect("uuid.db") as conn: - cursor = conn.cursor() - cursor.execute("INSERT INTO groups (group_openid, uuid) VALUES (?, ?)", - (self.group_openid, response_data.get("conversation_id"))) - except sqlite3.Error as e: - logging.error(f"数据库插入错误: {e}") - - # 提取 answer 值 - answer = response_data.get("answer","服务器繁忙,请稍后再逝") - return answer - else: - logging.info(f"请求失败!状态码: {response.status_code}") - logging.info(f"错误信息: {response.text}") # 打印错误信息 - return "服务器繁忙,请稍后再逝" - - except Exception as e: - logging.info(f"请求过程中出现异常: {e}") - return "服务器繁忙,请稍后再逝" - - - def get_uuid(self): - try: - with sqlite3.connect("uuid.db") as conn: - cursor = conn.cursor() - cursor.execute("SELECT uuid FROM groups WHERE group_openid = ?", (self.group_openid,)) - result = cursor.fetchone() - return result[0] if result else "" - except sqlite3.Error as e: - logging.error(f"数据库查询错误: {e}") - return "" - - def init_db(self): - try: - with sqlite3.connect("uuid.db") as conn: - cursor = conn.cursor() - cursor.execute(''' - CREATE TABLE IF NOT EXISTS groups ( - id INTEGER PRIMARY KEY AUTOINCREMENT, - group_openid TEXT UNIQUE NOT NULL, - uuid TEXT NOT NULL - ) - ''') - conn.commit() - except sqlite3.Error as e: - logging.error(f"数据库初始化失败: {e}") - - - diff --git a/model/Clear.py b/model/Clear.py index f4e8d39..0fc66b5 100644 --- a/model/Clear.py +++ b/model/Clear.py @@ -1,14 +1,29 @@ -import logging +from model.logger import setup_logger import toml import sqlite3 + +logger = setup_logger() class Clear: def __init__(self, user_id,group_id): self.user_id = user_id self.group_id = group_id def main(self): + with open("config.toml", "r", encoding="utf-8"): + config = toml.load("config.toml") + clean_all_success_message = config.get("clean_all_success_message") + clean_one_success_message = config.get("clean_one_success_message") + clean_one_notfound_message = config.get("clean_one_notfound_message") + clean_fail_message = config.get("clean_fail_message") + clean_nopermissoin_message = config.get("clean_nopermissoin_message") + + if "{group_id}" in clean_one_success_message: + clean_one_success_message.replace("{group_id}", self.group_id) + if "{group_id}" in clean_one_notfound_message: + clean_one_notfound_message.replace("{group_id}", self.group_id) + if self.is_root(): if self.group_id == "all": try: @@ -17,13 +32,13 @@ class Clear: # 清空表中所有数据 cursor.execute("DELETE FROM groups;") # 假设表名为 uuid_table,请根据实际表名修改 conn.commit() - logging.info("✅ 数据库表已成功清空") - return "✅ 已清空所有群组数据" + logger.info("已清空所有群组数据") + return clean_all_success_message except sqlite3.Error as e: if 'conn' in locals(): conn.close() - logging.error(f"❌ 数据库操作失败: {e}") - return "❌ 数据库操作失败" + logger.error(f"数据库操作失败: {e}") + return clean_fail_message else: try: with sqlite3.connect("uuid.db") as conn: @@ -32,19 +47,19 @@ class Clear: cursor.execute("DELETE FROM groups WHERE group_openid = ?", (self.group_id,)) conn.commit() if cursor.rowcount > 0: - logging.info(f"✅ 已成功删除 group_id = {self.group_id} 的数据") - return f"✅ 已成功删除 group_id = {self.group_id} 的数据" + logger.info(f"✅ 已成功删除 group_id = {self.group_id} 的数据") + return clean_one_success_message else: - logging.info(f"⚠️ 没有找到 group_id = {self.group_id} 的数据") - return f"⚠️ 没有找到 group_id = {self.group_id} 的数据" + logger.info(f"⚠️ 没有找到 group_id = {self.group_id} 的数据") + return clean_one_notfound_message except sqlite3.Error as e: if 'conn' in locals(): conn.close() - logging.error(f"❌ 数据库操作失败: {e}") - return "❌ 数据库操作失败" + logger.error(f"❌ 数据库操作失败: {e}") + return clean_fail_message else: - return "你不是管理员哦喵~" + return clean_nopermissoin_message def is_root(self): with open("./config.toml", "r", encoding="utf-8") as f: diff --git a/model/ai_model.py b/model/ai_model.py new file mode 100644 index 0000000..7df198a --- /dev/null +++ b/model/ai_model.py @@ -0,0 +1,35 @@ +import toml +from model.logger import setup_logger + + +logger = setup_logger() +def main(message,qid,group_openid): + + with open('./config.toml', 'r', encoding='utf-8') as f: + config = toml.load(f) + ai_service = config.get("ai_service") + enable_qid = config.get("enable_qid") + qid_prefix = config.get("qid_prefix") + qid_suffix = config.get("qid_suffix") + error_message = config.get("error_message") + + if enable_qid: + message = f"{qid_prefix}{qid}{qid_suffix}{message}" + else: + pass + + if ai_service == "dify": + from model.ai_models import dify + return dify.main(message, group_openid) + elif ai_service == "mcunc": + from model.ai_models import mcunc + return mcunc.main(message, group_openid) + elif ai_service == "xyit": + from model.ai_models import xyit + return xyit.main(message, group_openid) + else: + logger.error("未配置ai_service") + return error_message + + + diff --git a/model/ai_models/aliyun.py b/model/ai_models/aliyun.py new file mode 100644 index 0000000..e6c989d --- /dev/null +++ b/model/ai_models/aliyun.py @@ -0,0 +1,57 @@ +import toml +from model.logger import setup_logger +from http import HTTPStatus +from dashscope import Application +from model.sql_tools import init_db +from model.sql_tools import get_uuid +from model.sql_tools import add_uuid + + +logger = setup_logger() +def main(message, group_openid): + with open('./config.toml', 'r', encoding='utf-8') as f: + config = toml.load(f) + + error_message = config.get("error_message") + + # aliyun + aliyun_app_id = config.get("aliyun_app_id") + aliyun_api_key = config.get("aliyun_api_key") + init_db() + + uuid = get_uuid(group_openid) + + if uuid == "": + logger.info("未找到 UUID") + else: + logger.info(f"找到 UUID: {uuid}") + + # 发送请求 + response = Application.call( + # 若没有配置环境变量,可用百炼API Key将下行替换为:api_key="sk-xxx"。但不建议在生产环境中直接将API Key硬编码到代码中,以减少API Key泄露风险。 + api_key=aliyun_api_key, + app_id=aliyun_app_id, + prompt= message, + session_id = group_openid + ) + + + # 检查响应状态码 + if response.status_code != HTTPStatus.OK: + logger.info(f"请求失败!状态码: {response.status_code}") + logger.info(f"错误信息: {response.message}") # 打印错误信息 + return error_message + else: + logger.info("请求成功!返回结果:") + print(f"message: {response.output.text}, session_id: {response.output.session_id}") # test + # 存储uuid + if uuid == "": + uuid = response.output.session_id + add_uuid(group_openid, uuid) + + # 提取 answer 值 + answer = response.output.text + return answer + + + diff --git a/model/ai_models/dify.py b/model/ai_models/dify.py new file mode 100644 index 0000000..aba1d0b --- /dev/null +++ b/model/ai_models/dify.py @@ -0,0 +1,77 @@ +import requests +import json +from model.logger import setup_logger +import toml +from model.sql_tools import init_db +from model.sql_tools import get_uuid +from model.sql_tools import add_uuid + + +logger = setup_logger() +def main(message, group_openid): + with open('./config.toml', 'r', encoding='utf-8') as f: + config = toml.load(f) + + error_message = config.get("error_message") + + # dify + dify_ip = config.get("dify_ip") + dify_token = config.get("dify_token") + init_db() + + uuid = get_uuid(group_openid) + + if uuid == "": + logging.info("未找到 UUID") + else: + logging.info(f"找到 UUID: {uuid}") + + url = f"{dify_ip}/v1/chat-messages" # 替换为实际的 API 地址 + + # 请求头 + headers = { + "Content-Type": "application/json", + "Authorization": dify_token # 替换为你的 API 密钥 + } + + # 请求体 + payload = { + "query": message, # 用户输入/提问内容 + "inputs": {}, # App 定义的变量值(默认为空) + "response_mode": "blocking", # 流式模式或阻塞模式 + "user": "chatbot_napcat", # 用户标识,需保证唯一性 + "conversation_id": uuid, # (选填)会话 ID,继续对话时需要传入 + "files": [], # 文件列表(选填),适用于文件结合文本理解 + "auto_generate_name": True # (选填)自动生成标题,默认为 True + } + + logging.info("请求平台:dify") + + + # 发送 POST 请求 + try: + response = requests.post(url, headers=headers, data=json.dumps(payload)) + + # 检查响应状态码 + if response.status_code == 200: + logging.info("请求成功!返回结果:") + response_data = response.json() + print(response_data) # test + # 存储uuid + if uuid == "": + uuid = response_data.get("conversation_id") + add_uuid(group_openid, uuid) + + # 提取 answer 值 + answer = response_data.get("answer", error_message) + return answer + else: + logging.info(f"请求失败!状态码: {response.status_code}") + logging.info(f"错误信息: {response.text}") # 打印错误信息 + return error_message + + except Exception as e: + logging.info(f"请求过程中出现异常: {e}") + return error_message + + diff --git a/model/ai_models/mcunc.py b/model/ai_models/mcunc.py new file mode 100644 index 0000000..d9688f5 --- /dev/null +++ b/model/ai_models/mcunc.py @@ -0,0 +1,75 @@ +import requests +import json +from model.logger import setup_logger +import toml +from model.sql_tools import init_db +from model.sql_tools import get_uuid +from model.sql_tools import add_uuid + + + +logger = setup_logger() +def main(message, group_openid): + with open('./config.toml', 'r', encoding='utf-8') as f: + config = toml.load(f) + + error_message = config.get("error_message") + + # mcunc + mcunc_ip = config.get("mcunc_ip") + mcunc_appID = config.get("mcunc_appID") + mcunc_appKEY = config.get("mcunc_appKEY") + mcunc_model = config.get("mcunc_model") + + init_db() + + uuid = get_uuid(group_openid) + + if uuid == "": + logger.info("未找到 UUID") + else: + logger.info(f"找到 UUID: {uuid}") + + url = f"{mcunc_ip}/models/{mcunc_model}/" # 替换为实际的 API 地址 + + # 请求头 + headers = { + "Content-Type": "application/json", + "appID": mcunc_appID, # 替换为你的 API 密钥 + "appKEY": mcunc_appKEY + } + + # 请求体 + payload = { + "query": message, # 用户输入/提问内容 + "conversation_id": uuid, # (选填)会话 ID,继续对话时需要传入 + } + + logger.info("请求平台:mcunc") + + + # 发送 POST 请求 + try: + response = requests.post(url, headers=headers, data=json.dumps(payload)) + + # 检查响应状态码 + if response.status_code == 200: + logger.info("请求成功!返回结果:") + response_data = response.json() + print(response_data) # test + # 存储uuid + if uuid == "": + uuid = response_data.get("conversation_id") + add_uuid(group_openid, uuid) + + # 提取 answer 值 + answer = response_data.get("answer", error_message) + return answer + else: + logger.info(f"请求失败!状态码: {response.status_code}") + logger.info(f"错误信息: {response.text}") # 打印错误信息 + return error_message + + except Exception as e: + logger.info(f"请求过程中出现异常: {e}") + return error_message diff --git a/model/ai_models/xyit.py b/model/ai_models/xyit.py new file mode 100644 index 0000000..6888ae4 --- /dev/null +++ b/model/ai_models/xyit.py @@ -0,0 +1,76 @@ +import requests +import json +from model.logger import setup_logger +import toml +from model.sql_tools import init_db +from model.sql_tools import get_uuid +from model.sql_tools import add_uuid + + + +logger = setup_logger() +def main(message, group_openid): + with open('./config.toml', 'r', encoding='utf-8') as f: + config = toml.load(f) + + error_message = config.get("error_message") + + # xyit + xyit_ip = config.get("xyit_ip") + xyit_appID = config.get("xyit_appID") + xyit_appKEY = config.get("xyit_appKEY") + xyit_model = config.get("xyit_model") + + init_db() + + uuid = get_uuid(group_openid) + + if uuid == "": + logger.info("未找到 UUID") + else: + logger.info(f"找到 UUID: {uuid}") + + url = f"{xyit_ip}/models/{xyit_model}/" # 替换为实际的 API 地址 + + # 请求头 + headers = { + "Content-Type": "application/json", + "appID": xyit_appID, # 替换为你的 API 密钥 + "appKEY": xyit_appKEY + } + + # 请求体 + payload = { + "query": message, # 用户输入/提问内容 + "conversation_id": uuid, # (选填)会话 ID,继续对话时需要传入 + } + + logger.info("请求平台:xyit") + + + # 发送 POST 请求 + try: + response = requests.post(url, headers=headers, data=json.dumps(payload)) + + # 检查响应状态码 + if response.status_code == 200: + logger.info("请求成功!返回结果:") + response_data = response.json() + print(response_data) # test + # 存储uuid + if uuid == "": + uuid = response_data.get("conversation_id") + add_uuid(group_openid, uuid) + + # 提取 answer 值 + answer = response_data.get("answer", error_message) + return answer + else: + logger.info(f"请求失败!状态码: {response.status_code}") + logger.info(f"错误信息: {response.text}") # 打印错误信息 + return error_message + + except Exception as e: + logger.info(f"请求过程中出现异常: {e}") + return error_message + diff --git a/model/logger.py b/model/logger.py new file mode 100644 index 0000000..cc9dd6a --- /dev/null +++ b/model/logger.py @@ -0,0 +1,60 @@ +import logging +from logging.handlers import RotatingFileHandler +from datetime import datetime +from pathlib import Path + +def setup_logger(log_file=None, log_level=logging.INFO, max_bytes=10485760, backup_count=5): + """ + 初始化日志记录器,将日志按指定格式输出并记录到文件 + + Args: + log_file (str): 日志文件路径,默认为None,会自动生成以当天日期命名的日志文件 + log_level: 日志级别 + max_bytes (int): 单个日志文件最大字节数 + backup_count (int): 保留的备份日志文件数量 + + Returns: + logging.Logger: 配置好的日志记录器 + """ + # 创建logger + logger = logging.getLogger('mcsmapi') + logger.setLevel(log_level) + + # 避免重复添加handler + if logger.handlers: + return logger + + # 创建logs目录(如果不存在) + log_dir = Path('logs') + log_dir.mkdir(exist_ok=True) + + # 如果未指定日志文件名,则使用当天日期命名 + if log_file is None: + today = datetime.now().strftime('%Y-%m-%d') + log_file = log_dir / f'{today}.log' + + # 创建格式化器 + formatter = logging.Formatter( + '[%(asctime)s] [%(levelname)s] [%(filename)s:%(lineno)d] - %(message)s' + ) + + # 创建文件处理器(带轮转) + file_handler = RotatingFileHandler( + log_file, + maxBytes=max_bytes, + backupCount=backup_count, + encoding='utf-8' + ) + file_handler.setLevel(log_level) + file_handler.setFormatter(formatter) + + # 创建控制台处理器 + console_handler = logging.StreamHandler() + console_handler.setLevel(log_level) + console_handler.setFormatter(formatter) + + # 添加处理器到logger + logger.addHandler(file_handler) + logger.addHandler(console_handler) + + return logger \ No newline at end of file diff --git a/model/sql_tools.py b/model/sql_tools.py new file mode 100644 index 0000000..ab7f688 --- /dev/null +++ b/model/sql_tools.py @@ -0,0 +1,42 @@ +import sqlite3 +from model.logger import setup_logger + + + +logger = setup_logger() +def init_db(): + try: + with sqlite3.connect("uuid.db") as conn: + cursor = conn.cursor() + cursor.execute(''' + CREATE TABLE IF NOT EXISTS groups + ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + group_openid TEXT UNIQUE NOT NULL, + uuid TEXT NOT NULL + ) + ''') + conn.commit() + except sqlite3.Error as e: + logger.error(f"数据库初始化失败: {e}") + + +def get_uuid(group_openid): + try: + with sqlite3.connect("uuid.db") as conn: + cursor = conn.cursor() + cursor.execute("SELECT uuid FROM groups WHERE group_openid = ?", (group_openid,)) + result = cursor.fetchone() + return result[0] if result else "" + except sqlite3.Error as e: + logger.error(f"数据库查询错误: {e}") + return "" + +def add_uuid(group_openid, uuid): + try: + with sqlite3.connect("uuid.db") as conn: + cursor = conn.cursor() + cursor.execute("INSERT INTO groups (group_openid, uuid) VALUES (?, ?)", + (group_openid, uuid)) + except sqlite3.Error as e: + logger.error(f"数据库插入错误: {e}") \ No newline at end of file