AstrBot 4.5.0__py3-none-any.whl → 4.5.2__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/api/__init__.py +10 -11
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -36
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +7 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__main__.py +5 -5
- astrbot/cli/commands/__init__.py +3 -3
- astrbot/cli/commands/cmd_conf.py +19 -16
- astrbot/cli/commands/cmd_init.py +3 -2
- astrbot/cli/commands/cmd_plug.py +8 -10
- astrbot/cli/commands/cmd_run.py +5 -6
- astrbot/cli/utils/__init__.py +6 -6
- astrbot/cli/utils/basic.py +14 -14
- astrbot/cli/utils/plugin.py +24 -15
- astrbot/cli/utils/version_comparator.py +10 -12
- astrbot/core/__init__.py +8 -6
- astrbot/core/agent/agent.py +3 -2
- astrbot/core/agent/handoff.py +6 -2
- astrbot/core/agent/hooks.py +9 -6
- astrbot/core/agent/mcp_client.py +50 -15
- astrbot/core/agent/message.py +168 -0
- astrbot/core/agent/response.py +2 -1
- astrbot/core/agent/run_context.py +2 -3
- astrbot/core/agent/runners/base.py +10 -13
- astrbot/core/agent/runners/tool_loop_agent_runner.py +52 -51
- astrbot/core/agent/tool.py +60 -41
- astrbot/core/agent/tool_executor.py +9 -3
- astrbot/core/astr_agent_context.py +3 -1
- astrbot/core/astrbot_config_mgr.py +29 -9
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +28 -26
- astrbot/core/config/default.py +44 -6
- astrbot/core/conversation_mgr.py +105 -36
- astrbot/core/core_lifecycle.py +68 -54
- astrbot/core/db/__init__.py +33 -18
- astrbot/core/db/migration/helper.py +18 -13
- astrbot/core/db/migration/migra_3_to_4.py +53 -34
- astrbot/core/db/migration/migra_45_to_46.py +1 -1
- astrbot/core/db/migration/shared_preferences_v3.py +2 -1
- astrbot/core/db/migration/sqlite_v3.py +26 -23
- astrbot/core/db/po.py +27 -18
- astrbot/core/db/sqlite.py +74 -45
- astrbot/core/db/vec_db/base.py +10 -14
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +90 -77
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +9 -3
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +36 -31
- astrbot/core/event_bus.py +8 -6
- astrbot/core/file_token_service.py +6 -5
- astrbot/core/initial_loader.py +7 -5
- astrbot/core/knowledge_base/chunking/__init__.py +1 -3
- astrbot/core/knowledge_base/chunking/base.py +1 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +2 -0
- astrbot/core/knowledge_base/chunking/recursive.py +16 -10
- astrbot/core/knowledge_base/kb_db_sqlite.py +50 -48
- astrbot/core/knowledge_base/kb_helper.py +30 -17
- astrbot/core/knowledge_base/kb_mgr.py +6 -7
- astrbot/core/knowledge_base/models.py +10 -4
- astrbot/core/knowledge_base/parsers/__init__.py +3 -5
- astrbot/core/knowledge_base/parsers/base.py +1 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/pdf_parser.py +2 -1
- astrbot/core/knowledge_base/parsers/text_parser.py +1 -0
- astrbot/core/knowledge_base/parsers/util.py +1 -1
- astrbot/core/knowledge_base/retrieval/__init__.py +6 -8
- astrbot/core/knowledge_base/retrieval/manager.py +17 -14
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +7 -3
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +11 -5
- astrbot/core/log.py +21 -13
- astrbot/core/message/components.py +123 -217
- astrbot/core/message/message_event_result.py +24 -24
- astrbot/core/persona_mgr.py +20 -11
- astrbot/core/pipeline/__init__.py +7 -7
- astrbot/core/pipeline/content_safety_check/stage.py +13 -9
- astrbot/core/pipeline/content_safety_check/strategies/__init__.py +1 -2
- astrbot/core/pipeline/content_safety_check/strategies/baidu_aip.py +12 -13
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +1 -0
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +4 -1
- astrbot/core/pipeline/context_utils.py +77 -7
- astrbot/core/pipeline/preprocess_stage/stage.py +12 -9
- astrbot/core/pipeline/process_stage/method/llm_request.py +125 -72
- astrbot/core/pipeline/process_stage/method/star_request.py +19 -17
- astrbot/core/pipeline/process_stage/stage.py +13 -10
- astrbot/core/pipeline/process_stage/utils.py +6 -5
- astrbot/core/pipeline/rate_limit_check/stage.py +37 -36
- astrbot/core/pipeline/respond/stage.py +23 -20
- astrbot/core/pipeline/result_decorate/stage.py +31 -23
- astrbot/core/pipeline/scheduler.py +12 -8
- astrbot/core/pipeline/session_status_check/stage.py +12 -8
- astrbot/core/pipeline/stage.py +10 -4
- astrbot/core/pipeline/waking_check/stage.py +24 -18
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +76 -110
- astrbot/core/platform/astrbot_message.py +11 -13
- astrbot/core/platform/manager.py +16 -15
- astrbot/core/platform/message_session.py +5 -3
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +4 -4
- astrbot/core/platform/register.py +8 -8
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +23 -15
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +51 -33
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +47 -29
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +7 -3
- astrbot/core/platform/sources/discord/client.py +9 -6
- astrbot/core/platform/sources/discord/components.py +18 -14
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +45 -30
- astrbot/core/platform/sources/discord/discord_platform_event.py +38 -30
- astrbot/core/platform/sources/lark/lark_adapter.py +23 -17
- astrbot/core/platform/sources/lark/lark_event.py +21 -14
- astrbot/core/platform/sources/misskey/misskey_adapter.py +107 -67
- astrbot/core/platform/sources/misskey/misskey_api.py +153 -129
- astrbot/core/platform/sources/misskey/misskey_event.py +20 -15
- astrbot/core/platform/sources/misskey/misskey_utils.py +74 -62
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +63 -44
- astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py +41 -26
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_adapter.py +36 -17
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_event.py +3 -1
- astrbot/core/platform/sources/qqofficial_webhook/qo_webhook_server.py +12 -7
- astrbot/core/platform/sources/satori/satori_adapter.py +56 -38
- astrbot/core/platform/sources/satori/satori_event.py +34 -25
- astrbot/core/platform/sources/slack/client.py +11 -9
- astrbot/core/platform/sources/slack/slack_adapter.py +52 -36
- astrbot/core/platform/sources/slack/slack_event.py +34 -24
- astrbot/core/platform/sources/telegram/tg_adapter.py +38 -18
- astrbot/core/platform/sources/telegram/tg_event.py +32 -18
- astrbot/core/platform/sources/webchat/webchat_adapter.py +27 -17
- astrbot/core/platform/sources/webchat/webchat_event.py +14 -10
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +115 -120
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +9 -8
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +15 -16
- astrbot/core/platform/sources/wecom/wecom_adapter.py +35 -18
- astrbot/core/platform/sources/wecom/wecom_event.py +55 -48
- astrbot/core/platform/sources/wecom/wecom_kf.py +34 -44
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +26 -10
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +18 -10
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +3 -5
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +0 -1
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +61 -37
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +67 -28
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +8 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +18 -9
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +14 -12
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +22 -12
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +40 -26
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +47 -45
- astrbot/core/platform_message_history_mgr.py +5 -3
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +61 -75
- astrbot/core/provider/func_tool_manager.py +59 -55
- astrbot/core/provider/manager.py +40 -22
- astrbot/core/provider/provider.py +72 -46
- astrbot/core/provider/register.py +7 -7
- astrbot/core/provider/sources/anthropic_source.py +48 -30
- astrbot/core/provider/sources/azure_tts_source.py +17 -13
- astrbot/core/provider/sources/coze_api_client.py +27 -17
- astrbot/core/provider/sources/coze_source.py +104 -87
- astrbot/core/provider/sources/dashscope_source.py +18 -11
- astrbot/core/provider/sources/dashscope_tts.py +36 -23
- astrbot/core/provider/sources/dify_source.py +25 -20
- astrbot/core/provider/sources/edge_tts_source.py +21 -17
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +22 -14
- astrbot/core/provider/sources/gemini_embedding_source.py +12 -13
- astrbot/core/provider/sources/gemini_source.py +72 -58
- astrbot/core/provider/sources/gemini_tts_source.py +8 -6
- astrbot/core/provider/sources/gsv_selfhosted_source.py +17 -14
- astrbot/core/provider/sources/gsvi_tts_source.py +11 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +50 -40
- astrbot/core/provider/sources/openai_embedding_source.py +6 -8
- astrbot/core/provider/sources/openai_source.py +102 -69
- astrbot/core/provider/sources/openai_tts_api_source.py +14 -6
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +10 -4
- astrbot/core/provider/sources/volcengine_tts.py +38 -31
- astrbot/core/provider/sources/whisper_api_source.py +14 -12
- astrbot/core/provider/sources/whisper_selfhosted_source.py +15 -11
- astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
- astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
- astrbot/core/star/__init__.py +16 -11
- astrbot/core/star/config.py +10 -15
- astrbot/core/star/context.py +109 -84
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +30 -28
- astrbot/core/star/filter/command_group.py +27 -24
- astrbot/core/star/filter/custom_filter.py +6 -5
- astrbot/core/star/filter/event_message_type.py +4 -2
- astrbot/core/star/filter/permission.py +4 -2
- astrbot/core/star/filter/platform_adapter_type.py +4 -2
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -19
- astrbot/core/star/register/star.py +6 -2
- astrbot/core/star/register/star_handler.py +96 -73
- astrbot/core/star/session_llm_manager.py +48 -14
- astrbot/core/star/session_plugin_manager.py +29 -15
- astrbot/core/star/star.py +1 -2
- astrbot/core/star/star_handler.py +13 -8
- astrbot/core/star/star_manager.py +151 -59
- astrbot/core/star/star_tools.py +44 -37
- astrbot/core/star/updator.py +10 -10
- astrbot/core/umop_config_router.py +10 -4
- astrbot/core/updator.py +13 -5
- astrbot/core/utils/astrbot_path.py +3 -5
- astrbot/core/utils/dify_api_client.py +33 -15
- astrbot/core/utils/io.py +66 -42
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +7 -7
- astrbot/core/utils/path_util.py +15 -16
- astrbot/core/utils/pip_installer.py +5 -5
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +45 -20
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/network_strategy.py +35 -26
- astrbot/core/utils/t2i/renderer.py +11 -5
- astrbot/core/utils/t2i/template_manager.py +14 -15
- astrbot/core/utils/tencent_record_helper.py +19 -13
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +43 -40
- astrbot/dashboard/routes/__init__.py +18 -18
- astrbot/dashboard/routes/auth.py +10 -8
- astrbot/dashboard/routes/chat.py +30 -21
- astrbot/dashboard/routes/config.py +92 -75
- astrbot/dashboard/routes/conversation.py +46 -39
- astrbot/dashboard/routes/file.py +4 -2
- astrbot/dashboard/routes/knowledge_base.py +47 -40
- astrbot/dashboard/routes/log.py +9 -4
- astrbot/dashboard/routes/persona.py +19 -16
- astrbot/dashboard/routes/plugin.py +69 -55
- astrbot/dashboard/routes/route.py +3 -1
- astrbot/dashboard/routes/session_management.py +130 -116
- astrbot/dashboard/routes/stat.py +34 -34
- astrbot/dashboard/routes/t2i.py +15 -12
- astrbot/dashboard/routes/tools.py +47 -52
- astrbot/dashboard/routes/update.py +32 -28
- astrbot/dashboard/server.py +30 -26
- astrbot/dashboard/utils.py +8 -4
- {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/METADATA +4 -2
- astrbot-4.5.2.dist-info/RECORD +261 -0
- astrbot-4.5.0.dist-info/RECORD +0 -258
- {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
- {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.0.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import re
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
|
|
4
5
|
from astrbot.api import logger
|
|
5
6
|
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
6
|
-
from astrbot.api.platform import PlatformMetadata, AstrBotMessage
|
|
7
7
|
from astrbot.api.message_components import Plain
|
|
8
|
+
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
|
8
9
|
|
|
9
10
|
from .misskey_utils import (
|
|
10
|
-
serialize_message_chain,
|
|
11
|
-
resolve_visibility_from_raw_message,
|
|
12
|
-
is_valid_user_session_id,
|
|
13
|
-
is_valid_room_session_id,
|
|
14
11
|
add_at_mention_if_needed,
|
|
15
|
-
extract_user_id_from_session_id,
|
|
16
12
|
extract_room_id_from_session_id,
|
|
13
|
+
extract_user_id_from_session_id,
|
|
14
|
+
is_valid_room_session_id,
|
|
15
|
+
is_valid_user_session_id,
|
|
16
|
+
resolve_visibility_from_raw_message,
|
|
17
|
+
serialize_message_chain,
|
|
17
18
|
)
|
|
18
19
|
|
|
19
20
|
|
|
@@ -43,7 +44,7 @@ class MisskeyPlatformEvent(AstrMessageEvent):
|
|
|
43
44
|
"""发送消息,使用适配器的完整上传和发送逻辑"""
|
|
44
45
|
try:
|
|
45
46
|
logger.debug(
|
|
46
|
-
f"[MisskeyEvent] send 方法被调用,消息链包含 {len(message.chain)} 个组件"
|
|
47
|
+
f"[MisskeyEvent] send 方法被调用,消息链包含 {len(message.chain)} 个组件",
|
|
47
48
|
)
|
|
48
49
|
|
|
49
50
|
# 使用适配器的 send_by_session 方法,它包含文件上传逻辑
|
|
@@ -65,7 +66,7 @@ class MisskeyPlatformEvent(AstrMessageEvent):
|
|
|
65
66
|
)
|
|
66
67
|
|
|
67
68
|
logger.debug(
|
|
68
|
-
f"[MisskeyEvent] 检查适配器方法: hasattr(self.client, 'send_by_session') = {hasattr(self.client, 'send_by_session')}"
|
|
69
|
+
f"[MisskeyEvent] 检查适配器方法: hasattr(self.client, 'send_by_session') = {hasattr(self.client, 'send_by_session')}",
|
|
69
70
|
)
|
|
70
71
|
|
|
71
72
|
# 调用适配器的 send_by_session 方法
|
|
@@ -88,25 +89,27 @@ class MisskeyPlatformEvent(AstrMessageEvent):
|
|
|
88
89
|
user_info = {
|
|
89
90
|
"username": user_data.get("username", ""),
|
|
90
91
|
"nickname": user_data.get(
|
|
91
|
-
"name",
|
|
92
|
+
"name",
|
|
93
|
+
user_data.get("username", ""),
|
|
92
94
|
),
|
|
93
95
|
}
|
|
94
96
|
content = add_at_mention_if_needed(content, user_info, has_at)
|
|
95
97
|
|
|
96
98
|
# 根据会话类型选择发送方式
|
|
97
99
|
if hasattr(self.client, "send_message") and is_valid_user_session_id(
|
|
98
|
-
self.session_id
|
|
100
|
+
self.session_id,
|
|
99
101
|
):
|
|
100
102
|
user_id = extract_user_id_from_session_id(self.session_id)
|
|
101
103
|
await self.client.send_message(user_id, content)
|
|
102
104
|
elif hasattr(
|
|
103
|
-
self.client,
|
|
105
|
+
self.client,
|
|
106
|
+
"send_room_message",
|
|
104
107
|
) and is_valid_room_session_id(self.session_id):
|
|
105
108
|
room_id = extract_room_id_from_session_id(self.session_id)
|
|
106
109
|
await self.client.send_room_message(room_id, content)
|
|
107
110
|
elif original_message_id and hasattr(self.client, "create_note"):
|
|
108
111
|
visibility, visible_user_ids = resolve_visibility_from_raw_message(
|
|
109
|
-
raw_message
|
|
112
|
+
raw_message,
|
|
110
113
|
)
|
|
111
114
|
await self.client.create_note(
|
|
112
115
|
content,
|
|
@@ -124,7 +127,9 @@ class MisskeyPlatformEvent(AstrMessageEvent):
|
|
|
124
127
|
logger.error(f"[MisskeyEvent] 发送失败: {e}")
|
|
125
128
|
|
|
126
129
|
async def send_streaming(
|
|
127
|
-
self,
|
|
130
|
+
self,
|
|
131
|
+
generator: AsyncGenerator[MessageChain, None],
|
|
132
|
+
use_fallback: bool = False,
|
|
128
133
|
):
|
|
129
134
|
if not use_fallback:
|
|
130
135
|
buffer = None
|
|
@@ -134,7 +139,7 @@ class MisskeyPlatformEvent(AstrMessageEvent):
|
|
|
134
139
|
else:
|
|
135
140
|
buffer.chain.extend(chain.chain)
|
|
136
141
|
if not buffer:
|
|
137
|
-
return
|
|
142
|
+
return None
|
|
138
143
|
buffer.squash_plain()
|
|
139
144
|
await self.send(buffer)
|
|
140
145
|
return await super().send_streaming(generator, use_fallback)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Misskey 平台适配器通用工具函数"""
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
4
5
|
import astrbot.api.message_components as Comp
|
|
5
6
|
from astrbot.api.platform import AstrBotMessage, MessageMember, MessageType
|
|
6
7
|
|
|
@@ -9,7 +10,7 @@ class FileIDExtractor:
|
|
|
9
10
|
"""从 API 响应中提取文件 ID 的帮助类(无状态)。"""
|
|
10
11
|
|
|
11
12
|
@staticmethod
|
|
12
|
-
def extract_file_id(result: Any) ->
|
|
13
|
+
def extract_file_id(result: Any) -> str | None:
|
|
13
14
|
if not isinstance(result, dict):
|
|
14
15
|
return None
|
|
15
16
|
|
|
@@ -34,8 +35,10 @@ class MessagePayloadBuilder:
|
|
|
34
35
|
|
|
35
36
|
@staticmethod
|
|
36
37
|
def build_chat_payload(
|
|
37
|
-
user_id: str,
|
|
38
|
-
|
|
38
|
+
user_id: str,
|
|
39
|
+
text: str | None,
|
|
40
|
+
file_id: str | None = None,
|
|
41
|
+
) -> dict[str, Any]:
|
|
39
42
|
payload = {"toUserId": user_id}
|
|
40
43
|
if text:
|
|
41
44
|
payload["text"] = text
|
|
@@ -45,8 +48,10 @@ class MessagePayloadBuilder:
|
|
|
45
48
|
|
|
46
49
|
@staticmethod
|
|
47
50
|
def build_room_payload(
|
|
48
|
-
room_id: str,
|
|
49
|
-
|
|
51
|
+
room_id: str,
|
|
52
|
+
text: str | None,
|
|
53
|
+
file_id: str | None = None,
|
|
54
|
+
) -> dict[str, Any]:
|
|
50
55
|
payload = {"toRoomId": room_id}
|
|
51
56
|
if text:
|
|
52
57
|
payload["text"] = text
|
|
@@ -56,9 +61,11 @@ class MessagePayloadBuilder:
|
|
|
56
61
|
|
|
57
62
|
@staticmethod
|
|
58
63
|
def build_note_payload(
|
|
59
|
-
text:
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
text: str | None,
|
|
65
|
+
file_ids: list[str] | None = None,
|
|
66
|
+
**kwargs,
|
|
67
|
+
) -> dict[str, Any]:
|
|
68
|
+
payload: dict[str, Any] = {}
|
|
62
69
|
if text:
|
|
63
70
|
payload["text"] = text
|
|
64
71
|
if file_ids:
|
|
@@ -67,7 +74,7 @@ class MessagePayloadBuilder:
|
|
|
67
74
|
return payload
|
|
68
75
|
|
|
69
76
|
|
|
70
|
-
def serialize_message_chain(chain:
|
|
77
|
+
def serialize_message_chain(chain: list[Any]) -> tuple[str, bool]:
|
|
71
78
|
"""将消息链序列化为文本字符串"""
|
|
72
79
|
text_parts = []
|
|
73
80
|
has_at = False
|
|
@@ -76,27 +83,25 @@ def serialize_message_chain(chain: List[Any]) -> Tuple[str, bool]:
|
|
|
76
83
|
nonlocal has_at
|
|
77
84
|
if isinstance(component, Comp.Plain):
|
|
78
85
|
return component.text
|
|
79
|
-
|
|
86
|
+
if isinstance(component, Comp.File):
|
|
80
87
|
# 为文件组件返回占位符,但适配器仍会处理原组件
|
|
81
88
|
return "[文件]"
|
|
82
|
-
|
|
89
|
+
if isinstance(component, Comp.Image):
|
|
83
90
|
# 为图片组件返回占位符,但适配器仍会处理原组件
|
|
84
91
|
return "[图片]"
|
|
85
|
-
|
|
92
|
+
if isinstance(component, Comp.At):
|
|
86
93
|
has_at = True
|
|
87
94
|
# 优先使用name字段(用户名),如果没有则使用qq字段
|
|
88
95
|
# 这样可以避免在Misskey中生成 @<user_id> 这样的无效提及
|
|
89
96
|
if hasattr(component, "name") and component.name:
|
|
90
97
|
return f"@{component.name}"
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
elif hasattr(component, "text"):
|
|
98
|
+
return f"@{component.qq}"
|
|
99
|
+
if hasattr(component, "text"):
|
|
94
100
|
text = getattr(component, "text", "")
|
|
95
101
|
if "@" in text:
|
|
96
102
|
has_at = True
|
|
97
103
|
return text
|
|
98
|
-
|
|
99
|
-
return str(component)
|
|
104
|
+
return str(component)
|
|
100
105
|
|
|
101
106
|
for component in chain:
|
|
102
107
|
if isinstance(component, Comp.Node) and component.content:
|
|
@@ -113,12 +118,12 @@ def serialize_message_chain(chain: List[Any]) -> Tuple[str, bool]:
|
|
|
113
118
|
|
|
114
119
|
|
|
115
120
|
def resolve_message_visibility(
|
|
116
|
-
user_id:
|
|
117
|
-
user_cache:
|
|
118
|
-
self_id:
|
|
119
|
-
raw_message:
|
|
121
|
+
user_id: str | None = None,
|
|
122
|
+
user_cache: dict[str, Any] | None = None,
|
|
123
|
+
self_id: str | None = None,
|
|
124
|
+
raw_message: dict[str, Any] | None = None,
|
|
120
125
|
default_visibility: str = "public",
|
|
121
|
-
) ->
|
|
126
|
+
) -> tuple[str, list[str] | None]:
|
|
122
127
|
"""解析 Misskey 消息的可见性设置
|
|
123
128
|
|
|
124
129
|
可以从 user_cache 或 raw_message 中解析,支持两种调用方式:
|
|
@@ -169,13 +174,14 @@ def resolve_message_visibility(
|
|
|
169
174
|
|
|
170
175
|
# 保留旧函数名作为向后兼容的别名
|
|
171
176
|
def resolve_visibility_from_raw_message(
|
|
172
|
-
raw_message:
|
|
173
|
-
|
|
177
|
+
raw_message: dict[str, Any],
|
|
178
|
+
self_id: str | None = None,
|
|
179
|
+
) -> tuple[str, list[str] | None]:
|
|
174
180
|
"""从原始消息数据中解析可见性设置(已弃用,使用 resolve_message_visibility 替代)"""
|
|
175
181
|
return resolve_message_visibility(raw_message=raw_message, self_id=self_id)
|
|
176
182
|
|
|
177
183
|
|
|
178
|
-
def is_valid_user_session_id(session_id:
|
|
184
|
+
def is_valid_user_session_id(session_id: str | Any) -> bool:
|
|
179
185
|
"""检查 session_id 是否是有效的聊天用户 session_id (仅限chat%前缀)"""
|
|
180
186
|
if not isinstance(session_id, str) or "%" not in session_id:
|
|
181
187
|
return False
|
|
@@ -189,7 +195,7 @@ def is_valid_user_session_id(session_id: Union[str, Any]) -> bool:
|
|
|
189
195
|
)
|
|
190
196
|
|
|
191
197
|
|
|
192
|
-
def is_valid_room_session_id(session_id:
|
|
198
|
+
def is_valid_room_session_id(session_id: str | Any) -> bool:
|
|
193
199
|
"""检查 session_id 是否是有效的房间 session_id (仅限room%前缀)"""
|
|
194
200
|
if not isinstance(session_id, str) or "%" not in session_id:
|
|
195
201
|
return False
|
|
@@ -203,7 +209,7 @@ def is_valid_room_session_id(session_id: Union[str, Any]) -> bool:
|
|
|
203
209
|
)
|
|
204
210
|
|
|
205
211
|
|
|
206
|
-
def is_valid_chat_session_id(session_id:
|
|
212
|
+
def is_valid_chat_session_id(session_id: str | Any) -> bool:
|
|
207
213
|
"""检查 session_id 是否是有效的聊天 session_id (仅限chat%前缀)"""
|
|
208
214
|
if not isinstance(session_id, str) or "%" not in session_id:
|
|
209
215
|
return False
|
|
@@ -236,7 +242,9 @@ def extract_room_id_from_session_id(session_id: str) -> str:
|
|
|
236
242
|
|
|
237
243
|
|
|
238
244
|
def add_at_mention_if_needed(
|
|
239
|
-
text: str,
|
|
245
|
+
text: str,
|
|
246
|
+
user_info: dict[str, Any] | None,
|
|
247
|
+
has_at: bool = False,
|
|
240
248
|
) -> str:
|
|
241
249
|
"""如果需要且没有@用户,则添加@用户
|
|
242
250
|
|
|
@@ -258,7 +266,7 @@ def add_at_mention_if_needed(
|
|
|
258
266
|
return text
|
|
259
267
|
|
|
260
268
|
|
|
261
|
-
def create_file_component(file_info:
|
|
269
|
+
def create_file_component(file_info: dict[str, Any]) -> tuple[Any, str]:
|
|
262
270
|
"""创建文件组件和描述文本"""
|
|
263
271
|
file_url = file_info.get("url", "")
|
|
264
272
|
file_name = file_info.get("name", "未知文件")
|
|
@@ -266,16 +274,17 @@ def create_file_component(file_info: Dict[str, Any]) -> Tuple[Any, str]:
|
|
|
266
274
|
|
|
267
275
|
if file_type.startswith("image/"):
|
|
268
276
|
return Comp.Image(url=file_url, file=file_name), f"图片[{file_name}]"
|
|
269
|
-
|
|
277
|
+
if file_type.startswith("audio/"):
|
|
270
278
|
return Comp.Record(url=file_url, file=file_name), f"音频[{file_name}]"
|
|
271
|
-
|
|
279
|
+
if file_type.startswith("video/"):
|
|
272
280
|
return Comp.Video(url=file_url, file=file_name), f"视频[{file_name}]"
|
|
273
|
-
|
|
274
|
-
return Comp.File(name=file_name, url=file_url), f"文件[{file_name}]"
|
|
281
|
+
return Comp.File(name=file_name, url=file_url), f"文件[{file_name}]"
|
|
275
282
|
|
|
276
283
|
|
|
277
284
|
def process_files(
|
|
278
|
-
message: AstrBotMessage,
|
|
285
|
+
message: AstrBotMessage,
|
|
286
|
+
files: list,
|
|
287
|
+
include_text_parts: bool = True,
|
|
279
288
|
) -> list:
|
|
280
289
|
"""处理文件列表,添加到消息组件中并返回文本描述"""
|
|
281
290
|
file_parts = []
|
|
@@ -287,7 +296,7 @@ def process_files(
|
|
|
287
296
|
return file_parts
|
|
288
297
|
|
|
289
298
|
|
|
290
|
-
def format_poll(poll:
|
|
299
|
+
def format_poll(poll: dict[str, Any]) -> str:
|
|
291
300
|
"""将 Misskey 的 poll 对象格式化为可读字符串。"""
|
|
292
301
|
if not poll or not isinstance(poll, dict):
|
|
293
302
|
return ""
|
|
@@ -304,8 +313,9 @@ def format_poll(poll: Dict[str, Any]) -> str:
|
|
|
304
313
|
|
|
305
314
|
|
|
306
315
|
def extract_sender_info(
|
|
307
|
-
raw_data:
|
|
308
|
-
|
|
316
|
+
raw_data: dict[str, Any],
|
|
317
|
+
is_chat: bool = False,
|
|
318
|
+
) -> dict[str, Any]:
|
|
309
319
|
"""提取发送者信息"""
|
|
310
320
|
if is_chat:
|
|
311
321
|
sender = raw_data.get("fromUser", {})
|
|
@@ -323,11 +333,11 @@ def extract_sender_info(
|
|
|
323
333
|
|
|
324
334
|
|
|
325
335
|
def create_base_message(
|
|
326
|
-
raw_data:
|
|
327
|
-
sender_info:
|
|
336
|
+
raw_data: dict[str, Any],
|
|
337
|
+
sender_info: dict[str, Any],
|
|
328
338
|
client_self_id: str,
|
|
329
339
|
is_chat: bool = False,
|
|
330
|
-
room_id:
|
|
340
|
+
room_id: str | None = None,
|
|
331
341
|
unique_session: bool = False,
|
|
332
342
|
) -> AstrBotMessage:
|
|
333
343
|
"""创建基础消息对象"""
|
|
@@ -366,8 +376,11 @@ def create_base_message(
|
|
|
366
376
|
|
|
367
377
|
|
|
368
378
|
def process_at_mention(
|
|
369
|
-
message: AstrBotMessage,
|
|
370
|
-
|
|
379
|
+
message: AstrBotMessage,
|
|
380
|
+
raw_text: str,
|
|
381
|
+
bot_username: str,
|
|
382
|
+
client_self_id: str,
|
|
383
|
+
) -> tuple[list[str], str]:
|
|
371
384
|
"""处理@提及逻辑,返回消息部分列表和处理后的文本"""
|
|
372
385
|
message_parts = []
|
|
373
386
|
|
|
@@ -382,16 +395,15 @@ def process_at_mention(
|
|
|
382
395
|
message.message.append(Comp.Plain(remaining_text))
|
|
383
396
|
message_parts.append(remaining_text)
|
|
384
397
|
return message_parts, remaining_text
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
return message_parts, raw_text
|
|
398
|
+
message.message.append(Comp.Plain(raw_text))
|
|
399
|
+
message_parts.append(raw_text)
|
|
400
|
+
return message_parts, raw_text
|
|
389
401
|
|
|
390
402
|
|
|
391
403
|
def cache_user_info(
|
|
392
|
-
user_cache:
|
|
393
|
-
sender_info:
|
|
394
|
-
raw_data:
|
|
404
|
+
user_cache: dict[str, Any],
|
|
405
|
+
sender_info: dict[str, Any],
|
|
406
|
+
raw_data: dict[str, Any],
|
|
395
407
|
client_self_id: str,
|
|
396
408
|
is_chat: bool = False,
|
|
397
409
|
):
|
|
@@ -417,7 +429,9 @@ def cache_user_info(
|
|
|
417
429
|
|
|
418
430
|
|
|
419
431
|
def cache_room_info(
|
|
420
|
-
user_cache:
|
|
432
|
+
user_cache: dict[str, Any],
|
|
433
|
+
raw_data: dict[str, Any],
|
|
434
|
+
client_self_id: str,
|
|
421
435
|
):
|
|
422
436
|
"""缓存房间信息"""
|
|
423
437
|
room_data = raw_data.get("toRoom")
|
|
@@ -437,7 +451,7 @@ def cache_room_info(
|
|
|
437
451
|
|
|
438
452
|
async def resolve_component_url_or_path(
|
|
439
453
|
comp: Any,
|
|
440
|
-
) ->
|
|
454
|
+
) -> tuple[str | None, str | None]:
|
|
441
455
|
"""尝试从组件解析可上传的远程 URL 或本地路径。
|
|
442
456
|
|
|
443
457
|
返回 (url_candidate, local_path)。两者可能都为 None。
|
|
@@ -468,8 +482,7 @@ async def resolve_component_url_or_path(
|
|
|
468
482
|
if value.startswith("http"):
|
|
469
483
|
url_candidate = value
|
|
470
484
|
break
|
|
471
|
-
|
|
472
|
-
local_path = value
|
|
485
|
+
local_path = value
|
|
473
486
|
except Exception:
|
|
474
487
|
continue
|
|
475
488
|
|
|
@@ -491,9 +504,8 @@ async def resolve_component_url_or_path(
|
|
|
491
504
|
if value.startswith("http"):
|
|
492
505
|
url_candidate = value
|
|
493
506
|
break
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
break
|
|
507
|
+
local_path = value
|
|
508
|
+
break
|
|
497
509
|
except Exception:
|
|
498
510
|
continue
|
|
499
511
|
|
|
@@ -503,7 +515,7 @@ async def resolve_component_url_or_path(
|
|
|
503
515
|
return url_candidate, local_path
|
|
504
516
|
|
|
505
517
|
|
|
506
|
-
def summarize_component_for_log(comp: Any) ->
|
|
518
|
+
def summarize_component_for_log(comp: Any) -> dict[str, Any]:
|
|
507
519
|
"""生成适合日志的组件属性字典(尽量不抛异常)。"""
|
|
508
520
|
attrs = {}
|
|
509
521
|
for a in ("file", "url", "path", "src", "source", "name"):
|
|
@@ -519,15 +531,15 @@ def summarize_component_for_log(comp: Any) -> Dict[str, Any]:
|
|
|
519
531
|
async def upload_local_with_retries(
|
|
520
532
|
api: Any,
|
|
521
533
|
local_path: str,
|
|
522
|
-
preferred_name:
|
|
523
|
-
folder_id:
|
|
524
|
-
) ->
|
|
534
|
+
preferred_name: str | None,
|
|
535
|
+
folder_id: str | None,
|
|
536
|
+
) -> str | None:
|
|
525
537
|
"""尝试本地上传,返回 file id 或 None。如果文件类型不允许则直接失败。"""
|
|
526
538
|
try:
|
|
527
539
|
res = await api.upload_file(local_path, preferred_name, folder_id)
|
|
528
540
|
if isinstance(res, dict):
|
|
529
541
|
fid = res.get("id") or (res.get("raw") or {}).get("createdFile", {}).get(
|
|
530
|
-
"id"
|
|
542
|
+
"id",
|
|
531
543
|
)
|
|
532
544
|
if fid:
|
|
533
545
|
return str(fid)
|
|
@@ -1,25 +1,26 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import base64
|
|
3
|
+
import os
|
|
4
|
+
import random
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
import aiofiles
|
|
1
8
|
import botpy
|
|
2
9
|
import botpy.message
|
|
3
10
|
import botpy.types
|
|
4
11
|
import botpy.types.message
|
|
5
|
-
import asyncio
|
|
6
|
-
import base64
|
|
7
|
-
import aiofiles
|
|
8
|
-
from astrbot.core.utils.io import file_to_base64, download_image_by_url
|
|
9
|
-
from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk
|
|
10
|
-
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
11
|
-
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
12
|
-
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
|
13
|
-
from astrbot.api.message_components import Plain, Image, Record
|
|
14
12
|
from botpy import Client
|
|
15
13
|
from botpy.http import Route
|
|
16
|
-
from astrbot.api import logger
|
|
17
|
-
from botpy.types.message import Media
|
|
18
14
|
from botpy.types import message
|
|
19
|
-
from
|
|
20
|
-
|
|
21
|
-
import
|
|
22
|
-
import
|
|
15
|
+
from botpy.types.message import Media
|
|
16
|
+
|
|
17
|
+
from astrbot.api import logger
|
|
18
|
+
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
19
|
+
from astrbot.api.message_components import Image, Plain, Record
|
|
20
|
+
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
|
21
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
22
|
+
from astrbot.core.utils.io import download_image_by_url, file_to_base64
|
|
23
|
+
from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class QQOfficialMessageEvent(AstrMessageEvent):
|
|
@@ -75,9 +76,9 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
75
76
|
|
|
76
77
|
return await super().send_streaming(generator, use_fallback)
|
|
77
78
|
|
|
78
|
-
async def _post_send(self, stream: dict = None):
|
|
79
|
+
async def _post_send(self, stream: dict | None = None):
|
|
79
80
|
if not self.send_buffer:
|
|
80
|
-
return
|
|
81
|
+
return None
|
|
81
82
|
|
|
82
83
|
source = self.message_obj.raw_message
|
|
83
84
|
assert isinstance(
|
|
@@ -103,7 +104,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
103
104
|
and not image_path
|
|
104
105
|
and not record_file_path
|
|
105
106
|
):
|
|
106
|
-
return
|
|
107
|
+
return None
|
|
107
108
|
|
|
108
109
|
payload = {
|
|
109
110
|
"content": plain_text,
|
|
@@ -119,29 +120,38 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
119
120
|
case botpy.message.GroupMessage:
|
|
120
121
|
if image_base64:
|
|
121
122
|
media = await self.upload_group_and_c2c_image(
|
|
122
|
-
image_base64,
|
|
123
|
+
image_base64,
|
|
124
|
+
1,
|
|
125
|
+
group_openid=source.group_openid,
|
|
123
126
|
)
|
|
124
127
|
payload["media"] = media
|
|
125
128
|
payload["msg_type"] = 7
|
|
126
129
|
if record_file_path: # group record msg
|
|
127
130
|
media = await self.upload_group_and_c2c_record(
|
|
128
|
-
record_file_path,
|
|
131
|
+
record_file_path,
|
|
132
|
+
3,
|
|
133
|
+
group_openid=source.group_openid,
|
|
129
134
|
)
|
|
130
135
|
payload["media"] = media
|
|
131
136
|
payload["msg_type"] = 7
|
|
132
137
|
ret = await self.bot.api.post_group_message(
|
|
133
|
-
group_openid=source.group_openid,
|
|
138
|
+
group_openid=source.group_openid,
|
|
139
|
+
**payload,
|
|
134
140
|
)
|
|
135
141
|
case botpy.message.C2CMessage:
|
|
136
142
|
if image_base64:
|
|
137
143
|
media = await self.upload_group_and_c2c_image(
|
|
138
|
-
image_base64,
|
|
144
|
+
image_base64,
|
|
145
|
+
1,
|
|
146
|
+
openid=source.author.user_openid,
|
|
139
147
|
)
|
|
140
148
|
payload["media"] = media
|
|
141
149
|
payload["msg_type"] = 7
|
|
142
150
|
if record_file_path: # c2c record
|
|
143
151
|
media = await self.upload_group_and_c2c_record(
|
|
144
|
-
record_file_path,
|
|
152
|
+
record_file_path,
|
|
153
|
+
3,
|
|
154
|
+
openid=source.author.user_openid,
|
|
145
155
|
)
|
|
146
156
|
payload["media"] = media
|
|
147
157
|
payload["msg_type"] = 7
|
|
@@ -153,14 +163,16 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
153
163
|
)
|
|
154
164
|
else:
|
|
155
165
|
ret = await self.post_c2c_message(
|
|
156
|
-
openid=source.author.user_openid,
|
|
166
|
+
openid=source.author.user_openid,
|
|
167
|
+
**payload,
|
|
157
168
|
)
|
|
158
169
|
logger.debug(f"Message sent to C2C: {ret}")
|
|
159
170
|
case botpy.message.Message:
|
|
160
171
|
if image_path:
|
|
161
172
|
payload["file_image"] = image_path
|
|
162
173
|
ret = await self.bot.api.post_message(
|
|
163
|
-
channel_id=source.channel_id,
|
|
174
|
+
channel_id=source.channel_id,
|
|
175
|
+
**payload,
|
|
164
176
|
)
|
|
165
177
|
case botpy.message.DirectMessage:
|
|
166
178
|
if image_path:
|
|
@@ -174,7 +186,10 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
174
186
|
return ret
|
|
175
187
|
|
|
176
188
|
async def upload_group_and_c2c_image(
|
|
177
|
-
self,
|
|
189
|
+
self,
|
|
190
|
+
image_base64: str,
|
|
191
|
+
file_type: int,
|
|
192
|
+
**kwargs,
|
|
178
193
|
) -> botpy.types.message.Media:
|
|
179
194
|
payload = {
|
|
180
195
|
"file_data": image_base64,
|
|
@@ -185,7 +200,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
185
200
|
payload["openid"] = kwargs["openid"]
|
|
186
201
|
route = Route("POST", "/v2/users/{openid}/files", openid=kwargs["openid"])
|
|
187
202
|
return await self.bot.api._http.request(route, json=payload)
|
|
188
|
-
|
|
203
|
+
if "group_openid" in kwargs:
|
|
189
204
|
payload["group_openid"] = kwargs["group_openid"]
|
|
190
205
|
route = Route(
|
|
191
206
|
"POST",
|
|
@@ -195,11 +210,13 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
195
210
|
return await self.bot.api._http.request(route, json=payload)
|
|
196
211
|
|
|
197
212
|
async def upload_group_and_c2c_record(
|
|
198
|
-
self,
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
213
|
+
self,
|
|
214
|
+
file_source: str,
|
|
215
|
+
file_type: int,
|
|
216
|
+
srv_send_msg: bool = False,
|
|
217
|
+
**kwargs,
|
|
218
|
+
) -> Media | None:
|
|
219
|
+
"""上传媒体文件"""
|
|
203
220
|
# 构建基础payload
|
|
204
221
|
payload = {"file_type": file_type, "srv_send_msg": srv_send_msg}
|
|
205
222
|
|
|
@@ -248,17 +265,17 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
248
265
|
self,
|
|
249
266
|
openid: str,
|
|
250
267
|
msg_type: int = 0,
|
|
251
|
-
content: str = None,
|
|
252
|
-
embed: message.Embed = None,
|
|
253
|
-
ark: message.Ark = None,
|
|
254
|
-
message_reference: message.Reference = None,
|
|
255
|
-
media: message.Media = None,
|
|
256
|
-
msg_id: str = None,
|
|
268
|
+
content: str | None = None,
|
|
269
|
+
embed: message.Embed | None = None,
|
|
270
|
+
ark: message.Ark | None = None,
|
|
271
|
+
message_reference: message.Reference | None = None,
|
|
272
|
+
media: message.Media | None = None,
|
|
273
|
+
msg_id: str | None = None,
|
|
257
274
|
msg_seq: str = 1,
|
|
258
|
-
event_id: str = None,
|
|
259
|
-
markdown: message.MarkdownPayload = None,
|
|
260
|
-
keyboard: message.Keyboard = None,
|
|
261
|
-
stream: dict = None,
|
|
275
|
+
event_id: str | None = None,
|
|
276
|
+
markdown: message.MarkdownPayload | None = None,
|
|
277
|
+
keyboard: message.Keyboard | None = None,
|
|
278
|
+
stream: dict | None = None,
|
|
262
279
|
) -> message.Message:
|
|
263
280
|
payload = locals()
|
|
264
281
|
payload.pop("self", None)
|
|
@@ -291,11 +308,13 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
291
308
|
record_wav_path = await i.convert_to_file_path() # wav 路径
|
|
292
309
|
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
|
|
293
310
|
record_tecent_silk_path = os.path.join(
|
|
294
|
-
temp_dir,
|
|
311
|
+
temp_dir,
|
|
312
|
+
f"{uuid.uuid4()}.silk",
|
|
295
313
|
)
|
|
296
314
|
try:
|
|
297
315
|
duration = await wav_to_tencent_silk(
|
|
298
|
-
record_wav_path,
|
|
316
|
+
record_wav_path,
|
|
317
|
+
record_tecent_silk_path,
|
|
299
318
|
)
|
|
300
319
|
if duration > 0:
|
|
301
320
|
record_file_path = record_tecent_silk_path
|