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.
Files changed (288) hide show
  1. astrbot/api/__init__.py +16 -4
  2. astrbot/api/all.py +2 -1
  3. astrbot/api/event/__init__.py +5 -6
  4. astrbot/api/event/filter/__init__.py +37 -34
  5. astrbot/api/platform/__init__.py +7 -8
  6. astrbot/api/provider/__init__.py +8 -7
  7. astrbot/api/star/__init__.py +3 -4
  8. astrbot/api/util/__init__.py +2 -2
  9. astrbot/cli/__init__.py +1 -0
  10. astrbot/cli/__main__.py +18 -197
  11. astrbot/cli/commands/__init__.py +6 -0
  12. astrbot/cli/commands/cmd_conf.py +209 -0
  13. astrbot/cli/commands/cmd_init.py +56 -0
  14. astrbot/cli/commands/cmd_plug.py +245 -0
  15. astrbot/cli/commands/cmd_run.py +62 -0
  16. astrbot/cli/utils/__init__.py +18 -0
  17. astrbot/cli/utils/basic.py +76 -0
  18. astrbot/cli/utils/plugin.py +246 -0
  19. astrbot/cli/utils/version_comparator.py +90 -0
  20. astrbot/core/__init__.py +17 -19
  21. astrbot/core/agent/agent.py +14 -0
  22. astrbot/core/agent/handoff.py +38 -0
  23. astrbot/core/agent/hooks.py +30 -0
  24. astrbot/core/agent/mcp_client.py +385 -0
  25. astrbot/core/agent/message.py +175 -0
  26. astrbot/core/agent/response.py +14 -0
  27. astrbot/core/agent/run_context.py +22 -0
  28. astrbot/core/agent/runners/__init__.py +3 -0
  29. astrbot/core/agent/runners/base.py +65 -0
  30. astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
  31. astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
  32. astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
  33. astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
  34. astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
  35. astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
  36. astrbot/core/agent/tool.py +285 -0
  37. astrbot/core/agent/tool_executor.py +17 -0
  38. astrbot/core/astr_agent_context.py +19 -0
  39. astrbot/core/astr_agent_hooks.py +36 -0
  40. astrbot/core/astr_agent_run_util.py +80 -0
  41. astrbot/core/astr_agent_tool_exec.py +246 -0
  42. astrbot/core/astrbot_config_mgr.py +275 -0
  43. astrbot/core/config/__init__.py +2 -2
  44. astrbot/core/config/astrbot_config.py +60 -20
  45. astrbot/core/config/default.py +1972 -453
  46. astrbot/core/config/i18n_utils.py +110 -0
  47. astrbot/core/conversation_mgr.py +285 -75
  48. astrbot/core/core_lifecycle.py +167 -62
  49. astrbot/core/db/__init__.py +305 -102
  50. astrbot/core/db/migration/helper.py +69 -0
  51. astrbot/core/db/migration/migra_3_to_4.py +357 -0
  52. astrbot/core/db/migration/migra_45_to_46.py +44 -0
  53. astrbot/core/db/migration/migra_webchat_session.py +131 -0
  54. astrbot/core/db/migration/shared_preferences_v3.py +48 -0
  55. astrbot/core/db/migration/sqlite_v3.py +497 -0
  56. astrbot/core/db/po.py +259 -55
  57. astrbot/core/db/sqlite.py +773 -528
  58. astrbot/core/db/vec_db/base.py +73 -0
  59. astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
  60. astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
  61. astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
  62. astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
  63. astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
  64. astrbot/core/event_bus.py +26 -22
  65. astrbot/core/exceptions.py +9 -0
  66. astrbot/core/file_token_service.py +98 -0
  67. astrbot/core/initial_loader.py +19 -10
  68. astrbot/core/knowledge_base/chunking/__init__.py +9 -0
  69. astrbot/core/knowledge_base/chunking/base.py +25 -0
  70. astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
  71. astrbot/core/knowledge_base/chunking/recursive.py +161 -0
  72. astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
  73. astrbot/core/knowledge_base/kb_helper.py +642 -0
  74. astrbot/core/knowledge_base/kb_mgr.py +330 -0
  75. astrbot/core/knowledge_base/models.py +120 -0
  76. astrbot/core/knowledge_base/parsers/__init__.py +13 -0
  77. astrbot/core/knowledge_base/parsers/base.py +51 -0
  78. astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
  79. astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
  80. astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
  81. astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
  82. astrbot/core/knowledge_base/parsers/util.py +13 -0
  83. astrbot/core/knowledge_base/prompts.py +65 -0
  84. astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
  85. astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
  86. astrbot/core/knowledge_base/retrieval/manager.py +276 -0
  87. astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
  88. astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
  89. astrbot/core/log.py +21 -15
  90. astrbot/core/message/components.py +413 -287
  91. astrbot/core/message/message_event_result.py +35 -24
  92. astrbot/core/persona_mgr.py +192 -0
  93. astrbot/core/pipeline/__init__.py +14 -14
  94. astrbot/core/pipeline/content_safety_check/stage.py +13 -9
  95. astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
  96. astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +13 -14
  97. astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
  98. astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
  99. astrbot/core/pipeline/context.py +7 -1
  100. astrbot/core/pipeline/context_utils.py +107 -0
  101. astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
  102. astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
  103. astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
  104. astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
  105. astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
  106. astrbot/core/pipeline/process_stage/stage.py +21 -15
  107. astrbot/core/pipeline/process_stage/utils.py +125 -0
  108. astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
  109. astrbot/core/pipeline/respond/stage.py +142 -101
  110. astrbot/core/pipeline/result_decorate/stage.py +124 -57
  111. astrbot/core/pipeline/scheduler.py +21 -16
  112. astrbot/core/pipeline/session_status_check/stage.py +37 -0
  113. astrbot/core/pipeline/stage.py +11 -76
  114. astrbot/core/pipeline/waking_check/stage.py +69 -33
  115. astrbot/core/pipeline/whitelist_check/stage.py +10 -7
  116. astrbot/core/platform/__init__.py +6 -6
  117. astrbot/core/platform/astr_message_event.py +107 -129
  118. astrbot/core/platform/astrbot_message.py +32 -12
  119. astrbot/core/platform/manager.py +62 -18
  120. astrbot/core/platform/message_session.py +30 -0
  121. astrbot/core/platform/platform.py +16 -24
  122. astrbot/core/platform/platform_metadata.py +9 -4
  123. astrbot/core/platform/register.py +12 -7
  124. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
  125. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
  126. astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
  127. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
  128. astrbot/core/platform/sources/discord/client.py +129 -0
  129. astrbot/core/platform/sources/discord/components.py +139 -0
  130. astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
  131. astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
  132. astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
  133. astrbot/core/platform/sources/lark/lark_event.py +39 -13
  134. astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
  135. astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
  136. astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
  137. astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
  138. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
  139. astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
  140. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
  141. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
  142. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +14 -8
  143. astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
  144. astrbot/core/platform/sources/satori/satori_event.py +432 -0
  145. astrbot/core/platform/sources/slack/client.py +164 -0
  146. astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
  147. astrbot/core/platform/sources/slack/slack_event.py +253 -0
  148. astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
  149. astrbot/core/platform/sources/telegram/tg_event.py +136 -36
  150. astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
  151. astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
  152. astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
  153. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
  154. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
  155. astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
  156. astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
  157. astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
  158. astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
  159. astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
  160. astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
  161. astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
  162. astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
  163. astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
  164. astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
  165. astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
  166. astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
  167. astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
  168. astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
  169. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
  170. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
  171. astrbot/core/platform_message_history_mgr.py +49 -0
  172. astrbot/core/provider/__init__.py +2 -3
  173. astrbot/core/provider/entites.py +8 -8
  174. astrbot/core/provider/entities.py +154 -98
  175. astrbot/core/provider/func_tool_manager.py +446 -458
  176. astrbot/core/provider/manager.py +345 -207
  177. astrbot/core/provider/provider.py +188 -73
  178. astrbot/core/provider/register.py +9 -7
  179. astrbot/core/provider/sources/anthropic_source.py +295 -115
  180. astrbot/core/provider/sources/azure_tts_source.py +224 -0
  181. astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
  182. astrbot/core/provider/sources/dashscope_tts.py +138 -14
  183. astrbot/core/provider/sources/edge_tts_source.py +24 -19
  184. astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
  185. astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
  186. astrbot/core/provider/sources/gemini_source.py +310 -132
  187. astrbot/core/provider/sources/gemini_tts_source.py +81 -0
  188. astrbot/core/provider/sources/groq_source.py +15 -0
  189. astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
  190. astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
  191. astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
  192. astrbot/core/provider/sources/openai_embedding_source.py +40 -0
  193. astrbot/core/provider/sources/openai_source.py +241 -145
  194. astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
  195. astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
  196. astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
  197. astrbot/core/provider/sources/volcengine_tts.py +115 -0
  198. astrbot/core/provider/sources/whisper_api_source.py +18 -13
  199. astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
  200. astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
  201. astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
  202. astrbot/core/provider/sources/zhipu_source.py +6 -73
  203. astrbot/core/star/__init__.py +43 -11
  204. astrbot/core/star/config.py +17 -18
  205. astrbot/core/star/context.py +362 -138
  206. astrbot/core/star/filter/__init__.py +4 -3
  207. astrbot/core/star/filter/command.py +111 -35
  208. astrbot/core/star/filter/command_group.py +46 -34
  209. astrbot/core/star/filter/custom_filter.py +6 -5
  210. astrbot/core/star/filter/event_message_type.py +4 -2
  211. astrbot/core/star/filter/permission.py +4 -2
  212. astrbot/core/star/filter/platform_adapter_type.py +45 -12
  213. astrbot/core/star/filter/regex.py +4 -2
  214. astrbot/core/star/register/__init__.py +19 -15
  215. astrbot/core/star/register/star.py +41 -13
  216. astrbot/core/star/register/star_handler.py +236 -86
  217. astrbot/core/star/session_llm_manager.py +280 -0
  218. astrbot/core/star/session_plugin_manager.py +170 -0
  219. astrbot/core/star/star.py +36 -43
  220. astrbot/core/star/star_handler.py +47 -85
  221. astrbot/core/star/star_manager.py +442 -260
  222. astrbot/core/star/star_tools.py +167 -45
  223. astrbot/core/star/updator.py +17 -20
  224. astrbot/core/umop_config_router.py +106 -0
  225. astrbot/core/updator.py +38 -13
  226. astrbot/core/utils/astrbot_path.py +39 -0
  227. astrbot/core/utils/command_parser.py +1 -1
  228. astrbot/core/utils/io.py +119 -60
  229. astrbot/core/utils/log_pipe.py +1 -1
  230. astrbot/core/utils/metrics.py +11 -10
  231. astrbot/core/utils/migra_helper.py +73 -0
  232. astrbot/core/utils/path_util.py +63 -62
  233. astrbot/core/utils/pip_installer.py +37 -15
  234. astrbot/core/utils/session_lock.py +29 -0
  235. astrbot/core/utils/session_waiter.py +19 -20
  236. astrbot/core/utils/shared_preferences.py +174 -34
  237. astrbot/core/utils/t2i/__init__.py +4 -1
  238. astrbot/core/utils/t2i/local_strategy.py +386 -238
  239. astrbot/core/utils/t2i/network_strategy.py +109 -49
  240. astrbot/core/utils/t2i/renderer.py +29 -14
  241. astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
  242. astrbot/core/utils/t2i/template_manager.py +111 -0
  243. astrbot/core/utils/tencent_record_helper.py +115 -1
  244. astrbot/core/utils/version_comparator.py +10 -13
  245. astrbot/core/zip_updator.py +112 -65
  246. astrbot/dashboard/routes/__init__.py +20 -13
  247. astrbot/dashboard/routes/auth.py +20 -9
  248. astrbot/dashboard/routes/chat.py +297 -141
  249. astrbot/dashboard/routes/config.py +652 -55
  250. astrbot/dashboard/routes/conversation.py +107 -37
  251. astrbot/dashboard/routes/file.py +26 -0
  252. astrbot/dashboard/routes/knowledge_base.py +1244 -0
  253. astrbot/dashboard/routes/log.py +27 -2
  254. astrbot/dashboard/routes/persona.py +202 -0
  255. astrbot/dashboard/routes/plugin.py +197 -139
  256. astrbot/dashboard/routes/route.py +27 -7
  257. astrbot/dashboard/routes/session_management.py +354 -0
  258. astrbot/dashboard/routes/stat.py +85 -18
  259. astrbot/dashboard/routes/static_file.py +5 -2
  260. astrbot/dashboard/routes/t2i.py +233 -0
  261. astrbot/dashboard/routes/tools.py +184 -120
  262. astrbot/dashboard/routes/update.py +59 -36
  263. astrbot/dashboard/server.py +96 -36
  264. astrbot/dashboard/utils.py +165 -0
  265. astrbot-4.7.0.dist-info/METADATA +294 -0
  266. astrbot-4.7.0.dist-info/RECORD +274 -0
  267. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
  268. astrbot/core/db/plugin/sqlite_impl.py +0 -112
  269. astrbot/core/db/sqlite_init.sql +0 -50
  270. astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
  271. astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
  272. astrbot/core/platform/sources/gewechat/client.py +0 -806
  273. astrbot/core/platform/sources/gewechat/downloader.py +0 -55
  274. astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
  275. astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
  276. astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
  277. astrbot/core/provider/sources/dashscope_source.py +0 -203
  278. astrbot/core/provider/sources/dify_source.py +0 -281
  279. astrbot/core/provider/sources/llmtuner_source.py +0 -132
  280. astrbot/core/rag/embedding/openai_source.py +0 -20
  281. astrbot/core/rag/knowledge_db_mgr.py +0 -94
  282. astrbot/core/rag/store/__init__.py +0 -9
  283. astrbot/core/rag/store/chroma_db.py +0 -42
  284. astrbot/core/utils/dify_api_client.py +0 -152
  285. astrbot-3.5.6.dist-info/METADATA +0 -249
  286. astrbot-3.5.6.dist-info/RECORD +0 -158
  287. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
  288. {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
@@ -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
- from typing import Union, Awaitable, List, Optional, ClassVar
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 pathlib import Path
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[Optional[Context]] = None
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, session: Union[str, MessageSesion], message_chain: MessageChain
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: List[BaseMessageComponent],
128
+ message: list[BaseMessageComponent],
61
129
  message_str: str,
62
- raw_message: object,
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
- message_id (str): 消息ID
73
- sender (MessageMember): 发送者信息
74
- message (List[BaseMessageComponent]): 消息组件列表
75
- message_str (str): 消息字符串
76
- raw_message (object): 原始消息对象
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
- # todo: 添加构造事件的方法
95
- # async def create_event(
96
- # self, platform: str, umo: str, sender_id: str, session_id: str
97
- # ):
98
- # platform = self._context.get_platform(platform)
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
- # todo: 添加找到对应平台并提交对应事件的方法
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, name: str, func_args: list, desc: str, func_obj: Awaitable
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: Optional[str] = None) -> Path:
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().f_back
171
- module = inspect.getmodule(frame)
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
- data_dir = Path("data/plugin_data") / plugin_name
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)
@@ -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 = os.path.abspath(
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.format_repo_name(repo_url)
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 proxy:
38
- proxy = proxy.removesuffix("/")
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} 文件夹失败: {str(e)},使用覆盖安装。"
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"解压文件: {zip_path}")
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
- else:
71
- if os.path.exists(os.path.join(target_dir, f)):
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
- from .zip_updator import ReleaseInfo, RepoZipUpdator
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 = os.path.abspath(
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
- py = py.replace(" ", "\\ ")
54
+ if os.name == "nt":
55
+ py = f'"{sys.executable}"'
56
+ else:
57
+ py = sys.executable
58
+
53
59
  try:
54
- os.execl(py, py, *sys.argv)
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(self, url: str, current_version: str) -> ReleaseInfo:
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(self.ASTRBOT_RELEASE_API, VERSION)
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
- logger.info(f"正在尝试更新到指定 commit: {version}")
87
- file_url = "https://github.com/Soulter/AstrBot/archive/" + version + ".zip"
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"))