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,11 +1,30 @@
1
- import re
2
1
  import inspect
3
- from typing import List, Any, Type, Dict
4
- from . import HandlerFilter
5
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
2
+ import re
3
+ import types
4
+ import typing
5
+ from typing import Any
6
+
6
7
  from astrbot.core.config import AstrBotConfig
7
- from .custom_filter import CustomFilter
8
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
9
+
8
10
  from ..star_handler import StarHandlerMetadata
11
+ from . import HandlerFilter
12
+ from .custom_filter import CustomFilter
13
+
14
+
15
+ class GreedyStr(str):
16
+ """标记指令完成其他参数接收后的所有剩余文本。"""
17
+
18
+
19
+ def unwrap_optional(annotation) -> tuple:
20
+ """去掉 Optional[T] / Union[T, None] / T|None,返回 T"""
21
+ args = typing.get_args(annotation)
22
+ non_none_args = [a for a in args if a is not type(None)]
23
+ if len(non_none_args) == 1:
24
+ return (non_none_args[0],)
25
+ if len(non_none_args) > 1:
26
+ return tuple(non_none_args)
27
+ return ()
9
28
 
10
29
 
11
30
  # 标准指令受到 wake_prefix 的制约。
@@ -15,25 +34,32 @@ class CommandFilter(HandlerFilter):
15
34
  def __init__(
16
35
  self,
17
36
  command_name: str,
18
- alias: set = None,
19
- handler_md: StarHandlerMetadata = None,
20
- parent_command_names: List[str] = [""],
37
+ alias: set | None = None,
38
+ handler_md: StarHandlerMetadata | None = None,
39
+ parent_command_names: list[str] | None = None,
21
40
  ):
22
41
  self.command_name = command_name
23
42
  self.alias = alias if alias else set()
24
- self.parent_command_names = parent_command_names
43
+ self.parent_command_names = (
44
+ parent_command_names if parent_command_names is not None else [""]
45
+ )
25
46
  if handler_md:
26
47
  self.init_handler_md(handler_md)
27
- self.custom_filter_list: List[CustomFilter] = []
48
+ self.custom_filter_list: list[CustomFilter] = []
49
+
50
+ # Cache for complete command names list
51
+ self._cmpl_cmd_names: list | None = None
28
52
 
29
53
  def print_types(self):
30
- result = ""
54
+ parts = []
31
55
  for k, v in self.handler_params.items():
32
56
  if isinstance(v, type):
33
- result += f"{k}({v.__name__}),"
57
+ parts.append(f"{k}({v.__name__}),")
58
+ elif isinstance(v, types.UnionType) or typing.get_origin(v) is typing.Union:
59
+ parts.append(f"{k}({v}),")
34
60
  else:
35
- result += f"{k}({type(v).__name__})={v},"
36
- result = result.rstrip(",")
61
+ parts.append(f"{k}({type(v).__name__})={v},")
62
+ result = "".join(parts).rstrip(",")
37
63
  return result
38
64
 
39
65
  def init_handler_md(self, handle_md: StarHandlerMetadata):
@@ -64,23 +90,40 @@ class CommandFilter(HandlerFilter):
64
90
  return True
65
91
 
66
92
  def validate_and_convert_params(
67
- self, params: List[Any], param_type: Dict[str, Type]
68
- ) -> Dict[str, Any]:
93
+ self,
94
+ params: list[Any],
95
+ param_type: dict[str, type],
96
+ ) -> dict[str, Any]:
69
97
  """将参数列表 params 根据 param_type 转换为参数字典。"""
70
98
  result = {}
71
- for i, (param_name, param_type_or_default_val) in enumerate(param_type.items()):
99
+ param_items = list(param_type.items())
100
+ for i, (param_name, param_type_or_default_val) in enumerate(param_items):
101
+ is_greedy = param_type_or_default_val is GreedyStr
102
+
103
+ if is_greedy:
104
+ # GreedyStr 必须是最后一个参数
105
+ if i != len(param_items) - 1:
106
+ raise ValueError(
107
+ f"参数 '{param_name}' (GreedyStr) 必须是最后一个参数。",
108
+ )
109
+
110
+ # 将剩余的所有部分合并成一个字符串
111
+ remaining_params = params[i:]
112
+ result[param_name] = " ".join(remaining_params)
113
+ break
114
+ # 没有 GreedyStr 的情况
72
115
  if i >= len(params):
73
116
  if (
74
- isinstance(param_type_or_default_val, Type)
117
+ isinstance(param_type_or_default_val, (type, types.UnionType))
118
+ or typing.get_origin(param_type_or_default_val) is typing.Union
75
119
  or param_type_or_default_val is inspect.Parameter.empty
76
120
  ):
77
121
  # 是类型
78
122
  raise ValueError(
79
- f"必要参数缺失。该指令完整参数: {self.print_types()}"
123
+ f"必要参数缺失。该指令完整参数: {self.print_types()}",
80
124
  )
81
- else:
82
- # 是默认值
83
- result[param_name] = param_type_or_default_val
125
+ # 是默认值
126
+ result[param_name] = param_type_or_default_val
84
127
  else:
85
128
  # 尝试强制转换
86
129
  try:
@@ -92,18 +135,58 @@ class CommandFilter(HandlerFilter):
92
135
  elif isinstance(param_type_or_default_val, str):
93
136
  # 如果 param_type_or_default_val 是字符串,直接赋值
94
137
  result[param_name] = params[i]
138
+ elif isinstance(param_type_or_default_val, bool):
139
+ # 处理布尔类型
140
+ lower_param = str(params[i]).lower()
141
+ if lower_param in ["true", "yes", "1"]:
142
+ result[param_name] = True
143
+ elif lower_param in ["false", "no", "0"]:
144
+ result[param_name] = False
145
+ else:
146
+ raise ValueError(
147
+ f"参数 {param_name} 必须是布尔值(true/false, yes/no, 1/0)。",
148
+ )
95
149
  elif isinstance(param_type_or_default_val, int):
96
150
  result[param_name] = int(params[i])
97
151
  elif isinstance(param_type_or_default_val, float):
98
152
  result[param_name] = float(params[i])
99
153
  else:
100
- result[param_name] = param_type_or_default_val(params[i])
154
+ origin = typing.get_origin(param_type_or_default_val)
155
+ if origin in (typing.Union, types.UnionType):
156
+ # 注解是联合类型
157
+ # NOTE: 目前没有处理联合类型嵌套相关的注解写法
158
+ nn_types = unwrap_optional(param_type_or_default_val)
159
+ if len(nn_types) == 1:
160
+ # 只有一个非 NoneType 类型
161
+ result[param_name] = nn_types[0](params[i])
162
+ else:
163
+ # 没有或者有多个非 NoneType 类型,这里我们暂时直接赋值为原始值。
164
+ # NOTE: 目前还没有做类型校验
165
+ result[param_name] = params[i]
166
+ else:
167
+ result[param_name] = param_type_or_default_val(params[i])
101
168
  except ValueError:
102
169
  raise ValueError(
103
- f"参数 {param_name} 类型错误。完整参数: {self.print_types()}"
170
+ f"参数 {param_name} 类型错误。完整参数: {self.print_types()}",
104
171
  )
105
172
  return result
106
173
 
174
+ def get_complete_command_names(self):
175
+ if self._cmpl_cmd_names is not None:
176
+ return self._cmpl_cmd_names
177
+ self._cmpl_cmd_names = [
178
+ f"{parent} {cmd}" if parent else cmd
179
+ for cmd in [self.command_name] + list(self.alias)
180
+ for parent in self.parent_command_names or [""]
181
+ ]
182
+ return self._cmpl_cmd_names
183
+
184
+ def equals(self, message_str: str) -> bool:
185
+ for full_cmd in self.get_complete_command_names():
186
+ if message_str == full_cmd:
187
+ return True
188
+ return False
189
+
107
190
  def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool:
108
191
  if not event.is_at_or_wake_command:
109
192
  return False
@@ -113,18 +196,11 @@ class CommandFilter(HandlerFilter):
113
196
 
114
197
  # 检查是否以指令开头
115
198
  message_str = re.sub(r"\s+", " ", event.get_message_str().strip())
116
- candidates = [self.command_name] + list(self.alias)
117
199
  ok = False
118
- for candidate in candidates:
119
- for parent_command_name in self.parent_command_names:
120
- if parent_command_name:
121
- _full = f"{parent_command_name} {candidate}"
122
- else:
123
- _full = candidate
124
- if message_str.startswith(f"{_full} ") or message_str == _full:
125
- message_str = message_str[len(_full) :].strip()
126
- ok = True
127
- break
200
+ for full_cmd in self.get_complete_command_names():
201
+ if message_str.startswith(f"{full_cmd} ") or message_str == full_cmd:
202
+ ok = True
203
+ message_str = message_str[len(full_cmd) :].strip()
128
204
  if not ok:
129
205
  return False
130
206
 
@@ -1,10 +1,10 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import List, Union
3
+ from astrbot.core.config import AstrBotConfig
4
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
+
4
6
  from . import HandlerFilter
5
7
  from .command import CommandFilter
6
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
7
- from astrbot.core.config import AstrBotConfig
8
8
  from .custom_filter import CustomFilter
9
9
 
10
10
 
@@ -13,27 +13,35 @@ class CommandGroupFilter(HandlerFilter):
13
13
  def __init__(
14
14
  self,
15
15
  group_name: str,
16
- alias: set = None,
17
- parent_group: CommandGroupFilter = None,
16
+ alias: set | None = None,
17
+ parent_group: CommandGroupFilter | None = None,
18
18
  ):
19
19
  self.group_name = group_name
20
20
  self.alias = alias if alias else set()
21
- self.sub_command_filters: List[Union[CommandFilter, CommandGroupFilter]] = []
22
- self.custom_filter_list: List[CustomFilter] = []
21
+ self.sub_command_filters: list[CommandFilter | CommandGroupFilter] = []
22
+ self.custom_filter_list: list[CustomFilter] = []
23
23
  self.parent_group = parent_group
24
24
 
25
+ # Cache for complete command names list
26
+ self._cmpl_cmd_names: list | None = None
27
+
25
28
  def add_sub_command_filter(
26
- self, sub_command_filter: Union[CommandFilter, CommandGroupFilter]
29
+ self,
30
+ sub_command_filter: CommandFilter | CommandGroupFilter,
27
31
  ):
28
32
  self.sub_command_filters.append(sub_command_filter)
29
33
 
30
34
  def add_custom_filter(self, custom_filter: CustomFilter):
31
35
  self.custom_filter_list.append(custom_filter)
32
36
 
33
- def get_complete_command_names(self) -> List[str]:
37
+ def get_complete_command_names(self) -> list[str]:
34
38
  """遍历父节点获取完整的指令名。
35
39
 
36
- 新版本 v3.4.29 采用预编译指令,不再从指令组递归遍历子指令,因此这个方法是返回包括别名在内的整个指令名列表。"""
40
+ 新版本 v3.4.29 采用预编译指令,不再从指令组递归遍历子指令,因此这个方法是返回包括别名在内的整个指令名列表。
41
+ """
42
+ if self._cmpl_cmd_names is not None:
43
+ return self._cmpl_cmd_names
44
+
37
45
  parent_cmd_names = (
38
46
  self.parent_group.get_complete_command_names() if self.parent_group else []
39
47
  )
@@ -47,17 +55,18 @@ class CommandGroupFilter(HandlerFilter):
47
55
  for parent_cmd_name in parent_cmd_names:
48
56
  for candidate in candidates:
49
57
  result.append(parent_cmd_name + " " + candidate)
58
+ self._cmpl_cmd_names = result
50
59
  return result
51
60
 
52
61
  # 以树的形式打印出来
53
62
  def print_cmd_tree(
54
63
  self,
55
- sub_command_filters: List[Union[CommandFilter, CommandGroupFilter]],
64
+ sub_command_filters: list[CommandFilter | CommandGroupFilter],
56
65
  prefix: str = "",
57
- event: AstrMessageEvent = None,
58
- cfg: AstrBotConfig = None,
66
+ event: AstrMessageEvent | None = None,
67
+ cfg: AstrBotConfig | None = None,
59
68
  ) -> str:
60
- result = ""
69
+ parts = []
61
70
  for sub_filter in sub_command_filters:
62
71
  if isinstance(sub_filter, CommandFilter):
63
72
  custom_filter_pass = True
@@ -65,31 +74,32 @@ class CommandGroupFilter(HandlerFilter):
65
74
  custom_filter_pass = sub_filter.custom_filter_ok(event, cfg)
66
75
  if custom_filter_pass:
67
76
  cmd_th = sub_filter.print_types()
68
- result += f"{prefix}├── {sub_filter.command_name}"
77
+ line = f"{prefix}├── {sub_filter.command_name}"
69
78
  if cmd_th:
70
- result += f" ({cmd_th})"
79
+ line += f" ({cmd_th})"
71
80
  else:
72
- result += " (无参数指令)"
81
+ line += " (无参数指令)"
73
82
 
74
83
  if sub_filter.handler_md and sub_filter.handler_md.desc:
75
- result += f": {sub_filter.handler_md.desc}"
84
+ line += f": {sub_filter.handler_md.desc}"
76
85
 
77
- result += "\n"
86
+ parts.append(line + "\n")
78
87
  elif isinstance(sub_filter, CommandGroupFilter):
79
88
  custom_filter_pass = True
80
89
  if event and cfg:
81
90
  custom_filter_pass = sub_filter.custom_filter_ok(event, cfg)
82
91
  if custom_filter_pass:
83
- result += f"{prefix}├── {sub_filter.group_name}"
84
- result += "\n"
85
- result += sub_filter.print_cmd_tree(
86
- sub_filter.sub_command_filters,
87
- prefix + "│ ",
88
- event=event,
89
- cfg=cfg,
92
+ parts.append(f"{prefix}├── {sub_filter.group_name}\n")
93
+ parts.append(
94
+ sub_filter.print_cmd_tree(
95
+ sub_filter.sub_command_filters,
96
+ prefix + "│ ",
97
+ event=event,
98
+ cfg=cfg,
99
+ )
90
100
  )
91
101
 
92
- return result
102
+ return "".join(parts)
93
103
 
94
104
  def custom_filter_ok(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool:
95
105
  for custom_filter in self.custom_filter_list:
@@ -97,6 +107,12 @@ class CommandGroupFilter(HandlerFilter):
97
107
  return False
98
108
  return True
99
109
 
110
+ def startswith(self, message_str: str) -> bool:
111
+ return message_str.startswith(tuple(self.get_complete_command_names()))
112
+
113
+ def equals(self, message_str: str) -> bool:
114
+ return message_str in self.get_complete_command_names()
115
+
100
116
  def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool:
101
117
  if not event.is_at_or_wake_command:
102
118
  return False
@@ -105,18 +121,14 @@ class CommandGroupFilter(HandlerFilter):
105
121
  if not self.custom_filter_ok(event, cfg):
106
122
  return False
107
123
 
108
- complete_command_names = self.get_complete_command_names()
109
- if event.message_str.strip() in complete_command_names:
124
+ if self.equals(event.message_str.strip()):
110
125
  tree = (
111
126
  self.group_name
112
127
  + "\n"
113
128
  + self.print_cmd_tree(self.sub_command_filters, event=event, cfg=cfg)
114
129
  )
115
130
  raise ValueError(
116
- f"指令组 {self.group_name} 未填写完全。这个指令组下有如下指令:\n"
117
- + tree
131
+ f"参数不足。{self.group_name} 指令组下有如下指令,请参考:\n" + tree,
118
132
  )
119
133
 
120
- # complete_command_names = [name + " " for name in complete_command_names]
121
- # return event.message_str.startswith(tuple(complete_command_names))
122
- return False
134
+ return self.startswith(event.message_str)
@@ -1,8 +1,9 @@
1
- from abc import abstractmethod, ABCMeta
1
+ from abc import ABCMeta, abstractmethod
2
2
 
3
- from . import HandlerFilter
4
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
3
  from astrbot.core.config import AstrBotConfig
4
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
+
6
+ from . import HandlerFilter
6
7
 
7
8
 
8
9
  class CustomFilterMeta(ABCMeta):
@@ -38,7 +39,7 @@ class CustomFilterOr(CustomFilter):
38
39
  super().__init__()
39
40
  if not isinstance(filter1, (CustomFilter, CustomFilterAnd, CustomFilterOr)):
40
41
  raise ValueError(
41
- "CustomFilter lass can only operate with other CustomFilter."
42
+ "CustomFilter lass can only operate with other CustomFilter.",
42
43
  )
43
44
  self.filter1 = filter1
44
45
  self.filter2 = filter2
@@ -52,7 +53,7 @@ class CustomFilterAnd(CustomFilter):
52
53
  super().__init__()
53
54
  if not isinstance(filter1, (CustomFilter, CustomFilterAnd, CustomFilterOr)):
54
55
  raise ValueError(
55
- "CustomFilter lass can only operate with other CustomFilter."
56
+ "CustomFilter lass can only operate with other CustomFilter.",
56
57
  )
57
58
  self.filter1 = filter1
58
59
  self.filter2 = filter2
@@ -1,9 +1,11 @@
1
1
  import enum
2
- from . import HandlerFilter
3
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
2
+
4
3
  from astrbot.core.config import AstrBotConfig
4
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
5
  from astrbot.core.platform.message_type import MessageType
6
6
 
7
+ from . import HandlerFilter
8
+
7
9
 
8
10
  class EventMessageType(enum.Flag):
9
11
  GROUP_MESSAGE = enum.auto()
@@ -1,7 +1,9 @@
1
1
  import enum
2
- from . import HandlerFilter
3
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
2
+
4
3
  from astrbot.core.config import AstrBotConfig
4
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
+
6
+ from . import HandlerFilter
5
7
 
6
8
 
7
9
  class PermissionType(enum.Flag):
@@ -1,38 +1,71 @@
1
1
  import enum
2
- from . import HandlerFilter
3
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
2
+
4
3
  from astrbot.core.config import AstrBotConfig
5
- from typing import Union
4
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
+
6
+ from . import HandlerFilter
6
7
 
7
8
 
8
9
  class PlatformAdapterType(enum.Flag):
9
10
  AIOCQHTTP = enum.auto()
10
11
  QQOFFICIAL = enum.auto()
11
- VCHAT = enum.auto()
12
- GEWECHAT = enum.auto()
13
12
  TELEGRAM = enum.auto()
14
13
  WECOM = enum.auto()
15
14
  LARK = enum.auto()
16
- ALL = AIOCQHTTP | QQOFFICIAL | VCHAT | GEWECHAT | TELEGRAM | WECOM | LARK
15
+ WECHATPADPRO = enum.auto()
16
+ DINGTALK = enum.auto()
17
+ DISCORD = enum.auto()
18
+ SLACK = enum.auto()
19
+ KOOK = enum.auto()
20
+ VOCECHAT = enum.auto()
21
+ WEIXIN_OFFICIAL_ACCOUNT = enum.auto()
22
+ SATORI = enum.auto()
23
+ MISSKEY = enum.auto()
24
+ ALL = (
25
+ AIOCQHTTP
26
+ | QQOFFICIAL
27
+ | TELEGRAM
28
+ | WECOM
29
+ | LARK
30
+ | WECHATPADPRO
31
+ | DINGTALK
32
+ | DISCORD
33
+ | SLACK
34
+ | KOOK
35
+ | VOCECHAT
36
+ | WEIXIN_OFFICIAL_ACCOUNT
37
+ | SATORI
38
+ | MISSKEY
39
+ )
17
40
 
18
41
 
19
42
  ADAPTER_NAME_2_TYPE = {
20
43
  "aiocqhttp": PlatformAdapterType.AIOCQHTTP,
21
44
  "qq_official": PlatformAdapterType.QQOFFICIAL,
22
- "vchat": PlatformAdapterType.VCHAT,
23
- "gewechat": PlatformAdapterType.GEWECHAT,
24
45
  "telegram": PlatformAdapterType.TELEGRAM,
25
46
  "wecom": PlatformAdapterType.WECOM,
26
47
  "lark": PlatformAdapterType.LARK,
48
+ "dingtalk": PlatformAdapterType.DINGTALK,
49
+ "discord": PlatformAdapterType.DISCORD,
50
+ "slack": PlatformAdapterType.SLACK,
51
+ "kook": PlatformAdapterType.KOOK,
52
+ "wechatpadpro": PlatformAdapterType.WECHATPADPRO,
53
+ "vocechat": PlatformAdapterType.VOCECHAT,
54
+ "weixin_official_account": PlatformAdapterType.WEIXIN_OFFICIAL_ACCOUNT,
55
+ "satori": PlatformAdapterType.SATORI,
56
+ "misskey": PlatformAdapterType.MISSKEY,
27
57
  }
28
58
 
29
59
 
30
60
  class PlatformAdapterTypeFilter(HandlerFilter):
31
- def __init__(self, platform_adapter_type_or_str: Union[PlatformAdapterType, str]):
32
- self.type_or_str = platform_adapter_type_or_str
61
+ def __init__(self, platform_adapter_type_or_str: PlatformAdapterType | str):
62
+ if isinstance(platform_adapter_type_or_str, str):
63
+ self.platform_type = ADAPTER_NAME_2_TYPE.get(platform_adapter_type_or_str)
64
+ else:
65
+ self.platform_type = platform_adapter_type_or_str
33
66
 
34
67
  def filter(self, event: AstrMessageEvent, cfg: AstrBotConfig) -> bool:
35
68
  adapter_name = event.get_platform_name()
36
- if adapter_name in ADAPTER_NAME_2_TYPE:
37
- return ADAPTER_NAME_2_TYPE[adapter_name] & self.type_or_str
69
+ if adapter_name in ADAPTER_NAME_2_TYPE and self.platform_type is not None:
70
+ return bool(ADAPTER_NAME_2_TYPE[adapter_name] & self.platform_type)
38
71
  return False
@@ -1,7 +1,9 @@
1
1
  import re
2
- from . import HandlerFilter
3
- from astrbot.core.platform.astr_message_event import AstrMessageEvent
2
+
4
3
  from astrbot.core.config import AstrBotConfig
4
+ from astrbot.core.platform.astr_message_event import AstrMessageEvent
5
+
6
+ from . import HandlerFilter
5
7
 
6
8
 
7
9
  # 正则表达式过滤器不会受到 wake_prefix 的制约。
@@ -1,33 +1,37 @@
1
1
  from .star import register_star
2
2
  from .star_handler import (
3
+ register_after_message_sent,
4
+ register_agent,
3
5
  register_command,
4
6
  register_command_group,
5
- register_event_message_type,
6
- register_platform_adapter_type,
7
- register_regex,
8
- register_permission_type,
9
7
  register_custom_filter,
8
+ register_event_message_type,
9
+ register_llm_tool,
10
10
  register_on_astrbot_loaded,
11
+ register_on_decorating_result,
11
12
  register_on_llm_request,
12
13
  register_on_llm_response,
13
- register_llm_tool,
14
- register_on_decorating_result,
15
- register_after_message_sent,
14
+ register_on_platform_loaded,
15
+ register_permission_type,
16
+ register_platform_adapter_type,
17
+ register_regex,
16
18
  )
17
19
 
18
20
  __all__ = [
19
- "register_star",
21
+ "register_after_message_sent",
22
+ "register_agent",
20
23
  "register_command",
21
24
  "register_command_group",
22
- "register_event_message_type",
23
- "register_platform_adapter_type",
24
- "register_regex",
25
- "register_permission_type",
26
25
  "register_custom_filter",
26
+ "register_event_message_type",
27
+ "register_llm_tool",
27
28
  "register_on_astrbot_loaded",
29
+ "register_on_decorating_result",
28
30
  "register_on_llm_request",
29
31
  "register_on_llm_response",
30
- "register_llm_tool",
31
- "register_on_decorating_result",
32
- "register_after_message_sent",
32
+ "register_on_platform_loaded",
33
+ "register_permission_type",
34
+ "register_platform_adapter_type",
35
+ "register_regex",
36
+ "register_star",
33
37
  ]
@@ -1,9 +1,23 @@
1
- from ..star import star_registry, StarMetadata, star_map
1
+ import warnings
2
2
 
3
+ from astrbot.core.star import StarMetadata, star_map
3
4
 
4
- def register_star(name: str, author: str, desc: str, version: str, repo: str = None):
5
+ _warned_register_star = False
6
+
7
+
8
+ def register_star(
9
+ name: str,
10
+ author: str,
11
+ desc: str,
12
+ version: str,
13
+ repo: str | None = None,
14
+ ):
5
15
  """注册一个插件(Star)。
6
16
 
17
+ [DEPRECATED] 该装饰器已废弃,将在未来版本中移除。
18
+ 在 v3.5.19 版本之后(不含),您不需要使用该装饰器来装饰插件类,
19
+ AstrBot 会自动识别继承自 Star 的类并将其作为插件类加载。
20
+
7
21
  Args:
8
22
  name: 插件名称。
9
23
  author: 作者。
@@ -19,20 +33,34 @@ def register_star(name: str, author: str, desc: str, version: str, repo: str = N
19
33
  ...
20
34
 
21
35
  帮助信息会被自动提取。使用 `/plugin <插件名> 可以查看帮助信息。`
36
+
22
37
  """
38
+ global _warned_register_star
39
+ if not _warned_register_star:
40
+ _warned_register_star = True
41
+ warnings.warn(
42
+ "The 'register_star' decorator is deprecated and will be removed in a future version.",
43
+ DeprecationWarning,
44
+ stacklevel=2,
45
+ )
23
46
 
24
47
  def decorator(cls):
25
- star_metadata = StarMetadata(
26
- name=name,
27
- author=author,
28
- desc=desc,
29
- version=version,
30
- repo=repo,
31
- star_cls_type=cls,
32
- module_path=cls.__module__,
33
- )
34
- star_registry.append(star_metadata)
35
- star_map[cls.__module__] = star_metadata
48
+ if not star_map.get(cls.__module__):
49
+ metadata = StarMetadata(
50
+ name=name,
51
+ author=author,
52
+ desc=desc,
53
+ version=version,
54
+ repo=repo,
55
+ )
56
+ star_map[cls.__module__] = metadata
57
+ else:
58
+ star_map[cls.__module__].name = name
59
+ star_map[cls.__module__].author = author
60
+ star_map[cls.__module__].desc = desc
61
+ star_map[cls.__module__].version = version
62
+ star_map[cls.__module__].repo = repo
63
+
36
64
  return cls
37
65
 
38
66
  return decorator