Undefined-bot 2.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Undefined/__init__.py +3 -0
- Undefined/__main__.py +6 -0
- Undefined/ai.py +1215 -0
- Undefined/config.py +371 -0
- Undefined/end_summary_storage.py +48 -0
- Undefined/faq.py +244 -0
- Undefined/handlers.py +1247 -0
- Undefined/injection_response_agent.py +131 -0
- Undefined/main.py +126 -0
- Undefined/memory.py +120 -0
- Undefined/onebot.py +512 -0
- Undefined/rate_limit.py +130 -0
- Undefined/render.py +123 -0
- Undefined/scheduled_task_storage.py +88 -0
- Undefined/services/__init__.py +1 -0
- Undefined/services/queue_manager.py +206 -0
- Undefined/skills/README.md +53 -0
- Undefined/skills/__init__.py +10 -0
- Undefined/skills/agents/README.md +144 -0
- Undefined/skills/agents/__init__.py +116 -0
- Undefined/skills/agents/entertainment_agent/config.json +17 -0
- Undefined/skills/agents/entertainment_agent/handler.py +220 -0
- Undefined/skills/agents/entertainment_agent/intro.md +25 -0
- Undefined/skills/agents/entertainment_agent/prompt.md +20 -0
- Undefined/skills/agents/entertainment_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/config.json +34 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_draw_one/handler.py +62 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/config.json +22 -0
- Undefined/skills/agents/entertainment_agent/tools/ai_study_helper/handler.py +35 -0
- Undefined/skills/agents/entertainment_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/entertainment_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/entertainment_agent/tools/horoscope/config.json +24 -0
- Undefined/skills/agents/entertainment_agent/tools/horoscope/handler.py +141 -0
- Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/config.json +43 -0
- Undefined/skills/agents/entertainment_agent/tools/minecraft_skin/handler.py +55 -0
- Undefined/skills/agents/entertainment_agent/tools/novel_search/config.json +25 -0
- Undefined/skills/agents/entertainment_agent/tools/novel_search/handler.py +31 -0
- Undefined/skills/agents/entertainment_agent/tools/renjian/config.json +12 -0
- Undefined/skills/agents/entertainment_agent/tools/renjian/handler.py +30 -0
- Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/config.json +12 -0
- Undefined/skills/agents/entertainment_agent/tools/wenchang_dijun/handler.py +44 -0
- Undefined/skills/agents/file_analysis_agent/__init__.py +1 -0
- Undefined/skills/agents/file_analysis_agent/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/handler.py +248 -0
- Undefined/skills/agents/file_analysis_agent/intro.md +22 -0
- Undefined/skills/agents/file_analysis_agent/prompt.md +36 -0
- Undefined/skills/agents/file_analysis_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_code/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_code/handler.py +427 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/config.json +25 -0
- Undefined/skills/agents/file_analysis_agent/tools/analyze_multimodal/handler.py +178 -0
- Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/config.json +16 -0
- Undefined/skills/agents/file_analysis_agent/tools/cleanup_temp/handler.py +35 -0
- Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/detect_file_type/handler.py +221 -0
- Undefined/skills/agents/file_analysis_agent/tools/download_file/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/tools/download_file/handler.py +124 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_archive/config.json +25 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_archive/handler.py +190 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_docx/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_docx/handler.py +78 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pdf/handler.py +67 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_pptx/handler.py +73 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/config.json +17 -0
- Undefined/skills/agents/file_analysis_agent/tools/extract_xlsx/handler.py +101 -0
- Undefined/skills/agents/file_analysis_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/file_analysis_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/file_analysis_agent/tools/read_text_file/config.json +21 -0
- Undefined/skills/agents/file_analysis_agent/tools/read_text_file/handler.py +90 -0
- Undefined/skills/agents/info_agent/config.json +17 -0
- Undefined/skills/agents/info_agent/handler.py +220 -0
- Undefined/skills/agents/info_agent/intro.md +22 -0
- Undefined/skills/agents/info_agent/prompt.md +27 -0
- Undefined/skills/agents/info_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/info_agent/tools/baiduhot/config.json +18 -0
- Undefined/skills/agents/info_agent/tools/baiduhot/handler.py +49 -0
- Undefined/skills/agents/info_agent/tools/base64/config.json +22 -0
- Undefined/skills/agents/info_agent/tools/base64/handler.py +44 -0
- Undefined/skills/agents/info_agent/tools/douyinhot/config.json +18 -0
- Undefined/skills/agents/info_agent/tools/douyinhot/handler.py +53 -0
- Undefined/skills/agents/info_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/info_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/info_agent/tools/gold_price/config.json +12 -0
- Undefined/skills/agents/info_agent/tools/gold_price/handler.py +58 -0
- Undefined/skills/agents/info_agent/tools/hash/config.json +22 -0
- Undefined/skills/agents/info_agent/tools/hash/handler.py +43 -0
- Undefined/skills/agents/info_agent/tools/history/config.json +12 -0
- Undefined/skills/agents/info_agent/tools/history/handler.py +37 -0
- Undefined/skills/agents/info_agent/tools/net_check/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/net_check/handler.py +117 -0
- Undefined/skills/agents/info_agent/tools/news_tencent/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/news_tencent/handler.py +38 -0
- Undefined/skills/agents/info_agent/tools/qq_level_query/config.json +29 -0
- Undefined/skills/agents/info_agent/tools/qq_level_query/handler.py +48 -0
- Undefined/skills/agents/info_agent/tools/speed/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/speed/handler.py +37 -0
- Undefined/skills/agents/info_agent/tools/tcping/config.json +21 -0
- Undefined/skills/agents/info_agent/tools/tcping/handler.py +53 -0
- Undefined/skills/agents/info_agent/tools/weather_query/config.json +22 -0
- Undefined/skills/agents/info_agent/tools/weather_query/handler.py +207 -0
- Undefined/skills/agents/info_agent/tools/weibohot/config.json +18 -0
- Undefined/skills/agents/info_agent/tools/weibohot/handler.py +49 -0
- Undefined/skills/agents/info_agent/tools/whois/config.json +17 -0
- Undefined/skills/agents/info_agent/tools/whois/handler.py +63 -0
- Undefined/skills/agents/naga_code_analysis_agent/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/handler.py +222 -0
- Undefined/skills/agents/naga_code_analysis_agent/intro.md +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/prompt.md +19 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/glob/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/glob/handler.py +37 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/list_directory/handler.py +31 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/config.json +17 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_file/handler.py +66 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/config.json +12 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/read_naga_intro/handler.py +327 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/config.json +25 -0
- Undefined/skills/agents/naga_code_analysis_agent/tools/search_file_content/handler.py +46 -0
- Undefined/skills/agents/scheduler_agent/__init__.py +1 -0
- Undefined/skills/agents/scheduler_agent/config.json +17 -0
- Undefined/skills/agents/scheduler_agent/handler.py +218 -0
- Undefined/skills/agents/scheduler_agent/intro.md +17 -0
- Undefined/skills/agents/scheduler_agent/prompt.md +67 -0
- Undefined/skills/agents/scheduler_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/config.json +37 -0
- Undefined/skills/agents/scheduler_agent/tools/create_schedule_task/handler.py +68 -0
- Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/config.json +19 -0
- Undefined/skills/agents/scheduler_agent/tools/delete_schedule_task/handler.py +26 -0
- Undefined/skills/agents/scheduler_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/scheduler_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/config.json +11 -0
- Undefined/skills/agents/scheduler_agent/tools/list_schedule_tasks/handler.py +47 -0
- Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/config.json +39 -0
- Undefined/skills/agents/scheduler_agent/tools/update_schedule_task/handler.py +46 -0
- Undefined/skills/agents/social_agent/config.json +17 -0
- Undefined/skills/agents/social_agent/handler.py +220 -0
- Undefined/skills/agents/social_agent/intro.md +17 -0
- Undefined/skills/agents/social_agent/prompt.md +19 -0
- Undefined/skills/agents/social_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/social_agent/tools/bilibili_search/config.json +21 -0
- Undefined/skills/agents/social_agent/tools/bilibili_search/handler.py +68 -0
- Undefined/skills/agents/social_agent/tools/bilibili_user_info/config.json +17 -0
- Undefined/skills/agents/social_agent/tools/bilibili_user_info/handler.py +68 -0
- Undefined/skills/agents/social_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/social_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/social_agent/tools/music_global_search/config.json +21 -0
- Undefined/skills/agents/social_agent/tools/music_global_search/handler.py +47 -0
- Undefined/skills/agents/social_agent/tools/music_info_get/config.json +22 -0
- Undefined/skills/agents/social_agent/tools/music_info_get/handler.py +35 -0
- Undefined/skills/agents/social_agent/tools/music_lyrics/config.json +22 -0
- Undefined/skills/agents/social_agent/tools/music_lyrics/handler.py +26 -0
- Undefined/skills/agents/social_agent/tools/video_random_recommend/config.json +21 -0
- Undefined/skills/agents/social_agent/tools/video_random_recommend/handler.py +21 -0
- Undefined/skills/agents/web_agent/config.json +17 -0
- Undefined/skills/agents/web_agent/handler.py +221 -0
- Undefined/skills/agents/web_agent/intro.md +14 -0
- Undefined/skills/agents/web_agent/prompt.md +16 -0
- Undefined/skills/agents/web_agent/tools/__init__.py +1 -0
- Undefined/skills/agents/web_agent/tools/crawl_webpage/config.json +21 -0
- Undefined/skills/agents/web_agent/tools/crawl_webpage/handler.py +102 -0
- Undefined/skills/agents/web_agent/tools/get_current_time/config.json +12 -0
- Undefined/skills/agents/web_agent/tools/get_current_time/handler.py +5 -0
- Undefined/skills/agents/web_agent/tools/web_search/config.json +21 -0
- Undefined/skills/agents/web_agent/tools/web_search/handler.py +29 -0
- Undefined/skills/tools/README.md +85 -0
- Undefined/skills/tools/__init__.py +120 -0
- Undefined/skills/tools/debug/config.json +17 -0
- Undefined/skills/tools/debug/handler.py +35 -0
- Undefined/skills/tools/end/config.json +17 -0
- Undefined/skills/tools/end/handler.py +24 -0
- Undefined/skills/tools/get_current_time/config.json +12 -0
- Undefined/skills/tools/get_current_time/handler.py +5 -0
- Undefined/skills/tools/get_forward_msg/config.json +17 -0
- Undefined/skills/tools/get_forward_msg/handler.py +131 -0
- Undefined/skills/tools/get_group_member_info/config.json +38 -0
- Undefined/skills/tools/get_group_member_info/handler.py +142 -0
- Undefined/skills/tools/get_messages_by_time/config.json +30 -0
- Undefined/skills/tools/get_messages_by_time/handler.py +128 -0
- Undefined/skills/tools/get_picture/config.json +45 -0
- Undefined/skills/tools/get_picture/handler.py +191 -0
- Undefined/skills/tools/get_recent_messages/config.json +30 -0
- Undefined/skills/tools/get_recent_messages/handler.py +88 -0
- Undefined/skills/tools/qq_like/config.json +22 -0
- Undefined/skills/tools/qq_like/handler.py +58 -0
- Undefined/skills/tools/render_html/config.json +26 -0
- Undefined/skills/tools/render_html/handler.py +39 -0
- Undefined/skills/tools/render_latex/config.json +26 -0
- Undefined/skills/tools/render_latex/handler.py +78 -0
- Undefined/skills/tools/render_markdown/config.json +26 -0
- Undefined/skills/tools/render_markdown/handler.py +63 -0
- Undefined/skills/tools/save_memory/config.json +17 -0
- Undefined/skills/tools/save_memory/handler.py +17 -0
- Undefined/skills/tools/send_message/config.json +21 -0
- Undefined/skills/tools/send_message/handler.py +60 -0
- Undefined/skills/tools/send_private_message/config.json +21 -0
- Undefined/skills/tools/send_private_message/handler.py +35 -0
- Undefined/utils/__init__.py +0 -0
- Undefined/utils/common.py +186 -0
- Undefined/utils/history.py +284 -0
- Undefined/utils/scheduler.py +286 -0
- Undefined/utils/sender.py +140 -0
- undefined_bot-2.1.0.dist-info/METADATA +259 -0
- undefined_bot-2.1.0.dist-info/RECORD +211 -0
- undefined_bot-2.1.0.dist-info/WHEEL +4 -0
- undefined_bot-2.1.0.dist-info/entry_points.txt +2 -0
- undefined_bot-2.1.0.dist-info/licenses/LICENSE +7 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import logging
|
|
3
|
+
import importlib.util
|
|
4
|
+
import asyncio
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from typing import Dict, Any, List, Callable, Awaitable
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ToolRegistry:
|
|
12
|
+
def __init__(self, tools_dir: str | Path | None = None):
|
|
13
|
+
if tools_dir is None:
|
|
14
|
+
# 默认为此文件所在的目录
|
|
15
|
+
self.tools_dir = Path(__file__).parent
|
|
16
|
+
else:
|
|
17
|
+
self.tools_dir = Path(tools_dir)
|
|
18
|
+
|
|
19
|
+
self._tools_schema: List[Dict[str, Any]] = []
|
|
20
|
+
self._tools_handlers: Dict[
|
|
21
|
+
str, Callable[[Dict[str, Any], Dict[str, Any]], Awaitable[Any]]
|
|
22
|
+
] = {}
|
|
23
|
+
self.load_tools()
|
|
24
|
+
|
|
25
|
+
def load_tools(self) -> None:
|
|
26
|
+
"""从 tools 目录发现并加载工具。"""
|
|
27
|
+
self._tools_schema = []
|
|
28
|
+
self._tools_handlers = {}
|
|
29
|
+
|
|
30
|
+
if not self.tools_dir.exists():
|
|
31
|
+
logger.warning(f"工具目录不存在: {self.tools_dir}")
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
for item in self.tools_dir.iterdir():
|
|
35
|
+
if item.is_dir() and not item.name.startswith("_"):
|
|
36
|
+
self._load_tool_from_dir(item)
|
|
37
|
+
|
|
38
|
+
tool_names = list(self._tools_handlers.keys())
|
|
39
|
+
logger.info(
|
|
40
|
+
f"成功加载了 {len(self._tools_schema)} 个工具: {', '.join(tool_names)}"
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
def _load_tool_from_dir(self, tool_dir: Path) -> None:
|
|
44
|
+
"""从目录加载单个工具。"""
|
|
45
|
+
config_path = tool_dir / "config.json"
|
|
46
|
+
handler_path = tool_dir / "handler.py"
|
|
47
|
+
|
|
48
|
+
if not config_path.exists() or not handler_path.exists():
|
|
49
|
+
logger.debug(
|
|
50
|
+
f"[工具加载] 目录 {tool_dir} 缺少 config.json 或 handler.py,跳过"
|
|
51
|
+
)
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
# 加载配置
|
|
55
|
+
try:
|
|
56
|
+
with open(config_path, "r", encoding="utf-8") as f:
|
|
57
|
+
config = json.load(f)
|
|
58
|
+
|
|
59
|
+
# 基本验证
|
|
60
|
+
if "name" not in config.get("function", {}):
|
|
61
|
+
logger.error(f"[工具错误] 工具配置无效 {tool_dir}: 缺少 function.name")
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
tool_name = config["function"]["name"]
|
|
65
|
+
logger.debug(f"[工具加载] 正在从 {tool_dir} 加载工具: {tool_name}")
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
logger.error(f"[工具错误] 从 {tool_dir} 加载工具配置失败: {e}")
|
|
69
|
+
return
|
|
70
|
+
|
|
71
|
+
# 加载处理器
|
|
72
|
+
try:
|
|
73
|
+
spec = importlib.util.spec_from_file_location(
|
|
74
|
+
f"tools.{tool_name}", handler_path
|
|
75
|
+
)
|
|
76
|
+
if spec is None or spec.loader is None:
|
|
77
|
+
logger.error(f"从 {handler_path} 加载工具处理器 spec 失败")
|
|
78
|
+
return
|
|
79
|
+
|
|
80
|
+
module = importlib.util.module_from_spec(spec)
|
|
81
|
+
spec.loader.exec_module(module)
|
|
82
|
+
|
|
83
|
+
if not hasattr(module, "execute"):
|
|
84
|
+
logger.error(f"工具 {tool_dir} 的处理器缺少 'execute' 函数")
|
|
85
|
+
return
|
|
86
|
+
|
|
87
|
+
self._tools_schema.append(config)
|
|
88
|
+
self._tools_handlers[tool_name] = module.execute
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"从 {tool_dir} 加载工具处理器失败: {e}")
|
|
92
|
+
|
|
93
|
+
def get_tools_schema(self) -> List[Dict[str, Any]]:
|
|
94
|
+
"""返回 AI 模型的工具定义列表。"""
|
|
95
|
+
return self._tools_schema
|
|
96
|
+
|
|
97
|
+
async def execute_tool(
|
|
98
|
+
self, tool_name: str, args: Dict[str, Any], context: Dict[str, Any]
|
|
99
|
+
) -> str:
|
|
100
|
+
"""根据名称执行工具。"""
|
|
101
|
+
handler = self._tools_handlers.get(tool_name)
|
|
102
|
+
if not handler:
|
|
103
|
+
return f"未找到工具: {tool_name}"
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
# 检查处理器是否为协程
|
|
107
|
+
start_time = asyncio.get_event_loop().time()
|
|
108
|
+
if asyncio.iscoroutinefunction(handler):
|
|
109
|
+
result = await handler(args, context)
|
|
110
|
+
else:
|
|
111
|
+
# 我们预期工具是异步的,但也支持同步以防万一
|
|
112
|
+
# 注意:我们的类型提示是 Awaitable,所以这只是为了安全
|
|
113
|
+
result = handler(args, context)
|
|
114
|
+
|
|
115
|
+
duration = asyncio.get_event_loop().time() - start_time
|
|
116
|
+
logger.info(f"[工具执行] {tool_name} 执行成功, 耗时={duration:.4f}s")
|
|
117
|
+
return str(result)
|
|
118
|
+
except Exception as e:
|
|
119
|
+
logger.exception(f"[工具异常] 执行工具 {tool_name} 时出错")
|
|
120
|
+
return f"执行工具 {tool_name} 时出错: {str(e)}"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "debug",
|
|
5
|
+
"description": "记录调试信息,用于输出决策过程、思维路径、工具调用和结果等。仅在调试模式下使用(通过 debug:on 启用)。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"content": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "调试信息内容,包括触发条件判断、上下文分析、最终决策理由等"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": ["content"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import uuid
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Any, Dict
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
# 调试数据保存目录
|
|
9
|
+
DEBUG_DIR = Path("data/debug")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
13
|
+
"""执行 debug 工具,保存调试信息到文本文件"""
|
|
14
|
+
content = args.get("content", "")
|
|
15
|
+
if not content:
|
|
16
|
+
return "调试内容不能为空"
|
|
17
|
+
|
|
18
|
+
# 确保目录存在
|
|
19
|
+
DEBUG_DIR.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
# 生成 UUID 作为文件名
|
|
22
|
+
file_uuid = uuid.uuid4()
|
|
23
|
+
filename = f"{file_uuid}.txt"
|
|
24
|
+
filepath = DEBUG_DIR / filename
|
|
25
|
+
|
|
26
|
+
# 保存到文件
|
|
27
|
+
try:
|
|
28
|
+
with open(filepath, "w", encoding="utf-8") as f:
|
|
29
|
+
f.write(content)
|
|
30
|
+
|
|
31
|
+
logger.info(f"调试信息已保存到: {filepath}")
|
|
32
|
+
return f"调试信息已保存(UUID: {file_uuid})"
|
|
33
|
+
except Exception as e:
|
|
34
|
+
logger.error(f"保存调试信息失败: {e}")
|
|
35
|
+
return f"保存失败: {e}"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "end",
|
|
5
|
+
"description": "结束对话。发送完所有消息后,必须且只能调用一次此工具来结束对话。如果这次做了什么或以后可能要做什么,必须填写summary参数记录下来。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"summary": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "记录这次做了什么或以后可能要做什么。如果只是end了不做事,不需要填写。如果做了什么或以后可能要做什么,必须填写。最多保留100条记录。"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": []
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
8
|
+
summary = args.get("summary", "")
|
|
9
|
+
if summary:
|
|
10
|
+
end_summaries = context.get("end_summaries")
|
|
11
|
+
if end_summaries is not None:
|
|
12
|
+
end_summaries.append(summary)
|
|
13
|
+
logger.info(f"保存end记录: {summary[:50]}...")
|
|
14
|
+
|
|
15
|
+
# 持久化保存
|
|
16
|
+
end_summary_storage = context.get("end_summary_storage")
|
|
17
|
+
if end_summary_storage:
|
|
18
|
+
# 转换 deque 为 list 进行序列化
|
|
19
|
+
end_summary_storage.save(list(end_summaries))
|
|
20
|
+
|
|
21
|
+
# 通知调用方对话应结束
|
|
22
|
+
context["conversation_ended"] = True
|
|
23
|
+
|
|
24
|
+
return "对话已结束"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "get_forward_msg",
|
|
5
|
+
"description": "获取合并转发消息的详情内容。当你在聊天记录中看到 [合并转发: ID] 时,可以使用此工具获取其包含的具体消息列表。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"message_id": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "合并转发消息的 ID"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": ["message_id"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import logging
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
10
|
+
message_id = args.get("message_id")
|
|
11
|
+
if not message_id:
|
|
12
|
+
return "错误:message_id 不能为空"
|
|
13
|
+
|
|
14
|
+
get_forward_msg_callback = context.get("get_forward_msg_callback")
|
|
15
|
+
if not get_forward_msg_callback:
|
|
16
|
+
return "错误:获取合并转发消息的回调未设置"
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
nodes = await get_forward_msg_callback(message_id)
|
|
20
|
+
if not nodes:
|
|
21
|
+
return "未能获取到合并转发消息的内容或内容为空"
|
|
22
|
+
|
|
23
|
+
logger.info(f"成功获取合并转发内容,节点数: {len(nodes)}")
|
|
24
|
+
|
|
25
|
+
formatted_messages = []
|
|
26
|
+
for i, node in enumerate(nodes):
|
|
27
|
+
# 记录第一个节点的结构用于调试
|
|
28
|
+
if i == 0:
|
|
29
|
+
logger.debug(
|
|
30
|
+
f"合并转发节点示例结构: {json.dumps(node, ensure_ascii=False)[:500]}"
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
sender = node.get("sender") or {}
|
|
34
|
+
# 兼容有些实现直接把发送者信息放在节点根部
|
|
35
|
+
sender_name = (
|
|
36
|
+
sender.get("nickname")
|
|
37
|
+
or node.get("nickname")
|
|
38
|
+
or sender.get("card")
|
|
39
|
+
or node.get("card")
|
|
40
|
+
or "未知用户"
|
|
41
|
+
)
|
|
42
|
+
sender_id = sender.get("user_id") or node.get("user_id") or "未知ID"
|
|
43
|
+
|
|
44
|
+
node_time = node.get("time")
|
|
45
|
+
if node_time:
|
|
46
|
+
try:
|
|
47
|
+
timestamp = datetime.fromtimestamp(float(node_time)).strftime(
|
|
48
|
+
"%Y-%m-%d %H:%M:%S"
|
|
49
|
+
)
|
|
50
|
+
except (ValueError, TypeError):
|
|
51
|
+
timestamp = str(node_time)
|
|
52
|
+
else:
|
|
53
|
+
timestamp = "未知时间"
|
|
54
|
+
|
|
55
|
+
# 激进的内容提取:尝试所有可能的字段
|
|
56
|
+
raw_content = (
|
|
57
|
+
node.get("content") or node.get("message") or node.get("raw_message")
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
text_parts = []
|
|
61
|
+
if raw_content is None:
|
|
62
|
+
text_parts.append("(消息内容字段缺失)")
|
|
63
|
+
elif isinstance(raw_content, str):
|
|
64
|
+
text_parts.append(raw_content)
|
|
65
|
+
elif isinstance(raw_content, dict):
|
|
66
|
+
# 单个消息段
|
|
67
|
+
raw_content = [raw_content]
|
|
68
|
+
|
|
69
|
+
if isinstance(raw_content, list):
|
|
70
|
+
for segment in raw_content:
|
|
71
|
+
if isinstance(segment, str):
|
|
72
|
+
text_parts.append(segment)
|
|
73
|
+
continue
|
|
74
|
+
if not isinstance(segment, dict):
|
|
75
|
+
continue
|
|
76
|
+
|
|
77
|
+
seg_type = segment.get("type")
|
|
78
|
+
seg_data = segment.get("data", {})
|
|
79
|
+
|
|
80
|
+
if seg_type == "text":
|
|
81
|
+
text_parts.append(seg_data.get("text", ""))
|
|
82
|
+
elif seg_type == "at":
|
|
83
|
+
qq = seg_data.get("qq", "")
|
|
84
|
+
text_parts.append(f"[@ {qq}]")
|
|
85
|
+
elif seg_type == "image":
|
|
86
|
+
file = seg_data.get("file", "") or seg_data.get("url", "")
|
|
87
|
+
text_parts.append(f"[图片: {file}]")
|
|
88
|
+
elif seg_type == "forward":
|
|
89
|
+
inner_id = seg_data.get("id")
|
|
90
|
+
text_parts.append(f"[合并转发: {inner_id}]")
|
|
91
|
+
elif seg_type == "reply":
|
|
92
|
+
text_parts.append("[引用]")
|
|
93
|
+
elif seg_type == "face":
|
|
94
|
+
text_parts.append("[表情]")
|
|
95
|
+
elif seg_type == "json":
|
|
96
|
+
# 尝试从 JSON 中提取描述
|
|
97
|
+
try:
|
|
98
|
+
j_data = json.loads(seg_data.get("data", "{}"))
|
|
99
|
+
desc = (
|
|
100
|
+
j_data.get("meta", {})
|
|
101
|
+
.get("detail", {})
|
|
102
|
+
.get("desc", "JSON消息")
|
|
103
|
+
)
|
|
104
|
+
text_parts.append(f"[{desc}]")
|
|
105
|
+
except Exception:
|
|
106
|
+
text_parts.append("[JSON消息]")
|
|
107
|
+
elif seg_type == "xml":
|
|
108
|
+
text_parts.append("[XML消息]")
|
|
109
|
+
elif seg_type:
|
|
110
|
+
text_parts.append(f"[{seg_type}]")
|
|
111
|
+
|
|
112
|
+
text = "".join(text_parts).strip()
|
|
113
|
+
if not text:
|
|
114
|
+
# 如果还是空,把整个节点键名返回给 AI 辅助判断
|
|
115
|
+
keys = list(node.keys())
|
|
116
|
+
text = f"(无法解析内容,节点键名: {keys})"
|
|
117
|
+
|
|
118
|
+
# 格式:XML 标准化
|
|
119
|
+
formatted_messages.append(f"""<message sender="{sender_name}" sender_id="{sender_id}" location="合并转发" time="{timestamp}">
|
|
120
|
+
<content>{text}</content>
|
|
121
|
+
</message>""")
|
|
122
|
+
|
|
123
|
+
result = "\n---\n".join(formatted_messages)
|
|
124
|
+
logger.info(
|
|
125
|
+
f"get_forward_msg 处理完成,返回数据样例 (前500字符): {result[:500]}..."
|
|
126
|
+
)
|
|
127
|
+
return result
|
|
128
|
+
|
|
129
|
+
except Exception as e:
|
|
130
|
+
logger.exception(f"解析合并转发消息时出错: {e}")
|
|
131
|
+
return f"解析合并转发消息时出错: {str(e)}"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "get_group_member_info",
|
|
5
|
+
"description": "获取群聊成员的详细信息。可以指定要获取的字段和字段数目,支持获取:群昵称、QQ昵称、QQ号(cqid)、加群时间、入群时间戳、等级、活跃度、最后发言时间、最后活跃时间戳、历史消息数量等。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"group_id": {
|
|
10
|
+
"type": "integer",
|
|
11
|
+
"description": "群号"
|
|
12
|
+
},
|
|
13
|
+
"user_id": {
|
|
14
|
+
"type": "integer",
|
|
15
|
+
"description": "群成员的QQ号"
|
|
16
|
+
},
|
|
17
|
+
"fields": {
|
|
18
|
+
"type": "array",
|
|
19
|
+
"description": "指定要获取的字段列表。可选字段:nickname(QQ昵称)、card(群昵称)、user_id(QQ号)、join_time(加群时间)、last_sent_time(最后发言时间)、level(等级)、role(角色)、unfriendly(是否不友好)、title(头衔)、title_expire_time(头衔过期时间)、shut_up_timestamp(禁言截止时间)",
|
|
20
|
+
"items": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"enum": ["nickname", "card", "user_id", "join_time", "last_sent_time", "level", "role", "unfriendly", "title", "title_expire_time", "shut_up_timestamp"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"limit": {
|
|
26
|
+
"type": "integer",
|
|
27
|
+
"description": "限制返回的字段数量(当 fields 未指定时有效),默认返回所有字段"
|
|
28
|
+
},
|
|
29
|
+
"no_cache": {
|
|
30
|
+
"type": "boolean",
|
|
31
|
+
"description": "是否不使用缓存获取最新信息,默认 false",
|
|
32
|
+
"default": false
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"required": ["group_id", "user_id"]
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
import logging
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
|
|
5
|
+
logger = logging.getLogger(__name__)
|
|
6
|
+
|
|
7
|
+
FIELD_MAPPING = {
|
|
8
|
+
"nickname": "QQ昵称",
|
|
9
|
+
"card": "群昵称",
|
|
10
|
+
"user_id": "QQ号",
|
|
11
|
+
"join_time": "加群时间",
|
|
12
|
+
"last_sent_time": "最后发言时间",
|
|
13
|
+
"level": "等级",
|
|
14
|
+
"role": "角色",
|
|
15
|
+
"unfriendly": "是否不友好",
|
|
16
|
+
"title": "头衔",
|
|
17
|
+
"title_expire_time": "头衔过期时间",
|
|
18
|
+
"shut_up_timestamp": "禁言截止时间",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
23
|
+
"""获取群聊成员的详细信息
|
|
24
|
+
|
|
25
|
+
参数:
|
|
26
|
+
args: 工具参数,包含 group_id、user_id、fields、limit、no_cache
|
|
27
|
+
context: 工具上下文,包含 onebot_client 等回调函数
|
|
28
|
+
|
|
29
|
+
返回:
|
|
30
|
+
群成员详细信息描述
|
|
31
|
+
"""
|
|
32
|
+
group_id = args.get("group_id")
|
|
33
|
+
user_id = args.get("user_id")
|
|
34
|
+
fields = args.get("fields")
|
|
35
|
+
limit = args.get("limit")
|
|
36
|
+
no_cache = args.get("no_cache", False)
|
|
37
|
+
|
|
38
|
+
if group_id is None:
|
|
39
|
+
return "请提供群号(group_id参数)"
|
|
40
|
+
if user_id is None:
|
|
41
|
+
return "请提供要查询的群成员QQ号(user_id参数)"
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
group_id = int(group_id)
|
|
45
|
+
user_id = int(user_id)
|
|
46
|
+
except (ValueError, TypeError):
|
|
47
|
+
return "参数类型错误:group_id 和 user_id 必须是整数"
|
|
48
|
+
|
|
49
|
+
onebot_client = context.get("onebot_client")
|
|
50
|
+
if not onebot_client:
|
|
51
|
+
return "获取群成员信息功能不可用(OneBot 客户端未设置)"
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
member_info = await onebot_client.get_group_member_info(
|
|
55
|
+
group_id, user_id, no_cache
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
if not member_info:
|
|
59
|
+
return f"未找到群 {group_id} 中 QQ {user_id} 的成员信息,可能该用户已退群或群号不正确"
|
|
60
|
+
|
|
61
|
+
result_parts = []
|
|
62
|
+
nickname = member_info.get("nickname", "")
|
|
63
|
+
card = member_info.get("card", "")
|
|
64
|
+
|
|
65
|
+
result_parts.append(f"【群成员信息】群号: {group_id}")
|
|
66
|
+
|
|
67
|
+
display_name = card if card else nickname
|
|
68
|
+
if display_name:
|
|
69
|
+
result_parts.append(f"昵称: {display_name}")
|
|
70
|
+
|
|
71
|
+
if fields:
|
|
72
|
+
for field in fields:
|
|
73
|
+
if field in FIELD_MAPPING:
|
|
74
|
+
value = member_info.get(field)
|
|
75
|
+
if value is not None:
|
|
76
|
+
if field in [
|
|
77
|
+
"join_time",
|
|
78
|
+
"last_sent_time",
|
|
79
|
+
"title_expire_time",
|
|
80
|
+
"shut_up_timestamp",
|
|
81
|
+
]:
|
|
82
|
+
try:
|
|
83
|
+
if isinstance(value, (int, float)):
|
|
84
|
+
dt = datetime.fromtimestamp(value)
|
|
85
|
+
value = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
86
|
+
except (ValueError, TypeError, OSError):
|
|
87
|
+
pass
|
|
88
|
+
elif field == "role":
|
|
89
|
+
role_map = {
|
|
90
|
+
"owner": "群主",
|
|
91
|
+
"admin": "管理员",
|
|
92
|
+
"member": "成员",
|
|
93
|
+
}
|
|
94
|
+
value = role_map.get(str(value), str(value))
|
|
95
|
+
elif field == "unfriendly":
|
|
96
|
+
value = "是" if value else "否"
|
|
97
|
+
result_parts.append(f"{FIELD_MAPPING[field]}: {value}")
|
|
98
|
+
else:
|
|
99
|
+
for field, display_name_field in FIELD_MAPPING.items():
|
|
100
|
+
value = member_info.get(field)
|
|
101
|
+
if value is not None:
|
|
102
|
+
if field in [
|
|
103
|
+
"join_time",
|
|
104
|
+
"last_sent_time",
|
|
105
|
+
"title_expire_time",
|
|
106
|
+
"shut_up_timestamp",
|
|
107
|
+
]:
|
|
108
|
+
try:
|
|
109
|
+
if isinstance(value, (int, float)):
|
|
110
|
+
dt = datetime.fromtimestamp(value)
|
|
111
|
+
value = dt.strftime("%Y-%m-%d %H:%M:%S")
|
|
112
|
+
except (ValueError, TypeError, OSError):
|
|
113
|
+
pass
|
|
114
|
+
elif field == "role":
|
|
115
|
+
role_map = {
|
|
116
|
+
"owner": "群主",
|
|
117
|
+
"admin": "管理员",
|
|
118
|
+
"member": "成员",
|
|
119
|
+
}
|
|
120
|
+
value = role_map.get(str(value), str(value))
|
|
121
|
+
elif field == "unfriendly":
|
|
122
|
+
value = "是" if value else "否"
|
|
123
|
+
result_parts.append(f"{display_name_field}: {value}")
|
|
124
|
+
|
|
125
|
+
if limit and len(result_parts) > limit:
|
|
126
|
+
result_parts = result_parts[:limit]
|
|
127
|
+
|
|
128
|
+
result_str = "\n".join(result_parts)
|
|
129
|
+
return f"{result_str}\n✅ 信息获取成功"
|
|
130
|
+
|
|
131
|
+
except Exception as e:
|
|
132
|
+
logger.exception(f"获取群成员信息失败: {e}")
|
|
133
|
+
error_msg = str(e)
|
|
134
|
+
|
|
135
|
+
if "retcode=100" in error_msg:
|
|
136
|
+
return "获取失败:群号或QQ号不存在"
|
|
137
|
+
elif "retcode=140" in error_msg:
|
|
138
|
+
return "获取失败:无法获取非好友的群成员信息"
|
|
139
|
+
elif "retcode=150" in error_msg:
|
|
140
|
+
return "获取失败:群成员信息获取频率过高"
|
|
141
|
+
else:
|
|
142
|
+
return f"获取失败:{error_msg}"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "get_messages_by_time",
|
|
5
|
+
"description": "根据时间范围检索消息。用于查找特定时间段内的聊天记录。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"chat_id": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "聊天ID(群号或QQ号)"
|
|
12
|
+
},
|
|
13
|
+
"type": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "聊天类型(group 或 private)",
|
|
16
|
+
"enum": ["group", "private"]
|
|
17
|
+
},
|
|
18
|
+
"start_time": {
|
|
19
|
+
"type": "string",
|
|
20
|
+
"description": "开始时间(格式:YYYY-MM-DD HH:MM:SS,例如:2025-12-29 10:00:00)"
|
|
21
|
+
},
|
|
22
|
+
"end_time": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "结束时间(格式:YYYY-MM-DD HH:MM:SS,例如:2025-12-29 12:00:00)"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"required": ["chat_id", "type", "start_time", "end_time"]
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|