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,78 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import uuid
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
import matplotlib
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
12
|
+
content = args.get("content", "")
|
|
13
|
+
target_id = args.get("target_id")
|
|
14
|
+
message_type = args.get("message_type")
|
|
15
|
+
|
|
16
|
+
if not content:
|
|
17
|
+
return "内容不能为空"
|
|
18
|
+
if not target_id:
|
|
19
|
+
return "目标 ID 不能为空"
|
|
20
|
+
if not message_type:
|
|
21
|
+
return "消息类型不能为空"
|
|
22
|
+
if message_type not in ["group", "private"]:
|
|
23
|
+
return "消息类型必须是 group 或 private"
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
filename = f"render_{uuid.uuid4().hex[:16]}.png"
|
|
27
|
+
filepath = Path.cwd() / "img" / filename
|
|
28
|
+
filepath.parent.mkdir(exist_ok=True)
|
|
29
|
+
|
|
30
|
+
matplotlib.use("Agg")
|
|
31
|
+
|
|
32
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
|
33
|
+
ax.axis("off")
|
|
34
|
+
|
|
35
|
+
ax.text(
|
|
36
|
+
0.5,
|
|
37
|
+
0.5,
|
|
38
|
+
content,
|
|
39
|
+
transform=ax.transAxes,
|
|
40
|
+
fontsize=12,
|
|
41
|
+
verticalalignment="center",
|
|
42
|
+
horizontalalignment="center",
|
|
43
|
+
usetex=True,
|
|
44
|
+
wrap=True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
plt.tight_layout()
|
|
48
|
+
plt.savefig(filepath, dpi=150, bbox_inches="tight", pad_inches=0.1)
|
|
49
|
+
plt.close(fig)
|
|
50
|
+
|
|
51
|
+
send_image_callback = context.get("send_image_callback")
|
|
52
|
+
sender = context.get("sender")
|
|
53
|
+
|
|
54
|
+
if sender:
|
|
55
|
+
import os
|
|
56
|
+
|
|
57
|
+
abs_path = os.path.abspath(str(filepath))
|
|
58
|
+
message = f"[CQ:image,file={abs_path}]"
|
|
59
|
+
|
|
60
|
+
if message_type == "group":
|
|
61
|
+
await sender.send_group_message(int(target_id), message)
|
|
62
|
+
elif message_type == "private":
|
|
63
|
+
await sender.send_private_message(int(target_id), message)
|
|
64
|
+
|
|
65
|
+
return f"LaTeX 图片已渲染并发送到 {message_type} {target_id}"
|
|
66
|
+
|
|
67
|
+
elif send_image_callback:
|
|
68
|
+
await send_image_callback(target_id, message_type, str(filepath))
|
|
69
|
+
return f"LaTeX 图片已渲染并发送到 {message_type} {target_id}"
|
|
70
|
+
else:
|
|
71
|
+
return "发送图片回调未设置"
|
|
72
|
+
|
|
73
|
+
except ImportError as e:
|
|
74
|
+
missing_pkg = str(e).split("'")[1] if "'" in str(e) else "未知包"
|
|
75
|
+
return f"渲染失败:缺少依赖包 {missing_pkg},请运行: uv add {missing_pkg}"
|
|
76
|
+
except Exception as e:
|
|
77
|
+
logger.exception(f"渲染并发送 LaTeX 图片失败: {e}")
|
|
78
|
+
return f"渲染失败: {e}"
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "render_markdown",
|
|
5
|
+
"description": "将 Markdown 文本渲染为图片并发送到指定目标(群聊或私聊)。支持标准 Markdown 格式,包括标题、列表、代码块、表格等。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"content": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "要渲染的 Markdown 内容。支持标准 Markdown 格式。"
|
|
12
|
+
},
|
|
13
|
+
"target_id": {
|
|
14
|
+
"type": "integer",
|
|
15
|
+
"description": "目标 ID(群号或用户 QQ 号)"
|
|
16
|
+
},
|
|
17
|
+
"message_type": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"description": "消息类型",
|
|
20
|
+
"enum": ["group", "private"]
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"required": ["content", "target_id", "message_type"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
import logging
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
import uuid
|
|
5
|
+
from Undefined.render import render_markdown_to_html, render_html_to_image
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
11
|
+
content = args.get("content", "")
|
|
12
|
+
target_id = args.get("target_id")
|
|
13
|
+
message_type = args.get("message_type")
|
|
14
|
+
|
|
15
|
+
if not content:
|
|
16
|
+
return "内容不能为空"
|
|
17
|
+
if not target_id:
|
|
18
|
+
return "目标 ID 不能为空"
|
|
19
|
+
if not message_type:
|
|
20
|
+
return "消息类型不能为空"
|
|
21
|
+
if message_type not in ["group", "private"]:
|
|
22
|
+
return "消息类型必须是 group 或 private"
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
filename = f"render_{uuid.uuid4().hex[:16]}.png"
|
|
26
|
+
filepath = Path.cwd() / "img" / filename
|
|
27
|
+
filepath.parent.mkdir(exist_ok=True)
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
html_content = await render_markdown_to_html(content)
|
|
31
|
+
await render_html_to_image(html_content, str(filepath))
|
|
32
|
+
except Exception as e:
|
|
33
|
+
logger.exception(f"Markdown 渲染失败: {e}")
|
|
34
|
+
return f"Markdown 渲染失败: {e}"
|
|
35
|
+
|
|
36
|
+
send_image_callback = context.get("send_image_callback")
|
|
37
|
+
sender = context.get("sender")
|
|
38
|
+
|
|
39
|
+
if sender:
|
|
40
|
+
import os
|
|
41
|
+
|
|
42
|
+
abs_path = os.path.abspath(str(filepath))
|
|
43
|
+
message = f"[CQ:image,file={abs_path}]"
|
|
44
|
+
|
|
45
|
+
if message_type == "group":
|
|
46
|
+
await sender.send_group_message(int(target_id), message)
|
|
47
|
+
elif message_type == "private":
|
|
48
|
+
await sender.send_private_message(int(target_id), message)
|
|
49
|
+
|
|
50
|
+
return f"Markdown 图片已渲染并发送到 {message_type} {target_id}"
|
|
51
|
+
|
|
52
|
+
elif send_image_callback:
|
|
53
|
+
await send_image_callback(target_id, message_type, str(filepath))
|
|
54
|
+
return f"Markdown 图片已渲染并发送到 {message_type} {target_id}"
|
|
55
|
+
else:
|
|
56
|
+
return "发送图片回调未设置"
|
|
57
|
+
|
|
58
|
+
except ImportError as e:
|
|
59
|
+
missing_pkg = str(e).split("'")[1] if "'" in str(e) else "未知包"
|
|
60
|
+
return f"渲染失败:缺少依赖包 {missing_pkg},请运行: uv add {missing_pkg}"
|
|
61
|
+
except Exception as e:
|
|
62
|
+
logger.exception(f"渲染并发送 Markdown 图片失败: {e}")
|
|
63
|
+
return f"渲染失败: {e}"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "save_memory",
|
|
5
|
+
"description": "保存一条记忆。当你觉得某些信息值得记住(比如用户的偏好、重要的事情、约定等),可以调用此工具保存。就事论事,就人论人,不做会话隔离。上限100条,超出上限会自动移除最旧的记忆。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"fact": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "要记住的内容(简短、具体、有价值的信息)"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"required": ["fact"]
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
4
|
+
fact = args.get("fact", "")
|
|
5
|
+
if not fact:
|
|
6
|
+
return "记忆内容不能为空"
|
|
7
|
+
|
|
8
|
+
memory_storage = context.get("memory_storage")
|
|
9
|
+
|
|
10
|
+
if memory_storage:
|
|
11
|
+
success = memory_storage.add(fact)
|
|
12
|
+
if success:
|
|
13
|
+
return f"已保存记忆: {fact[:50]}..."
|
|
14
|
+
else:
|
|
15
|
+
return "保存失败(可能已存在相同记忆)"
|
|
16
|
+
else:
|
|
17
|
+
return "记忆存储未初始化"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "send_message",
|
|
5
|
+
"description": "发送群聊消息。可以在回答过程中多次调用,用于发送中间结果、进展信息或最终答案。最后必须调用 end 工具结束对话。注意:群号已自动设置,无需传递 group_id 参数。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"message": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "要发送的消息内容"
|
|
12
|
+
},
|
|
13
|
+
"at_user": {
|
|
14
|
+
"type": "integer",
|
|
15
|
+
"description": "要 @ 的用户 QQ 号(可选)"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["message"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
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
|
+
message = args.get("message", "")
|
|
9
|
+
if not message:
|
|
10
|
+
logger.warning("[发送消息] 收到空消息请求")
|
|
11
|
+
return "消息内容不能为空"
|
|
12
|
+
|
|
13
|
+
message = message.replace("\\", "")
|
|
14
|
+
|
|
15
|
+
# 如果可用,使用 context.recent_replies 检查重复
|
|
16
|
+
recent_replies = context.get("recent_replies")
|
|
17
|
+
if recent_replies is not None and message in recent_replies:
|
|
18
|
+
logger.info(f"[发送消息] 检测到重复消息内容: {message[:50]}...")
|
|
19
|
+
|
|
20
|
+
at_user = args.get("at_user")
|
|
21
|
+
send_message_callback = context.get("send_message_callback")
|
|
22
|
+
sender = context.get("sender")
|
|
23
|
+
|
|
24
|
+
# 优先使用 sender 接口
|
|
25
|
+
if sender:
|
|
26
|
+
ai_client = context.get("ai_client")
|
|
27
|
+
group_id = ai_client.current_group_id if ai_client else None
|
|
28
|
+
|
|
29
|
+
if group_id:
|
|
30
|
+
logger.info(f"[发送消息] 准备发送到群 {group_id}: {message[:100]}")
|
|
31
|
+
if at_user:
|
|
32
|
+
logger.debug(f"[发送消息] 同时 @ 用户: {at_user}")
|
|
33
|
+
message = f"[CQ:at,qq={at_user}] {message}"
|
|
34
|
+
try:
|
|
35
|
+
await sender.send_group_message(group_id, message)
|
|
36
|
+
if recent_replies is not None:
|
|
37
|
+
recent_replies.append(message)
|
|
38
|
+
return "消息已发送"
|
|
39
|
+
except Exception as e:
|
|
40
|
+
logger.exception(f"[发送消息] 发送到群 {group_id} 失败: {e}")
|
|
41
|
+
return f"发送失败: {e}"
|
|
42
|
+
elif send_message_callback:
|
|
43
|
+
logger.info(f"[发送消息] 无法确定群ID,尝试使用回调发送: {message[:100]}")
|
|
44
|
+
await send_message_callback(message, at_user)
|
|
45
|
+
if recent_replies is not None:
|
|
46
|
+
recent_replies.append(message)
|
|
47
|
+
return "消息已发送"
|
|
48
|
+
else:
|
|
49
|
+
logger.error("[发送消息] 发送失败:无法确定群组 ID 且无回调可用")
|
|
50
|
+
return "发送失败:无法确定群组 ID"
|
|
51
|
+
|
|
52
|
+
elif send_message_callback:
|
|
53
|
+
logger.info(f"[发送消息] 使用回调发送私聊或默认消息: {message[:100]}")
|
|
54
|
+
await send_message_callback(message, at_user)
|
|
55
|
+
if recent_replies is not None:
|
|
56
|
+
recent_replies.append(message)
|
|
57
|
+
return "消息已发送"
|
|
58
|
+
else:
|
|
59
|
+
logger.error("[发送消息] 发送消息回调和 sender 均未设置")
|
|
60
|
+
return "发送消息回调未设置"
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "function",
|
|
3
|
+
"function": {
|
|
4
|
+
"name": "send_private_message",
|
|
5
|
+
"description": "发送私聊消息给指定用户。只能在群聊中使用,用于给特定用户发送私聊消息。最后必须调用 end 工具结束对话。",
|
|
6
|
+
"parameters": {
|
|
7
|
+
"type": "object",
|
|
8
|
+
"properties": {
|
|
9
|
+
"user_id": {
|
|
10
|
+
"type": "integer",
|
|
11
|
+
"description": "目标用户的 QQ 号"
|
|
12
|
+
},
|
|
13
|
+
"message": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"description": "要发送的私聊消息内容"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["user_id", "message"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
5
|
+
|
|
6
|
+
async def execute(args: Dict[str, Any], context: Dict[str, Any]) -> str:
|
|
7
|
+
user_id = args.get("user_id")
|
|
8
|
+
message = args.get("message", "")
|
|
9
|
+
|
|
10
|
+
if not user_id:
|
|
11
|
+
return "目标用户 QQ 号不能为空"
|
|
12
|
+
if not message:
|
|
13
|
+
return "消息内容不能为空"
|
|
14
|
+
|
|
15
|
+
message = message.replace("\\", "")
|
|
16
|
+
|
|
17
|
+
recent_replies = context.get("recent_replies")
|
|
18
|
+
if recent_replies is not None and message in recent_replies:
|
|
19
|
+
logger.info(f"发送了重复私聊消息(已移除屏蔽): {message[:50]}...")
|
|
20
|
+
|
|
21
|
+
send_private_message_callback = context.get("send_private_message_callback")
|
|
22
|
+
sender = context.get("sender")
|
|
23
|
+
|
|
24
|
+
if sender:
|
|
25
|
+
await sender.send_private_message(user_id, message)
|
|
26
|
+
if recent_replies is not None:
|
|
27
|
+
recent_replies.append(message)
|
|
28
|
+
return f"私聊消息已发送给用户 {user_id}"
|
|
29
|
+
elif send_private_message_callback:
|
|
30
|
+
await send_private_message_callback(user_id, message)
|
|
31
|
+
if recent_replies is not None:
|
|
32
|
+
recent_replies.append(message)
|
|
33
|
+
return f"私聊消息已发送给用户 {user_id}"
|
|
34
|
+
else:
|
|
35
|
+
return "私聊发送回调未设置"
|
|
File without changes
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""通用工具函数"""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
import logging
|
|
5
|
+
from typing import Any, Awaitable, Callable
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def extract_text(message_content: list[dict[str, Any]], bot_qq: int = 0) -> str:
|
|
11
|
+
"""提取消息中的文本内容
|
|
12
|
+
|
|
13
|
+
参数:
|
|
14
|
+
message_content: 消息内容列表
|
|
15
|
+
bot_qq: 机器人 QQ 号(用于过滤 @ 机器人的内容),默认为 0(不过滤)
|
|
16
|
+
|
|
17
|
+
返回:
|
|
18
|
+
提取的文本
|
|
19
|
+
"""
|
|
20
|
+
texts: list[str] = []
|
|
21
|
+
for segment in message_content:
|
|
22
|
+
type_ = segment.get("type", "")
|
|
23
|
+
data = segment.get("data", {})
|
|
24
|
+
|
|
25
|
+
if type_ == "text":
|
|
26
|
+
texts.append(data.get("text", ""))
|
|
27
|
+
elif type_ == "at":
|
|
28
|
+
qq = data.get("qq", "")
|
|
29
|
+
# 如果指定了 bot_qq 且 @ 的是 bot,则不显示 @
|
|
30
|
+
if bot_qq and str(qq) == str(bot_qq):
|
|
31
|
+
continue
|
|
32
|
+
texts.append(f"[@ {qq}]")
|
|
33
|
+
elif type_ == "image":
|
|
34
|
+
file = data.get("file", "") or data.get("url", "")
|
|
35
|
+
texts.append(f"[图片: {file}]")
|
|
36
|
+
|
|
37
|
+
return "".join(texts).strip()
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def parse_message_content_for_history(
|
|
41
|
+
message_content: list[dict[str, Any]],
|
|
42
|
+
bot_qq: int,
|
|
43
|
+
get_msg_func: Callable[[int], Awaitable[dict[str, Any] | None]] | None = None,
|
|
44
|
+
) -> str:
|
|
45
|
+
"""解析消息内容用于历史记录(支持合并转发和 @ 格式化)
|
|
46
|
+
|
|
47
|
+
参数:
|
|
48
|
+
message_content: 消息内容列表
|
|
49
|
+
bot_qq: 机器人 QQ 号
|
|
50
|
+
get_msg_func: 获取消息详情的异步函数(可选,用于处理回复引用)
|
|
51
|
+
|
|
52
|
+
返回:
|
|
53
|
+
解析后的文本
|
|
54
|
+
"""
|
|
55
|
+
texts: list[str] = []
|
|
56
|
+
for segment in message_content:
|
|
57
|
+
type_ = segment.get("type")
|
|
58
|
+
data = segment.get("data", {})
|
|
59
|
+
|
|
60
|
+
if type_ == "text":
|
|
61
|
+
texts.append(data.get("text", ""))
|
|
62
|
+
|
|
63
|
+
elif type_ == "at":
|
|
64
|
+
qq = data.get("qq", "")
|
|
65
|
+
# 仅当 @ 的不是机器人时才显示,且使用指定格式
|
|
66
|
+
if str(qq) != str(bot_qq):
|
|
67
|
+
texts.append(f"[@ {qq}]")
|
|
68
|
+
|
|
69
|
+
elif type_ == "image":
|
|
70
|
+
file = data.get("file", "") or data.get("url", "")
|
|
71
|
+
texts.append(f"[图片: {file}]")
|
|
72
|
+
|
|
73
|
+
elif type_ == "forward":
|
|
74
|
+
msg_id = data.get("id")
|
|
75
|
+
if msg_id:
|
|
76
|
+
texts.append(f"[合并转发: {msg_id}]")
|
|
77
|
+
|
|
78
|
+
elif type_ == "reply":
|
|
79
|
+
msg_id = data.get("id")
|
|
80
|
+
if msg_id and get_msg_func:
|
|
81
|
+
try:
|
|
82
|
+
# 尝试获取引用的消息内容
|
|
83
|
+
reply_msg = await get_msg_func(int(msg_id))
|
|
84
|
+
if reply_msg:
|
|
85
|
+
sender = reply_msg.get("sender", {}).get("nickname", "未知")
|
|
86
|
+
content = reply_msg.get("message", [])
|
|
87
|
+
# 使用 extract_text 解析引用内容
|
|
88
|
+
quote_text = extract_text(content, bot_qq)
|
|
89
|
+
texts.append(f'<quote sender="{sender}">{quote_text}</quote>\n')
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.warning(f"获取回复消息失败: {e}")
|
|
92
|
+
|
|
93
|
+
elif type_ == "face":
|
|
94
|
+
texts.append("[表情]")
|
|
95
|
+
|
|
96
|
+
return "".join(texts).strip()
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def message_to_segments(message: str) -> list[dict[str, Any]]:
|
|
100
|
+
"""将包含 CQ 码的字符串转换为 OneBot 消息段数组
|
|
101
|
+
|
|
102
|
+
参数:
|
|
103
|
+
message: 包含 CQ 码的字符串
|
|
104
|
+
|
|
105
|
+
返回:
|
|
106
|
+
消息段列表
|
|
107
|
+
"""
|
|
108
|
+
segments = []
|
|
109
|
+
# 匹配 CQ 码的正则
|
|
110
|
+
# [CQ:type,arg1=val1,arg2=val2]
|
|
111
|
+
cq_pattern = re.compile(r"\[CQ:([a-zA-Z0-9_-]+),?([^\]]*)\]")
|
|
112
|
+
|
|
113
|
+
last_pos = 0
|
|
114
|
+
for match in cq_pattern.finditer(message):
|
|
115
|
+
# 处理 CQ 码之前的文本
|
|
116
|
+
text_part = message[last_pos : match.start()]
|
|
117
|
+
if text_part:
|
|
118
|
+
segments.append({"type": "text", "data": {"text": text_part}})
|
|
119
|
+
|
|
120
|
+
# 处理 CQ 码
|
|
121
|
+
cq_type = match.group(1)
|
|
122
|
+
cq_args_str = match.group(2)
|
|
123
|
+
|
|
124
|
+
# 解析参数
|
|
125
|
+
data = {}
|
|
126
|
+
if cq_args_str:
|
|
127
|
+
for arg_pair in cq_args_str.split(","):
|
|
128
|
+
if "=" in arg_pair:
|
|
129
|
+
k, v = arg_pair.split("=", 1)
|
|
130
|
+
# 注意:这里假设输入的 CQ 码已经是经过 OneBot 转义的格式
|
|
131
|
+
data[k.strip()] = v.strip()
|
|
132
|
+
|
|
133
|
+
segments.append({"type": cq_type, "data": data})
|
|
134
|
+
last_pos = match.end()
|
|
135
|
+
|
|
136
|
+
# 处理剩余的文本
|
|
137
|
+
remaining_text = message[last_pos:]
|
|
138
|
+
if remaining_text:
|
|
139
|
+
segments.append({"type": "text", "data": {"text": remaining_text}})
|
|
140
|
+
|
|
141
|
+
return segments
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def matches_xinliweiyuan(text: str) -> bool:
|
|
145
|
+
"""判断文本是否匹配心理委员触发规则
|
|
146
|
+
|
|
147
|
+
规则:
|
|
148
|
+
1. 单独“心理委员” (可选加标点/空格)
|
|
149
|
+
2. 前后(同时仅1)添加 5 个字以内(标点/空格不计入字数)
|
|
150
|
+
"""
|
|
151
|
+
keyword = "心理委员"
|
|
152
|
+
if keyword not in text:
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
# 分割文本,找到关键字的位置
|
|
156
|
+
parts = text.split(keyword)
|
|
157
|
+
# 如果出现多次关键词,只要其中一个位置满足条件即可触发
|
|
158
|
+
# 但为了简单和符合直觉,我们检查是否【任何】一种分割方式满足条件
|
|
159
|
+
# 通常文本里只会有一个“心理委员”用于触发
|
|
160
|
+
|
|
161
|
+
# 标点符号和空白字符正则
|
|
162
|
+
# \s 匹配空白,[^\w\s] 在大多数情况下匹配标点(但在 Python 3 中 \w 包含中文)
|
|
163
|
+
# 我们直接定义非“字”的模式:空白、常见标点
|
|
164
|
+
punc_pattern = r'[ \t\n\r\f\v\s!"#$%&\'()*+,\-./:;<=>?@\[\\\]^_`{|}~,。!?、;:""\'\'()【】「」《》—…·]'
|
|
165
|
+
|
|
166
|
+
def count_real_chars(s: str) -> int:
|
|
167
|
+
"""移除标点和空格后的长度"""
|
|
168
|
+
return len(re.sub(punc_pattern, "", s))
|
|
169
|
+
|
|
170
|
+
# 遍历所有可能的分割(以防文本中有多个“心理委员”)
|
|
171
|
+
for i in range(len(parts) - 1):
|
|
172
|
+
prefix = keyword.join(parts[: i + 1])
|
|
173
|
+
suffix = keyword.join(parts[i + 1 :])
|
|
174
|
+
|
|
175
|
+
prefix_count = count_real_chars(prefix)
|
|
176
|
+
suffix_count = count_real_chars(suffix)
|
|
177
|
+
|
|
178
|
+
# 同时仅1:不能前后都有字(标点不计)
|
|
179
|
+
if prefix_count > 0 and suffix_count > 0:
|
|
180
|
+
continue
|
|
181
|
+
|
|
182
|
+
# 字数限制:添加的部分总字数 <= 5
|
|
183
|
+
if (prefix_count + suffix_count) <= 5:
|
|
184
|
+
return True
|
|
185
|
+
|
|
186
|
+
return False
|