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
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import
|
|
2
|
-
import aiohttp
|
|
1
|
+
import json
|
|
3
2
|
import os
|
|
4
|
-
|
|
5
3
|
import ssl
|
|
6
|
-
import
|
|
4
|
+
import traceback
|
|
5
|
+
from datetime import datetime
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
import aiohttp
|
|
8
|
+
import certifi
|
|
10
9
|
from quart import request
|
|
11
|
-
|
|
10
|
+
|
|
11
|
+
from astrbot.core import DEMO_MODE, file_token_service, logger
|
|
12
12
|
from astrbot.core.core_lifecycle import AstrBotCoreLifecycle
|
|
13
|
-
from astrbot.core.star.star_handler import star_handlers_registry
|
|
14
13
|
from astrbot.core.star.filter.command import CommandFilter
|
|
15
14
|
from astrbot.core.star.filter.command_group import CommandGroupFilter
|
|
16
15
|
from astrbot.core.star.filter.permission import PermissionTypeFilter
|
|
17
16
|
from astrbot.core.star.filter.regex import RegexFilter
|
|
18
|
-
from astrbot.core.star.star_handler import EventType
|
|
19
|
-
from astrbot.core import
|
|
17
|
+
from astrbot.core.star.star_handler import EventType, star_handlers_registry
|
|
18
|
+
from astrbot.core.star.star_manager import PluginManager
|
|
19
|
+
|
|
20
|
+
from .route import Response, Route, RouteContext
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class PluginRoute(Route):
|
|
@@ -38,8 +39,6 @@ class PluginRoute(Route):
|
|
|
38
39
|
"/plugin/on": ("POST", self.on_plugin),
|
|
39
40
|
"/plugin/reload": ("POST", self.reload_plugins),
|
|
40
41
|
"/plugin/readme": ("GET", self.get_plugin_readme),
|
|
41
|
-
"/plugin/platform_enable/get": ("GET", self.get_plugin_platform_enable),
|
|
42
|
-
"/plugin/platform_enable/set": ("POST", self.set_plugin_platform_enable),
|
|
43
42
|
}
|
|
44
43
|
self.core_lifecycle = core_lifecycle
|
|
45
44
|
self.plugin_manager = plugin_manager
|
|
@@ -54,6 +53,8 @@ class PluginRoute(Route):
|
|
|
54
53
|
EventType.OnAfterMessageSentEvent: "发送消息后",
|
|
55
54
|
}
|
|
56
55
|
|
|
56
|
+
self._logo_cache = {}
|
|
57
|
+
|
|
57
58
|
async def reload_plugins(self):
|
|
58
59
|
if DEMO_MODE:
|
|
59
60
|
return (
|
|
@@ -75,34 +76,185 @@ class PluginRoute(Route):
|
|
|
75
76
|
|
|
76
77
|
async def get_online_plugins(self):
|
|
77
78
|
custom = request.args.get("custom_registry")
|
|
79
|
+
force_refresh = request.args.get("force_refresh", "false").lower() == "true"
|
|
80
|
+
|
|
81
|
+
cache_file = "data/plugins.json"
|
|
78
82
|
|
|
79
83
|
if custom:
|
|
80
84
|
urls = [custom]
|
|
81
85
|
else:
|
|
82
|
-
urls = [
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
urls = [
|
|
87
|
+
"https://api.soulter.top/astrbot/plugins",
|
|
88
|
+
"https://github.com/AstrBotDevs/AstrBot_Plugins_Collection/raw/refs/heads/main/plugin_cache_original.json",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
# 如果不是强制刷新,先检查缓存是否有效
|
|
92
|
+
cached_data = None
|
|
93
|
+
if not force_refresh:
|
|
94
|
+
# 先检查MD5是否匹配,如果匹配则使用缓存
|
|
95
|
+
if await self._is_cache_valid(cache_file):
|
|
96
|
+
cached_data = self._load_plugin_cache(cache_file)
|
|
97
|
+
if cached_data:
|
|
98
|
+
logger.debug("缓存MD5匹配,使用缓存的插件市场数据")
|
|
99
|
+
return Response().ok(cached_data).__dict__
|
|
100
|
+
|
|
101
|
+
# 尝试获取远程数据
|
|
102
|
+
remote_data = None
|
|
85
103
|
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
|
86
104
|
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
105
|
+
|
|
87
106
|
for url in urls:
|
|
88
107
|
try:
|
|
89
|
-
async with
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
108
|
+
async with (
|
|
109
|
+
aiohttp.ClientSession(
|
|
110
|
+
trust_env=True,
|
|
111
|
+
connector=connector,
|
|
112
|
+
) as session,
|
|
113
|
+
session.get(url) as response,
|
|
114
|
+
):
|
|
115
|
+
if response.status == 200:
|
|
116
|
+
remote_data = await response.json()
|
|
117
|
+
|
|
118
|
+
# 检查远程数据是否为空
|
|
119
|
+
if not remote_data or (
|
|
120
|
+
isinstance(remote_data, dict) and len(remote_data) == 0
|
|
121
|
+
):
|
|
122
|
+
logger.warning(f"远程插件市场数据为空: {url}")
|
|
123
|
+
continue # 继续尝试其他URL或使用缓存
|
|
124
|
+
|
|
125
|
+
logger.info("成功获取远程插件市场数据")
|
|
126
|
+
# 获取最新的MD5并保存到缓存
|
|
127
|
+
current_md5 = await self._get_remote_md5()
|
|
128
|
+
self._save_plugin_cache(
|
|
129
|
+
cache_file,
|
|
130
|
+
remote_data,
|
|
131
|
+
current_md5,
|
|
132
|
+
)
|
|
133
|
+
return Response().ok(remote_data).__dict__
|
|
134
|
+
logger.error(f"请求 {url} 失败,状态码:{response.status}")
|
|
98
135
|
except Exception as e:
|
|
99
136
|
logger.error(f"请求 {url} 失败,错误:{e}")
|
|
100
137
|
|
|
101
|
-
|
|
138
|
+
# 如果远程获取失败,尝试使用缓存数据
|
|
139
|
+
if not cached_data:
|
|
140
|
+
cached_data = self._load_plugin_cache(cache_file)
|
|
141
|
+
|
|
142
|
+
if cached_data:
|
|
143
|
+
logger.warning("远程插件市场数据获取失败,使用缓存数据")
|
|
144
|
+
return Response().ok(cached_data, "使用缓存数据,可能不是最新版本").__dict__
|
|
145
|
+
|
|
146
|
+
return Response().error("获取插件列表失败,且没有可用的缓存数据").__dict__
|
|
147
|
+
|
|
148
|
+
async def _is_cache_valid(self, cache_file: str) -> bool:
|
|
149
|
+
"""检查缓存是否有效(基于MD5)"""
|
|
150
|
+
try:
|
|
151
|
+
if not os.path.exists(cache_file):
|
|
152
|
+
return False
|
|
153
|
+
|
|
154
|
+
# 加载缓存文件
|
|
155
|
+
with open(cache_file, encoding="utf-8") as f:
|
|
156
|
+
cache_data = json.load(f)
|
|
157
|
+
|
|
158
|
+
cached_md5 = cache_data.get("md5")
|
|
159
|
+
if not cached_md5:
|
|
160
|
+
logger.debug("缓存文件中没有MD5信息")
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
# 获取远程MD5
|
|
164
|
+
remote_md5 = await self._get_remote_md5()
|
|
165
|
+
if not remote_md5:
|
|
166
|
+
logger.warning("无法获取远程MD5,将使用缓存")
|
|
167
|
+
return True # 如果无法获取远程MD5,认为缓存有效
|
|
168
|
+
|
|
169
|
+
is_valid = cached_md5 == remote_md5
|
|
170
|
+
logger.debug(
|
|
171
|
+
f"插件数据MD5: 本地={cached_md5}, 远程={remote_md5}, 有效={is_valid}",
|
|
172
|
+
)
|
|
173
|
+
return is_valid
|
|
174
|
+
|
|
175
|
+
except Exception as e:
|
|
176
|
+
logger.warning(f"检查缓存有效性失败: {e}")
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
async def _get_remote_md5(self) -> str:
|
|
180
|
+
"""获取远程插件数据的MD5"""
|
|
181
|
+
try:
|
|
182
|
+
ssl_context = ssl.create_default_context(cafile=certifi.where())
|
|
183
|
+
connector = aiohttp.TCPConnector(ssl=ssl_context)
|
|
184
|
+
|
|
185
|
+
async with (
|
|
186
|
+
aiohttp.ClientSession(
|
|
187
|
+
trust_env=True,
|
|
188
|
+
connector=connector,
|
|
189
|
+
) as session,
|
|
190
|
+
session.get(
|
|
191
|
+
"https://api.soulter.top/astrbot/plugins-md5",
|
|
192
|
+
) as response,
|
|
193
|
+
):
|
|
194
|
+
if response.status == 200:
|
|
195
|
+
data = await response.json()
|
|
196
|
+
return data.get("md5", "")
|
|
197
|
+
logger.error(f"获取MD5失败,状态码:{response.status}")
|
|
198
|
+
return ""
|
|
199
|
+
except Exception as e:
|
|
200
|
+
logger.error(f"获取远程MD5失败: {e}")
|
|
201
|
+
return ""
|
|
202
|
+
|
|
203
|
+
def _load_plugin_cache(self, cache_file: str):
|
|
204
|
+
"""加载本地缓存的插件市场数据"""
|
|
205
|
+
try:
|
|
206
|
+
if os.path.exists(cache_file):
|
|
207
|
+
with open(cache_file, encoding="utf-8") as f:
|
|
208
|
+
cache_data = json.load(f)
|
|
209
|
+
# 检查缓存是否有效
|
|
210
|
+
if "data" in cache_data and "timestamp" in cache_data:
|
|
211
|
+
logger.debug(
|
|
212
|
+
f"加载缓存文件: {cache_file}, 缓存时间: {cache_data['timestamp']}",
|
|
213
|
+
)
|
|
214
|
+
return cache_data["data"]
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.warning(f"加载插件市场缓存失败: {e}")
|
|
217
|
+
return None
|
|
218
|
+
|
|
219
|
+
def _save_plugin_cache(self, cache_file: str, data, md5: str | None = None):
|
|
220
|
+
"""保存插件市场数据到本地缓存"""
|
|
221
|
+
try:
|
|
222
|
+
# 确保目录存在
|
|
223
|
+
os.makedirs(os.path.dirname(cache_file), exist_ok=True)
|
|
224
|
+
|
|
225
|
+
cache_data = {
|
|
226
|
+
"timestamp": datetime.now().isoformat(),
|
|
227
|
+
"data": data,
|
|
228
|
+
"md5": md5 or "",
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
with open(cache_file, "w", encoding="utf-8") as f:
|
|
232
|
+
json.dump(cache_data, f, ensure_ascii=False, indent=2)
|
|
233
|
+
logger.debug(f"插件市场数据已缓存到: {cache_file}, MD5: {md5}")
|
|
234
|
+
except Exception as e:
|
|
235
|
+
logger.warning(f"保存插件市场缓存失败: {e}")
|
|
236
|
+
|
|
237
|
+
async def get_plugin_logo_token(self, logo_path: str):
|
|
238
|
+
try:
|
|
239
|
+
if token := self._logo_cache.get(logo_path):
|
|
240
|
+
if not await file_token_service.check_token_expired(token):
|
|
241
|
+
return self._logo_cache[logo_path]
|
|
242
|
+
token = await file_token_service.register_file(logo_path, timeout=300)
|
|
243
|
+
self._logo_cache[logo_path] = token
|
|
244
|
+
return token
|
|
245
|
+
except Exception as e:
|
|
246
|
+
logger.warning(f"获取插件 Logo 失败: {e}")
|
|
247
|
+
return None
|
|
102
248
|
|
|
103
249
|
async def get_plugins(self):
|
|
104
250
|
_plugin_resp = []
|
|
251
|
+
plugin_name = request.args.get("name")
|
|
105
252
|
for plugin in self.plugin_manager.context.get_all_stars():
|
|
253
|
+
if plugin_name and plugin.name != plugin_name:
|
|
254
|
+
continue
|
|
255
|
+
logo_url = None
|
|
256
|
+
if plugin.logo_path:
|
|
257
|
+
logo_url = await self.get_plugin_logo_token(plugin.logo_path)
|
|
106
258
|
_t = {
|
|
107
259
|
"name": plugin.name,
|
|
108
260
|
"repo": "" if plugin.repo is None else plugin.repo,
|
|
@@ -113,8 +265,10 @@ class PluginRoute(Route):
|
|
|
113
265
|
"activated": plugin.activated,
|
|
114
266
|
"online_vesion": "",
|
|
115
267
|
"handlers": await self.get_plugin_handlers_info(
|
|
116
|
-
plugin.star_handler_full_names
|
|
268
|
+
plugin.star_handler_full_names,
|
|
117
269
|
),
|
|
270
|
+
"display_name": plugin.display_name,
|
|
271
|
+
"logo": f"/api/file/{logo_url}" if logo_url else None,
|
|
118
272
|
}
|
|
119
273
|
_plugin_resp.append(_t)
|
|
120
274
|
return (
|
|
@@ -130,13 +284,15 @@ class PluginRoute(Route):
|
|
|
130
284
|
for handler_full_name in handler_full_names:
|
|
131
285
|
info = {}
|
|
132
286
|
handler = star_handlers_registry.star_handlers_map.get(
|
|
133
|
-
handler_full_name,
|
|
287
|
+
handler_full_name,
|
|
288
|
+
None,
|
|
134
289
|
)
|
|
135
290
|
if handler is None:
|
|
136
291
|
continue
|
|
137
292
|
info["event_type"] = handler.event_type.name
|
|
138
293
|
info["event_type_h"] = self.translated_event_type.get(
|
|
139
|
-
handler.event_type,
|
|
294
|
+
handler.event_type,
|
|
295
|
+
handler.event_type.name,
|
|
140
296
|
)
|
|
141
297
|
info["handler_full_name"] = handler.handler_full_name
|
|
142
298
|
info["desc"] = handler.desc
|
|
@@ -145,9 +301,7 @@ class PluginRoute(Route):
|
|
|
145
301
|
if handler.event_type == EventType.AdapterMessageEvent:
|
|
146
302
|
# 处理平台适配器消息事件
|
|
147
303
|
has_admin = False
|
|
148
|
-
for (
|
|
149
|
-
filter
|
|
150
|
-
) in (
|
|
304
|
+
for filter in (
|
|
151
305
|
handler.event_filters
|
|
152
306
|
): # 正常handler就只有 1~2 个 filter,因此这里时间复杂度不会太高
|
|
153
307
|
if isinstance(filter, CommandFilter):
|
|
@@ -156,29 +310,13 @@ class PluginRoute(Route):
|
|
|
156
310
|
f"{filter.parent_command_names[0]} {filter.command_name}"
|
|
157
311
|
)
|
|
158
312
|
info["cmd"] = info["cmd"].strip()
|
|
159
|
-
if (
|
|
160
|
-
self.core_lifecycle.astrbot_config["wake_prefix"]
|
|
161
|
-
and len(self.core_lifecycle.astrbot_config["wake_prefix"])
|
|
162
|
-
> 0
|
|
163
|
-
):
|
|
164
|
-
info["cmd"] = (
|
|
165
|
-
f"{self.core_lifecycle.astrbot_config['wake_prefix'][0]}{info['cmd']}"
|
|
166
|
-
)
|
|
167
313
|
elif isinstance(filter, CommandGroupFilter):
|
|
168
314
|
info["type"] = "指令组"
|
|
169
315
|
info["cmd"] = filter.get_complete_command_names()[0]
|
|
170
316
|
info["cmd"] = info["cmd"].strip()
|
|
171
317
|
info["sub_command"] = filter.print_cmd_tree(
|
|
172
|
-
filter.sub_command_filters
|
|
318
|
+
filter.sub_command_filters,
|
|
173
319
|
)
|
|
174
|
-
if (
|
|
175
|
-
self.core_lifecycle.astrbot_config["wake_prefix"]
|
|
176
|
-
and len(self.core_lifecycle.astrbot_config["wake_prefix"])
|
|
177
|
-
> 0
|
|
178
|
-
):
|
|
179
|
-
info["cmd"] = (
|
|
180
|
-
f"{self.core_lifecycle.astrbot_config['wake_prefix'][0]}{info['cmd']}"
|
|
181
|
-
)
|
|
182
320
|
elif isinstance(filter, RegexFilter):
|
|
183
321
|
info["type"] = "正则匹配"
|
|
184
322
|
info["cmd"] = filter.regex_str
|
|
@@ -257,9 +395,15 @@ class PluginRoute(Route):
|
|
|
257
395
|
|
|
258
396
|
post_data = await request.json
|
|
259
397
|
plugin_name = post_data["name"]
|
|
398
|
+
delete_config = post_data.get("delete_config", False)
|
|
399
|
+
delete_data = post_data.get("delete_data", False)
|
|
260
400
|
try:
|
|
261
401
|
logger.info(f"正在卸载插件 {plugin_name}")
|
|
262
|
-
await self.plugin_manager.uninstall_plugin(
|
|
402
|
+
await self.plugin_manager.uninstall_plugin(
|
|
403
|
+
plugin_name,
|
|
404
|
+
delete_config=delete_config,
|
|
405
|
+
delete_data=delete_data,
|
|
406
|
+
)
|
|
263
407
|
logger.info(f"卸载插件 {plugin_name} 成功")
|
|
264
408
|
return Response().ok(None, "卸载成功").__dict__
|
|
265
409
|
except Exception as e:
|
|
@@ -343,7 +487,8 @@ class PluginRoute(Route):
|
|
|
343
487
|
return Response().error(f"插件 {plugin_name} 不存在").__dict__
|
|
344
488
|
|
|
345
489
|
plugin_dir = os.path.join(
|
|
346
|
-
self.plugin_manager.plugin_store_path,
|
|
490
|
+
self.plugin_manager.plugin_store_path,
|
|
491
|
+
plugin_obj.root_dir_name,
|
|
347
492
|
)
|
|
348
493
|
|
|
349
494
|
if not os.path.isdir(plugin_dir):
|
|
@@ -357,7 +502,7 @@ class PluginRoute(Route):
|
|
|
357
502
|
return Response().error(f"插件 {plugin_name} 没有README文件").__dict__
|
|
358
503
|
|
|
359
504
|
try:
|
|
360
|
-
with open(readme_path,
|
|
505
|
+
with open(readme_path, encoding="utf-8") as f:
|
|
361
506
|
readme_content = f.read()
|
|
362
507
|
|
|
363
508
|
return (
|
|
@@ -367,91 +512,4 @@ class PluginRoute(Route):
|
|
|
367
512
|
)
|
|
368
513
|
except Exception as e:
|
|
369
514
|
logger.error(f"/api/plugin/readme: {traceback.format_exc()}")
|
|
370
|
-
return Response().error(f"读取README文件失败: {
|
|
371
|
-
|
|
372
|
-
async def get_plugin_platform_enable(self):
|
|
373
|
-
"""获取插件在各平台的可用性配置"""
|
|
374
|
-
try:
|
|
375
|
-
platform_enable = self.core_lifecycle.astrbot_config.get(
|
|
376
|
-
"platform_settings", {}
|
|
377
|
-
).get("plugin_enable", {})
|
|
378
|
-
|
|
379
|
-
# 获取所有可用平台
|
|
380
|
-
platforms = []
|
|
381
|
-
|
|
382
|
-
for platform in self.core_lifecycle.astrbot_config.get("platform", []):
|
|
383
|
-
platform_type = platform.get("type", "")
|
|
384
|
-
platform_id = platform.get("id", "")
|
|
385
|
-
|
|
386
|
-
platforms.append(
|
|
387
|
-
{
|
|
388
|
-
"name": platform_id, # 使用type作为name,这是系统内部使用的平台名称
|
|
389
|
-
"id": platform_id, # 保留id字段以便前端可以显示
|
|
390
|
-
"type": platform_type,
|
|
391
|
-
"display_name": f"{platform_type}({platform_id})",
|
|
392
|
-
}
|
|
393
|
-
)
|
|
394
|
-
|
|
395
|
-
adjusted_platform_enable = {}
|
|
396
|
-
for platform_id, plugins in platform_enable.items():
|
|
397
|
-
adjusted_platform_enable[platform_id] = plugins
|
|
398
|
-
|
|
399
|
-
# 获取所有插件,包括系统内部插件
|
|
400
|
-
plugins = []
|
|
401
|
-
for plugin in self.plugin_manager.context.get_all_stars():
|
|
402
|
-
plugins.append(
|
|
403
|
-
{
|
|
404
|
-
"name": plugin.name,
|
|
405
|
-
"desc": plugin.desc,
|
|
406
|
-
"reserved": plugin.reserved, # 添加reserved标志
|
|
407
|
-
}
|
|
408
|
-
)
|
|
409
|
-
|
|
410
|
-
logger.debug(
|
|
411
|
-
f"获取插件平台配置: 原始配置={platform_enable}, 调整后={adjusted_platform_enable}"
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
return (
|
|
415
|
-
Response()
|
|
416
|
-
.ok(
|
|
417
|
-
{
|
|
418
|
-
"platforms": platforms,
|
|
419
|
-
"plugins": plugins,
|
|
420
|
-
"platform_enable": adjusted_platform_enable,
|
|
421
|
-
}
|
|
422
|
-
)
|
|
423
|
-
.__dict__
|
|
424
|
-
)
|
|
425
|
-
except Exception as e:
|
|
426
|
-
logger.error(f"/api/plugin/platform_enable/get: {traceback.format_exc()}")
|
|
427
|
-
return Response().error(str(e)).__dict__
|
|
428
|
-
|
|
429
|
-
async def set_plugin_platform_enable(self):
|
|
430
|
-
"""设置插件在各平台的可用性配置"""
|
|
431
|
-
if DEMO_MODE:
|
|
432
|
-
return (
|
|
433
|
-
Response()
|
|
434
|
-
.error("You are not permitted to do this operation in demo mode")
|
|
435
|
-
.__dict__
|
|
436
|
-
)
|
|
437
|
-
|
|
438
|
-
try:
|
|
439
|
-
data = await request.json
|
|
440
|
-
platform_enable = data.get("platform_enable", {})
|
|
441
|
-
|
|
442
|
-
# 更新配置
|
|
443
|
-
config = self.core_lifecycle.astrbot_config
|
|
444
|
-
platform_settings = config.get("platform_settings", {})
|
|
445
|
-
platform_settings["plugin_enable"] = platform_enable
|
|
446
|
-
config["platform_settings"] = platform_settings
|
|
447
|
-
config.save_config()
|
|
448
|
-
|
|
449
|
-
# 更新插件的平台兼容性缓存
|
|
450
|
-
await self.plugin_manager.update_all_platform_compatibility()
|
|
451
|
-
|
|
452
|
-
logger.info(f"插件平台可用性配置已更新: {platform_enable}")
|
|
453
|
-
|
|
454
|
-
return Response().ok(None, "插件平台可用性配置已更新").__dict__
|
|
455
|
-
except Exception as e:
|
|
456
|
-
logger.error(f"/api/plugin/platform_enable/set: {traceback.format_exc()}")
|
|
457
|
-
return Response().error(str(e)).__dict__
|
|
515
|
+
return Response().error(f"读取README文件失败: {e!s}").__dict__
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
from astrbot.core.config.astrbot_config import AstrBotConfig
|
|
2
1
|
from dataclasses import dataclass
|
|
2
|
+
|
|
3
3
|
from quart import Quart
|
|
4
4
|
|
|
5
|
+
from astrbot.core.config.astrbot_config import AstrBotConfig
|
|
6
|
+
|
|
5
7
|
|
|
6
8
|
@dataclass
|
|
7
9
|
class RouteContext:
|
|
@@ -15,23 +17,41 @@ class Route:
|
|
|
15
17
|
self.config = context.config
|
|
16
18
|
|
|
17
19
|
def register_routes(self):
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
def _add_rule(path, method, func):
|
|
21
|
+
# 统一添加 /api 前缀
|
|
22
|
+
full_path = f"/api{path}"
|
|
23
|
+
self.app.add_url_rule(full_path, view_func=func, methods=[method])
|
|
24
|
+
|
|
25
|
+
# 兼容字典和列表两种格式
|
|
26
|
+
routes_to_register = (
|
|
27
|
+
self.routes.items() if isinstance(self.routes, dict) else self.routes
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
for route, definition in routes_to_register:
|
|
31
|
+
# 兼容一个路由多个方法
|
|
32
|
+
if isinstance(definition, list):
|
|
33
|
+
for method, func in definition:
|
|
34
|
+
_add_rule(route, method, func)
|
|
35
|
+
else:
|
|
36
|
+
method, func = definition
|
|
37
|
+
_add_rule(route, method, func)
|
|
20
38
|
|
|
21
39
|
|
|
22
40
|
@dataclass
|
|
23
41
|
class Response:
|
|
24
|
-
status: str = None
|
|
25
|
-
message: str = None
|
|
26
|
-
data: dict = None
|
|
42
|
+
status: str | None = None
|
|
43
|
+
message: str | None = None
|
|
44
|
+
data: dict | list | None = None
|
|
27
45
|
|
|
28
46
|
def error(self, message: str):
|
|
29
47
|
self.status = "error"
|
|
30
48
|
self.message = message
|
|
31
49
|
return self
|
|
32
50
|
|
|
33
|
-
def ok(self, data: dict =
|
|
51
|
+
def ok(self, data: dict | list | None = None, message: str | None = None):
|
|
34
52
|
self.status = "ok"
|
|
53
|
+
if data is None:
|
|
54
|
+
data = {}
|
|
35
55
|
self.data = data
|
|
36
56
|
self.message = message
|
|
37
57
|
return self
|