AstrBot 3.5.6__py3-none-any.whl → 4.7.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.
- astrbot/api/__init__.py +16 -4
- astrbot/api/all.py +2 -1
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -34
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +8 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__init__.py +1 -0
- astrbot/cli/__main__.py +18 -197
- astrbot/cli/commands/__init__.py +6 -0
- astrbot/cli/commands/cmd_conf.py +209 -0
- astrbot/cli/commands/cmd_init.py +56 -0
- astrbot/cli/commands/cmd_plug.py +245 -0
- astrbot/cli/commands/cmd_run.py +62 -0
- astrbot/cli/utils/__init__.py +18 -0
- astrbot/cli/utils/basic.py +76 -0
- astrbot/cli/utils/plugin.py +246 -0
- astrbot/cli/utils/version_comparator.py +90 -0
- astrbot/core/__init__.py +17 -19
- astrbot/core/agent/agent.py +14 -0
- astrbot/core/agent/handoff.py +38 -0
- astrbot/core/agent/hooks.py +30 -0
- astrbot/core/agent/mcp_client.py +385 -0
- astrbot/core/agent/message.py +175 -0
- astrbot/core/agent/response.py +14 -0
- astrbot/core/agent/run_context.py +22 -0
- astrbot/core/agent/runners/__init__.py +3 -0
- astrbot/core/agent/runners/base.py +65 -0
- astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
- astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
- astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
- astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
- astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
- astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
- astrbot/core/agent/tool.py +285 -0
- astrbot/core/agent/tool_executor.py +17 -0
- astrbot/core/astr_agent_context.py +19 -0
- astrbot/core/astr_agent_hooks.py +36 -0
- astrbot/core/astr_agent_run_util.py +80 -0
- astrbot/core/astr_agent_tool_exec.py +246 -0
- astrbot/core/astrbot_config_mgr.py +275 -0
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +60 -20
- astrbot/core/config/default.py +1972 -453
- astrbot/core/config/i18n_utils.py +110 -0
- astrbot/core/conversation_mgr.py +285 -75
- astrbot/core/core_lifecycle.py +167 -62
- astrbot/core/db/__init__.py +305 -102
- astrbot/core/db/migration/helper.py +69 -0
- astrbot/core/db/migration/migra_3_to_4.py +357 -0
- astrbot/core/db/migration/migra_45_to_46.py +44 -0
- astrbot/core/db/migration/migra_webchat_session.py +131 -0
- astrbot/core/db/migration/shared_preferences_v3.py +48 -0
- astrbot/core/db/migration/sqlite_v3.py +497 -0
- astrbot/core/db/po.py +259 -55
- astrbot/core/db/sqlite.py +773 -528
- astrbot/core/db/vec_db/base.py +73 -0
- astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
- astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
- astrbot/core/event_bus.py +26 -22
- astrbot/core/exceptions.py +9 -0
- astrbot/core/file_token_service.py +98 -0
- astrbot/core/initial_loader.py +19 -10
- astrbot/core/knowledge_base/chunking/__init__.py +9 -0
- astrbot/core/knowledge_base/chunking/base.py +25 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
- astrbot/core/knowledge_base/chunking/recursive.py +161 -0
- astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
- astrbot/core/knowledge_base/kb_helper.py +642 -0
- astrbot/core/knowledge_base/kb_mgr.py +330 -0
- astrbot/core/knowledge_base/models.py +120 -0
- astrbot/core/knowledge_base/parsers/__init__.py +13 -0
- astrbot/core/knowledge_base/parsers/base.py +51 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
- astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
- astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
- astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
- astrbot/core/knowledge_base/parsers/util.py +13 -0
- astrbot/core/knowledge_base/prompts.py +65 -0
- astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
- astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
- astrbot/core/knowledge_base/retrieval/manager.py +276 -0
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
- astrbot/core/log.py +21 -15
- astrbot/core/message/components.py +413 -287
- astrbot/core/message/message_event_result.py +35 -24
- astrbot/core/persona_mgr.py +192 -0
- astrbot/core/pipeline/__init__.py +14 -14
- 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 +13 -14
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +7 -1
- astrbot/core/pipeline/context_utils.py +107 -0
- astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
- astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
- astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
- astrbot/core/pipeline/process_stage/stage.py +21 -15
- astrbot/core/pipeline/process_stage/utils.py +125 -0
- astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
- astrbot/core/pipeline/respond/stage.py +142 -101
- astrbot/core/pipeline/result_decorate/stage.py +124 -57
- astrbot/core/pipeline/scheduler.py +21 -16
- astrbot/core/pipeline/session_status_check/stage.py +37 -0
- astrbot/core/pipeline/stage.py +11 -76
- astrbot/core/pipeline/waking_check/stage.py +69 -33
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +107 -129
- astrbot/core/platform/astrbot_message.py +32 -12
- astrbot/core/platform/manager.py +62 -18
- astrbot/core/platform/message_session.py +30 -0
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +9 -4
- astrbot/core/platform/register.py +12 -7
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
- astrbot/core/platform/sources/discord/client.py +129 -0
- astrbot/core/platform/sources/discord/components.py +139 -0
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
- astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
- astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
- astrbot/core/platform/sources/lark/lark_event.py +39 -13
- astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
- astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
- astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
- astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
- 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 +14 -8
- astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
- astrbot/core/platform/sources/satori/satori_event.py +432 -0
- astrbot/core/platform/sources/slack/client.py +164 -0
- astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
- astrbot/core/platform/sources/slack/slack_event.py +253 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
- astrbot/core/platform/sources/telegram/tg_event.py +136 -36
- astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
- astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
- astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
- astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
- astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
- astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
- astrbot/core/platform_message_history_mgr.py +49 -0
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +154 -98
- astrbot/core/provider/func_tool_manager.py +446 -458
- astrbot/core/provider/manager.py +345 -207
- astrbot/core/provider/provider.py +188 -73
- astrbot/core/provider/register.py +9 -7
- astrbot/core/provider/sources/anthropic_source.py +295 -115
- astrbot/core/provider/sources/azure_tts_source.py +224 -0
- astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
- astrbot/core/provider/sources/dashscope_tts.py +138 -14
- astrbot/core/provider/sources/edge_tts_source.py +24 -19
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
- astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
- astrbot/core/provider/sources/gemini_source.py +310 -132
- astrbot/core/provider/sources/gemini_tts_source.py +81 -0
- astrbot/core/provider/sources/groq_source.py +15 -0
- astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
- astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
- astrbot/core/provider/sources/openai_embedding_source.py +40 -0
- astrbot/core/provider/sources/openai_source.py +241 -145
- astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
- astrbot/core/provider/sources/volcengine_tts.py +115 -0
- astrbot/core/provider/sources/whisper_api_source.py +18 -13
- astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
- astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
- astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
- astrbot/core/provider/sources/zhipu_source.py +6 -73
- astrbot/core/star/__init__.py +43 -11
- astrbot/core/star/config.py +17 -18
- astrbot/core/star/context.py +362 -138
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +111 -35
- astrbot/core/star/filter/command_group.py +46 -34
- 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 +45 -12
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -15
- astrbot/core/star/register/star.py +41 -13
- astrbot/core/star/register/star_handler.py +236 -86
- astrbot/core/star/session_llm_manager.py +280 -0
- astrbot/core/star/session_plugin_manager.py +170 -0
- astrbot/core/star/star.py +36 -43
- astrbot/core/star/star_handler.py +47 -85
- astrbot/core/star/star_manager.py +442 -260
- astrbot/core/star/star_tools.py +167 -45
- astrbot/core/star/updator.py +17 -20
- astrbot/core/umop_config_router.py +106 -0
- astrbot/core/updator.py +38 -13
- astrbot/core/utils/astrbot_path.py +39 -0
- astrbot/core/utils/command_parser.py +1 -1
- astrbot/core/utils/io.py +119 -60
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +11 -10
- astrbot/core/utils/migra_helper.py +73 -0
- astrbot/core/utils/path_util.py +63 -62
- astrbot/core/utils/pip_installer.py +37 -15
- astrbot/core/utils/session_lock.py +29 -0
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +174 -34
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/local_strategy.py +386 -238
- astrbot/core/utils/t2i/network_strategy.py +109 -49
- astrbot/core/utils/t2i/renderer.py +29 -14
- astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
- astrbot/core/utils/t2i/template_manager.py +111 -0
- astrbot/core/utils/tencent_record_helper.py +115 -1
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +112 -65
- astrbot/dashboard/routes/__init__.py +20 -13
- astrbot/dashboard/routes/auth.py +20 -9
- astrbot/dashboard/routes/chat.py +297 -141
- astrbot/dashboard/routes/config.py +652 -55
- astrbot/dashboard/routes/conversation.py +107 -37
- astrbot/dashboard/routes/file.py +26 -0
- astrbot/dashboard/routes/knowledge_base.py +1244 -0
- astrbot/dashboard/routes/log.py +27 -2
- astrbot/dashboard/routes/persona.py +202 -0
- astrbot/dashboard/routes/plugin.py +197 -139
- astrbot/dashboard/routes/route.py +27 -7
- astrbot/dashboard/routes/session_management.py +354 -0
- astrbot/dashboard/routes/stat.py +85 -18
- astrbot/dashboard/routes/static_file.py +5 -2
- astrbot/dashboard/routes/t2i.py +233 -0
- astrbot/dashboard/routes/tools.py +184 -120
- astrbot/dashboard/routes/update.py +59 -36
- astrbot/dashboard/server.py +96 -36
- astrbot/dashboard/utils.py +165 -0
- astrbot-4.7.0.dist-info/METADATA +294 -0
- astrbot-4.7.0.dist-info/RECORD +274 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
- astrbot/core/db/plugin/sqlite_impl.py +0 -112
- astrbot/core/db/sqlite_init.sql +0 -50
- astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
- astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
- astrbot/core/platform/sources/gewechat/client.py +0 -806
- astrbot/core/platform/sources/gewechat/downloader.py +0 -55
- astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
- astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
- astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
- astrbot/core/provider/sources/dashscope_source.py +0 -203
- astrbot/core/provider/sources/dify_source.py +0 -281
- astrbot/core/provider/sources/llmtuner_source.py +0 -132
- astrbot/core/rag/embedding/openai_source.py +0 -20
- astrbot/core/rag/knowledge_db_mgr.py +0 -94
- astrbot/core/rag/store/__init__.py +0 -9
- astrbot/core/rag/store/chroma_db.py +0 -42
- astrbot/core/utils/dify_api_client.py +0 -152
- astrbot-3.5.6.dist-info/METADATA +0 -249
- astrbot-3.5.6.dist-info/RECORD +0 -158
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
astrbot/core/star/star_tools.py
CHANGED
|
@@ -1,38 +1,68 @@
|
|
|
1
|
+
"""插件开发工具集
|
|
2
|
+
封装了许多常用的操作,方便插件开发者使用
|
|
3
|
+
|
|
4
|
+
说明:
|
|
5
|
+
|
|
6
|
+
主动发送消息: send_message(session, message_chain)
|
|
7
|
+
根据 session (unified_msg_origin) 主动发送消息, 前提是需要提前获得或构造 session
|
|
8
|
+
|
|
9
|
+
根据id直接主动发送消息: send_message_by_id(type, id, message_chain, platform="aiocqhttp")
|
|
10
|
+
根据 id (例如 qq 号, 群号等) 直接, 主动地发送消息
|
|
11
|
+
|
|
12
|
+
以上两种方式需要构造消息链, 也就是消息组件的列表
|
|
13
|
+
|
|
14
|
+
构造事件:
|
|
15
|
+
|
|
16
|
+
首先需要构造一个 AstrBotMessage 对象, 使用 create_message 方法
|
|
17
|
+
然后使用 create_event 方法提交事件到指定平台
|
|
18
|
+
"""
|
|
19
|
+
|
|
1
20
|
import inspect
|
|
2
|
-
|
|
21
|
+
import os
|
|
22
|
+
import uuid
|
|
23
|
+
from collections.abc import Awaitable, Callable
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
from typing import Any, ClassVar
|
|
26
|
+
|
|
27
|
+
from astrbot.api.platform import AstrBotMessage, MessageMember, MessageType
|
|
3
28
|
from astrbot.core.message.components import BaseMessageComponent
|
|
4
29
|
from astrbot.core.message.message_event_result import MessageChain
|
|
5
|
-
from astrbot.api.platform import MessageMember, AstrBotMessage
|
|
6
30
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
31
|
+
from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import (
|
|
32
|
+
AiocqhttpMessageEvent,
|
|
33
|
+
)
|
|
34
|
+
from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_platform_adapter import (
|
|
35
|
+
AiocqhttpAdapter,
|
|
36
|
+
)
|
|
7
37
|
from astrbot.core.star.context import Context
|
|
8
38
|
from astrbot.core.star.star import star_map
|
|
9
|
-
from
|
|
39
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
10
40
|
|
|
11
41
|
|
|
12
42
|
class StarTools:
|
|
13
|
-
"""
|
|
14
|
-
提供给插件使用的便捷工具函数集合
|
|
43
|
+
"""提供给插件使用的便捷工具函数集合
|
|
15
44
|
这些方法封装了一些常用操作,使插件开发更加简单便捷!
|
|
16
45
|
"""
|
|
17
46
|
|
|
18
|
-
_context: ClassVar[
|
|
47
|
+
_context: ClassVar[Context | None] = None
|
|
19
48
|
|
|
20
49
|
@classmethod
|
|
21
50
|
def initialize(cls, context: Context) -> None:
|
|
22
|
-
"""
|
|
23
|
-
初始化StarTools,设置context引用
|
|
51
|
+
"""初始化StarTools,设置context引用
|
|
24
52
|
|
|
25
53
|
Args:
|
|
26
54
|
context: 暴露给插件的上下文
|
|
55
|
+
|
|
27
56
|
"""
|
|
28
57
|
cls._context = context
|
|
29
58
|
|
|
30
59
|
@classmethod
|
|
31
60
|
async def send_message(
|
|
32
|
-
cls,
|
|
61
|
+
cls,
|
|
62
|
+
session: str | MessageSesion,
|
|
63
|
+
message_chain: MessageChain,
|
|
33
64
|
) -> bool:
|
|
34
|
-
"""
|
|
35
|
-
根据session(unified_msg_origin)主动发送消息
|
|
65
|
+
"""根据session(unified_msg_origin)主动发送消息
|
|
36
66
|
|
|
37
67
|
Args:
|
|
38
68
|
session: 消息会话。通过event.session或者event.unified_msg_origin获取
|
|
@@ -46,43 +76,85 @@ class StarTools:
|
|
|
46
76
|
|
|
47
77
|
Note:
|
|
48
78
|
qq_official(QQ官方API平台)不支持此方法
|
|
79
|
+
|
|
49
80
|
"""
|
|
81
|
+
if cls._context is None:
|
|
82
|
+
raise ValueError("StarTools not initialized")
|
|
50
83
|
return await cls._context.send_message(session, message_chain)
|
|
51
84
|
|
|
85
|
+
@classmethod
|
|
86
|
+
async def send_message_by_id(
|
|
87
|
+
cls,
|
|
88
|
+
type: str,
|
|
89
|
+
id: str,
|
|
90
|
+
message_chain: MessageChain,
|
|
91
|
+
platform: str = "aiocqhttp",
|
|
92
|
+
):
|
|
93
|
+
"""根据 id(例如qq号, 群号等) 直接, 主动地发送消息
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
type (str): 消息类型, 可选: PrivateMessage, GroupMessage
|
|
97
|
+
id (str): 目标ID, 例如QQ号, 群号等
|
|
98
|
+
message_chain (MessageChain): 消息链
|
|
99
|
+
platform (str): 可选的平台名称,默认平台(aiocqhttp), 目前只支持 aiocqhttp
|
|
100
|
+
|
|
101
|
+
"""
|
|
102
|
+
if cls._context is None:
|
|
103
|
+
raise ValueError("StarTools not initialized")
|
|
104
|
+
platforms = cls._context.platform_manager.get_insts()
|
|
105
|
+
if platform == "aiocqhttp":
|
|
106
|
+
adapter = next(
|
|
107
|
+
(p for p in platforms if isinstance(p, AiocqhttpAdapter)),
|
|
108
|
+
None,
|
|
109
|
+
)
|
|
110
|
+
if adapter is None:
|
|
111
|
+
raise ValueError("未找到适配器: AiocqhttpAdapter")
|
|
112
|
+
await AiocqhttpMessageEvent.send_message(
|
|
113
|
+
bot=adapter.bot,
|
|
114
|
+
message_chain=message_chain,
|
|
115
|
+
is_group=(type == "GroupMessage"),
|
|
116
|
+
session_id=id,
|
|
117
|
+
)
|
|
118
|
+
else:
|
|
119
|
+
raise ValueError(f"不支持的平台: {platform}")
|
|
120
|
+
|
|
52
121
|
@classmethod
|
|
53
122
|
async def create_message(
|
|
54
123
|
cls,
|
|
55
124
|
type: str,
|
|
56
125
|
self_id: str,
|
|
57
126
|
session_id: str,
|
|
58
|
-
message_id: str,
|
|
59
127
|
sender: MessageMember,
|
|
60
|
-
message:
|
|
128
|
+
message: list[BaseMessageComponent],
|
|
61
129
|
message_str: str,
|
|
62
|
-
|
|
130
|
+
message_id: str = "",
|
|
131
|
+
raw_message: object = None,
|
|
63
132
|
group_id: str = "",
|
|
64
|
-
):
|
|
65
|
-
"""
|
|
66
|
-
创建一个AstrBot消息对象
|
|
133
|
+
) -> AstrBotMessage:
|
|
134
|
+
"""创建一个AstrBot消息对象
|
|
67
135
|
|
|
68
136
|
Args:
|
|
69
|
-
type (str):
|
|
137
|
+
type (str): 消息类型, 例如 "GroupMessage" "FriendMessage" "OtherMessage"
|
|
70
138
|
self_id (str): 机器人自身ID
|
|
71
139
|
session_id (str): 会话ID(通常为用户ID)(QQ号, 群号等)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
140
|
+
sender (MessageMember): 发送者信息, 例如 MessageMember(user_id="123456", nickname="昵称")
|
|
141
|
+
message (List[BaseMessageComponent]): 消息组件列表, 也就是消息链, 这个不会发给 llm, 但是会经过其他处理
|
|
142
|
+
message_str (str): 消息字符串, 也就是纯文本消息, 也就是发送给 llm 的消息, 与消息链一致
|
|
143
|
+
|
|
144
|
+
message_id (str): 消息ID, 构造消息时可以随意填写也可不填
|
|
145
|
+
raw_message (object): 原始消息对象, 可以随意填写也可不填
|
|
77
146
|
group_id (str, optional): 群组ID, 如果为私聊则为空. Defaults to "".
|
|
78
147
|
|
|
79
148
|
Returns:
|
|
80
149
|
AstrBotMessage: 创建的消息对象
|
|
150
|
+
|
|
81
151
|
"""
|
|
82
152
|
abm = AstrBotMessage()
|
|
83
|
-
abm.type = type
|
|
153
|
+
abm.type = MessageType(type)
|
|
84
154
|
abm.self_id = self_id
|
|
85
155
|
abm.session_id = session_id
|
|
156
|
+
if message_id == "":
|
|
157
|
+
message_id = uuid.uuid4().hex
|
|
86
158
|
abm.message_id = message_id
|
|
87
159
|
abm.sender = sender
|
|
88
160
|
abm.message = message
|
|
@@ -91,65 +163,106 @@ class StarTools:
|
|
|
91
163
|
abm.group_id = group_id
|
|
92
164
|
return abm
|
|
93
165
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
166
|
+
@classmethod
|
|
167
|
+
async def create_event(
|
|
168
|
+
cls,
|
|
169
|
+
abm: AstrBotMessage,
|
|
170
|
+
platform: str = "aiocqhttp",
|
|
171
|
+
is_wake: bool = True,
|
|
172
|
+
) -> None:
|
|
173
|
+
"""创建并提交事件到指定平台
|
|
174
|
+
当有需要创建一个事件, 触发某些处理流程时, 使用该方法
|
|
175
|
+
|
|
176
|
+
Args:
|
|
177
|
+
abm (AstrBotMessage): 要提交的消息对象, 请先使用 create_message 创建
|
|
178
|
+
platform (str): 可选的平台名称,默认平台(aiocqhttp), 目前只支持 aiocqhttp
|
|
179
|
+
is_wake (bool): 是否标记为唤醒事件, 默认为 True, 只有唤醒事件才会被 llm 响应
|
|
99
180
|
|
|
100
|
-
|
|
181
|
+
"""
|
|
182
|
+
if cls._context is None:
|
|
183
|
+
raise ValueError("StarTools not initialized")
|
|
184
|
+
platforms = cls._context.platform_manager.get_insts()
|
|
185
|
+
if platform == "aiocqhttp":
|
|
186
|
+
adapter = next(
|
|
187
|
+
(p for p in platforms if isinstance(p, AiocqhttpAdapter)),
|
|
188
|
+
None,
|
|
189
|
+
)
|
|
190
|
+
if adapter is None:
|
|
191
|
+
raise ValueError("未找到适配器: AiocqhttpAdapter")
|
|
192
|
+
event = AiocqhttpMessageEvent(
|
|
193
|
+
message_str=abm.message_str,
|
|
194
|
+
message_obj=abm,
|
|
195
|
+
platform_meta=adapter.metadata,
|
|
196
|
+
session_id=abm.session_id,
|
|
197
|
+
bot=adapter.bot,
|
|
198
|
+
)
|
|
199
|
+
event.is_wake = is_wake
|
|
200
|
+
adapter.commit_event(event)
|
|
201
|
+
else:
|
|
202
|
+
raise ValueError(f"不支持的平台: {platform}")
|
|
101
203
|
|
|
102
204
|
@classmethod
|
|
103
205
|
def activate_llm_tool(cls, name: str) -> bool:
|
|
104
|
-
"""
|
|
105
|
-
激活一个已经注册的函数调用工具
|
|
206
|
+
"""激活一个已经注册的函数调用工具
|
|
106
207
|
注册的工具默认是激活状态
|
|
107
208
|
|
|
108
209
|
Args:
|
|
109
210
|
name (str): 工具名称
|
|
211
|
+
|
|
110
212
|
"""
|
|
213
|
+
if cls._context is None:
|
|
214
|
+
raise ValueError("StarTools not initialized")
|
|
111
215
|
return cls._context.activate_llm_tool(name)
|
|
112
216
|
|
|
113
217
|
@classmethod
|
|
114
218
|
def deactivate_llm_tool(cls, name: str) -> bool:
|
|
115
|
-
"""
|
|
116
|
-
停用一个已经注册的函数调用工具
|
|
219
|
+
"""停用一个已经注册的函数调用工具
|
|
117
220
|
|
|
118
221
|
Args:
|
|
119
222
|
name (str): 工具名称
|
|
223
|
+
|
|
120
224
|
"""
|
|
225
|
+
if cls._context is None:
|
|
226
|
+
raise ValueError("StarTools not initialized")
|
|
121
227
|
return cls._context.deactivate_llm_tool(name)
|
|
122
228
|
|
|
123
229
|
@classmethod
|
|
124
230
|
def register_llm_tool(
|
|
125
|
-
cls,
|
|
231
|
+
cls,
|
|
232
|
+
name: str,
|
|
233
|
+
func_args: list,
|
|
234
|
+
desc: str,
|
|
235
|
+
func_obj: Callable[..., Awaitable[Any]],
|
|
126
236
|
) -> None:
|
|
127
|
-
"""
|
|
128
|
-
为函数调用(function-calling/tools-use)添加工具
|
|
237
|
+
"""为函数调用(function-calling/tools-use)添加工具
|
|
129
238
|
|
|
130
239
|
Args:
|
|
131
240
|
name (str): 工具名称
|
|
132
241
|
func_args (list): 函数参数列表
|
|
133
242
|
desc (str): 工具描述
|
|
134
243
|
func_obj (Awaitable): 函数对象,必须是异步函数
|
|
244
|
+
|
|
135
245
|
"""
|
|
246
|
+
if cls._context is None:
|
|
247
|
+
raise ValueError("StarTools not initialized")
|
|
136
248
|
cls._context.register_llm_tool(name, func_args, desc, func_obj)
|
|
137
249
|
|
|
138
250
|
@classmethod
|
|
139
251
|
def unregister_llm_tool(cls, name: str) -> None:
|
|
140
|
-
"""
|
|
141
|
-
删除一个函数调用工具
|
|
252
|
+
"""删除一个函数调用工具
|
|
142
253
|
如果再要启用,需要重新注册
|
|
143
254
|
|
|
144
255
|
Args:
|
|
145
256
|
name (str): 工具名称
|
|
257
|
+
|
|
146
258
|
"""
|
|
259
|
+
if cls._context is None:
|
|
260
|
+
raise ValueError("StarTools not initialized")
|
|
147
261
|
cls._context.unregister_llm_tool(name)
|
|
148
262
|
|
|
149
263
|
@classmethod
|
|
150
|
-
def get_data_dir(cls, plugin_name:
|
|
151
|
-
"""
|
|
152
|
-
返回插件数据目录的绝对路径。
|
|
264
|
+
def get_data_dir(cls, plugin_name: str | None = None) -> Path:
|
|
265
|
+
"""返回插件数据目录的绝对路径。
|
|
153
266
|
|
|
154
267
|
此方法会在 data/plugin_data 目录下为插件创建一个专属的数据目录。如果未提供插件名称,
|
|
155
268
|
会自动从调用栈中获取插件信息。
|
|
@@ -165,10 +278,14 @@ class StarTools:
|
|
|
165
278
|
- 无法获取调用者模块信息
|
|
166
279
|
- 无法获取模块的元数据信息
|
|
167
280
|
- 创建目录失败(权限不足或其他IO错误)
|
|
281
|
+
|
|
168
282
|
"""
|
|
169
283
|
if not plugin_name:
|
|
170
|
-
frame = inspect.currentframe()
|
|
171
|
-
module =
|
|
284
|
+
frame = inspect.currentframe()
|
|
285
|
+
module = None
|
|
286
|
+
if frame:
|
|
287
|
+
frame = frame.f_back
|
|
288
|
+
module = inspect.getmodule(frame)
|
|
172
289
|
|
|
173
290
|
if not module:
|
|
174
291
|
raise RuntimeError("无法获取调用者模块信息")
|
|
@@ -180,7 +297,12 @@ class StarTools:
|
|
|
180
297
|
|
|
181
298
|
plugin_name = metadata.name
|
|
182
299
|
|
|
183
|
-
|
|
300
|
+
if not plugin_name:
|
|
301
|
+
raise ValueError("无法获取插件名称")
|
|
302
|
+
|
|
303
|
+
data_dir = Path(
|
|
304
|
+
os.path.join(get_astrbot_data_path(), "plugin_data", plugin_name),
|
|
305
|
+
)
|
|
184
306
|
|
|
185
307
|
try:
|
|
186
308
|
data_dir.mkdir(parents=True, exist_ok=True)
|
astrbot/core/star/updator.py
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import zipfile
|
|
3
2
|
import shutil
|
|
3
|
+
import zipfile
|
|
4
4
|
|
|
5
|
-
from ..updator import RepoZipUpdator
|
|
6
|
-
from astrbot.core.utils.io import remove_dir, on_error
|
|
7
|
-
from ..star.star import StarMetadata
|
|
8
5
|
from astrbot.core import logger
|
|
6
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_plugin_path
|
|
7
|
+
from astrbot.core.utils.io import on_error, remove_dir
|
|
8
|
+
|
|
9
|
+
from ..star.star import StarMetadata
|
|
10
|
+
from ..updator import RepoZipUpdator
|
|
9
11
|
|
|
10
12
|
|
|
11
13
|
class PluginUpdator(RepoZipUpdator):
|
|
12
14
|
def __init__(self, repo_mirror: str = "") -> None:
|
|
13
15
|
super().__init__(repo_mirror)
|
|
14
|
-
self.plugin_store_path =
|
|
15
|
-
os.path.join(
|
|
16
|
-
os.path.dirname(os.path.abspath(__file__)), "../../../data/plugins"
|
|
17
|
-
)
|
|
18
|
-
)
|
|
16
|
+
self.plugin_store_path = get_astrbot_plugin_path()
|
|
19
17
|
|
|
20
18
|
def get_plugin_store_path(self) -> str:
|
|
21
19
|
return self.plugin_store_path
|
|
22
20
|
|
|
23
21
|
async def install(self, repo_url: str, proxy="") -> str:
|
|
24
|
-
repo_name = self.
|
|
22
|
+
_, repo_name, _ = self.parse_github_url(repo_url)
|
|
23
|
+
repo_name = self.format_name(repo_name)
|
|
25
24
|
plugin_path = os.path.join(self.plugin_store_path, repo_name)
|
|
26
25
|
await self.download_from_repo_url(plugin_path, repo_url, proxy)
|
|
27
26
|
self.unzip_file(plugin_path + ".zip", plugin_path)
|
|
@@ -34,9 +33,8 @@ class PluginUpdator(RepoZipUpdator):
|
|
|
34
33
|
if not repo_url:
|
|
35
34
|
raise Exception(f"插件 {plugin.name} 没有指定仓库地址。")
|
|
36
35
|
|
|
37
|
-
if
|
|
38
|
-
|
|
39
|
-
repo_url = f"{proxy}/{repo_url}"
|
|
36
|
+
if not plugin.root_dir_name:
|
|
37
|
+
raise Exception(f"插件 {plugin.name} 的根目录名未指定。")
|
|
40
38
|
|
|
41
39
|
plugin_path = os.path.join(self.plugin_store_path, plugin.root_dir_name)
|
|
42
40
|
|
|
@@ -47,7 +45,7 @@ class PluginUpdator(RepoZipUpdator):
|
|
|
47
45
|
remove_dir(plugin_path)
|
|
48
46
|
except BaseException as e:
|
|
49
47
|
logger.error(
|
|
50
|
-
f"删除旧版本插件 {plugin_path} 文件夹失败: {
|
|
48
|
+
f"删除旧版本插件 {plugin_path} 文件夹失败: {e!s},使用覆盖安装。",
|
|
51
49
|
)
|
|
52
50
|
|
|
53
51
|
self.unzip_file(plugin_path + ".zip", plugin_path)
|
|
@@ -57,7 +55,7 @@ class PluginUpdator(RepoZipUpdator):
|
|
|
57
55
|
def unzip_file(self, zip_path: str, target_dir: str):
|
|
58
56
|
os.makedirs(target_dir, exist_ok=True)
|
|
59
57
|
update_dir = ""
|
|
60
|
-
logger.info(f"
|
|
58
|
+
logger.info(f"正在解压压缩包: {zip_path}")
|
|
61
59
|
with zipfile.ZipFile(zip_path, "r") as z:
|
|
62
60
|
update_dir = z.namelist()[0]
|
|
63
61
|
z.extractall(target_dir)
|
|
@@ -67,18 +65,17 @@ class PluginUpdator(RepoZipUpdator):
|
|
|
67
65
|
if os.path.isdir(os.path.join(target_dir, update_dir, f)):
|
|
68
66
|
if os.path.exists(os.path.join(target_dir, f)):
|
|
69
67
|
shutil.rmtree(os.path.join(target_dir, f), onerror=on_error)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
os.remove(os.path.join(target_dir, f))
|
|
68
|
+
elif os.path.exists(os.path.join(target_dir, f)):
|
|
69
|
+
os.remove(os.path.join(target_dir, f))
|
|
73
70
|
shutil.move(os.path.join(target_dir, update_dir, f), target_dir)
|
|
74
71
|
|
|
75
72
|
try:
|
|
76
73
|
logger.info(
|
|
77
|
-
f"删除临时文件: {zip_path} 和 {os.path.join(target_dir, update_dir)}"
|
|
74
|
+
f"删除临时文件: {zip_path} 和 {os.path.join(target_dir, update_dir)}",
|
|
78
75
|
)
|
|
79
76
|
shutil.rmtree(os.path.join(target_dir, update_dir), onerror=on_error)
|
|
80
77
|
os.remove(zip_path)
|
|
81
78
|
except BaseException:
|
|
82
79
|
logger.warning(
|
|
83
|
-
f"删除更新文件失败,可以手动删除 {zip_path} 和 {os.path.join(target_dir, update_dir)}"
|
|
80
|
+
f"删除更新文件失败,可以手动删除 {zip_path} 和 {os.path.join(target_dir, update_dir)}",
|
|
84
81
|
)
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
from astrbot.core.utils.shared_preferences import SharedPreferences
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class UmopConfigRouter:
|
|
5
|
+
"""UMOP 配置路由器"""
|
|
6
|
+
|
|
7
|
+
def __init__(self, sp: SharedPreferences):
|
|
8
|
+
self.umop_to_conf_id: dict[str, str] = {}
|
|
9
|
+
"""UMOP 到配置文件 ID 的映射"""
|
|
10
|
+
self.sp = sp
|
|
11
|
+
|
|
12
|
+
self._load_routing_table()
|
|
13
|
+
|
|
14
|
+
def _load_routing_table(self):
|
|
15
|
+
"""加载路由表"""
|
|
16
|
+
# 从 SharedPreferences 中加载 umop_to_conf_id 映射
|
|
17
|
+
sp_data = self.sp.get(
|
|
18
|
+
"umop_config_routing",
|
|
19
|
+
{},
|
|
20
|
+
scope="global",
|
|
21
|
+
scope_id="global",
|
|
22
|
+
)
|
|
23
|
+
self.umop_to_conf_id = sp_data
|
|
24
|
+
|
|
25
|
+
def _is_umo_match(self, p1: str, p2: str) -> bool:
|
|
26
|
+
"""判断 p2 umo 是否逻辑包含于 p1 umo"""
|
|
27
|
+
p1_ls = p1.split(":")
|
|
28
|
+
p2_ls = p2.split(":")
|
|
29
|
+
|
|
30
|
+
if len(p1_ls) != 3 or len(p2_ls) != 3:
|
|
31
|
+
return False # 非法格式
|
|
32
|
+
|
|
33
|
+
return all(p == "" or p == "*" or p == t for p, t in zip(p1_ls, p2_ls))
|
|
34
|
+
|
|
35
|
+
def get_conf_id_for_umop(self, umo: str) -> str | None:
|
|
36
|
+
"""根据 UMO 获取对应的配置文件 ID
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
umo (str): UMO 字符串
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
str | None: 配置文件 ID,如果没有找到则返回 None
|
|
43
|
+
|
|
44
|
+
"""
|
|
45
|
+
for pattern, conf_id in self.umop_to_conf_id.items():
|
|
46
|
+
if self._is_umo_match(pattern, umo):
|
|
47
|
+
return conf_id
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
async def update_routing_data(self, new_routing: dict[str, str]):
|
|
51
|
+
"""更新路由表
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
new_routing (dict[str, str]): 新的 UMOP 到配置文件 ID 的映射。umo 由三个部分组成 [platform_id]:[message_type]:[session_id]。
|
|
55
|
+
umop 可以是 "::" (代表所有), 可以是 "[platform_id]::" (代表指定平台下的所有类型消息和会话)。
|
|
56
|
+
|
|
57
|
+
Raises:
|
|
58
|
+
ValueError: 如果 new_routing 中的 key 格式不正确
|
|
59
|
+
|
|
60
|
+
"""
|
|
61
|
+
for part in new_routing:
|
|
62
|
+
if not isinstance(part, str) or len(part.split(":")) != 3:
|
|
63
|
+
raise ValueError(
|
|
64
|
+
"umop keys must be strings in the format [platform_id]:[message_type]:[session_id], with optional wildcards * or empty for all",
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
self.umop_to_conf_id = new_routing
|
|
68
|
+
await self.sp.global_put("umop_config_routing", self.umop_to_conf_id)
|
|
69
|
+
|
|
70
|
+
async def update_route(self, umo: str, conf_id: str):
|
|
71
|
+
"""更新一条路由
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
umo (str): UMO 字符串
|
|
75
|
+
conf_id (str): 配置文件 ID
|
|
76
|
+
|
|
77
|
+
Raises:
|
|
78
|
+
ValueError: 如果 umo 格式不正确
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
if not isinstance(umo, str) or len(umo.split(":")) != 3:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
"umop must be a string in the format [platform_id]:[message_type]:[session_id], with optional wildcards * or empty for all",
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
self.umop_to_conf_id[umo] = conf_id
|
|
87
|
+
await self.sp.global_put("umop_config_routing", self.umop_to_conf_id)
|
|
88
|
+
|
|
89
|
+
async def delete_route(self, umo: str):
|
|
90
|
+
"""删除一条路由
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
umo (str): 需要删除的 UMO 字符串
|
|
94
|
+
|
|
95
|
+
Raises:
|
|
96
|
+
ValueError: 当 umo 格式不正确时抛出
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
if not isinstance(umo, str) or len(umo.split(":")) != 3:
|
|
100
|
+
raise ValueError(
|
|
101
|
+
"umop must be a string in the format [platform_id]:[message_type]:[session_id], with optional wildcards * or empty for all",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
if umo in self.umop_to_conf_id:
|
|
105
|
+
del self.umop_to_conf_id[umo]
|
|
106
|
+
await self.sp.global_put("umop_config_routing", self.umop_to_conf_id)
|
astrbot/core/updator.py
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import psutil
|
|
3
2
|
import sys
|
|
4
3
|
import time
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
import psutil
|
|
6
|
+
|
|
6
7
|
from astrbot.core import logger
|
|
7
8
|
from astrbot.core.config.default import VERSION
|
|
9
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_path
|
|
8
10
|
from astrbot.core.utils.io import download_file
|
|
9
11
|
|
|
12
|
+
from .zip_updator import ReleaseInfo, RepoZipUpdator
|
|
13
|
+
|
|
10
14
|
|
|
11
15
|
class AstrBotUpdator(RepoZipUpdator):
|
|
12
16
|
"""AstrBot 更新器,继承自 RepoZipUpdator 类
|
|
@@ -16,9 +20,7 @@ class AstrBotUpdator(RepoZipUpdator):
|
|
|
16
20
|
|
|
17
21
|
def __init__(self, repo_mirror: str = "") -> None:
|
|
18
22
|
super().__init__(repo_mirror)
|
|
19
|
-
self.MAIN_PATH =
|
|
20
|
-
os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../")
|
|
21
|
-
)
|
|
23
|
+
self.MAIN_PATH = get_astrbot_path()
|
|
22
24
|
self.ASTRBOT_RELEASE_API = "https://api.soulter.top/releases"
|
|
23
25
|
|
|
24
26
|
def terminate_child_processes(self):
|
|
@@ -45,20 +47,40 @@ class AstrBotUpdator(RepoZipUpdator):
|
|
|
45
47
|
def _reboot(self, delay: int = 3):
|
|
46
48
|
"""重启当前程序
|
|
47
49
|
在指定的延迟后,终止所有子进程并重新启动程序
|
|
50
|
+
这里只能使用 os.exec* 来重启程序
|
|
48
51
|
"""
|
|
49
|
-
py = sys.executable
|
|
50
52
|
time.sleep(delay)
|
|
51
53
|
self.terminate_child_processes()
|
|
52
|
-
|
|
54
|
+
if os.name == "nt":
|
|
55
|
+
py = f'"{sys.executable}"'
|
|
56
|
+
else:
|
|
57
|
+
py = sys.executable
|
|
58
|
+
|
|
53
59
|
try:
|
|
54
|
-
os.
|
|
60
|
+
if "astrbot" in os.path.basename(sys.argv[0]): # 兼容cli
|
|
61
|
+
if os.name == "nt":
|
|
62
|
+
args = [f'"{arg}"' if " " in arg else arg for arg in sys.argv[1:]]
|
|
63
|
+
else:
|
|
64
|
+
args = sys.argv[1:]
|
|
65
|
+
os.execl(sys.executable, py, "-m", "astrbot.cli.__main__", *args)
|
|
66
|
+
else:
|
|
67
|
+
os.execl(sys.executable, py, *sys.argv)
|
|
55
68
|
except Exception as e:
|
|
56
69
|
logger.error(f"重启失败({py}, {e}),请尝试手动重启。")
|
|
57
70
|
raise e
|
|
58
71
|
|
|
59
|
-
async def check_update(
|
|
72
|
+
async def check_update(
|
|
73
|
+
self,
|
|
74
|
+
url: str,
|
|
75
|
+
current_version: str,
|
|
76
|
+
consider_prerelease: bool = True,
|
|
77
|
+
) -> ReleaseInfo:
|
|
60
78
|
"""检查更新"""
|
|
61
|
-
return await super().check_update(
|
|
79
|
+
return await super().check_update(
|
|
80
|
+
self.ASTRBOT_RELEASE_API,
|
|
81
|
+
VERSION,
|
|
82
|
+
consider_prerelease,
|
|
83
|
+
)
|
|
62
84
|
|
|
63
85
|
async def get_releases(self) -> list:
|
|
64
86
|
return await self.fetch_release_info(self.ASTRBOT_RELEASE_API)
|
|
@@ -67,6 +89,9 @@ class AstrBotUpdator(RepoZipUpdator):
|
|
|
67
89
|
update_data = await self.fetch_release_info(self.ASTRBOT_RELEASE_API, latest)
|
|
68
90
|
file_url = None
|
|
69
91
|
|
|
92
|
+
if os.environ.get("ASTRBOT_CLI"):
|
|
93
|
+
raise Exception("不支持更新CLI启动的AstrBot") # 避免版本管理混乱
|
|
94
|
+
|
|
70
95
|
if latest:
|
|
71
96
|
latest_version = update_data[0]["tag_name"]
|
|
72
97
|
if self.compare_version(VERSION, latest_version) >= 0:
|
|
@@ -74,7 +99,6 @@ class AstrBotUpdator(RepoZipUpdator):
|
|
|
74
99
|
file_url = update_data[0]["zipball_url"]
|
|
75
100
|
elif str(version).startswith("v"):
|
|
76
101
|
# 更新到指定版本
|
|
77
|
-
logger.info(f"正在更新到指定版本: {version}")
|
|
78
102
|
for data in update_data:
|
|
79
103
|
if data["tag_name"] == version:
|
|
80
104
|
file_url = data["zipball_url"]
|
|
@@ -83,8 +107,8 @@ class AstrBotUpdator(RepoZipUpdator):
|
|
|
83
107
|
else:
|
|
84
108
|
if len(str(version)) != 40:
|
|
85
109
|
raise Exception("commit hash 长度不正确,应为 40")
|
|
86
|
-
|
|
87
|
-
|
|
110
|
+
file_url = f"https://github.com/AstrBotDevs/AstrBot/archive/{version}.zip"
|
|
111
|
+
logger.info(f"准备更新至指定版本的 AstrBot Core: {version}")
|
|
88
112
|
|
|
89
113
|
if proxy:
|
|
90
114
|
proxy = proxy.removesuffix("/")
|
|
@@ -92,6 +116,7 @@ class AstrBotUpdator(RepoZipUpdator):
|
|
|
92
116
|
|
|
93
117
|
try:
|
|
94
118
|
await download_file(file_url, "temp.zip")
|
|
119
|
+
logger.info("下载 AstrBot Core 更新文件完成,正在执行解压...")
|
|
95
120
|
self.unzip_file("temp.zip", self.MAIN_PATH)
|
|
96
121
|
except BaseException as e:
|
|
97
122
|
raise e
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""Astrbot统一路径获取
|
|
2
|
+
|
|
3
|
+
项目路径:固定为源码所在路径
|
|
4
|
+
根目录路径:默认为当前工作目录,可通过环境变量 ASTRBOT_ROOT 指定
|
|
5
|
+
数据目录路径:固定为根目录下的 data 目录
|
|
6
|
+
配置文件路径:固定为数据目录下的 config 目录
|
|
7
|
+
插件目录路径:固定为数据目录下的 plugins 目录
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def get_astrbot_path() -> str:
|
|
14
|
+
"""获取Astrbot项目路径"""
|
|
15
|
+
return os.path.realpath(
|
|
16
|
+
os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../../"),
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_astrbot_root() -> str:
|
|
21
|
+
"""获取Astrbot根目录路径"""
|
|
22
|
+
if path := os.environ.get("ASTRBOT_ROOT"):
|
|
23
|
+
return os.path.realpath(path)
|
|
24
|
+
return os.path.realpath(os.getcwd())
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def get_astrbot_data_path() -> str:
|
|
28
|
+
"""获取Astrbot数据目录路径"""
|
|
29
|
+
return os.path.realpath(os.path.join(get_astrbot_root(), "data"))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def get_astrbot_config_path() -> str:
|
|
33
|
+
"""获取Astrbot配置文件路径"""
|
|
34
|
+
return os.path.realpath(os.path.join(get_astrbot_data_path(), "config"))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def get_astrbot_plugin_path() -> str:
|
|
38
|
+
"""获取Astrbot插件目录路径"""
|
|
39
|
+
return os.path.realpath(os.path.join(get_astrbot_data_path(), "plugins"))
|