AstrBot 4.5.1__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 +4 -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 +12 -10
- 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 +42 -27
- 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 +32 -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 +77 -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 +16 -8
- astrbot/core/provider/sources/xinference_stt_provider.py +35 -25
- astrbot/core/star/__init__.py +16 -11
- astrbot/core/star/config.py +10 -15
- astrbot/core/star/context.py +97 -75
- 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.1.dist-info → astrbot-4.5.2.dist-info}/METADATA +2 -1
- astrbot-4.5.2.dist-info/RECORD +261 -0
- astrbot-4.5.1.dist-info/RECORD +0 -260
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/WHEEL +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/entry_points.txt +0 -0
- {astrbot-4.5.1.dist-info → astrbot-4.5.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import discord
|
|
3
|
-
import sys
|
|
4
2
|
import re
|
|
3
|
+
import sys
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import discord
|
|
5
7
|
from discord.abc import Messageable
|
|
6
8
|
from discord.channel import DMChannel
|
|
9
|
+
|
|
10
|
+
from astrbot import logger
|
|
11
|
+
from astrbot.api.event import MessageChain
|
|
12
|
+
from astrbot.api.message_components import File, Image, Plain
|
|
7
13
|
from astrbot.api.platform import (
|
|
8
|
-
Platform,
|
|
9
14
|
AstrBotMessage,
|
|
10
15
|
MessageMember,
|
|
11
|
-
PlatformMetadata,
|
|
12
16
|
MessageType,
|
|
17
|
+
Platform,
|
|
18
|
+
PlatformMetadata,
|
|
19
|
+
register_platform_adapter,
|
|
13
20
|
)
|
|
14
|
-
from astrbot.api.event import MessageChain
|
|
15
|
-
from astrbot.api.message_components import Plain, Image, File
|
|
16
21
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
17
|
-
from astrbot.api.platform import register_platform_adapter
|
|
18
|
-
from astrbot import logger
|
|
19
|
-
from .client import DiscordBotClient
|
|
20
|
-
from .discord_platform_event import DiscordPlatformEvent
|
|
21
|
-
|
|
22
|
-
from typing import Any, Tuple
|
|
23
22
|
from astrbot.core.star.filter.command import CommandFilter
|
|
24
23
|
from astrbot.core.star.filter.command_group import CommandGroupFilter
|
|
25
24
|
from astrbot.core.star.star import star_map
|
|
26
25
|
from astrbot.core.star.star_handler import StarHandlerMetadata, star_handlers_registry
|
|
27
26
|
|
|
27
|
+
from .client import DiscordBotClient
|
|
28
|
+
from .discord_platform_event import DiscordPlatformEvent
|
|
29
|
+
|
|
28
30
|
if sys.version_info >= (3, 12):
|
|
29
31
|
from typing import override
|
|
30
32
|
else:
|
|
@@ -35,7 +37,10 @@ else:
|
|
|
35
37
|
@register_platform_adapter("discord", "Discord 适配器 (基于 Pycord)")
|
|
36
38
|
class DiscordPlatformAdapter(Platform):
|
|
37
39
|
def __init__(
|
|
38
|
-
self,
|
|
40
|
+
self,
|
|
41
|
+
platform_config: dict,
|
|
42
|
+
platform_settings: dict,
|
|
43
|
+
event_queue: asyncio.Queue,
|
|
39
44
|
) -> None:
|
|
40
45
|
super().__init__(event_queue)
|
|
41
46
|
self.config = platform_config
|
|
@@ -51,7 +56,9 @@ class DiscordPlatformAdapter(Platform):
|
|
|
51
56
|
|
|
52
57
|
@override
|
|
53
58
|
async def send_by_session(
|
|
54
|
-
self,
|
|
59
|
+
self,
|
|
60
|
+
session: MessageSesion,
|
|
61
|
+
message_chain: MessageChain,
|
|
55
62
|
):
|
|
56
63
|
"""通过会话发送消息"""
|
|
57
64
|
# 创建一个 message_obj 以便在 event 中使用
|
|
@@ -71,18 +78,19 @@ class DiscordPlatformAdapter(Platform):
|
|
|
71
78
|
message_obj.group_id = self._get_channel_id(channel)
|
|
72
79
|
else:
|
|
73
80
|
logger.warning(
|
|
74
|
-
f"[Discord] Can't get channel info for {channel_id_str}, will guess message type."
|
|
81
|
+
f"[Discord] Can't get channel info for {channel_id_str}, will guess message type.",
|
|
75
82
|
)
|
|
76
83
|
message_obj.type = MessageType.GROUP_MESSAGE
|
|
77
84
|
message_obj.group_id = session.session_id
|
|
78
85
|
|
|
79
86
|
message_obj.message_str = message_chain.get_plain_text()
|
|
80
87
|
message_obj.sender = MessageMember(
|
|
81
|
-
user_id=str(self.client_self_id),
|
|
88
|
+
user_id=str(self.client_self_id),
|
|
89
|
+
nickname=self.client.user.display_name,
|
|
82
90
|
)
|
|
83
91
|
message_obj.self_id = self.client_self_id
|
|
84
92
|
message_obj.session_id = session.session_id
|
|
85
|
-
message_obj.message = message_chain
|
|
93
|
+
message_obj.message = message_chain.chain
|
|
86
94
|
|
|
87
95
|
# 创建临时事件对象来发送消息
|
|
88
96
|
temp_event = DiscordPlatformEvent(
|
|
@@ -149,7 +157,9 @@ class DiscordPlatformAdapter(Platform):
|
|
|
149
157
|
logger.error(f"[Discord] 适配器运行时发生意外错误: {e}", exc_info=True)
|
|
150
158
|
|
|
151
159
|
def _get_message_type(
|
|
152
|
-
self,
|
|
160
|
+
self,
|
|
161
|
+
channel: Messageable,
|
|
162
|
+
guild_id: int | None = None,
|
|
153
163
|
) -> MessageType:
|
|
154
164
|
"""根据 channel 对象和 guild_id 判断消息类型"""
|
|
155
165
|
if guild_id is not None:
|
|
@@ -201,7 +211,8 @@ class DiscordPlatformAdapter(Platform):
|
|
|
201
211
|
abm.group_id = self._get_channel_id(message.channel)
|
|
202
212
|
abm.message_str = content
|
|
203
213
|
abm.sender = MessageMember(
|
|
204
|
-
user_id=str(message.author.id),
|
|
214
|
+
user_id=str(message.author.id),
|
|
215
|
+
nickname=message.author.display_name,
|
|
205
216
|
)
|
|
206
217
|
message_chain = []
|
|
207
218
|
if abm.message_str:
|
|
@@ -209,14 +220,14 @@ class DiscordPlatformAdapter(Platform):
|
|
|
209
220
|
if message.attachments:
|
|
210
221
|
for attachment in message.attachments:
|
|
211
222
|
if attachment.content_type and attachment.content_type.startswith(
|
|
212
|
-
"image/"
|
|
223
|
+
"image/",
|
|
213
224
|
):
|
|
214
225
|
message_chain.append(
|
|
215
|
-
Image(file=attachment.url, filename=attachment.filename)
|
|
226
|
+
Image(file=attachment.url, filename=attachment.filename),
|
|
216
227
|
)
|
|
217
228
|
else:
|
|
218
229
|
message_chain.append(
|
|
219
|
-
File(name=attachment.filename, url=attachment.url)
|
|
230
|
+
File(name=attachment.filename, url=attachment.url),
|
|
220
231
|
)
|
|
221
232
|
abm.message = message_chain
|
|
222
233
|
abm.raw_message = message
|
|
@@ -260,7 +271,7 @@ class DiscordPlatformAdapter(Platform):
|
|
|
260
271
|
if hasattr(message.raw_message, "guild") and message.raw_message.guild:
|
|
261
272
|
try:
|
|
262
273
|
bot_member = message.raw_message.guild.get_member(
|
|
263
|
-
self.client.user.id
|
|
274
|
+
self.client.user.id,
|
|
264
275
|
)
|
|
265
276
|
except Exception:
|
|
266
277
|
bot_member = None
|
|
@@ -346,7 +357,7 @@ class DiscordPlatformAdapter(Platform):
|
|
|
346
357
|
description="指令的所有参数",
|
|
347
358
|
type=discord.SlashCommandOptionType.string,
|
|
348
359
|
required=False,
|
|
349
|
-
)
|
|
360
|
+
),
|
|
350
361
|
]
|
|
351
362
|
|
|
352
363
|
# 创建SlashCommand
|
|
@@ -362,7 +373,7 @@ class DiscordPlatformAdapter(Platform):
|
|
|
362
373
|
|
|
363
374
|
if registered_commands:
|
|
364
375
|
logger.info(
|
|
365
|
-
f"[Discord] 准备同步 {len(registered_commands)} 个指令: {', '.join(registered_commands)}"
|
|
376
|
+
f"[Discord] 准备同步 {len(registered_commands)} 个指令: {', '.join(registered_commands)}",
|
|
366
377
|
)
|
|
367
378
|
else:
|
|
368
379
|
logger.info("[Discord] 没有发现可注册的指令。")
|
|
@@ -375,7 +386,9 @@ class DiscordPlatformAdapter(Platform):
|
|
|
375
386
|
def _create_dynamic_callback(self, cmd_name: str):
|
|
376
387
|
"""为每个指令动态创建一个异步回调函数"""
|
|
377
388
|
|
|
378
|
-
async def dynamic_callback(
|
|
389
|
+
async def dynamic_callback(
|
|
390
|
+
ctx: discord.ApplicationContext, params: str | None = None
|
|
391
|
+
):
|
|
379
392
|
# 将平台特定的前缀'/'剥离,以适配通用的CommandFilter
|
|
380
393
|
logger.debug(f"[Discord] 回调函数触发: {cmd_name}")
|
|
381
394
|
logger.debug(f"[Discord] 回调函数参数: {ctx}")
|
|
@@ -387,7 +400,7 @@ class DiscordPlatformAdapter(Platform):
|
|
|
387
400
|
logger.debug(
|
|
388
401
|
f"[Discord] 斜杠指令 '{cmd_name}' 被触发。 "
|
|
389
402
|
f"原始参数: '{params}'. "
|
|
390
|
-
f"构建的指令字符串: '{message_str_for_filter}'"
|
|
403
|
+
f"构建的指令字符串: '{message_str_for_filter}'",
|
|
391
404
|
)
|
|
392
405
|
|
|
393
406
|
# 尝试立即响应,防止超时
|
|
@@ -404,7 +417,8 @@ class DiscordPlatformAdapter(Platform):
|
|
|
404
417
|
abm.group_id = self._get_channel_id(ctx.channel)
|
|
405
418
|
abm.message_str = message_str_for_filter
|
|
406
419
|
abm.sender = MessageMember(
|
|
407
|
-
user_id=str(ctx.author.id),
|
|
420
|
+
user_id=str(ctx.author.id),
|
|
421
|
+
nickname=ctx.author.display_name,
|
|
408
422
|
)
|
|
409
423
|
abm.message = [Plain(text=message_str_for_filter)]
|
|
410
424
|
abm.raw_message = ctx.interaction
|
|
@@ -419,8 +433,9 @@ class DiscordPlatformAdapter(Platform):
|
|
|
419
433
|
|
|
420
434
|
@staticmethod
|
|
421
435
|
def _extract_command_info(
|
|
422
|
-
event_filter: Any,
|
|
423
|
-
|
|
436
|
+
event_filter: Any,
|
|
437
|
+
handler_metadata: StarHandlerMetadata,
|
|
438
|
+
) -> tuple[str, str, CommandFilter] | None:
|
|
424
439
|
"""从事件过滤器中提取指令信息"""
|
|
425
440
|
cmd_name = None
|
|
426
441
|
# is_group = False
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import discord
|
|
3
2
|
import base64
|
|
3
|
+
import binascii
|
|
4
|
+
import sys
|
|
4
5
|
from io import BytesIO
|
|
5
6
|
from pathlib import Path
|
|
6
|
-
from typing import Optional
|
|
7
|
-
import sys
|
|
8
7
|
|
|
8
|
+
import discord
|
|
9
|
+
|
|
10
|
+
from astrbot import logger
|
|
9
11
|
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
10
|
-
from astrbot.api.platform import AstrBotMessage, PlatformMetadata, At
|
|
11
12
|
from astrbot.api.message_components import (
|
|
12
|
-
Plain,
|
|
13
|
-
Image,
|
|
14
|
-
File,
|
|
15
13
|
BaseMessageComponent,
|
|
14
|
+
File,
|
|
15
|
+
Image,
|
|
16
|
+
Plain,
|
|
16
17
|
Reply,
|
|
17
18
|
)
|
|
18
|
-
from astrbot import
|
|
19
|
+
from astrbot.api.platform import AstrBotMessage, At, PlatformMetadata
|
|
20
|
+
|
|
19
21
|
from .client import DiscordBotClient
|
|
20
22
|
from .components import DiscordEmbed, DiscordView
|
|
21
23
|
|
|
@@ -41,7 +43,7 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
41
43
|
platform_meta: PlatformMetadata,
|
|
42
44
|
session_id: str,
|
|
43
45
|
client: DiscordBotClient,
|
|
44
|
-
interaction_followup_webhook:
|
|
46
|
+
interaction_followup_webhook: discord.Webhook | None = None,
|
|
45
47
|
):
|
|
46
48
|
super().__init__(message_str, message_obj, platform_meta, session_id)
|
|
47
49
|
self.client = client
|
|
@@ -50,7 +52,6 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
50
52
|
@override
|
|
51
53
|
async def send(self, message: MessageChain):
|
|
52
54
|
"""发送消息到Discord平台"""
|
|
53
|
-
|
|
54
55
|
# 解析消息链为 Discord 所需的对象
|
|
55
56
|
try:
|
|
56
57
|
(
|
|
@@ -90,20 +91,19 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
90
91
|
channel = await self._get_channel()
|
|
91
92
|
if not channel:
|
|
92
93
|
return
|
|
93
|
-
|
|
94
|
-
await channel.send(**kwargs)
|
|
94
|
+
await channel.send(**kwargs)
|
|
95
95
|
|
|
96
96
|
except Exception as e:
|
|
97
97
|
logger.error(f"[Discord] 发送消息时发生未知错误: {e}", exc_info=True)
|
|
98
98
|
|
|
99
99
|
await super().send(message)
|
|
100
100
|
|
|
101
|
-
async def _get_channel(self) ->
|
|
101
|
+
async def _get_channel(self) -> discord.abc.Messageable | None:
|
|
102
102
|
"""获取当前事件对应的频道对象"""
|
|
103
103
|
try:
|
|
104
104
|
channel_id = int(self.session_id)
|
|
105
105
|
return self.client.get_channel(
|
|
106
|
-
channel_id
|
|
106
|
+
channel_id,
|
|
107
107
|
) or await self.client.fetch_channel(channel_id)
|
|
108
108
|
except (ValueError, discord.errors.NotFound, discord.errors.Forbidden):
|
|
109
109
|
logger.error(f"[Discord] 无法获取频道 {self.session_id}")
|
|
@@ -112,20 +112,20 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
112
112
|
async def _parse_to_discord(
|
|
113
113
|
self,
|
|
114
114
|
message: MessageChain,
|
|
115
|
-
) -> tuple[str, list[discord.File],
|
|
115
|
+
) -> tuple[str, list[discord.File], discord.ui.View | None, list[discord.Embed]]:
|
|
116
116
|
"""将 MessageChain 解析为 Discord 发送所需的内容"""
|
|
117
|
-
|
|
117
|
+
content_parts = []
|
|
118
118
|
files = []
|
|
119
119
|
view = None
|
|
120
120
|
embeds = []
|
|
121
121
|
reference_message_id = None
|
|
122
122
|
for i in message.chain: # 遍历消息链
|
|
123
123
|
if isinstance(i, Plain): # 如果是文字类型的
|
|
124
|
-
|
|
124
|
+
content_parts.append(i.text)
|
|
125
125
|
elif isinstance(i, Reply):
|
|
126
126
|
reference_message_id = i.id
|
|
127
127
|
elif isinstance(i, At):
|
|
128
|
-
|
|
128
|
+
content_parts.append(f"<@{i.qq}>")
|
|
129
129
|
elif isinstance(i, Image):
|
|
130
130
|
logger.debug(f"[Discord] 开始处理 Image 组件: {i}")
|
|
131
131
|
try:
|
|
@@ -146,13 +146,14 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
146
146
|
continue
|
|
147
147
|
|
|
148
148
|
# 2. File URI
|
|
149
|
-
|
|
149
|
+
if file_content.startswith("file:///"):
|
|
150
150
|
logger.debug(f"[Discord] 处理 File URI: {file_content}")
|
|
151
151
|
path = Path(file_content[8:])
|
|
152
152
|
if await asyncio.to_thread(path.exists):
|
|
153
153
|
file_bytes = await asyncio.to_thread(path.read_bytes)
|
|
154
154
|
discord_file = discord.File(
|
|
155
|
-
BytesIO(file_bytes),
|
|
155
|
+
BytesIO(file_bytes),
|
|
156
|
+
filename=filename or path.name,
|
|
156
157
|
)
|
|
157
158
|
else:
|
|
158
159
|
logger.warning(f"[Discord] 图片文件不存在: {path}")
|
|
@@ -166,7 +167,8 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
166
167
|
b64_data += "=" * (4 - missing_padding)
|
|
167
168
|
img_bytes = base64.b64decode(b64_data)
|
|
168
169
|
discord_file = discord.File(
|
|
169
|
-
BytesIO(img_bytes),
|
|
170
|
+
BytesIO(img_bytes),
|
|
171
|
+
filename=filename or "image.png",
|
|
170
172
|
)
|
|
171
173
|
|
|
172
174
|
# 4. 裸 Base64 或本地路径
|
|
@@ -179,17 +181,19 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
179
181
|
b64_data += "=" * (4 - missing_padding)
|
|
180
182
|
img_bytes = base64.b64decode(b64_data)
|
|
181
183
|
discord_file = discord.File(
|
|
182
|
-
BytesIO(img_bytes),
|
|
184
|
+
BytesIO(img_bytes),
|
|
185
|
+
filename=filename or "image.png",
|
|
183
186
|
)
|
|
184
|
-
except (ValueError, TypeError,
|
|
187
|
+
except (ValueError, TypeError, binascii.Error):
|
|
185
188
|
logger.debug(
|
|
186
|
-
f"[Discord] 裸 Base64 解码失败,作为本地路径处理: {file_content}"
|
|
189
|
+
f"[Discord] 裸 Base64 解码失败,作为本地路径处理: {file_content}",
|
|
187
190
|
)
|
|
188
191
|
path = Path(file_content)
|
|
189
192
|
if await asyncio.to_thread(path.exists):
|
|
190
193
|
file_bytes = await asyncio.to_thread(path.read_bytes)
|
|
191
194
|
discord_file = discord.File(
|
|
192
|
-
BytesIO(file_bytes),
|
|
195
|
+
BytesIO(file_bytes),
|
|
196
|
+
filename=filename or path.name,
|
|
193
197
|
)
|
|
194
198
|
else:
|
|
195
199
|
logger.warning(f"[Discord] 图片文件不存在: {path}")
|
|
@@ -212,11 +216,11 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
212
216
|
if await asyncio.to_thread(path.exists):
|
|
213
217
|
file_bytes = await asyncio.to_thread(path.read_bytes)
|
|
214
218
|
files.append(
|
|
215
|
-
discord.File(BytesIO(file_bytes), filename=i.name)
|
|
219
|
+
discord.File(BytesIO(file_bytes), filename=i.name),
|
|
216
220
|
)
|
|
217
221
|
else:
|
|
218
222
|
logger.warning(
|
|
219
|
-
f"[Discord] 获取文件失败,路径不存在: {file_path_str}"
|
|
223
|
+
f"[Discord] 获取文件失败,路径不存在: {file_path_str}",
|
|
220
224
|
)
|
|
221
225
|
else:
|
|
222
226
|
logger.warning(f"[Discord] 获取文件失败: {i.name}")
|
|
@@ -235,6 +239,7 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
235
239
|
else:
|
|
236
240
|
logger.debug(f"[Discord] 忽略了不支持的消息组件: {i.type}")
|
|
237
241
|
|
|
242
|
+
content = "".join(content_parts)
|
|
238
243
|
if len(content) > 2000:
|
|
239
244
|
logger.warning("[Discord] 消息内容超过2000字符,将被截断。")
|
|
240
245
|
content = content[:2000]
|
|
@@ -244,7 +249,8 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
244
249
|
"""对原消息添加反应"""
|
|
245
250
|
try:
|
|
246
251
|
if hasattr(self.message_obj, "raw_message") and hasattr(
|
|
247
|
-
self.message_obj.raw_message,
|
|
252
|
+
self.message_obj.raw_message,
|
|
253
|
+
"add_reaction",
|
|
248
254
|
):
|
|
249
255
|
await self.message_obj.raw_message.add_reaction(emoji)
|
|
250
256
|
except Exception as e:
|
|
@@ -279,7 +285,8 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
279
285
|
def is_mentioned(self) -> bool:
|
|
280
286
|
"""判断机器人是否被@"""
|
|
281
287
|
if hasattr(self.message_obj, "raw_message") and hasattr(
|
|
282
|
-
self.message_obj.raw_message,
|
|
288
|
+
self.message_obj.raw_message,
|
|
289
|
+
"mentions",
|
|
283
290
|
):
|
|
284
291
|
return any(
|
|
285
292
|
mention.id == int(self.message_obj.self_id)
|
|
@@ -290,7 +297,8 @@ class DiscordPlatformEvent(AstrMessageEvent):
|
|
|
290
297
|
def get_mention_clean_content(self) -> str:
|
|
291
298
|
"""获取去除@后的清洁内容"""
|
|
292
299
|
if hasattr(self.message_obj, "raw_message") and hasattr(
|
|
293
|
-
self.message_obj.raw_message,
|
|
300
|
+
self.message_obj.raw_message,
|
|
301
|
+
"clean_content",
|
|
294
302
|
):
|
|
295
303
|
return self.message_obj.raw_message.clean_content
|
|
296
304
|
return self.message_str
|
|
@@ -1,30 +1,35 @@
|
|
|
1
|
-
import base64
|
|
2
1
|
import asyncio
|
|
2
|
+
import base64
|
|
3
3
|
import json
|
|
4
4
|
import re
|
|
5
5
|
import uuid
|
|
6
|
-
import astrbot.api.message_components as Comp
|
|
7
6
|
|
|
7
|
+
import lark_oapi as lark
|
|
8
|
+
from lark_oapi.api.im.v1 import *
|
|
9
|
+
|
|
10
|
+
import astrbot.api.message_components as Comp
|
|
11
|
+
from astrbot import logger
|
|
12
|
+
from astrbot.api.event import MessageChain
|
|
8
13
|
from astrbot.api.platform import (
|
|
9
|
-
Platform,
|
|
10
14
|
AstrBotMessage,
|
|
11
15
|
MessageMember,
|
|
12
16
|
MessageType,
|
|
17
|
+
Platform,
|
|
13
18
|
PlatformMetadata,
|
|
14
19
|
)
|
|
15
|
-
from astrbot.api.event import MessageChain
|
|
16
20
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
17
|
-
|
|
21
|
+
|
|
18
22
|
from ...register import register_platform_adapter
|
|
19
|
-
from
|
|
20
|
-
import lark_oapi as lark
|
|
21
|
-
from lark_oapi.api.im.v1 import *
|
|
23
|
+
from .lark_event import LarkMessageEvent
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
@register_platform_adapter("lark", "飞书机器人官方 API 适配器")
|
|
25
27
|
class LarkPlatformAdapter(Platform):
|
|
26
28
|
def __init__(
|
|
27
|
-
self,
|
|
29
|
+
self,
|
|
30
|
+
platform_config: dict,
|
|
31
|
+
platform_settings: dict,
|
|
32
|
+
event_queue: asyncio.Queue,
|
|
28
33
|
) -> None:
|
|
29
34
|
super().__init__(event_queue)
|
|
30
35
|
|
|
@@ -65,14 +70,16 @@ class LarkPlatformAdapter(Platform):
|
|
|
65
70
|
)
|
|
66
71
|
|
|
67
72
|
async def send_by_session(
|
|
68
|
-
self,
|
|
73
|
+
self,
|
|
74
|
+
session: MessageSesion,
|
|
75
|
+
message_chain: MessageChain,
|
|
69
76
|
):
|
|
70
77
|
res = await LarkMessageEvent._convert_to_lark(message_chain, self.lark_api)
|
|
71
78
|
wrapped = {
|
|
72
79
|
"zh_cn": {
|
|
73
80
|
"title": "",
|
|
74
81
|
"content": res,
|
|
75
|
-
}
|
|
82
|
+
},
|
|
76
83
|
}
|
|
77
84
|
|
|
78
85
|
if session.message_type == MessageType.GROUP_MESSAGE:
|
|
@@ -91,7 +98,7 @@ class LarkPlatformAdapter(Platform):
|
|
|
91
98
|
.content(json.dumps(wrapped))
|
|
92
99
|
.msg_type("post")
|
|
93
100
|
.uuid(str(uuid.uuid4()))
|
|
94
|
-
.build()
|
|
101
|
+
.build(),
|
|
95
102
|
)
|
|
96
103
|
.build()
|
|
97
104
|
)
|
|
@@ -160,7 +167,7 @@ class LarkPlatformAdapter(Platform):
|
|
|
160
167
|
content_json_b = _ls
|
|
161
168
|
elif message.message_type == "image":
|
|
162
169
|
content_json_b = [
|
|
163
|
-
{"tag": "img", "image_key": content_json_b["image_key"], "style": []}
|
|
170
|
+
{"tag": "img", "image_key": content_json_b["image_key"], "style": []},
|
|
164
171
|
]
|
|
165
172
|
|
|
166
173
|
if message.message_type in ("post", "image"):
|
|
@@ -200,11 +207,10 @@ class LarkPlatformAdapter(Platform):
|
|
|
200
207
|
abm.session_id = abm.group_id
|
|
201
208
|
else:
|
|
202
209
|
abm.session_id = abm.sender.user_id
|
|
210
|
+
elif abm.type == MessageType.GROUP_MESSAGE:
|
|
211
|
+
abm.session_id = f"{abm.sender.user_id}%{abm.group_id}" # 也保留群组id
|
|
203
212
|
else:
|
|
204
|
-
|
|
205
|
-
abm.session_id = f"{abm.sender.user_id}%{abm.group_id}" # 也保留群组id
|
|
206
|
-
else:
|
|
207
|
-
abm.session_id = abm.sender.user_id
|
|
213
|
+
abm.session_id = abm.sender.user_id
|
|
208
214
|
|
|
209
215
|
logger.debug(abm)
|
|
210
216
|
await self.handle_msg(abm)
|
|
@@ -1,27 +1,34 @@
|
|
|
1
|
+
import base64
|
|
1
2
|
import json
|
|
2
3
|
import os
|
|
3
4
|
import uuid
|
|
4
|
-
import base64
|
|
5
|
-
import lark_oapi as lark
|
|
6
5
|
from io import BytesIO
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
from astrbot.api.message_components import Plain, Image as AstrBotImage, At
|
|
10
|
-
from astrbot.core.utils.io import download_image_by_url
|
|
6
|
+
|
|
7
|
+
import lark_oapi as lark
|
|
11
8
|
from lark_oapi.api.im.v1 import *
|
|
9
|
+
|
|
12
10
|
from astrbot import logger
|
|
11
|
+
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
12
|
+
from astrbot.api.message_components import At, Plain
|
|
13
|
+
from astrbot.api.message_components import Image as AstrBotImage
|
|
13
14
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
15
|
+
from astrbot.core.utils.io import download_image_by_url
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
class LarkMessageEvent(AstrMessageEvent):
|
|
17
19
|
def __init__(
|
|
18
|
-
self,
|
|
20
|
+
self,
|
|
21
|
+
message_str,
|
|
22
|
+
message_obj,
|
|
23
|
+
platform_meta,
|
|
24
|
+
session_id,
|
|
25
|
+
bot: lark.Client,
|
|
19
26
|
):
|
|
20
27
|
super().__init__(message_str, message_obj, platform_meta, session_id)
|
|
21
28
|
self.bot = bot
|
|
22
29
|
|
|
23
30
|
@staticmethod
|
|
24
|
-
async def _convert_to_lark(message: MessageChain, lark_client: lark.Client) ->
|
|
31
|
+
async def _convert_to_lark(message: MessageChain, lark_client: lark.Client) -> list:
|
|
25
32
|
ret = []
|
|
26
33
|
_stage = []
|
|
27
34
|
for comp in message.chain:
|
|
@@ -58,7 +65,7 @@ class LarkMessageEvent(AstrMessageEvent):
|
|
|
58
65
|
CreateImageRequestBody.builder()
|
|
59
66
|
.image_type("message")
|
|
60
67
|
.image(image_file)
|
|
61
|
-
.build()
|
|
68
|
+
.build(),
|
|
62
69
|
)
|
|
63
70
|
.build()
|
|
64
71
|
)
|
|
@@ -83,7 +90,7 @@ class LarkMessageEvent(AstrMessageEvent):
|
|
|
83
90
|
"zh_cn": {
|
|
84
91
|
"title": "",
|
|
85
92
|
"content": res,
|
|
86
|
-
}
|
|
93
|
+
},
|
|
87
94
|
}
|
|
88
95
|
|
|
89
96
|
request = (
|
|
@@ -95,7 +102,7 @@ class LarkMessageEvent(AstrMessageEvent):
|
|
|
95
102
|
.msg_type("post")
|
|
96
103
|
.uuid(str(uuid.uuid4()))
|
|
97
104
|
.reply_in_thread(False)
|
|
98
|
-
.build()
|
|
105
|
+
.build(),
|
|
99
106
|
)
|
|
100
107
|
.build()
|
|
101
108
|
)
|
|
@@ -114,14 +121,14 @@ class LarkMessageEvent(AstrMessageEvent):
|
|
|
114
121
|
.request_body(
|
|
115
122
|
CreateMessageReactionRequestBody.builder()
|
|
116
123
|
.reaction_type(Emoji.builder().emoji_type(emoji).build())
|
|
117
|
-
.build()
|
|
124
|
+
.build(),
|
|
118
125
|
)
|
|
119
126
|
.build()
|
|
120
127
|
)
|
|
121
128
|
response = await self.bot.im.v1.message_reaction.acreate(request)
|
|
122
129
|
if not response.success():
|
|
123
130
|
logger.error(f"发送飞书表情回应失败({response.code}): {response.msg}")
|
|
124
|
-
return
|
|
131
|
+
return
|
|
125
132
|
|
|
126
133
|
async def send_streaming(self, generator, use_fallback: bool = False):
|
|
127
134
|
buffer = None
|
|
@@ -131,7 +138,7 @@ class LarkMessageEvent(AstrMessageEvent):
|
|
|
131
138
|
else:
|
|
132
139
|
buffer.chain.extend(chain.chain)
|
|
133
140
|
if not buffer:
|
|
134
|
-
return
|
|
141
|
+
return None
|
|
135
142
|
buffer.squash_plain()
|
|
136
143
|
await self.send(buffer)
|
|
137
144
|
return await super().send_streaming(generator, use_fallback)
|