AstrBot 4.5.1__py3-none-any.whl → 4.5.2__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.
- astrbot/api/__init__.py +10 -11
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -36
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +7 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__main__.py +5 -5
- astrbot/cli/commands/__init__.py +3 -3
- astrbot/cli/commands/cmd_conf.py +19 -16
- astrbot/cli/commands/cmd_init.py +3 -2
- astrbot/cli/commands/cmd_plug.py +8 -10
- astrbot/cli/commands/cmd_run.py +5 -6
- astrbot/cli/utils/__init__.py +6 -6
- astrbot/cli/utils/basic.py +14 -14
- astrbot/cli/utils/plugin.py +24 -15
- astrbot/cli/utils/version_comparator.py +10 -12
- astrbot/core/__init__.py +8 -6
- astrbot/core/agent/agent.py +3 -2
- astrbot/core/agent/handoff.py +6 -2
- astrbot/core/agent/hooks.py +9 -6
- astrbot/core/agent/mcp_client.py +50 -15
- astrbot/core/agent/message.py +168 -0
- astrbot/core/agent/response.py +2 -1
- astrbot/core/agent/run_context.py +2 -3
- astrbot/core/agent/runners/base.py +10 -13
- astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
- astrbot/core/agent/tool.py +60 -41
- astrbot/core/agent/tool_executor.py +9 -3
- astrbot/core/astr_agent_context.py +3 -1
- astrbot/core/astrbot_config_mgr.py +29 -9
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +28 -26
- astrbot/core/config/default.py +4 -6
- astrbot/core/conversation_mgr.py +105 -36
- astrbot/core/core_lifecycle.py +68 -54
- astrbot/core/db/__init__.py +33 -18
- astrbot/core/db/migration/helper.py +12 -10
- astrbot/core/db/migration/migra_3_to_4.py +53 -34
- astrbot/core/db/migration/migra_45_to_46.py +1 -1
- astrbot/core/db/migration/shared_preferences_v3.py +2 -1
- astrbot/core/db/migration/sqlite_v3.py +26 -23
- astrbot/core/db/po.py +27 -18
- astrbot/core/db/sqlite.py +74 -45
- astrbot/core/db/vec_db/base.py +10 -14
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
- astrbot/core/event_bus.py +8 -6
- astrbot/core/file_token_service.py +6 -5
- astrbot/core/initial_loader.py +7 -5
- astrbot/core/knowledge_base/chunking/__init__.py +1 -3
- astrbot/core/knowledge_base/chunking/base.py +1 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
- astrbot/core/knowledge_base/chunking/recursive.py +16 -10
- astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
- astrbot/core/knowledge_base/kb_helper.py +30 -17
- astrbot/core/knowledge_base/kb_mgr.py +6 -7
- astrbot/core/knowledge_base/models.py +10 -4
- astrbot/core/knowledge_base/parsers/__init__.py +3 -5
- astrbot/core/knowledge_base/parsers/base.py +1 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
- astrbot/core/knowledge_base/parsers/util.py +1 -1
- astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
- astrbot/core/knowledge_base/retrieval/manager.py +17 -14
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
- astrbot/core/log.py +21 -13
- astrbot/core/message/components.py +123 -217
- astrbot/core/message/message_event_result.py +24 -24
- astrbot/core/persona_mgr.py +20 -11
- astrbot/core/pipeline/__init__.py +7 -7
- astrbot/core/pipeline/content_safety_check/stage.py +13 -9
- astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +4 -1
- astrbot/core/pipeline/context_utils.py +77 -7
- astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
- astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
- astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
- astrbot/core/pipeline/process_stage/stage.py +13 -10
- astrbot/core/pipeline/process_stage/utils.py +6 -5
- astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
- astrbot/core/pipeline/respond/stage.py +23 -20
- astrbot/core/pipeline/result_decorate/stage.py +31 -23
- astrbot/core/pipeline/scheduler.py +12 -8
- astrbot/core/pipeline/session_status_check/stage.py +12 -8
- astrbot/core/pipeline/stage.py +10 -4
- astrbot/core/pipeline/waking_check/stage.py +24 -18
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +76 -110
- astrbot/core/platform/astrbot_message.py +11 -13
- astrbot/core/platform/manager.py +16 -15
- astrbot/core/platform/message_session.py +5 -3
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +4 -4
- astrbot/core/platform/register.py +8 -8
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +42 -27
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
- astrbot/core/platform/sources/discord/client.py +9 -6
- astrbot/core/platform/sources/discord/components.py +18 -14
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
- astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
- astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
- astrbot/core/platform/sources/lark/lark_event.py +21 -14
- astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
- astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
- astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
- astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
- astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
- astrbot/core/platform/sources/satori/satori_event.py +34 -25
- astrbot/core/platform/sources/slack/client.py +11 -9
- astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
- astrbot/core/platform/sources/slack/slack_event.py +34 -24
- astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
- astrbot/core/platform/sources/telegram/tg_event.py +32 -18
- astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
- astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
- astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
- astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
- astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
- astrbot/core/platform_message_history_mgr.py +5 -3
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +61 -75
- astrbot/core/provider/func_tool_manager.py +59 -55
- astrbot/core/provider/manager.py +32 -22
- astrbot/core/provider/provider.py +72 -46
- astrbot/core/provider/register.py +7 -7
- astrbot/core/provider/sources/anthropic_source.py +48 -30
- astrbot/core/provider/sources/azure_tts_source.py +17 -13
- astrbot/core/provider/sources/coze_api_client.py +27 -17
- astrbot/core/provider/sources/coze_source.py +104 -87
- astrbot/core/provider/sources/dashscope_source.py +18 -11
- astrbot/core/provider/sources/dashscope_tts.py +36 -23
- astrbot/core/provider/sources/dify_source.py +25 -20
- astrbot/core/provider/sources/edge_tts_source.py +21 -17
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
- astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
- astrbot/core/provider/sources/gemini_source.py +72 -58
- astrbot/core/provider/sources/gemini_tts_source.py +8 -6
- astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
- astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
- astrbot/core/provider/sources/openai_embedding_source.py +6 -8
- astrbot/core/provider/sources/openai_source.py +77 -69
- astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
- astrbot/core/provider/sources/volcengine_tts.py +38 -31
- astrbot/core/provider/sources/whisper_api_source.py +14 -12
- astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
- astrbot/core/provider/sources/xinference_rerank_source.py +16 -8
- astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
- astrbot/core/star/__init__.py +16 -11
- astrbot/core/star/config.py +10 -15
- astrbot/core/star/context.py +97 -75
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +30 -28
- astrbot/core/star/filter/command_group.py +27 -24
- astrbot/core/star/filter/custom_filter.py +6 -5
- astrbot/core/star/filter/event_message_type.py +4 -2
- astrbot/core/star/filter/permission.py +4 -2
- astrbot/core/star/filter/platform_adapter_type.py +4 -2
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -19
- astrbot/core/star/register/star.py +6 -2
- astrbot/core/star/register/star_handler.py +96 -73
- astrbot/core/star/session_llm_manager.py +48 -14
- astrbot/core/star/session_plugin_manager.py +29 -15
- astrbot/core/star/star.py +1 -2
- astrbot/core/star/star_handler.py +13 -8
- astrbot/core/star/star_manager.py +151 -59
- astrbot/core/star/star_tools.py +44 -37
- astrbot/core/star/updator.py +10 -10
- astrbot/core/umop_config_router.py +10 -4
- astrbot/core/updator.py +13 -5
- astrbot/core/utils/astrbot_path.py +3 -5
- astrbot/core/utils/dify_api_client.py +33 -15
- astrbot/core/utils/io.py +66 -42
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +7 -7
- astrbot/core/utils/path_util.py +15 -16
- astrbot/core/utils/pip_installer.py +5 -5
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +45 -20
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/network_strategy.py +35 -26
- astrbot/core/utils/t2i/renderer.py +11 -5
- astrbot/core/utils/t2i/template_manager.py +14 -15
- astrbot/core/utils/tencent_record_helper.py +19 -13
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +43 -40
- astrbot/dashboard/routes/__init__.py +18 -18
- astrbot/dashboard/routes/auth.py +10 -8
- astrbot/dashboard/routes/chat.py +30 -21
- astrbot/dashboard/routes/config.py +92 -75
- astrbot/dashboard/routes/conversation.py +46 -39
- astrbot/dashboard/routes/file.py +4 -2
- astrbot/dashboard/routes/knowledge_base.py +47 -40
- astrbot/dashboard/routes/log.py +9 -4
- astrbot/dashboard/routes/persona.py +19 -16
- astrbot/dashboard/routes/plugin.py +69 -55
- astrbot/dashboard/routes/route.py +3 -1
- astrbot/dashboard/routes/session_management.py +130 -116
- astrbot/dashboard/routes/stat.py +34 -34
- astrbot/dashboard/routes/t2i.py +15 -12
- astrbot/dashboard/routes/tools.py +47 -52
- astrbot/dashboard/routes/update.py +32 -28
- astrbot/dashboard/server.py +30 -26
- astrbot/dashboard/utils.py +8 -4
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/METADATA +2 -1
- astrbot-4.5.2.dist-info/RECORD +261 -0
- astrbot-4.5.1.dist-info/RECORD +0 -260
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
astrbot/core/utils/io.py
CHANGED
|
@@ -1,29 +1,26 @@
|
|
|
1
|
+
import base64
|
|
2
|
+
import logging
|
|
1
3
|
import os
|
|
2
|
-
from pathlib import Path
|
|
3
|
-
import ssl
|
|
4
4
|
import shutil
|
|
5
5
|
import socket
|
|
6
|
+
import ssl
|
|
6
7
|
import time
|
|
7
|
-
import aiohttp
|
|
8
|
-
import base64
|
|
9
|
-
import zipfile
|
|
10
8
|
import uuid
|
|
11
|
-
import
|
|
12
|
-
import
|
|
9
|
+
import zipfile
|
|
10
|
+
from pathlib import Path
|
|
13
11
|
|
|
12
|
+
import aiohttp
|
|
14
13
|
import certifi
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import psutil
|
|
17
15
|
from PIL import Image
|
|
16
|
+
|
|
18
17
|
from .astrbot_path import get_astrbot_data_path
|
|
19
18
|
|
|
20
19
|
logger = logging.getLogger("astrbot")
|
|
21
20
|
|
|
22
21
|
|
|
23
22
|
def on_error(func, path, exc_info):
|
|
24
|
-
"""
|
|
25
|
-
a callback of the rmtree function.
|
|
26
|
-
"""
|
|
23
|
+
"""A callback of the rmtree function."""
|
|
27
24
|
import stat
|
|
28
25
|
|
|
29
26
|
if not os.access(path, os.W_OK):
|
|
@@ -78,61 +75,75 @@ def save_temp_img(img: Image.Image | str) -> str:
|
|
|
78
75
|
|
|
79
76
|
|
|
80
77
|
async def download_image_by_url(
|
|
81
|
-
url: str,
|
|
78
|
+
url: str,
|
|
79
|
+
post: bool = False,
|
|
80
|
+
post_data: dict | None = None,
|
|
81
|
+
path: str | None = None,
|
|
82
82
|
) -> str:
|
|
83
|
-
"""
|
|
84
|
-
下载图片, 返回 path
|
|
85
|
-
"""
|
|
83
|
+
"""下载图片, 返回 path"""
|
|
86
84
|
try:
|
|
87
85
|
ssl_context = ssl.create_default_context(
|
|
88
|
-
cafile=certifi.where()
|
|
86
|
+
cafile=certifi.where(),
|
|
89
87
|
) # 使用 certifi 提供的 CA 证书
|
|
90
88
|
connector = aiohttp.TCPConnector(ssl=ssl_context) # 使用 certifi 的根证书
|
|
91
89
|
async with aiohttp.ClientSession(
|
|
92
|
-
trust_env=True,
|
|
90
|
+
trust_env=True,
|
|
91
|
+
connector=connector,
|
|
93
92
|
) as session:
|
|
94
93
|
if post:
|
|
95
94
|
async with session.post(url, json=post_data) as resp:
|
|
96
95
|
if not path:
|
|
97
96
|
return save_temp_img(await resp.read())
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return path
|
|
97
|
+
with open(path, "wb") as f:
|
|
98
|
+
f.write(await resp.read())
|
|
99
|
+
return path
|
|
102
100
|
else:
|
|
103
101
|
async with session.get(url) as resp:
|
|
104
102
|
if not path:
|
|
105
103
|
return save_temp_img(await resp.read())
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return path
|
|
104
|
+
with open(path, "wb") as f:
|
|
105
|
+
f.write(await resp.read())
|
|
106
|
+
return path
|
|
110
107
|
except (aiohttp.ClientConnectorSSLError, aiohttp.ClientConnectorCertificateError):
|
|
111
|
-
# 关闭SSL
|
|
108
|
+
# 关闭SSL验证(仅在证书验证失败时作为fallback)
|
|
109
|
+
logger.warning(
|
|
110
|
+
f"SSL certificate verification failed for {url}. "
|
|
111
|
+
"Disabling SSL verification (CERT_NONE) as a fallback. "
|
|
112
|
+
"This is insecure and exposes the application to man-in-the-middle attacks. "
|
|
113
|
+
"Please investigate and resolve certificate issues."
|
|
114
|
+
)
|
|
112
115
|
ssl_context = ssl.create_default_context()
|
|
113
|
-
ssl_context.
|
|
116
|
+
ssl_context.check_hostname = False
|
|
117
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
114
118
|
async with aiohttp.ClientSession() as session:
|
|
115
119
|
if post:
|
|
116
|
-
async with session.
|
|
117
|
-
|
|
120
|
+
async with session.post(url, json=post_data, ssl=ssl_context) as resp:
|
|
121
|
+
if not path:
|
|
122
|
+
return save_temp_img(await resp.read())
|
|
123
|
+
with open(path, "wb") as f:
|
|
124
|
+
f.write(await resp.read())
|
|
125
|
+
return path
|
|
118
126
|
else:
|
|
119
127
|
async with session.get(url, ssl=ssl_context) as resp:
|
|
120
|
-
|
|
128
|
+
if not path:
|
|
129
|
+
return save_temp_img(await resp.read())
|
|
130
|
+
with open(path, "wb") as f:
|
|
131
|
+
f.write(await resp.read())
|
|
132
|
+
return path
|
|
121
133
|
except Exception as e:
|
|
122
134
|
raise e
|
|
123
135
|
|
|
124
136
|
|
|
125
137
|
async def download_file(url: str, path: str, show_progress: bool = False):
|
|
126
|
-
"""
|
|
127
|
-
从指定 url 下载文件到指定路径 path
|
|
128
|
-
"""
|
|
138
|
+
"""从指定 url 下载文件到指定路径 path"""
|
|
129
139
|
try:
|
|
130
140
|
ssl_context = ssl.create_default_context(
|
|
131
|
-
cafile=certifi.where()
|
|
141
|
+
cafile=certifi.where(),
|
|
132
142
|
) # 使用 certifi 提供的 CA 证书
|
|
133
143
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
134
144
|
async with aiohttp.ClientSession(
|
|
135
|
-
trust_env=True,
|
|
145
|
+
trust_env=True,
|
|
146
|
+
connector=connector,
|
|
136
147
|
) as session:
|
|
137
148
|
async with session.get(url, timeout=1800) as resp:
|
|
138
149
|
if resp.status != 200:
|
|
@@ -161,9 +172,19 @@ async def download_file(url: str, path: str, show_progress: bool = False):
|
|
|
161
172
|
end="",
|
|
162
173
|
)
|
|
163
174
|
except (aiohttp.ClientConnectorSSLError, aiohttp.ClientConnectorCertificateError):
|
|
164
|
-
# 关闭SSL
|
|
175
|
+
# 关闭SSL验证(仅在证书验证失败时作为fallback)
|
|
176
|
+
logger.warning(
|
|
177
|
+
"SSL 证书验证失败,已关闭 SSL 验证(不安全,仅用于临时下载)。请检查目标服务器的证书配置。"
|
|
178
|
+
)
|
|
179
|
+
logger.warning(
|
|
180
|
+
f"SSL certificate verification failed for {url}. "
|
|
181
|
+
"Falling back to unverified connection (CERT_NONE). "
|
|
182
|
+
"This is insecure and exposes the application to man-in-the-middle attacks. "
|
|
183
|
+
"Please investigate certificate issues with the remote server."
|
|
184
|
+
)
|
|
165
185
|
ssl_context = ssl.create_default_context()
|
|
166
|
-
ssl_context.
|
|
186
|
+
ssl_context.check_hostname = False
|
|
187
|
+
ssl_context.verify_mode = ssl.CERT_NONE
|
|
167
188
|
async with aiohttp.ClientSession() as session:
|
|
168
189
|
async with session.get(url, ssl=ssl_context, timeout=120) as resp:
|
|
169
190
|
total_size = int(resp.headers.get("content-length", 0))
|
|
@@ -227,7 +248,6 @@ async def download_dashboard(
|
|
|
227
248
|
proxy: str | None = None,
|
|
228
249
|
) -> None:
|
|
229
250
|
"""下载管理面板文件"""
|
|
230
|
-
|
|
231
251
|
if path is None:
|
|
232
252
|
zip_path = Path(get_astrbot_data_path()).absolute() / "dashboard.zip"
|
|
233
253
|
else:
|
|
@@ -237,11 +257,13 @@ async def download_dashboard(
|
|
|
237
257
|
ver_name = "latest" if latest else version
|
|
238
258
|
dashboard_release_url = f"https://astrbot-registry.soulter.top/download/astrbot-dashboard/{ver_name}/dist.zip"
|
|
239
259
|
logger.info(
|
|
240
|
-
f"准备下载指定发行版本的 AstrBot WebUI 文件: {dashboard_release_url}"
|
|
260
|
+
f"准备下载指定发行版本的 AstrBot WebUI 文件: {dashboard_release_url}",
|
|
241
261
|
)
|
|
242
262
|
try:
|
|
243
263
|
await download_file(
|
|
244
|
-
dashboard_release_url,
|
|
264
|
+
dashboard_release_url,
|
|
265
|
+
str(zip_path),
|
|
266
|
+
show_progress=True,
|
|
245
267
|
)
|
|
246
268
|
except BaseException as _:
|
|
247
269
|
if latest:
|
|
@@ -251,7 +273,9 @@ async def download_dashboard(
|
|
|
251
273
|
if proxy:
|
|
252
274
|
dashboard_release_url = f"{proxy}/{dashboard_release_url}"
|
|
253
275
|
await download_file(
|
|
254
|
-
dashboard_release_url,
|
|
276
|
+
dashboard_release_url,
|
|
277
|
+
str(zip_path),
|
|
278
|
+
show_progress=True,
|
|
255
279
|
)
|
|
256
280
|
else:
|
|
257
281
|
url = f"https://github.com/AstrBotDevs/astrbot-release-harbour/releases/download/release-{version}/dist.zip"
|
astrbot/core/utils/log_pipe.py
CHANGED
astrbot/core/utils/metrics.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import aiohttp
|
|
2
|
-
import sys
|
|
3
1
|
import os
|
|
4
2
|
import socket
|
|
3
|
+
import sys
|
|
5
4
|
import uuid
|
|
6
|
-
|
|
5
|
+
|
|
6
|
+
import aiohttp
|
|
7
|
+
|
|
7
8
|
from astrbot.core import db_helper, logger
|
|
9
|
+
from astrbot.core.config import VERSION
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class Metric:
|
|
@@ -21,7 +23,7 @@ class Metric:
|
|
|
21
23
|
|
|
22
24
|
if os.path.exists(id_file):
|
|
23
25
|
try:
|
|
24
|
-
with open(id_file
|
|
26
|
+
with open(id_file) as f:
|
|
25
27
|
Metric._iid_cache = f.read().strip()
|
|
26
28
|
return Metric._iid_cache
|
|
27
29
|
except Exception:
|
|
@@ -39,8 +41,7 @@ class Metric:
|
|
|
39
41
|
|
|
40
42
|
@staticmethod
|
|
41
43
|
async def upload(**kwargs):
|
|
42
|
-
"""
|
|
43
|
-
上传相关非敏感的指标以更好地了解 AstrBot 的使用情况。上传的指标不会包含任何有关消息文本、用户信息等敏感信息。
|
|
44
|
+
"""上传相关非敏感的指标以更好地了解 AstrBot 的使用情况。上传的指标不会包含任何有关消息文本、用户信息等敏感信息。
|
|
44
45
|
|
|
45
46
|
Powered by TickStats.
|
|
46
47
|
"""
|
|
@@ -64,7 +65,6 @@ class Metric:
|
|
|
64
65
|
)
|
|
65
66
|
except Exception as e:
|
|
66
67
|
logger.error(f"保存指标到数据库失败: {e}")
|
|
67
|
-
pass
|
|
68
68
|
|
|
69
69
|
try:
|
|
70
70
|
async with aiohttp.ClientSession(trust_env=True) as session:
|
astrbot/core/utils/path_util.py
CHANGED
|
@@ -19,24 +19,23 @@ def path_Mapping(mappings, srcPath: str) -> str:
|
|
|
19
19
|
# 切割后大于4个项目,或者只有1个项目,那肯定是错误的,只能是2,3,4个项目
|
|
20
20
|
logger.warning(f"路径映射规则错误: {mapping}")
|
|
21
21
|
continue
|
|
22
|
+
# rule.len == 3 or 4
|
|
23
|
+
elif os.path.exists(rule[0] + ":" + rule[1]):
|
|
24
|
+
# 前面两个项目合并路径存在,说明是本地Window路径。后面一个或两个项目组成的路径本地大概率无法解析,直接拼接
|
|
25
|
+
from_ = rule[0] + ":" + rule[1]
|
|
26
|
+
if len(rule) == 3:
|
|
27
|
+
to_ = rule[2]
|
|
28
|
+
else:
|
|
29
|
+
to_ = rule[2] + ":" + rule[3]
|
|
22
30
|
else:
|
|
23
|
-
#
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if len(rule) == 3:
|
|
28
|
-
to_ = rule[2]
|
|
29
|
-
else:
|
|
30
|
-
to_ = rule[2] + ":" + rule[3]
|
|
31
|
+
# 前面两个项目合并路径不存在,说明第一个项目是本地Linux路径,后面一个或两个项目直接拼接。
|
|
32
|
+
from_ = rule[0]
|
|
33
|
+
if len(rule) == 3:
|
|
34
|
+
to_ = rule[1] + ":" + rule[2]
|
|
31
35
|
else:
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
to_ = rule[1] + ":" + rule[2]
|
|
36
|
-
else:
|
|
37
|
-
# 这种情况下存在四个项目,说明规则也是错误的
|
|
38
|
-
logger.warning(f"路径映射规则错误: {mapping}")
|
|
39
|
-
continue
|
|
36
|
+
# 这种情况下存在四个项目,说明规则也是错误的
|
|
37
|
+
logger.warning(f"路径映射规则错误: {mapping}")
|
|
38
|
+
continue
|
|
40
39
|
|
|
41
40
|
from_ = from_.removesuffix("/")
|
|
42
41
|
from_ = from_.removesuffix("\\")
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import logging
|
|
2
1
|
import asyncio
|
|
2
|
+
import logging
|
|
3
3
|
import sys
|
|
4
4
|
|
|
5
5
|
logger = logging.getLogger("astrbot")
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class PipInstaller:
|
|
9
|
-
def __init__(self, pip_install_arg: str, pypi_index_url: str = None):
|
|
9
|
+
def __init__(self, pip_install_arg: str, pypi_index_url: str | None = None):
|
|
10
10
|
self.pip_install_arg = pip_install_arg
|
|
11
11
|
self.pypi_index_url = pypi_index_url
|
|
12
12
|
|
|
13
13
|
async def install(
|
|
14
14
|
self,
|
|
15
|
-
package_name: str = None,
|
|
16
|
-
requirements_path: str = None,
|
|
17
|
-
mirror: str = None,
|
|
15
|
+
package_name: str | None = None,
|
|
16
|
+
requirements_path: str | None = None,
|
|
17
|
+
mirror: str | None = None,
|
|
18
18
|
):
|
|
19
19
|
args = ["install"]
|
|
20
20
|
if package_name:
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
"""
|
|
2
|
-
会话控制
|
|
3
|
-
"""
|
|
1
|
+
"""会话控制"""
|
|
4
2
|
|
|
5
3
|
import abc
|
|
6
4
|
import asyncio
|
|
7
|
-
import time
|
|
8
|
-
import functools
|
|
9
5
|
import copy
|
|
6
|
+
import functools
|
|
7
|
+
import time
|
|
8
|
+
from collections.abc import Awaitable, Callable
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
10
11
|
import astrbot.core.message.components as Comp
|
|
11
|
-
from typing import Dict, Any, Callable, Awaitable, List
|
|
12
12
|
from astrbot.core.platform import AstrMessageEvent
|
|
13
13
|
|
|
14
|
-
USER_SESSIONS:
|
|
15
|
-
FILTERS:
|
|
14
|
+
USER_SESSIONS: dict[str, "SessionWaiter"] = {} # 存储 SessionWaiter 实例
|
|
15
|
+
FILTERS: list["SessionFilter"] = [] # 存储 SessionFilter 实例
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class SessionController:
|
|
19
|
-
"""
|
|
20
|
-
控制一个 Session 是否已经结束
|
|
21
|
-
"""
|
|
19
|
+
"""控制一个 Session 是否已经结束"""
|
|
22
20
|
|
|
23
21
|
def __init__(self):
|
|
24
22
|
self.future = asyncio.Future()
|
|
@@ -29,7 +27,7 @@ class SessionController:
|
|
|
29
27
|
self.timeout: float | int = None
|
|
30
28
|
"""上次保持(keep)开始时的超时时间"""
|
|
31
29
|
|
|
32
|
-
self.history_chains:
|
|
30
|
+
self.history_chains: list[list[Comp.BaseMessageComponent]] = []
|
|
33
31
|
|
|
34
32
|
def stop(self, error: Exception = None):
|
|
35
33
|
"""立即结束这个会话"""
|
|
@@ -39,13 +37,14 @@ class SessionController:
|
|
|
39
37
|
else:
|
|
40
38
|
self.future.set_result(None)
|
|
41
39
|
|
|
42
|
-
def keep(self, timeout: float
|
|
40
|
+
def keep(self, timeout: float = 0, reset_timeout=False):
|
|
43
41
|
"""保持这个会话
|
|
44
42
|
|
|
45
43
|
Args:
|
|
46
44
|
timeout (float): 必填。会话超时时间。
|
|
47
45
|
当 reset_timeout 设置为 True 时, 代表重置超时时间, timeout 必须 > 0, 如果 <= 0 则立即结束会话。
|
|
48
46
|
当 reset_timeout 设置为 False 时, 代表继续维持原来的超时时间, 新 timeout = 原来剩余的timeout + timeout (可以 < 0)
|
|
47
|
+
|
|
49
48
|
"""
|
|
50
49
|
new_ts = time.time()
|
|
51
50
|
|
|
@@ -81,7 +80,7 @@ class SessionController:
|
|
|
81
80
|
pass # 避免报错
|
|
82
81
|
# finally:
|
|
83
82
|
|
|
84
|
-
def get_history_chains(self) ->
|
|
83
|
+
def get_history_chains(self) -> list[list[Comp.BaseMessageComponent]]:
|
|
85
84
|
"""获取历史消息链"""
|
|
86
85
|
return self.history_chains
|
|
87
86
|
|
|
@@ -92,7 +91,6 @@ class SessionFilter:
|
|
|
92
91
|
@abc.abstractmethod
|
|
93
92
|
def filter(self, event: AstrMessageEvent) -> str:
|
|
94
93
|
"""根据事件返回一个会话标识符"""
|
|
95
|
-
pass
|
|
96
94
|
|
|
97
95
|
|
|
98
96
|
class DefaultSessionFilter(SessionFilter):
|
|
@@ -120,7 +118,9 @@ class SessionWaiter:
|
|
|
120
118
|
"""需要保证一个 session 同时只有一个 trigger"""
|
|
121
119
|
|
|
122
120
|
async def register_wait(
|
|
123
|
-
self,
|
|
121
|
+
self,
|
|
122
|
+
handler: Callable[[str], Awaitable[Any]],
|
|
123
|
+
timeout: int = 30,
|
|
124
124
|
) -> Any:
|
|
125
125
|
"""等待外部输入并处理"""
|
|
126
126
|
self.handler = handler
|
|
@@ -149,7 +149,7 @@ class SessionWaiter:
|
|
|
149
149
|
@classmethod
|
|
150
150
|
async def trigger(cls, session_id: str, event: AstrMessageEvent):
|
|
151
151
|
"""外部输入触发会话处理"""
|
|
152
|
-
session = USER_SESSIONS.get(session_id
|
|
152
|
+
session = USER_SESSIONS.get(session_id)
|
|
153
153
|
if not session or session.session_controller.future.done():
|
|
154
154
|
return
|
|
155
155
|
|
|
@@ -157,7 +157,7 @@ class SessionWaiter:
|
|
|
157
157
|
if not session.session_controller.future.done():
|
|
158
158
|
if session.record_history_chains:
|
|
159
159
|
session.session_controller.history_chains.append(
|
|
160
|
-
[copy.deepcopy(comp) for comp in event.get_messages()]
|
|
160
|
+
[copy.deepcopy(comp) for comp in event.get_messages()],
|
|
161
161
|
)
|
|
162
162
|
try:
|
|
163
163
|
# TODO: 这里使用 create_task,跟踪 task,防止超时后这里 handler 仍然在执行
|
|
@@ -167,8 +167,7 @@ class SessionWaiter:
|
|
|
167
167
|
|
|
168
168
|
|
|
169
169
|
def session_waiter(timeout: int = 30, record_history_chains: bool = False):
|
|
170
|
-
"""
|
|
171
|
-
装饰器:自动将函数注册为 SessionWaiter 处理函数,并等待外部输入触发执行。
|
|
170
|
+
"""装饰器:自动将函数注册为 SessionWaiter 处理函数,并等待外部输入触发执行。
|
|
172
171
|
|
|
173
172
|
:param timeout: 超时时间(秒)
|
|
174
173
|
:param record_history_chain: 是否自动记录历史消息链。可以通过 controller.get_history_chains() 获取。深拷贝。
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
from astrbot.core.db import BaseDatabase
|
|
2
|
-
from astrbot.core.db.po import Preference
|
|
3
|
-
import threading
|
|
4
1
|
import asyncio
|
|
5
2
|
import os
|
|
6
|
-
|
|
7
|
-
from
|
|
3
|
+
import threading
|
|
4
|
+
from typing import Any, TypeVar, overload
|
|
8
5
|
|
|
6
|
+
from astrbot.core.db import BaseDatabase
|
|
7
|
+
from astrbot.core.db.po import Preference
|
|
8
|
+
|
|
9
|
+
from .astrbot_path import get_astrbot_data_path
|
|
9
10
|
|
|
10
11
|
_VT = TypeVar("_VT")
|
|
11
12
|
|
|
@@ -14,7 +15,8 @@ class SharedPreferences:
|
|
|
14
15
|
def __init__(self, db_helper: BaseDatabase, json_storage_path=None):
|
|
15
16
|
if json_storage_path is None:
|
|
16
17
|
json_storage_path = os.path.join(
|
|
17
|
-
get_astrbot_data_path(),
|
|
18
|
+
get_astrbot_data_path(),
|
|
19
|
+
"shared_preferences.json",
|
|
18
20
|
)
|
|
19
21
|
self.path = json_storage_path
|
|
20
22
|
self.db_helper = db_helper
|
|
@@ -38,13 +40,15 @@ class SharedPreferences:
|
|
|
38
40
|
else:
|
|
39
41
|
ret = default
|
|
40
42
|
return ret
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
)
|
|
43
|
+
raise ValueError(
|
|
44
|
+
"scope_id and key cannot be None when getting a specific preference.",
|
|
45
|
+
)
|
|
45
46
|
|
|
46
47
|
async def range_get_async(
|
|
47
|
-
self,
|
|
48
|
+
self,
|
|
49
|
+
scope: str,
|
|
50
|
+
scope_id: str | None = None,
|
|
51
|
+
key: str | None = None,
|
|
48
52
|
) -> list[Preference]:
|
|
49
53
|
"""获取指定范围的偏好设置
|
|
50
54
|
Note: 返回 Preference 列表,其中的 value 属性是一个 dict,value["val"] 为值。scope_id 和 key 可以为 None,这时返回该范围下所有的偏好设置。
|
|
@@ -54,21 +58,33 @@ class SharedPreferences:
|
|
|
54
58
|
|
|
55
59
|
@overload
|
|
56
60
|
async def session_get(
|
|
57
|
-
self,
|
|
61
|
+
self,
|
|
62
|
+
umo: None,
|
|
63
|
+
key: str,
|
|
64
|
+
default: Any = None,
|
|
58
65
|
) -> list[Preference]: ...
|
|
59
66
|
|
|
60
67
|
@overload
|
|
61
68
|
async def session_get(
|
|
62
|
-
self,
|
|
69
|
+
self,
|
|
70
|
+
umo: str,
|
|
71
|
+
key: None,
|
|
72
|
+
default: Any = None,
|
|
63
73
|
) -> list[Preference]: ...
|
|
64
74
|
|
|
65
75
|
@overload
|
|
66
76
|
async def session_get(
|
|
67
|
-
self,
|
|
77
|
+
self,
|
|
78
|
+
umo: None,
|
|
79
|
+
key: None,
|
|
80
|
+
default: Any = None,
|
|
68
81
|
) -> list[Preference]: ...
|
|
69
82
|
|
|
70
83
|
async def session_get(
|
|
71
|
-
self,
|
|
84
|
+
self,
|
|
85
|
+
umo: str | None,
|
|
86
|
+
key: str | None = None,
|
|
87
|
+
default: _VT = None,
|
|
72
88
|
) -> _VT | list[Preference]:
|
|
73
89
|
"""获取会话范围的偏好设置
|
|
74
90
|
|
|
@@ -85,7 +101,9 @@ class SharedPreferences:
|
|
|
85
101
|
async def global_get(self, key: str, default: _VT = None) -> _VT: ...
|
|
86
102
|
|
|
87
103
|
async def global_get(
|
|
88
|
-
self,
|
|
104
|
+
self,
|
|
105
|
+
key: str | None,
|
|
106
|
+
default: _VT = None,
|
|
89
107
|
) -> _VT | list[Preference]:
|
|
90
108
|
"""获取全局范围的偏好设置
|
|
91
109
|
|
|
@@ -98,7 +116,10 @@ class SharedPreferences:
|
|
|
98
116
|
async def put_async(self, scope: str, scope_id: str, key: str, value: Any):
|
|
99
117
|
"""设置指定范围和键的偏好设置"""
|
|
100
118
|
await self.db_helper.insert_preference_or_update(
|
|
101
|
-
scope,
|
|
119
|
+
scope,
|
|
120
|
+
scope_id,
|
|
121
|
+
key,
|
|
122
|
+
{"val": value},
|
|
102
123
|
)
|
|
103
124
|
|
|
104
125
|
async def session_put(self, umo: str, key: str, value: Any):
|
|
@@ -139,7 +160,7 @@ class SharedPreferences:
|
|
|
139
160
|
if scope_id is None or key is None:
|
|
140
161
|
# result = asyncio.run(self.range_get_async(scope, scope_id, key))
|
|
141
162
|
raise ValueError(
|
|
142
|
-
"scope_id and key cannot be None when getting a specific preference."
|
|
163
|
+
"scope_id and key cannot be None when getting a specific preference.",
|
|
143
164
|
)
|
|
144
165
|
result = asyncio.run_coroutine_threadsafe(
|
|
145
166
|
self.get_async(scope or "unknown", scope_id or "unknown", key, default),
|
|
@@ -149,11 +170,15 @@ class SharedPreferences:
|
|
|
149
170
|
return result if result is not None else default
|
|
150
171
|
|
|
151
172
|
def range_get(
|
|
152
|
-
self,
|
|
173
|
+
self,
|
|
174
|
+
scope: str,
|
|
175
|
+
scope_id: str | None = None,
|
|
176
|
+
key: str | None = None,
|
|
153
177
|
) -> list[Preference]:
|
|
154
178
|
"""获取指定范围的偏好设置(已弃用)"""
|
|
155
179
|
result = asyncio.run_coroutine_threadsafe(
|
|
156
|
-
self.range_get_async(scope, scope_id, key),
|
|
180
|
+
self.range_get_async(scope, scope_id, key),
|
|
181
|
+
self._sync_loop,
|
|
157
182
|
).result()
|
|
158
183
|
|
|
159
184
|
return result
|