AstrBot 4.5.1__py3-none-any.whl → 4.5.3__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 (244) hide show
  1. astrbot/api/__init__.py +10 -11
  2. astrbot/api/event/__init__.py +5 -6
  3. astrbot/api/event/filter/__init__.py +37 -36
  4. astrbot/api/platform/__init__.py +7 -8
  5. astrbot/api/provider/__init__.py +7 -7
  6. astrbot/api/star/__init__.py +3 -4
  7. astrbot/api/util/__init__.py +2 -2
  8. astrbot/cli/__main__.py +5 -5
  9. astrbot/cli/commands/__init__.py +3 -3
  10. astrbot/cli/commands/cmd_conf.py +19 -16
  11. astrbot/cli/commands/cmd_init.py +3 -2
  12. astrbot/cli/commands/cmd_plug.py +8 -10
  13. astrbot/cli/commands/cmd_run.py +5 -6
  14. astrbot/cli/utils/__init__.py +6 -6
  15. astrbot/cli/utils/basic.py +14 -14
  16. astrbot/cli/utils/plugin.py +24 -15
  17. astrbot/cli/utils/version_comparator.py +10 -12
  18. astrbot/core/__init__.py +8 -6
  19. astrbot/core/agent/agent.py +3 -2
  20. astrbot/core/agent/handoff.py +6 -2
  21. astrbot/core/agent/hooks.py +9 -6
  22. astrbot/core/agent/mcp_client.py +50 -15
  23. astrbot/core/agent/message.py +168 -0
  24. astrbot/core/agent/response.py +2 -1
  25. astrbot/core/agent/run_context.py +2 -3
  26. astrbot/core/agent/runners/base.py +10 -13
  27. astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
  28. astrbot/core/agent/tool.py +60 -41
  29. astrbot/core/agent/tool_executor.py +9 -3
  30. astrbot/core/astr_agent_context.py +3 -1
  31. astrbot/core/astrbot_config_mgr.py +29 -9
  32. astrbot/core/config/__init__.py +2 -2
  33. astrbot/core/config/astrbot_config.py +28 -26
  34. astrbot/core/config/default.py +4 -6
  35. astrbot/core/conversation_mgr.py +105 -36
  36. astrbot/core/core_lifecycle.py +68 -54
  37. astrbot/core/db/__init__.py +33 -18
  38. astrbot/core/db/migration/helper.py +12 -10
  39. astrbot/core/db/migration/migra_3_to_4.py +53 -34
  40. astrbot/core/db/migration/migra_45_to_46.py +1 -1
  41. astrbot/core/db/migration/shared_preferences_v3.py +2 -1
  42. astrbot/core/db/migration/sqlite_v3.py +26 -23
  43. astrbot/core/db/po.py +27 -18
  44. astrbot/core/db/sqlite.py +74 -45
  45. astrbot/core/db/vec_db/base.py +10 -14
  46. astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
  47. astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
  48. astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
  49. astrbot/core/event_bus.py +8 -6
  50. astrbot/core/file_token_service.py +6 -5
  51. astrbot/core/initial_loader.py +7 -5
  52. astrbot/core/knowledge_base/chunking/__init__.py +1 -3
  53. astrbot/core/knowledge_base/chunking/base.py +1 -0
  54. astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
  55. astrbot/core/knowledge_base/chunking/recursive.py +16 -10
  56. astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
  57. astrbot/core/knowledge_base/kb_helper.py +30 -17
  58. astrbot/core/knowledge_base/kb_mgr.py +6 -7
  59. astrbot/core/knowledge_base/models.py +10 -4
  60. astrbot/core/knowledge_base/parsers/__init__.py +3 -5
  61. astrbot/core/knowledge_base/parsers/base.py +1 -0
  62. astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
  63. astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
  64. astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
  65. astrbot/core/knowledge_base/parsers/util.py +1 -1
  66. astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
  67. astrbot/core/knowledge_base/retrieval/manager.py +17 -14
  68. astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
  69. astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
  70. astrbot/core/log.py +21 -13
  71. astrbot/core/message/components.py +123 -217
  72. astrbot/core/message/message_event_result.py +24 -24
  73. astrbot/core/persona_mgr.py +20 -11
  74. astrbot/core/pipeline/__init__.py +7 -7
  75. astrbot/core/pipeline/content_safety_check/stage.py +13 -9
  76. astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
  77. astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
  78. astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
  79. astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
  80. astrbot/core/pipeline/context.py +4 -1
  81. astrbot/core/pipeline/context_utils.py +77 -7
  82. astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
  83. astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
  84. astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
  85. astrbot/core/pipeline/process_stage/stage.py +13 -10
  86. astrbot/core/pipeline/process_stage/utils.py +6 -5
  87. astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
  88. astrbot/core/pipeline/respond/stage.py +23 -20
  89. astrbot/core/pipeline/result_decorate/stage.py +31 -23
  90. astrbot/core/pipeline/scheduler.py +12 -8
  91. astrbot/core/pipeline/session_status_check/stage.py +12 -8
  92. astrbot/core/pipeline/stage.py +10 -4
  93. astrbot/core/pipeline/waking_check/stage.py +24 -18
  94. astrbot/core/pipeline/whitelist_check/stage.py +10 -7
  95. astrbot/core/platform/__init__.py +6 -6
  96. astrbot/core/platform/astr_message_event.py +76 -110
  97. astrbot/core/platform/astrbot_message.py +11 -13
  98. astrbot/core/platform/manager.py +16 -15
  99. astrbot/core/platform/message_session.py +5 -3
  100. astrbot/core/platform/platform.py +16 -24
  101. astrbot/core/platform/platform_metadata.py +4 -4
  102. astrbot/core/platform/register.py +8 -8
  103. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
  104. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
  105. astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +42 -27
  106. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
  107. astrbot/core/platform/sources/discord/client.py +9 -6
  108. astrbot/core/platform/sources/discord/components.py +18 -14
  109. astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
  110. astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
  111. astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
  112. astrbot/core/platform/sources/lark/lark_event.py +21 -14
  113. astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
  114. astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
  115. astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
  116. astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
  117. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
  118. astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
  119. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
  120. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
  121. astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
  122. astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
  123. astrbot/core/platform/sources/satori/satori_event.py +34 -25
  124. astrbot/core/platform/sources/slack/client.py +11 -9
  125. astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
  126. astrbot/core/platform/sources/slack/slack_event.py +34 -24
  127. astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
  128. astrbot/core/platform/sources/telegram/tg_event.py +32 -18
  129. astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
  130. astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
  131. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
  132. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
  133. astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
  134. astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
  135. astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
  136. astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
  137. astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
  138. astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
  139. astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
  140. astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
  141. astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
  142. astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
  143. astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
  144. astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
  145. astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
  146. astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
  147. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
  148. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
  149. astrbot/core/platform_message_history_mgr.py +5 -3
  150. astrbot/core/provider/__init__.py +2 -3
  151. astrbot/core/provider/entites.py +8 -8
  152. astrbot/core/provider/entities.py +61 -75
  153. astrbot/core/provider/func_tool_manager.py +59 -55
  154. astrbot/core/provider/manager.py +32 -22
  155. astrbot/core/provider/provider.py +72 -46
  156. astrbot/core/provider/register.py +7 -7
  157. astrbot/core/provider/sources/anthropic_source.py +48 -30
  158. astrbot/core/provider/sources/azure_tts_source.py +17 -13
  159. astrbot/core/provider/sources/coze_api_client.py +27 -17
  160. astrbot/core/provider/sources/coze_source.py +104 -87
  161. astrbot/core/provider/sources/dashscope_source.py +18 -11
  162. astrbot/core/provider/sources/dashscope_tts.py +36 -23
  163. astrbot/core/provider/sources/dify_source.py +25 -20
  164. astrbot/core/provider/sources/edge_tts_source.py +21 -17
  165. astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
  166. astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
  167. astrbot/core/provider/sources/gemini_source.py +72 -58
  168. astrbot/core/provider/sources/gemini_tts_source.py +8 -6
  169. astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
  170. astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
  171. astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
  172. astrbot/core/provider/sources/openai_embedding_source.py +6 -8
  173. astrbot/core/provider/sources/openai_source.py +77 -69
  174. astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
  175. astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
  176. astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
  177. astrbot/core/provider/sources/volcengine_tts.py +38 -31
  178. astrbot/core/provider/sources/whisper_api_source.py +14 -12
  179. astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
  180. astrbot/core/provider/sources/xinference_rerank_source.py +16 -8
  181. astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
  182. astrbot/core/star/__init__.py +16 -11
  183. astrbot/core/star/config.py +10 -15
  184. astrbot/core/star/context.py +97 -75
  185. astrbot/core/star/filter/__init__.py +4 -3
  186. astrbot/core/star/filter/command.py +30 -28
  187. astrbot/core/star/filter/command_group.py +27 -24
  188. astrbot/core/star/filter/custom_filter.py +6 -5
  189. astrbot/core/star/filter/event_message_type.py +4 -2
  190. astrbot/core/star/filter/permission.py +4 -2
  191. astrbot/core/star/filter/platform_adapter_type.py +4 -2
  192. astrbot/core/star/filter/regex.py +4 -2
  193. astrbot/core/star/register/__init__.py +19 -19
  194. astrbot/core/star/register/star.py +6 -2
  195. astrbot/core/star/register/star_handler.py +96 -73
  196. astrbot/core/star/session_llm_manager.py +48 -14
  197. astrbot/core/star/session_plugin_manager.py +29 -15
  198. astrbot/core/star/star.py +1 -2
  199. astrbot/core/star/star_handler.py +13 -8
  200. astrbot/core/star/star_manager.py +151 -59
  201. astrbot/core/star/star_tools.py +44 -37
  202. astrbot/core/star/updator.py +10 -10
  203. astrbot/core/umop_config_router.py +10 -4
  204. astrbot/core/updator.py +13 -5
  205. astrbot/core/utils/astrbot_path.py +3 -5
  206. astrbot/core/utils/dify_api_client.py +33 -15
  207. astrbot/core/utils/io.py +66 -42
  208. astrbot/core/utils/log_pipe.py +1 -1
  209. astrbot/core/utils/metrics.py +7 -7
  210. astrbot/core/utils/path_util.py +15 -16
  211. astrbot/core/utils/pip_installer.py +5 -5
  212. astrbot/core/utils/session_waiter.py +19 -20
  213. astrbot/core/utils/shared_preferences.py +45 -20
  214. astrbot/core/utils/t2i/__init__.py +4 -1
  215. astrbot/core/utils/t2i/network_strategy.py +35 -26
  216. astrbot/core/utils/t2i/renderer.py +11 -5
  217. astrbot/core/utils/t2i/template_manager.py +14 -15
  218. astrbot/core/utils/tencent_record_helper.py +19 -13
  219. astrbot/core/utils/version_comparator.py +10 -13
  220. astrbot/core/zip_updator.py +43 -40
  221. astrbot/dashboard/routes/__init__.py +18 -18
  222. astrbot/dashboard/routes/auth.py +10 -8
  223. astrbot/dashboard/routes/chat.py +30 -21
  224. astrbot/dashboard/routes/config.py +92 -75
  225. astrbot/dashboard/routes/conversation.py +46 -39
  226. astrbot/dashboard/routes/file.py +4 -2
  227. astrbot/dashboard/routes/knowledge_base.py +47 -40
  228. astrbot/dashboard/routes/log.py +9 -4
  229. astrbot/dashboard/routes/persona.py +19 -16
  230. astrbot/dashboard/routes/plugin.py +69 -55
  231. astrbot/dashboard/routes/route.py +3 -1
  232. astrbot/dashboard/routes/session_management.py +130 -116
  233. astrbot/dashboard/routes/stat.py +34 -34
  234. astrbot/dashboard/routes/t2i.py +15 -12
  235. astrbot/dashboard/routes/tools.py +56 -53
  236. astrbot/dashboard/routes/update.py +32 -28
  237. astrbot/dashboard/server.py +30 -26
  238. astrbot/dashboard/utils.py +8 -4
  239. {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/METADATA +2 -1
  240. astrbot-4.5.3.dist-info/RECORD +261 -0
  241. astrbot-4.5.1.dist-info/RECORD +0 -260
  242. {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/WHEEL +0 -0
  243. {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/entry_points.txt +0 -0
  244. {astrbot-4.5.1.dist-info → astrbot-4.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,30 +1,31 @@
1
1
  from __future__ import annotations
2
2
 
3
- import botpy
3
+ import asyncio
4
4
  import logging
5
+ import os
5
6
  import time
6
- import asyncio
7
+
8
+ import botpy
7
9
  import botpy.message
8
10
  import botpy.types
9
11
  import botpy.types.message
10
- import os
11
-
12
12
  from botpy import Client
13
+
14
+ from astrbot import logger
15
+ from astrbot.api.event import MessageChain
16
+ from astrbot.api.message_components import At, Image, Plain
13
17
  from astrbot.api.platform import (
14
- Platform,
15
18
  AstrBotMessage,
16
19
  MessageMember,
17
20
  MessageType,
21
+ Platform,
18
22
  PlatformMetadata,
19
23
  )
20
- from astrbot import logger
21
- from astrbot.api.event import MessageChain
22
- from typing import Union, List
23
- from astrbot.api.message_components import Image, Plain, At
24
+ from astrbot.core.message.components import BaseMessageComponent
24
25
  from astrbot.core.platform.astr_message_event import MessageSesion
25
- from .qqofficial_message_event import QQOfficialMessageEvent
26
+
26
27
  from ...register import register_platform_adapter
27
- from astrbot.core.message.components import BaseMessageComponent
28
+ from .qqofficial_message_event import QQOfficialMessageEvent
28
29
 
29
30
  # remove logger handler
30
31
  for handler in logging.root.handlers[:]:
@@ -33,13 +34,14 @@ for handler in logging.root.handlers[:]:
33
34
 
34
35
  # QQ 机器人官方框架
35
36
  class botClient(Client):
36
- def set_platform(self, platform: "QQOfficialPlatformAdapter"):
37
+ def set_platform(self, platform: QQOfficialPlatformAdapter):
37
38
  self.platform = platform
38
39
 
39
40
  # 收到群消息
40
41
  async def on_group_at_message_create(self, message: botpy.message.GroupMessage):
41
42
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
42
- message, MessageType.GROUP_MESSAGE
43
+ message,
44
+ MessageType.GROUP_MESSAGE,
43
45
  )
44
46
  abm.session_id = (
45
47
  abm.sender.user_id if self.platform.unique_session else message.group_openid
@@ -49,7 +51,8 @@ class botClient(Client):
49
51
  # 收到频道消息
50
52
  async def on_at_message_create(self, message: botpy.message.Message):
51
53
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
52
- message, MessageType.GROUP_MESSAGE
54
+ message,
55
+ MessageType.GROUP_MESSAGE,
53
56
  )
54
57
  abm.session_id = (
55
58
  abm.sender.user_id if self.platform.unique_session else message.channel_id
@@ -59,7 +62,8 @@ class botClient(Client):
59
62
  # 收到私聊消息
60
63
  async def on_direct_message_create(self, message: botpy.message.DirectMessage):
61
64
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
62
- message, MessageType.FRIEND_MESSAGE
65
+ message,
66
+ MessageType.FRIEND_MESSAGE,
63
67
  )
64
68
  abm.session_id = abm.sender.user_id
65
69
  self._commit(abm)
@@ -67,7 +71,8 @@ class botClient(Client):
67
71
  # 收到 C2C 消息
68
72
  async def on_c2c_message_create(self, message: botpy.message.C2CMessage):
69
73
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
70
- message, MessageType.FRIEND_MESSAGE
74
+ message,
75
+ MessageType.FRIEND_MESSAGE,
71
76
  )
72
77
  abm.session_id = abm.sender.user_id
73
78
  self._commit(abm)
@@ -80,14 +85,17 @@ class botClient(Client):
80
85
  self.platform.meta(),
81
86
  abm.session_id,
82
87
  self.platform.client,
83
- )
88
+ ),
84
89
  )
85
90
 
86
91
 
87
92
  @register_platform_adapter("qq_official", "QQ 机器人官方 API 适配器")
88
93
  class QQOfficialPlatformAdapter(Platform):
89
94
  def __init__(
90
- self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue
95
+ self,
96
+ platform_config: dict,
97
+ platform_settings: dict,
98
+ event_queue: asyncio.Queue,
91
99
  ) -> None:
92
100
  super().__init__(event_queue)
93
101
 
@@ -107,7 +115,8 @@ class QQOfficialPlatformAdapter(Platform):
107
115
  )
108
116
  else:
109
117
  self.intents = botpy.Intents(
110
- public_guild_messages=True, direct_message=guild_dm
118
+ public_guild_messages=True,
119
+ direct_message=guild_dm,
111
120
  )
112
121
  self.client = botClient(
113
122
  intents=self.intents,
@@ -120,7 +129,9 @@ class QQOfficialPlatformAdapter(Platform):
120
129
  self.test_mode = os.environ.get("TEST_MODE", "off") == "on"
121
130
 
122
131
  async def send_by_session(
123
- self, session: MessageSesion, message_chain: MessageChain
132
+ self,
133
+ session: MessageSesion,
134
+ message_chain: MessageChain,
124
135
  ):
125
136
  raise NotImplementedError("QQ 机器人官方 API 适配器不支持 send_by_session")
126
137
 
@@ -133,7 +144,7 @@ class QQOfficialPlatformAdapter(Platform):
133
144
 
134
145
  @staticmethod
135
146
  def _parse_from_qqofficial(
136
- message: Union[botpy.message.Message, botpy.message.GroupMessage],
147
+ message: botpy.message.Message | botpy.message.GroupMessage,
137
148
  message_type: MessageType,
138
149
  ):
139
150
  abm = AstrBotMessage()
@@ -142,10 +153,11 @@ class QQOfficialPlatformAdapter(Platform):
142
153
  abm.raw_message = message
143
154
  abm.message_id = message.id
144
155
  abm.tag = "qq_official"
145
- msg: List[BaseMessageComponent] = []
156
+ msg: list[BaseMessageComponent] = []
146
157
 
147
158
  if isinstance(message, botpy.message.GroupMessage) or isinstance(
148
- message, botpy.message.C2CMessage
159
+ message,
160
+ botpy.message.C2CMessage,
149
161
  ):
150
162
  if isinstance(message, botpy.message.GroupMessage):
151
163
  abm.sender = MessageMember(message.author.member_openid, "")
@@ -167,7 +179,8 @@ class QQOfficialPlatformAdapter(Platform):
167
179
  abm.message = msg
168
180
 
169
181
  elif isinstance(message, botpy.message.Message) or isinstance(
170
- message, botpy.message.DirectMessage
182
+ message,
183
+ botpy.message.DirectMessage,
171
184
  ):
172
185
  try:
173
186
  abm.self_id = str(message.mentions[0].id)
@@ -175,7 +188,8 @@ class QQOfficialPlatformAdapter(Platform):
175
188
  abm.self_id = ""
176
189
 
177
190
  plain_content = message.content.replace(
178
- "<@!" + str(abm.self_id) + ">", ""
191
+ "<@!" + str(abm.self_id) + ">",
192
+ "",
179
193
  ).strip()
180
194
 
181
195
  if message.attachments:
@@ -189,7 +203,8 @@ class QQOfficialPlatformAdapter(Platform):
189
203
  abm.message = msg
190
204
  abm.message_str = plain_content
191
205
  abm.sender = MessageMember(
192
- str(message.author.id), str(message.author.username)
206
+ str(message.author.id),
207
+ str(message.author.username),
193
208
  )
194
209
  msg.append(At(qq="qq_official"))
195
210
  msg.append(Plain(plain_content))
@@ -1,19 +1,21 @@
1
- import botpy
2
- import logging
3
1
  import asyncio
2
+ import logging
3
+
4
+ import botpy
4
5
  import botpy.message
5
6
  import botpy.types
6
7
  import botpy.types.message
7
-
8
8
  from botpy import Client
9
- from astrbot.api.platform import Platform, AstrBotMessage, MessageType, PlatformMetadata
9
+
10
+ from astrbot import logger
10
11
  from astrbot.api.event import MessageChain
12
+ from astrbot.api.platform import AstrBotMessage, MessageType, Platform, PlatformMetadata
11
13
  from astrbot.core.platform.astr_message_event import MessageSesion
12
- from .qo_webhook_event import QQOfficialWebhookMessageEvent
14
+
13
15
  from ...register import register_platform_adapter
14
- from .qo_webhook_server import QQOfficialWebhook
15
16
  from ..qqofficial.qqofficial_platform_adapter import QQOfficialPlatformAdapter
16
- from astrbot import logger
17
+ from .qo_webhook_event import QQOfficialWebhookMessageEvent
18
+ from .qo_webhook_server import QQOfficialWebhook
17
19
 
18
20
  # remove logger handler
19
21
  for handler in logging.root.handlers[:]:
@@ -28,7 +30,8 @@ class botClient(Client):
28
30
  # 收到群消息
29
31
  async def on_group_at_message_create(self, message: botpy.message.GroupMessage):
30
32
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
31
- message, MessageType.GROUP_MESSAGE
33
+ message,
34
+ MessageType.GROUP_MESSAGE,
32
35
  )
33
36
  abm.session_id = (
34
37
  abm.sender.user_id if self.platform.unique_session else message.group_openid
@@ -38,7 +41,8 @@ class botClient(Client):
38
41
  # 收到频道消息
39
42
  async def on_at_message_create(self, message: botpy.message.Message):
40
43
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
41
- message, MessageType.GROUP_MESSAGE
44
+ message,
45
+ MessageType.GROUP_MESSAGE,
42
46
  )
43
47
  abm.session_id = (
44
48
  abm.sender.user_id if self.platform.unique_session else message.channel_id
@@ -48,7 +52,8 @@ class botClient(Client):
48
52
  # 收到私聊消息
49
53
  async def on_direct_message_create(self, message: botpy.message.DirectMessage):
50
54
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
51
- message, MessageType.FRIEND_MESSAGE
55
+ message,
56
+ MessageType.FRIEND_MESSAGE,
52
57
  )
53
58
  abm.session_id = abm.sender.user_id
54
59
  self._commit(abm)
@@ -56,7 +61,8 @@ class botClient(Client):
56
61
  # 收到 C2C 消息
57
62
  async def on_c2c_message_create(self, message: botpy.message.C2CMessage):
58
63
  abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
59
- message, MessageType.FRIEND_MESSAGE
64
+ message,
65
+ MessageType.FRIEND_MESSAGE,
60
66
  )
61
67
  abm.session_id = abm.sender.user_id
62
68
  self._commit(abm)
@@ -64,15 +70,22 @@ class botClient(Client):
64
70
  def _commit(self, abm: AstrBotMessage):
65
71
  self.platform.commit_event(
66
72
  QQOfficialWebhookMessageEvent(
67
- abm.message_str, abm, self.platform.meta(), abm.session_id, self
68
- )
73
+ abm.message_str,
74
+ abm,
75
+ self.platform.meta(),
76
+ abm.session_id,
77
+ self,
78
+ ),
69
79
  )
70
80
 
71
81
 
72
82
  @register_platform_adapter("qq_official_webhook", "QQ 机器人官方 API 适配器(Webhook)")
73
83
  class QQOfficialWebhookPlatformAdapter(Platform):
74
84
  def __init__(
75
- self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue
85
+ self,
86
+ platform_config: dict,
87
+ platform_settings: dict,
88
+ event_queue: asyncio.Queue,
76
89
  ) -> None:
77
90
  super().__init__(event_queue)
78
91
 
@@ -83,7 +96,9 @@ class QQOfficialWebhookPlatformAdapter(Platform):
83
96
  self.unique_session = platform_settings["unique_session"]
84
97
 
85
98
  intents = botpy.Intents(
86
- public_messages=True, public_guild_messages=True, direct_message=True
99
+ public_messages=True,
100
+ public_guild_messages=True,
101
+ direct_message=True,
87
102
  )
88
103
  self.client = botClient(
89
104
  intents=intents, # 已经无用
@@ -93,7 +108,9 @@ class QQOfficialWebhookPlatformAdapter(Platform):
93
108
  self.client.set_platform(self)
94
109
 
95
110
  async def send_by_session(
96
- self, session: MessageSesion, message_chain: MessageChain
111
+ self,
112
+ session: MessageSesion,
113
+ message_chain: MessageChain,
97
114
  ):
98
115
  raise NotImplementedError("QQ 机器人官方 API 适配器不支持 send_by_session")
99
116
 
@@ -106,7 +123,9 @@ class QQOfficialWebhookPlatformAdapter(Platform):
106
123
 
107
124
  async def run(self):
108
125
  self.webhook_helper = QQOfficialWebhook(
109
- self.config, self._event_queue, self.client
126
+ self.config,
127
+ self._event_queue,
128
+ self.client,
110
129
  )
111
130
  await self.webhook_helper.initialize()
112
131
  await self.webhook_helper.start_polling()
@@ -1,5 +1,7 @@
1
- from astrbot.api.platform import AstrBotMessage, PlatformMetadata
2
1
  from botpy import Client
2
+
3
+ from astrbot.api.platform import AstrBotMessage, PlatformMetadata
4
+
3
5
  from ..qqofficial.qqofficial_message_event import QQOfficialMessageEvent
4
6
 
5
7
 
@@ -1,10 +1,12 @@
1
- import quart
2
- import logging
3
1
  import asyncio
4
- from botpy import BotAPI, BotHttp, Client, Token, BotWebSocket, ConnectionSession
5
- from astrbot.api import logger
2
+ import logging
3
+
4
+ import quart
5
+ from botpy import BotAPI, BotHttp, BotWebSocket, Client, ConnectionSession, Token
6
6
  from cryptography.hazmat.primitives.asymmetric import ed25519
7
7
 
8
+ from astrbot.api import logger
9
+
8
10
  # remove logger handler
9
11
  for handler in logging.root.handlers[:]:
10
12
  logging.root.removeHandler(handler)
@@ -27,7 +29,9 @@ class QQOfficialWebhook:
27
29
 
28
30
  self.server = quart.Quart(__name__)
29
31
  self.server.add_url_rule(
30
- "/astrbot-qo-webhook/callback", view_func=self.callback, methods=["POST"]
32
+ "/astrbot-qo-webhook/callback",
33
+ view_func=self.callback,
34
+ methods=["POST"],
31
35
  )
32
36
  self.client = botpy_client
33
37
  self.event_queue = event_queue
@@ -62,7 +66,8 @@ class QQOfficialWebhook:
62
66
  seed = await self.repeat_seed(self.secret)
63
67
  private_key = ed25519.Ed25519PrivateKey.from_private_bytes(seed)
64
68
  msg = validation_payload.get("event_ts", "") + validation_payload.get(
65
- "plain_token", ""
69
+ "plain_token",
70
+ "",
66
71
  )
67
72
  # sign
68
73
  signature = private_key.sign(msg.encode()).hex()
@@ -99,7 +104,7 @@ class QQOfficialWebhook:
99
104
 
100
105
  async def start_polling(self):
101
106
  logger.info(
102
- f"将在 {self.callback_server_host}:{self.port} 端口启动 QQ 官方机器人 webhook 适配器。"
107
+ f"将在 {self.callback_server_host}:{self.port} 端口启动 QQ 官方机器人 webhook 适配器。",
103
108
  )
104
109
  await self.server.run_task(
105
110
  host=self.callback_server_host,
@@ -1,13 +1,22 @@
1
1
  import asyncio
2
2
  import json
3
3
  import time
4
+ from xml.etree import ElementTree as ET
5
+
4
6
  import websockets
5
- from websockets.asyncio.client import connect
6
- from typing import Optional
7
7
  from aiohttp import ClientSession, ClientTimeout
8
- from websockets.asyncio.client import ClientConnection
8
+ from websockets.asyncio.client import ClientConnection, connect
9
+
9
10
  from astrbot.api import logger
10
11
  from astrbot.api.event import MessageChain
12
+ from astrbot.api.message_components import (
13
+ At,
14
+ File,
15
+ Image,
16
+ Plain,
17
+ Record,
18
+ Reply,
19
+ )
11
20
  from astrbot.api.platform import (
12
21
  AstrBotMessage,
13
22
  MessageMember,
@@ -17,15 +26,6 @@ from astrbot.api.platform import (
17
26
  register_platform_adapter,
18
27
  )
19
28
  from astrbot.core.platform.astr_message_event import MessageSession
20
- from astrbot.api.message_components import (
21
- Plain,
22
- Image,
23
- At,
24
- File,
25
- Record,
26
- Reply,
27
- )
28
- from xml.etree import ElementTree as ET
29
29
 
30
30
 
31
31
  @register_platform_adapter(
@@ -34,18 +34,23 @@ from xml.etree import ElementTree as ET
34
34
  )
35
35
  class SatoriPlatformAdapter(Platform):
36
36
  def __init__(
37
- self, platform_config: dict, platform_settings: dict, event_queue: asyncio.Queue
37
+ self,
38
+ platform_config: dict,
39
+ platform_settings: dict,
40
+ event_queue: asyncio.Queue,
38
41
  ) -> None:
39
42
  super().__init__(event_queue)
40
43
  self.config = platform_config
41
44
  self.settings = platform_settings
42
45
 
43
46
  self.api_base_url = self.config.get(
44
- "satori_api_base_url", "http://localhost:5140/satori/v1"
47
+ "satori_api_base_url",
48
+ "http://localhost:5140/satori/v1",
45
49
  )
46
50
  self.token = self.config.get("satori_token", "")
47
51
  self.endpoint = self.config.get(
48
- "satori_endpoint", "ws://localhost:5140/satori/v1/events"
52
+ "satori_endpoint",
53
+ "ws://localhost:5140/satori/v1/events",
49
54
  )
50
55
  self.auto_reconnect = self.config.get("satori_auto_reconnect", True)
51
56
  self.heartbeat_interval = self.config.get("satori_heartbeat_interval", 10)
@@ -57,21 +62,25 @@ class SatoriPlatformAdapter(Platform):
57
62
  id=self.config["id"],
58
63
  )
59
64
 
60
- self.ws: Optional[ClientConnection] = None
61
- self.session: Optional[ClientSession] = None
65
+ self.ws: ClientConnection | None = None
66
+ self.session: ClientSession | None = None
62
67
  self.sequence = 0
63
68
  self.logins = []
64
69
  self.running = False
65
- self.heartbeat_task: Optional[asyncio.Task] = None
70
+ self.heartbeat_task: asyncio.Task | None = None
66
71
  self.ready_received = False
67
72
 
68
73
  async def send_by_session(
69
- self, session: MessageSession, message_chain: MessageChain
74
+ self,
75
+ session: MessageSession,
76
+ message_chain: MessageChain,
70
77
  ):
71
78
  from .satori_event import SatoriPlatformEvent
72
79
 
73
80
  await SatoriPlatformEvent.send_with_adapter(
74
- self, message_chain, session.session_id
81
+ self,
82
+ message_chain,
83
+ session.session_id,
75
84
  )
76
85
  await super().send_by_session(session, message_chain)
77
86
 
@@ -85,10 +94,9 @@ class SatoriPlatformAdapter(Platform):
85
94
  try:
86
95
  if hasattr(ws, "closed"):
87
96
  return ws.closed
88
- elif hasattr(ws, "close_code"):
97
+ if hasattr(ws, "close_code"):
89
98
  return ws.close_code is not None
90
- else:
91
- return False
99
+ return False
92
100
  except AttributeError:
93
101
  return False
94
102
 
@@ -240,7 +248,7 @@ class SatoriPlatformAdapter(Platform):
240
248
  user_id = user.get("id", "")
241
249
  user_name = user.get("name", "")
242
250
  logger.info(
243
- f"Satori 连接成功 - Bot {i + 1}: platform={platform}, user_id={user_id}, user_name={user_name}"
251
+ f"Satori 连接成功 - Bot {i + 1}: platform={platform}, user_id={user_id}, user_name={user_name}",
244
252
  )
245
253
 
246
254
  if "sn" in body:
@@ -282,7 +290,12 @@ class SatoriPlatformAdapter(Platform):
282
290
  return
283
291
 
284
292
  abm = await self.convert_satori_message(
285
- message, user, channel, guild, login, timestamp
293
+ message,
294
+ user,
295
+ channel,
296
+ guild,
297
+ login,
298
+ timestamp,
286
299
  )
287
300
  if abm:
288
301
  await self.handle_msg(abm)
@@ -295,10 +308,10 @@ class SatoriPlatformAdapter(Platform):
295
308
  message: dict,
296
309
  user: dict,
297
310
  channel: dict,
298
- guild: Optional[dict],
311
+ guild: dict | None,
299
312
  login: dict,
300
- timestamp: Optional[int] = None,
301
- ) -> Optional[AstrBotMessage]:
313
+ timestamp: int | None = None,
314
+ ) -> AstrBotMessage | None:
302
315
  try:
303
316
  abm = AstrBotMessage()
304
317
  abm.message_id = message.get("id", "")
@@ -438,7 +451,7 @@ class SatoriPlatformAdapter(Platform):
438
451
 
439
452
  return prefixes
440
453
 
441
- async def _extract_quote_element(self, content: str) -> Optional[dict]:
454
+ async def _extract_quote_element(self, content: str) -> dict | None:
442
455
  """提取<quote>标签信息"""
443
456
  try:
444
457
  # 处理命名空间前缀问题
@@ -451,7 +464,7 @@ class SatoriPlatformAdapter(Platform):
451
464
  [
452
465
  f'xmlns:{prefix}="http://temp.uri/{prefix}"'
453
466
  for prefix in prefixes
454
- ]
467
+ ],
455
468
  )
456
469
 
457
470
  # 包装内容
@@ -483,14 +496,17 @@ class SatoriPlatformAdapter(Platform):
483
496
  inner_content += quote_element.text
484
497
  for child in quote_element:
485
498
  inner_content += ET.tostring(
486
- child, encoding="unicode", method="xml"
499
+ child,
500
+ encoding="unicode",
501
+ method="xml",
487
502
  )
488
503
  if child.tail:
489
504
  inner_content += child.tail
490
505
 
491
506
  # 构造移除了<quote>标签的内容
492
507
  content_without_quote = content.replace(
493
- ET.tostring(quote_element, encoding="unicode", method="xml"), ""
508
+ ET.tostring(quote_element, encoding="unicode", method="xml"),
509
+ "",
494
510
  )
495
511
 
496
512
  return {
@@ -506,7 +522,7 @@ class SatoriPlatformAdapter(Platform):
506
522
  logger.error(f"提取<quote>标签时发生错误: {e}")
507
523
  return None
508
524
 
509
- async def _extract_quote_with_regex(self, content: str) -> Optional[dict]:
525
+ async def _extract_quote_with_regex(self, content: str) -> dict | None:
510
526
  """使用正则表达式提取quote标签信息"""
511
527
  import re
512
528
 
@@ -529,7 +545,7 @@ class SatoriPlatformAdapter(Platform):
529
545
  "content_without_quote": content_without_quote,
530
546
  }
531
547
 
532
- async def _convert_quote_message(self, quote: dict) -> Optional[AstrBotMessage]:
548
+ async def _convert_quote_message(self, quote: dict) -> AstrBotMessage | None:
533
549
  """转换引用消息"""
534
550
  try:
535
551
  quote_abm = AstrBotMessage()
@@ -587,7 +603,7 @@ class SatoriPlatformAdapter(Platform):
587
603
  [
588
604
  f'xmlns:{prefix}="http://temp.uri/{prefix}"'
589
605
  for prefix in prefixes
590
- ]
606
+ ],
591
607
  )
592
608
 
593
609
  # 包装内容
@@ -747,13 +763,15 @@ class SatoriPlatformAdapter(Platform):
747
763
 
748
764
  try:
749
765
  async with self.session.request(
750
- method, url, json=data, headers=headers
766
+ method,
767
+ url,
768
+ json=data,
769
+ headers=headers,
751
770
  ) as response:
752
771
  if response.status == 200:
753
772
  result = await response.json()
754
773
  return result
755
- else:
756
- return {}
774
+ return {}
757
775
  except Exception as e:
758
776
  logger.error(f"Satori HTTP 请求异常: {e}")
759
777
  return {}