AstrBot 4.1.1__py3-none-any.whl → 4.1.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 (47) hide show
  1. astrbot/cli/utils/plugin.py +22 -18
  2. astrbot/core/agent/response.py +1 -0
  3. astrbot/core/agent/run_context.py +1 -0
  4. astrbot/core/agent/runners/tool_loop_agent_runner.py +1 -1
  5. astrbot/core/config/default.py +34 -11
  6. astrbot/core/db/migration/helper.py +1 -1
  7. astrbot/core/db/migration/shared_preferences_v3.py +2 -0
  8. astrbot/core/db/migration/sqlite_v3.py +2 -1
  9. astrbot/core/db/vec_db/faiss_impl/__init__.py +1 -1
  10. astrbot/core/db/vec_db/faiss_impl/vec_db.py +2 -1
  11. astrbot/core/pipeline/context_utils.py +1 -1
  12. astrbot/core/pipeline/respond/stage.py +103 -77
  13. astrbot/core/platform/astr_message_event.py +1 -1
  14. astrbot/core/platform/astrbot_message.py +23 -1
  15. astrbot/core/platform/manager.py +9 -9
  16. astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +4 -1
  17. astrbot/core/platform/sources/dingtalk/dingtalk_event.py +2 -2
  18. astrbot/core/platform/sources/discord/client.py +2 -2
  19. astrbot/core/platform/sources/discord/components.py +3 -1
  20. astrbot/core/platform/sources/discord/discord_platform_event.py +8 -3
  21. astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +36 -30
  22. astrbot/core/platform/sources/slack/slack_adapter.py +3 -1
  23. astrbot/core/platform/sources/slack/slack_event.py +7 -1
  24. astrbot/core/platform/sources/telegram/tg_event.py +3 -1
  25. astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +2 -0
  26. astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +8 -4
  27. astrbot/core/platform/sources/wecom/wecom_kf.py +15 -4
  28. astrbot/core/platform/sources/wecom/wecom_kf_message.py +27 -6
  29. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +3 -1
  30. astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +0 -1
  31. astrbot/core/provider/entities.py +1 -0
  32. astrbot/core/provider/manager.py +5 -1
  33. astrbot/core/provider/sources/fishaudio_tts_api_source.py +1 -1
  34. astrbot/core/star/filter/command.py +7 -12
  35. astrbot/core/star/filter/command_group.py +1 -2
  36. astrbot/core/star/session_plugin_manager.py +4 -1
  37. astrbot/core/star/star_manager.py +2 -2
  38. astrbot/core/star/star_tools.py +22 -8
  39. astrbot/dashboard/routes/config.py +0 -1
  40. astrbot/dashboard/routes/log.py +12 -4
  41. astrbot/dashboard/routes/route.py +0 -1
  42. astrbot/dashboard/routes/tools.py +0 -1
  43. {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/METADATA +1 -1
  44. {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/RECORD +47 -47
  45. {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/WHEEL +0 -0
  46. {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/entry_points.txt +0 -0
  47. {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/licenses/LICENSE +0 -0
@@ -79,9 +79,12 @@ class DiscordButton(BaseMessageComponent):
79
79
  self.url = url
80
80
  self.disabled = disabled
81
81
 
82
+
82
83
  class DiscordReference(BaseMessageComponent):
83
84
  """Discord引用组件"""
85
+
84
86
  type: str = "discord_reference"
87
+
85
88
  def __init__(self, message_id: str, channel_id: str):
86
89
  self.message_id = message_id
87
90
  self.channel_id = channel_id
@@ -98,7 +101,6 @@ class DiscordView(BaseMessageComponent):
98
101
  self.components = components or []
99
102
  self.timeout = timeout
100
103
 
101
-
102
104
  def to_discord_view(self) -> discord.ui.View:
103
105
  """转换为Discord View对象"""
104
106
  view = discord.ui.View(timeout=self.timeout)
@@ -53,7 +53,13 @@ class DiscordPlatformEvent(AstrMessageEvent):
53
53
 
54
54
  # 解析消息链为 Discord 所需的对象
55
55
  try:
56
- content, files, view, embeds, reference_message_id = await self._parse_to_discord(message)
56
+ (
57
+ content,
58
+ files,
59
+ view,
60
+ embeds,
61
+ reference_message_id,
62
+ ) = await self._parse_to_discord(message)
57
63
  except Exception as e:
58
64
  logger.error(f"[Discord] 解析消息链时失败: {e}", exc_info=True)
59
65
  return
@@ -206,8 +212,7 @@ class DiscordPlatformEvent(AstrMessageEvent):
206
212
  if await asyncio.to_thread(path.exists):
207
213
  file_bytes = await asyncio.to_thread(path.read_bytes)
208
214
  files.append(
209
- discord.File(BytesIO(file_bytes),
210
- filename=i.name)
215
+ discord.File(BytesIO(file_bytes), filename=i.name)
211
216
  )
212
217
  else:
213
218
  logger.warning(
@@ -94,10 +94,15 @@ class QQOfficialMessageEvent(AstrMessageEvent):
94
94
  plain_text,
95
95
  image_base64,
96
96
  image_path,
97
- record_file_path
97
+ record_file_path,
98
98
  ) = await QQOfficialMessageEvent._parse_to_qqofficial(self.send_buffer)
99
99
 
100
- if not plain_text and not image_base64 and not image_path and not record_file_path:
100
+ if (
101
+ not plain_text
102
+ and not image_base64
103
+ and not image_path
104
+ and not record_file_path
105
+ ):
101
106
  return
102
107
 
103
108
  payload = {
@@ -118,7 +123,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
118
123
  )
119
124
  payload["media"] = media
120
125
  payload["msg_type"] = 7
121
- if record_file_path: # group record msg
126
+ if record_file_path: # group record msg
122
127
  media = await self.upload_group_and_c2c_record(
123
128
  record_file_path, 3, group_openid=source.group_openid
124
129
  )
@@ -134,9 +139,9 @@ class QQOfficialMessageEvent(AstrMessageEvent):
134
139
  )
135
140
  payload["media"] = media
136
141
  payload["msg_type"] = 7
137
- if record_file_path: # c2c record
142
+ if record_file_path: # c2c record
138
143
  media = await self.upload_group_and_c2c_record(
139
- record_file_path, 3, openid = source.author.user_openid
144
+ record_file_path, 3, openid=source.author.user_openid
140
145
  )
141
146
  payload["media"] = media
142
147
  payload["msg_type"] = 7
@@ -190,58 +195,55 @@ class QQOfficialMessageEvent(AstrMessageEvent):
190
195
  return await self.bot.api._http.request(route, json=payload)
191
196
 
192
197
  async def upload_group_and_c2c_record(
193
- self,
194
- file_source: str,
195
- file_type: int,
196
- srv_send_msg: bool = False,
197
- **kwargs
198
+ self, file_source: str, file_type: int, srv_send_msg: bool = False, **kwargs
198
199
  ) -> Optional[Media]:
199
200
  """
200
201
  上传媒体文件
201
202
  """
202
203
  # 构建基础payload
203
- payload = {
204
- "file_type": file_type,
205
- "srv_send_msg": srv_send_msg
206
- }
207
-
204
+ payload = {"file_type": file_type, "srv_send_msg": srv_send_msg}
205
+
208
206
  # 处理文件数据
209
207
  if os.path.exists(file_source):
210
208
  # 读取本地文件
211
- async with aiofiles.open(file_source, 'rb') as f:
209
+ async with aiofiles.open(file_source, "rb") as f:
212
210
  file_content = await f.read()
213
211
  # use base64 encode
214
- payload["file_data"] = base64.b64encode(file_content).decode('utf-8')
212
+ payload["file_data"] = base64.b64encode(file_content).decode("utf-8")
215
213
  else:
216
214
  # 使用URL
217
215
  payload["url"] = file_source
218
-
216
+
219
217
  # 添加接收者信息和确定路由
220
218
  if "openid" in kwargs:
221
219
  payload["openid"] = kwargs["openid"]
222
220
  route = Route("POST", "/v2/users/{openid}/files", openid=kwargs["openid"])
223
221
  elif "group_openid" in kwargs:
224
- payload["group_openid"] =kwargs["group_openid"]
225
- route = Route("POST", "/v2/groups/{group_openid}/files", group_openid=kwargs["group_openid"])
222
+ payload["group_openid"] = kwargs["group_openid"]
223
+ route = Route(
224
+ "POST",
225
+ "/v2/groups/{group_openid}/files",
226
+ group_openid=kwargs["group_openid"],
227
+ )
226
228
  else:
227
229
  return None
228
-
230
+
229
231
  try:
230
232
  # 使用底层HTTP请求
231
233
  result = await self.bot.api._http.request(route, json=payload)
232
-
234
+
233
235
  if result:
234
236
  return Media(
235
237
  file_uuid=result.get("file_uuid"),
236
238
  file_info=result.get("file_info"),
237
239
  ttl=result.get("ttl", 0),
238
- file_id=result.get("id", "")
240
+ file_id=result.get("id", ""),
239
241
  )
240
242
  except Exception as e:
241
243
  logger.error(f"上传请求错误: {e}")
242
-
244
+
243
245
  return None
244
-
246
+
245
247
  async def post_c2c_message(
246
248
  self,
247
249
  openid: str,
@@ -286,19 +288,23 @@ class QQOfficialMessageEvent(AstrMessageEvent):
286
288
  image_base64 = image_base64.removeprefix("base64://")
287
289
  elif isinstance(i, Record):
288
290
  if i.file:
289
- record_wav_path = await i.convert_to_file_path() # wav 路径
291
+ record_wav_path = await i.convert_to_file_path() # wav 路径
290
292
  temp_dir = os.path.join(get_astrbot_data_path(), "temp")
291
- record_tecent_silk_path = os.path.join(temp_dir, f"{uuid.uuid4()}.silk")
293
+ record_tecent_silk_path = os.path.join(
294
+ temp_dir, f"{uuid.uuid4()}.silk"
295
+ )
292
296
  try:
293
- duration = await wav_to_tencent_silk(record_wav_path, record_tecent_silk_path)
297
+ duration = await wav_to_tencent_silk(
298
+ record_wav_path, record_tecent_silk_path
299
+ )
294
300
  if duration > 0:
295
301
  record_file_path = record_tecent_silk_path
296
302
  else:
297
- record_file_path = None
303
+ record_file_path = None
298
304
  logger.error("转换音频格式时出错:音频时长不大于0")
299
305
  except Exception as e:
300
306
  logger.error(f"处理语音时出错: {e}")
301
- record_file_path = None
307
+ record_file_path = None
302
308
  else:
303
309
  logger.debug(f"qq_official 忽略 {i.type}")
304
310
  return plain_text, image_base64, image_file_path, record_file_path
@@ -308,7 +308,9 @@ class SlackAdapter(Platform):
308
308
  base64_content = base64.b64encode(content).decode("utf-8")
309
309
  return base64_content
310
310
  else:
311
- logger.error(f"Failed to download slack file: {resp.status} {await resp.text()}")
311
+ logger.error(
312
+ f"Failed to download slack file: {resp.status} {await resp.text()}"
313
+ )
312
314
  raise Exception(f"下载文件失败: {resp.status}")
313
315
 
314
316
  async def run(self) -> Awaitable[Any]:
@@ -75,7 +75,13 @@ class SlackMessageEvent(AstrMessageEvent):
75
75
  "text": {"type": "mrkdwn", "text": "文件上传失败"},
76
76
  }
77
77
  file_url = response["files"][0]["permalink"]
78
- return {"type": "section", "text": {"type": "mrkdwn", "text": f"文件: <{file_url}|{segment.name or '文件'}>"}}
78
+ return {
79
+ "type": "section",
80
+ "text": {
81
+ "type": "mrkdwn",
82
+ "text": f"文件: <{file_url}|{segment.name or '文件'}>",
83
+ },
84
+ }
79
85
  else:
80
86
  return {"type": "section", "text": {"type": "mrkdwn", "text": str(segment)}}
81
87
 
@@ -66,7 +66,9 @@ class TelegramPlatformEvent(AstrMessageEvent):
66
66
  return chunks
67
67
 
68
68
  @classmethod
69
- async def send_with_client(cls, client: ExtBot, message: MessageChain, user_name: str):
69
+ async def send_with_client(
70
+ cls, client: ExtBot, message: MessageChain, user_name: str
71
+ ):
70
72
  image_path = None
71
73
 
72
74
  has_reply = False
@@ -1,5 +1,6 @@
1
1
  import asyncio
2
2
 
3
+
3
4
  class WebChatQueueMgr:
4
5
  def __init__(self) -> None:
5
6
  self.queues = {}
@@ -30,4 +31,5 @@ class WebChatQueueMgr:
30
31
  """Check if a queue exists for the given conversation ID"""
31
32
  return conversation_id in self.queues
32
33
 
34
+
33
35
  webchat_queue_mgr = WebChatQueueMgr()
@@ -213,10 +213,10 @@ class WeChatPadProAdapter(Platform):
213
213
  def _extract_auth_key(self, data):
214
214
  """Helper method to extract auth_key from response data."""
215
215
  if isinstance(data, dict):
216
- auth_keys = data.get("authKeys") # 新接口
216
+ auth_keys = data.get("authKeys") # 新接口
217
217
  if isinstance(auth_keys, list) and auth_keys:
218
218
  return auth_keys[0]
219
- elif isinstance(data, list) and data: # 旧接口
219
+ elif isinstance(data, list) and data: # 旧接口
220
220
  return data[0]
221
221
  return None
222
222
 
@@ -234,7 +234,9 @@ class WeChatPadProAdapter(Platform):
234
234
  try:
235
235
  async with session.post(url, params=params, json=payload) as response:
236
236
  if response.status != 200:
237
- logger.error(f"生成授权码失败: {response.status}, {await response.text()}")
237
+ logger.error(
238
+ f"生成授权码失败: {response.status}, {await response.text()}"
239
+ )
238
240
  return
239
241
 
240
242
  response_data = await response.json()
@@ -245,7 +247,9 @@ class WeChatPadProAdapter(Platform):
245
247
  if self.auth_key:
246
248
  logger.info("成功获取授权码")
247
249
  else:
248
- logger.error(f"生成授权码成功但未找到授权码: {response_data}")
250
+ logger.error(
251
+ f"生成授权码成功但未找到授权码: {response_data}"
252
+ )
249
253
  else:
250
254
  logger.error(f"生成授权码失败: {response_data}")
251
255
  except aiohttp.ClientConnectorError as e:
@@ -48,7 +48,12 @@ class WeChatKF(BaseWeChatAPI):
48
48
  注意:可能会出现返回条数少于limit的情况,需结合返回的has_more字段判断是否继续请求。
49
49
  :return: 接口调用结果
50
50
  """
51
- data = {"token": token, "cursor": cursor, "limit": limit, "open_kfid": open_kfid}
51
+ data = {
52
+ "token": token,
53
+ "cursor": cursor,
54
+ "limit": limit,
55
+ "open_kfid": open_kfid,
56
+ }
52
57
  return self._post("kf/sync_msg", data=data)
53
58
 
54
59
  def get_service_state(self, open_kfid, external_userid):
@@ -72,7 +77,9 @@ class WeChatKF(BaseWeChatAPI):
72
77
  }
73
78
  return self._post("kf/service_state/get", data=data)
74
79
 
75
- def trans_service_state(self, open_kfid, external_userid, service_state, servicer_userid=""):
80
+ def trans_service_state(
81
+ self, open_kfid, external_userid, service_state, servicer_userid=""
82
+ ):
76
83
  """
77
84
  变更会话状态
78
85
 
@@ -180,7 +187,9 @@ class WeChatKF(BaseWeChatAPI):
180
187
  """
181
188
  return self._get("kf/customer/get_upgrade_service_config")
182
189
 
183
- def upgrade_service(self, open_kfid, external_userid, service_type, member=None, groupchat=None):
190
+ def upgrade_service(
191
+ self, open_kfid, external_userid, service_type, member=None, groupchat=None
192
+ ):
184
193
  """
185
194
  为客户升级为专员或客户群服务
186
195
 
@@ -246,7 +255,9 @@ class WeChatKF(BaseWeChatAPI):
246
255
  data = {"open_kfid": open_kfid, "start_time": start_time, "end_time": end_time}
247
256
  return self._post("kf/get_corp_statistic", data=data)
248
257
 
249
- def get_servicer_statistic(self, start_time, end_time, open_kfid=None, servicer_userid=None):
258
+ def get_servicer_statistic(
259
+ self, start_time, end_time, open_kfid=None, servicer_userid=None
260
+ ):
250
261
  """
251
262
  获取「客户数据统计」接待人员明细数据
252
263
 
@@ -26,6 +26,7 @@ from optionaldict import optionaldict
26
26
 
27
27
  from wechatpy.client.api.base import BaseWeChatAPI
28
28
 
29
+
29
30
  class WeChatKFMessage(BaseWeChatAPI):
30
31
  """
31
32
  发送微信客服消息
@@ -125,35 +126,55 @@ class WeChatKFMessage(BaseWeChatAPI):
125
126
  msg={"msgtype": "news", "link": {"link": articles_data}},
126
127
  )
127
128
 
128
- def send_msgmenu(self, user_id, open_kfid, head_content, menu_list, tail_content, msgid=""):
129
+ def send_msgmenu(
130
+ self, user_id, open_kfid, head_content, menu_list, tail_content, msgid=""
131
+ ):
129
132
  return self.send(
130
133
  user_id,
131
134
  open_kfid,
132
135
  msgid,
133
136
  msg={
134
137
  "msgtype": "msgmenu",
135
- "msgmenu": {"head_content": head_content, "list": menu_list, "tail_content": tail_content},
138
+ "msgmenu": {
139
+ "head_content": head_content,
140
+ "list": menu_list,
141
+ "tail_content": tail_content,
142
+ },
136
143
  },
137
144
  )
138
145
 
139
- def send_location(self, user_id, open_kfid, name, address, latitude, longitude, msgid=""):
146
+ def send_location(
147
+ self, user_id, open_kfid, name, address, latitude, longitude, msgid=""
148
+ ):
140
149
  return self.send(
141
150
  user_id,
142
151
  open_kfid,
143
152
  msgid,
144
153
  msg={
145
154
  "msgtype": "location",
146
- "msgmenu": {"name": name, "address": address, "latitude": latitude, "longitude": longitude},
155
+ "msgmenu": {
156
+ "name": name,
157
+ "address": address,
158
+ "latitude": latitude,
159
+ "longitude": longitude,
160
+ },
147
161
  },
148
162
  )
149
163
 
150
- def send_miniprogram(self, user_id, open_kfid, appid, title, thumb_media_id, pagepath, msgid=""):
164
+ def send_miniprogram(
165
+ self, user_id, open_kfid, appid, title, thumb_media_id, pagepath, msgid=""
166
+ ):
151
167
  return self.send(
152
168
  user_id,
153
169
  open_kfid,
154
170
  msgid,
155
171
  msg={
156
172
  "msgtype": "miniprogram",
157
- "msgmenu": {"appid": appid, "title": title, "thumb_media_id": thumb_media_id, "pagepath": pagepath},
173
+ "msgmenu": {
174
+ "appid": appid,
175
+ "title": title,
176
+ "thumb_media_id": thumb_media_id,
177
+ "pagepath": pagepath,
178
+ },
158
179
  },
159
180
  )
@@ -160,7 +160,9 @@ class WeixinOfficialAccountPlatformAdapter(Platform):
160
160
  self.wexin_event_workers[msg.id] = future
161
161
  await self.convert_message(msg, future)
162
162
  # I love shield so much!
163
- result = await asyncio.wait_for(asyncio.shield(future), 60) # wait for 60s
163
+ result = await asyncio.wait_for(
164
+ asyncio.shield(future), 60
165
+ ) # wait for 60s
164
166
  logger.debug(f"Got future result: {result}")
165
167
  self.wexin_event_workers.pop(msg.id, None)
166
168
  return result # xml. see weixin_offacc_event.py
@@ -150,7 +150,6 @@ class WeixinOfficialAccountPlatformEvent(AstrMessageEvent):
150
150
  return
151
151
  logger.info(f"微信公众平台上传语音返回: {response}")
152
152
 
153
-
154
153
  if active_send_mode:
155
154
  self.client.message.send_voice(
156
155
  message_obj.sender.user_id,
@@ -297,6 +297,7 @@ class LLMResponse:
297
297
  )
298
298
  return ret
299
299
 
300
+
300
301
  @dataclass
301
302
  class RerankResult:
302
303
  index: int
@@ -366,7 +366,10 @@ class ProviderManager:
366
366
  if not self.curr_provider_inst:
367
367
  self.curr_provider_inst = inst
368
368
 
369
- elif provider_metadata.provider_type in [ProviderType.EMBEDDING, ProviderType.RERANK]:
369
+ elif provider_metadata.provider_type in [
370
+ ProviderType.EMBEDDING,
371
+ ProviderType.RERANK,
372
+ ]:
370
373
  inst = provider_metadata.cls_type(
371
374
  provider_config, self.provider_settings
372
375
  )
@@ -388,6 +391,7 @@ class ProviderManager:
388
391
 
389
392
  # 和配置文件保持同步
390
393
  config_ids = [provider["id"] for provider in self.providers_config]
394
+ logger.debug(f"providers in user's config: {config_ids}")
391
395
  for key in list(self.inst_map.keys()):
392
396
  if key not in config_ids:
393
397
  await self.terminate_provider(key)
@@ -98,7 +98,7 @@ class ProviderFishAudioTTSAPI(TTSProvider):
98
98
 
99
99
  # FishAudio的reference_id通常是32位十六进制字符串
100
100
  # 例如: 626bb6d3f3364c9cbc3aa6a67300a664
101
- pattern = r'^[a-fA-F0-9]{32}$'
101
+ pattern = r"^[a-fA-F0-9]{32}$"
102
102
  return bool(re.match(pattern, reference_id.strip()))
103
103
 
104
104
  async def _generate_request(self, text: str) -> dict:
@@ -7,6 +7,7 @@ from astrbot.core.config import AstrBotConfig
7
7
  from .custom_filter import CustomFilter
8
8
  from ..star_handler import StarHandlerMetadata
9
9
 
10
+
10
11
  class GreedyStr(str):
11
12
  """标记指令完成其他参数接收后的所有剩余文本。"""
12
13
 
@@ -51,10 +52,11 @@ class CommandFilter(HandlerFilter):
51
52
  # 忽略前两个参数,即 self 和 event
52
53
  idx += 1
53
54
  continue
54
- if v.default == inspect.Parameter.empty:
55
- self.handler_params[k] = v.annotation
56
- else:
55
+ # 优先类型注解 其次默认值
56
+ if v.annotation == inspect.Parameter.empty:
57
57
  self.handler_params[k] = v.default
58
+ else:
59
+ self.handler_params[k] = v.annotation
58
60
 
59
61
  def get_handler_md(self) -> StarHandlerMetadata:
60
62
  return self.handler_md
@@ -152,17 +154,10 @@ class CommandFilter(HandlerFilter):
152
154
  _full = f"{parent_command_name} {candidate}"
153
155
  else:
154
156
  _full = candidate
155
- if message_str == _full:
156
- # 完全等于命令名 没参数
157
- message_str = ""
158
- ok = True
159
- break
160
- elif message_str.startswith(_full):
161
- # 命令名后面无论是空格还是直接连参数都可以
162
- message_str = message_str[len(_full):].lstrip()
157
+ if message_str.startswith(f"{_full} ") or message_str == _full:
158
+ message_str = message_str[len(_full) :].strip()
163
159
  ok = True
164
160
  break
165
-
166
161
  if not ok:
167
162
  return False
168
163
 
@@ -113,8 +113,7 @@ class CommandGroupFilter(HandlerFilter):
113
113
  + self.print_cmd_tree(self.sub_command_filters, event=event, cfg=cfg)
114
114
  )
115
115
  raise ValueError(
116
- f"参数不足。{self.group_name} 指令组下有如下指令,请参考:\n"
117
- + tree
116
+ f"参数不足。{self.group_name} 指令组下有如下指令,请参考:\n" + tree
118
117
  )
119
118
 
120
119
  # complete_command_names = [name + " " for name in complete_command_names]
@@ -84,7 +84,10 @@ class SessionPluginManager:
84
84
  session_config["disabled_plugins"] = disabled_plugins
85
85
  session_plugin_config[session_id] = session_config
86
86
  sp.put(
87
- "session_plugin_config", session_plugin_config, scope="umo", scope_id=session_id
87
+ "session_plugin_config",
88
+ session_plugin_config,
89
+ scope="umo",
90
+ scope_id=session_id,
88
91
  )
89
92
 
90
93
  logger.info(
@@ -791,11 +791,11 @@ class PluginManager:
791
791
  if star_metadata.star_cls is None:
792
792
  return
793
793
 
794
- if '__del__' in star_metadata.star_cls_type.__dict__:
794
+ if "__del__" in star_metadata.star_cls_type.__dict__:
795
795
  asyncio.get_event_loop().run_in_executor(
796
796
  None, star_metadata.star_cls.__del__
797
797
  )
798
- elif 'terminate' in star_metadata.star_cls_type.__dict__:
798
+ elif "terminate" in star_metadata.star_cls_type.__dict__:
799
799
  await star_metadata.star_cls.terminate()
800
800
 
801
801
  async def turn_on_plugin(self, plugin_name: str):
@@ -30,8 +30,13 @@ from astrbot.core.platform.astr_message_event import MessageSesion
30
30
  from astrbot.core.star.context import Context
31
31
  from astrbot.core.star.star import star_map
32
32
  from astrbot.core.utils.astrbot_path import get_astrbot_data_path
33
- from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import AiocqhttpMessageEvent
34
- from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_platform_adapter import AiocqhttpAdapter
33
+ from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_message_event import (
34
+ AiocqhttpMessageEvent,
35
+ )
36
+ from astrbot.core.platform.sources.aiocqhttp.aiocqhttp_platform_adapter import (
37
+ AiocqhttpAdapter,
38
+ )
39
+
35
40
 
36
41
  class StarTools:
37
42
  """
@@ -77,7 +82,11 @@ class StarTools:
77
82
 
78
83
  @classmethod
79
84
  async def send_message_by_id(
80
- cls, type: str, id: str, message_chain: MessageChain, platform: str = "aiocqhttp"
85
+ cls,
86
+ type: str,
87
+ id: str,
88
+ message_chain: MessageChain,
89
+ platform: str = "aiocqhttp",
81
90
  ):
82
91
  """
83
92
  根据 id(例如qq号, 群号等) 直接, 主动地发送消息
@@ -92,7 +101,9 @@ class StarTools:
92
101
  raise ValueError("StarTools not initialized")
93
102
  platforms = cls._context.platform_manager.get_insts()
94
103
  if platform == "aiocqhttp":
95
- adapter = next((p for p in platforms if isinstance(p, AiocqhttpAdapter)), None)
104
+ adapter = next(
105
+ (p for p in platforms if isinstance(p, AiocqhttpAdapter)), None
106
+ )
96
107
  if adapter is None:
97
108
  raise ValueError("未找到适配器: AiocqhttpAdapter")
98
109
  await AiocqhttpMessageEvent.send_message(
@@ -115,7 +126,7 @@ class StarTools:
115
126
  message_str: str,
116
127
  message_id: str = "",
117
128
  raw_message: object = None,
118
- group_id: str = ""
129
+ group_id: str = "",
119
130
  ) -> AstrBotMessage:
120
131
  """
121
132
  创建一个AstrBot消息对象
@@ -152,7 +163,6 @@ class StarTools:
152
163
  @classmethod
153
164
  async def create_event(
154
165
  cls, abm: AstrBotMessage, platform: str = "aiocqhttp", is_wake: bool = True
155
-
156
166
  ) -> None:
157
167
  """
158
168
  创建并提交事件到指定平台
@@ -167,7 +177,9 @@ class StarTools:
167
177
  raise ValueError("StarTools not initialized")
168
178
  platforms = cls._context.platform_manager.get_insts()
169
179
  if platform == "aiocqhttp":
170
- adapter = next((p for p in platforms if isinstance(p, AiocqhttpAdapter)), None)
180
+ adapter = next(
181
+ (p for p in platforms if isinstance(p, AiocqhttpAdapter)), None
182
+ )
171
183
  if adapter is None:
172
184
  raise ValueError("未找到适配器: AiocqhttpAdapter")
173
185
  event = AiocqhttpMessageEvent(
@@ -277,7 +289,9 @@ class StarTools:
277
289
  if not plugin_name:
278
290
  raise ValueError("无法获取插件名称")
279
291
 
280
- data_dir = Path(os.path.join(get_astrbot_data_path(), "plugin_data", plugin_name))
292
+ data_dir = Path(
293
+ os.path.join(get_astrbot_data_path(), "plugin_data", plugin_name)
294
+ )
281
295
 
282
296
  try:
283
297
  data_dir.mkdir(parents=True, exist_ok=True)
@@ -1,7 +1,6 @@
1
1
  import typing
2
2
  import traceback
3
3
  import os
4
- import copy
5
4
  from .route import Route, Response, RouteContext
6
5
  from astrbot.core.provider.entities import ProviderType
7
6
  from quart import request
@@ -10,7 +10,9 @@ class LogRoute(Route):
10
10
  super().__init__(context)
11
11
  self.log_broker = log_broker
12
12
  self.app.add_url_rule("/api/live-log", view_func=self.log, methods=["GET"])
13
- self.app.add_url_rule("/api/log-history", view_func=self.log_history, methods=["GET"])
13
+ self.app.add_url_rule(
14
+ "/api/log-history", view_func=self.log_history, methods=["GET"]
15
+ )
14
16
 
15
17
  async def log(self):
16
18
  async def stream():
@@ -48,9 +50,15 @@ class LogRoute(Route):
48
50
  """获取日志历史"""
49
51
  try:
50
52
  logs = list(self.log_broker.log_cache)
51
- return Response().ok(data={
52
- "logs": logs,
53
- }).__dict__
53
+ return (
54
+ Response()
55
+ .ok(
56
+ data={
57
+ "logs": logs,
58
+ }
59
+ )
60
+ .__dict__
61
+ )
54
62
  except BaseException as e:
55
63
  logger.error(f"获取日志历史失败: {e}")
56
64
  return Response().error(f"获取日志历史失败: {e}").__dict__