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.
- astrbot/cli/utils/plugin.py +22 -18
- astrbot/core/agent/response.py +1 -0
- astrbot/core/agent/run_context.py +1 -0
- astrbot/core/agent/runners/tool_loop_agent_runner.py +1 -1
- astrbot/core/config/default.py +34 -11
- astrbot/core/db/migration/helper.py +1 -1
- astrbot/core/db/migration/shared_preferences_v3.py +2 -0
- astrbot/core/db/migration/sqlite_v3.py +2 -1
- astrbot/core/db/vec_db/faiss_impl/__init__.py +1 -1
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +2 -1
- astrbot/core/pipeline/context_utils.py +1 -1
- astrbot/core/pipeline/respond/stage.py +103 -77
- astrbot/core/platform/astr_message_event.py +1 -1
- astrbot/core/platform/astrbot_message.py +23 -1
- astrbot/core/platform/manager.py +9 -9
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +4 -1
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +2 -2
- astrbot/core/platform/sources/discord/client.py +2 -2
- astrbot/core/platform/sources/discord/components.py +3 -1
- astrbot/core/platform/sources/discord/discord_platform_event.py +8 -3
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +36 -30
- astrbot/core/platform/sources/slack/slack_adapter.py +3 -1
- astrbot/core/platform/sources/slack/slack_event.py +7 -1
- astrbot/core/platform/sources/telegram/tg_event.py +3 -1
- astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +2 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +8 -4
- astrbot/core/platform/sources/wecom/wecom_kf.py +15 -4
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +27 -6
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +3 -1
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +0 -1
- astrbot/core/provider/entities.py +1 -0
- astrbot/core/provider/manager.py +5 -1
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +1 -1
- astrbot/core/star/filter/command.py +7 -12
- astrbot/core/star/filter/command_group.py +1 -2
- astrbot/core/star/session_plugin_manager.py +4 -1
- astrbot/core/star/star_manager.py +2 -2
- astrbot/core/star/star_tools.py +22 -8
- astrbot/dashboard/routes/config.py +0 -1
- astrbot/dashboard/routes/log.py +12 -4
- astrbot/dashboard/routes/route.py +0 -1
- astrbot/dashboard/routes/tools.py +0 -1
- {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/METADATA +1 -1
- {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/RECORD +47 -47
- {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/WHEEL +0 -0
- {astrbot-4.1.1.dist-info → astrbot-4.1.3.dist-info}/entry_points.txt +0 -0
- {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
|
-
|
|
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
|
|
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:
|
|
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:
|
|
142
|
+
if record_file_path: # c2c record
|
|
138
143
|
media = await self.upload_group_and_c2c_record(
|
|
139
|
-
record_file_path, 3, 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
|
-
|
|
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,
|
|
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(
|
|
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(
|
|
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()
|
|
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(
|
|
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(
|
|
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(
|
|
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 {
|
|
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(
|
|
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(
|
|
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(
|
|
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 = {
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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": {
|
|
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(
|
|
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": {
|
|
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(
|
|
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": {
|
|
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(
|
|
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
|
astrbot/core/provider/manager.py
CHANGED
|
@@ -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 [
|
|
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
|
|
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
|
-
|
|
55
|
-
|
|
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",
|
|
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
|
|
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
|
|
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):
|
astrbot/core/star/star_tools.py
CHANGED
|
@@ -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
|
|
34
|
-
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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)
|
astrbot/dashboard/routes/log.py
CHANGED
|
@@ -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(
|
|
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
|
|
52
|
-
|
|
53
|
-
|
|
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__
|