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,19 @@
|
|
|
1
|
-
from typing import List, Dict, Type
|
|
2
|
-
from .platform_metadata import PlatformMetadata
|
|
3
1
|
from astrbot.core import logger
|
|
4
2
|
|
|
5
|
-
|
|
3
|
+
from .platform_metadata import PlatformMetadata
|
|
4
|
+
|
|
5
|
+
platform_registry: list[PlatformMetadata] = []
|
|
6
6
|
"""维护了通过装饰器注册的平台适配器"""
|
|
7
|
-
platform_cls_map:
|
|
7
|
+
platform_cls_map: dict[str, type] = {}
|
|
8
8
|
"""维护了平台适配器名称和适配器类的映射"""
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
def register_platform_adapter(
|
|
12
12
|
adapter_name: str,
|
|
13
13
|
desc: str,
|
|
14
|
-
default_config_tmpl: dict = None,
|
|
15
|
-
adapter_display_name: str = None,
|
|
16
|
-
logo_path: str = None,
|
|
14
|
+
default_config_tmpl: dict | None = None,
|
|
15
|
+
adapter_display_name: str | None = None,
|
|
16
|
+
logo_path: str | None = None,
|
|
17
17
|
):
|
|
18
18
|
"""用于注册平台适配器的带参装饰器。
|
|
19
19
|
|
|
@@ -24,7 +24,7 @@ def register_platform_adapter(
|
|
|
24
24
|
def decorator(cls):
|
|
25
25
|
if adapter_name in platform_cls_map:
|
|
26
26
|
raise ValueError(
|
|
27
|
-
f"平台适配器 {adapter_name} 已经注册过了,可能发生了适配器命名冲突。"
|
|
27
|
+
f"平台适配器 {adapter_name} 已经注册过了,可能发生了适配器命名冲突。",
|
|
28
28
|
)
|
|
29
29
|
|
|
30
30
|
# 添加必备选项
|
|
@@ -1,24 +1,31 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import re
|
|
3
|
-
from
|
|
3
|
+
from collections.abc import AsyncGenerator
|
|
4
|
+
|
|
4
5
|
from aiocqhttp import CQHttp, Event
|
|
6
|
+
|
|
5
7
|
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
6
8
|
from astrbot.api.message_components import (
|
|
9
|
+
BaseMessageComponent,
|
|
10
|
+
File,
|
|
7
11
|
Image,
|
|
8
12
|
Node,
|
|
9
13
|
Nodes,
|
|
10
14
|
Plain,
|
|
11
15
|
Record,
|
|
12
16
|
Video,
|
|
13
|
-
File,
|
|
14
|
-
BaseMessageComponent,
|
|
15
17
|
)
|
|
16
18
|
from astrbot.api.platform import Group, MessageMember
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
20
22
|
def __init__(
|
|
21
|
-
self,
|
|
23
|
+
self,
|
|
24
|
+
message_str,
|
|
25
|
+
message_obj,
|
|
26
|
+
platform_meta,
|
|
27
|
+
session_id,
|
|
28
|
+
bot: CQHttp,
|
|
22
29
|
):
|
|
23
30
|
super().__init__(message_str, message_obj, platform_meta, session_id)
|
|
24
31
|
self.bot = bot
|
|
@@ -35,16 +42,15 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
35
42
|
"file": f"base64://{bs64}",
|
|
36
43
|
},
|
|
37
44
|
}
|
|
38
|
-
|
|
45
|
+
if isinstance(segment, File):
|
|
39
46
|
# For File segments, we need to handle the file differently
|
|
40
47
|
d = await segment.to_dict()
|
|
41
48
|
return d
|
|
42
|
-
|
|
49
|
+
if isinstance(segment, Video):
|
|
43
50
|
d = await segment.to_dict()
|
|
44
51
|
return d
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return segment.toDict()
|
|
52
|
+
# For other segments, we simply convert them to a dict by calling toDict
|
|
53
|
+
return segment.toDict()
|
|
48
54
|
|
|
49
55
|
@staticmethod
|
|
50
56
|
async def _parse_onebot_json(message_chain: MessageChain):
|
|
@@ -78,7 +84,7 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
78
84
|
await bot.send(event=event, message=messages)
|
|
79
85
|
else:
|
|
80
86
|
raise ValueError(
|
|
81
|
-
f"无法发送消息:缺少有效的数字 session_id({session_id}) 或 event({event})"
|
|
87
|
+
f"无法发送消息:缺少有效的数字 session_id({session_id}) 或 event({event})",
|
|
82
88
|
)
|
|
83
89
|
|
|
84
90
|
@classmethod
|
|
@@ -88,7 +94,7 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
88
94
|
message_chain: MessageChain,
|
|
89
95
|
event: Event | None = None,
|
|
90
96
|
is_group: bool = False,
|
|
91
|
-
session_id: str = None,
|
|
97
|
+
session_id: str | None = None,
|
|
92
98
|
):
|
|
93
99
|
"""发送消息至 QQ 协议端(aiocqhttp)。
|
|
94
100
|
|
|
@@ -98,8 +104,8 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
98
104
|
event (Event | None, optional): aiocqhttp 事件对象.
|
|
99
105
|
is_group (bool, optional): 是否为群消息.
|
|
100
106
|
session_id (str | None, optional): 会话 ID(群号或 QQ 号
|
|
101
|
-
"""
|
|
102
107
|
|
|
108
|
+
"""
|
|
103
109
|
# 转发消息、文件消息不能和普通消息混在一起发送
|
|
104
110
|
send_one_by_one = any(
|
|
105
111
|
isinstance(seg, (Node, Nodes, File)) for seg in message_chain.chain
|
|
@@ -152,7 +158,9 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
152
158
|
await super().send(message)
|
|
153
159
|
|
|
154
160
|
async def send_streaming(
|
|
155
|
-
self,
|
|
161
|
+
self,
|
|
162
|
+
generator: AsyncGenerator,
|
|
163
|
+
use_fallback: bool = False,
|
|
156
164
|
):
|
|
157
165
|
if not use_fallback:
|
|
158
166
|
buffer = None
|
|
@@ -162,7 +170,7 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
162
170
|
else:
|
|
163
171
|
buffer.chain.extend(chain.chain)
|
|
164
172
|
if not buffer:
|
|
165
|
-
return
|
|
173
|
+
return None
|
|
166
174
|
buffer.squash_plain()
|
|
167
175
|
await self.send(buffer)
|
|
168
176
|
return await super().send_streaming(generator, use_fallback)
|
|
@@ -198,7 +206,7 @@ class AiocqhttpMessageEvent(AstrMessageEvent):
|
|
|
198
206
|
group_id=group_id,
|
|
199
207
|
)
|
|
200
208
|
|
|
201
|
-
members:
|
|
209
|
+
members: list[dict] = await self.bot.call_action(
|
|
202
210
|
"get_group_member_list",
|
|
203
211
|
group_id=group_id,
|
|
204
212
|
)
|
|
@@ -1,33 +1,41 @@
|
|
|
1
|
-
import time
|
|
2
1
|
import asyncio
|
|
2
|
+
import itertools
|
|
3
3
|
import logging
|
|
4
|
+
import time
|
|
4
5
|
import uuid
|
|
5
|
-
import
|
|
6
|
-
from typing import
|
|
6
|
+
from collections.abc import Awaitable
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
7
9
|
from aiocqhttp import CQHttp, Event
|
|
10
|
+
from aiocqhttp.exceptions import ActionFailed
|
|
11
|
+
|
|
12
|
+
from astrbot.api import logger
|
|
13
|
+
from astrbot.api.event import MessageChain
|
|
14
|
+
from astrbot.api.message_components import *
|
|
8
15
|
from astrbot.api.platform import (
|
|
9
|
-
Platform,
|
|
10
16
|
AstrBotMessage,
|
|
11
17
|
MessageMember,
|
|
12
18
|
MessageType,
|
|
19
|
+
Platform,
|
|
13
20
|
PlatformMetadata,
|
|
14
21
|
)
|
|
15
|
-
from astrbot.api.event import MessageChain
|
|
16
|
-
from .aiocqhttp_message_event import * # noqa: F403
|
|
17
|
-
from astrbot.api.message_components import * # noqa: F403
|
|
18
|
-
from astrbot.api import logger
|
|
19
|
-
from .aiocqhttp_message_event import AiocqhttpMessageEvent
|
|
20
22
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
23
|
+
|
|
21
24
|
from ...register import register_platform_adapter
|
|
22
|
-
from
|
|
25
|
+
from .aiocqhttp_message_event import *
|
|
26
|
+
from .aiocqhttp_message_event import AiocqhttpMessageEvent
|
|
23
27
|
|
|
24
28
|
|
|
25
29
|
@register_platform_adapter(
|
|
26
|
-
"aiocqhttp",
|
|
30
|
+
"aiocqhttp",
|
|
31
|
+
"适用于 OneBot V11 标准的消息平台适配器,支持反向 WebSockets。",
|
|
27
32
|
)
|
|
28
33
|
class AiocqhttpAdapter(Platform):
|
|
29
34
|
def __init__(
|
|
30
|
-
self,
|
|
35
|
+
self,
|
|
36
|
+
platform_config: dict,
|
|
37
|
+
platform_settings: dict,
|
|
38
|
+
event_queue: asyncio.Queue,
|
|
31
39
|
) -> None:
|
|
32
40
|
super().__init__(event_queue)
|
|
33
41
|
|
|
@@ -48,7 +56,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
48
56
|
import_name="aiocqhttp",
|
|
49
57
|
api_timeout_sec=180,
|
|
50
58
|
access_token=platform_config.get(
|
|
51
|
-
"ws_reverse_token"
|
|
59
|
+
"ws_reverse_token",
|
|
52
60
|
), # 以防旧版本配置不存在
|
|
53
61
|
)
|
|
54
62
|
|
|
@@ -81,7 +89,9 @@ class AiocqhttpAdapter(Platform):
|
|
|
81
89
|
logger.info("aiocqhttp(OneBot v11) 适配器已连接。")
|
|
82
90
|
|
|
83
91
|
async def send_by_session(
|
|
84
|
-
self,
|
|
92
|
+
self,
|
|
93
|
+
session: MessageSesion,
|
|
94
|
+
message_chain: MessageChain,
|
|
85
95
|
):
|
|
86
96
|
is_group = session.message_type == MessageType.GROUP_MESSAGE
|
|
87
97
|
if is_group:
|
|
@@ -97,14 +107,14 @@ class AiocqhttpAdapter(Platform):
|
|
|
97
107
|
)
|
|
98
108
|
await super().send_by_session(session, message_chain)
|
|
99
109
|
|
|
100
|
-
async def convert_message(self, event: Event) -> AstrBotMessage:
|
|
110
|
+
async def convert_message(self, event: Event) -> AstrBotMessage | None:
|
|
101
111
|
logger.debug(f"[aiocqhttp] RawMessage {event}")
|
|
102
112
|
|
|
103
113
|
if event["post_type"] == "message":
|
|
104
114
|
abm = await self._convert_handle_message_event(event)
|
|
105
115
|
if abm.sender.user_id == "2854196310":
|
|
106
116
|
# 屏蔽 QQ 管家的消息
|
|
107
|
-
return
|
|
117
|
+
return None
|
|
108
118
|
elif event["post_type"] == "notice":
|
|
109
119
|
abm = await self._convert_handle_notice_event(event)
|
|
110
120
|
elif event["post_type"] == "request":
|
|
@@ -118,7 +128,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
118
128
|
abm.self_id = str(event.self_id)
|
|
119
129
|
abm.sender = MessageMember(user_id=str(event.user_id), nickname=event.user_id)
|
|
120
130
|
abm.type = MessageType.OTHER_MESSAGE
|
|
121
|
-
if
|
|
131
|
+
if event.get("group_id"):
|
|
122
132
|
abm.type = MessageType.GROUP_MESSAGE
|
|
123
133
|
abm.group_id = str(event.group_id)
|
|
124
134
|
else:
|
|
@@ -144,7 +154,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
144
154
|
abm.self_id = str(event.self_id)
|
|
145
155
|
abm.sender = MessageMember(user_id=str(event.user_id), nickname=event.user_id)
|
|
146
156
|
abm.type = MessageType.OTHER_MESSAGE
|
|
147
|
-
if
|
|
157
|
+
if event.get("group_id"):
|
|
148
158
|
abm.group_id = str(event.group_id)
|
|
149
159
|
abm.type = MessageType.GROUP_MESSAGE
|
|
150
160
|
else:
|
|
@@ -167,12 +177,14 @@ class AiocqhttpAdapter(Platform):
|
|
|
167
177
|
|
|
168
178
|
if "sub_type" in event:
|
|
169
179
|
if event["sub_type"] == "poke" and "target_id" in event:
|
|
170
|
-
abm.message.append(Poke(qq=str(event["target_id"]), type="poke"))
|
|
180
|
+
abm.message.append(Poke(qq=str(event["target_id"]), type="poke"))
|
|
171
181
|
|
|
172
182
|
return abm
|
|
173
183
|
|
|
174
184
|
async def _convert_handle_message_event(
|
|
175
|
-
self,
|
|
185
|
+
self,
|
|
186
|
+
event: Event,
|
|
187
|
+
get_reply=True,
|
|
176
188
|
) -> AstrBotMessage:
|
|
177
189
|
"""OneBot V11 消息类事件
|
|
178
190
|
|
|
@@ -207,13 +219,13 @@ class AiocqhttpAdapter(Platform):
|
|
|
207
219
|
|
|
208
220
|
message_str = ""
|
|
209
221
|
if not isinstance(event.message, list):
|
|
210
|
-
err = f"aiocqhttp: 无法识别的消息类型: {
|
|
222
|
+
err = f"aiocqhttp: 无法识别的消息类型: {event.message!s},此条消息将被忽略。如果您在使用 go-cqhttp,请将其配置文件中的 message.post-format 更改为 array。"
|
|
211
223
|
logger.critical(err)
|
|
212
224
|
try:
|
|
213
|
-
self.bot.send(event, err)
|
|
225
|
+
await self.bot.send(event, err)
|
|
214
226
|
except BaseException as e:
|
|
215
227
|
logger.error(f"回复消息失败: {e}")
|
|
216
|
-
return
|
|
228
|
+
return None
|
|
217
229
|
|
|
218
230
|
# 按消息段类型类型适配
|
|
219
231
|
for t, m_group in itertools.groupby(event.message, key=lambda x: x["type"]):
|
|
@@ -224,7 +236,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
224
236
|
# 如果文本段为空,则跳过
|
|
225
237
|
continue
|
|
226
238
|
message_str += current_text
|
|
227
|
-
a = ComponentTypes[t](text=current_text)
|
|
239
|
+
a = ComponentTypes[t](text=current_text)
|
|
228
240
|
abm.message.append(a)
|
|
229
241
|
|
|
230
242
|
elif t == "file":
|
|
@@ -264,7 +276,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
264
276
|
elif t == "reply":
|
|
265
277
|
for m in m_group:
|
|
266
278
|
if not get_reply:
|
|
267
|
-
a = ComponentTypes[t](**m["data"])
|
|
279
|
+
a = ComponentTypes[t](**m["data"])
|
|
268
280
|
abm.message.append(a)
|
|
269
281
|
else:
|
|
270
282
|
try:
|
|
@@ -277,11 +289,12 @@ class AiocqhttpAdapter(Platform):
|
|
|
277
289
|
new_event = Event.from_payload(reply_event_data)
|
|
278
290
|
if not new_event:
|
|
279
291
|
logger.error(
|
|
280
|
-
f"无法从回复消息数据构造 Event 对象: {reply_event_data}"
|
|
292
|
+
f"无法从回复消息数据构造 Event 对象: {reply_event_data}",
|
|
281
293
|
)
|
|
282
294
|
continue
|
|
283
295
|
abm_reply = await self._convert_handle_message_event(
|
|
284
|
-
new_event,
|
|
296
|
+
new_event,
|
|
297
|
+
get_reply=False,
|
|
285
298
|
)
|
|
286
299
|
|
|
287
300
|
reply_seg = Reply(
|
|
@@ -298,10 +311,12 @@ class AiocqhttpAdapter(Platform):
|
|
|
298
311
|
abm.message.append(reply_seg)
|
|
299
312
|
except BaseException as e:
|
|
300
313
|
logger.error(f"获取引用消息失败: {e}。")
|
|
301
|
-
a = ComponentTypes[t](**m["data"])
|
|
314
|
+
a = ComponentTypes[t](**m["data"])
|
|
302
315
|
abm.message.append(a)
|
|
303
316
|
elif t == "at":
|
|
304
317
|
first_at_self_processed = False
|
|
318
|
+
# Accumulate @ mention text for efficient concatenation
|
|
319
|
+
at_parts = []
|
|
305
320
|
|
|
306
321
|
for m in m_group:
|
|
307
322
|
try:
|
|
@@ -324,7 +339,8 @@ class AiocqhttpAdapter(Platform):
|
|
|
324
339
|
no_cache=False,
|
|
325
340
|
)
|
|
326
341
|
nickname = at_info.get("nick", "") or at_info.get(
|
|
327
|
-
"nickname",
|
|
342
|
+
"nickname",
|
|
343
|
+
"",
|
|
328
344
|
)
|
|
329
345
|
is_at_self = str(m["data"]["qq"]) in {abm.self_id, "all"}
|
|
330
346
|
|
|
@@ -332,7 +348,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
332
348
|
At(
|
|
333
349
|
qq=m["data"]["qq"],
|
|
334
350
|
name=nickname,
|
|
335
|
-
)
|
|
351
|
+
),
|
|
336
352
|
)
|
|
337
353
|
|
|
338
354
|
if is_at_self and not first_at_self_processed:
|
|
@@ -340,16 +356,18 @@ class AiocqhttpAdapter(Platform):
|
|
|
340
356
|
first_at_self_processed = True
|
|
341
357
|
else:
|
|
342
358
|
# 非第一个@机器人或@其他用户,添加到message_str
|
|
343
|
-
|
|
359
|
+
at_parts.append(f" @{nickname}({m['data']['qq']}) ")
|
|
344
360
|
else:
|
|
345
361
|
abm.message.append(At(qq=str(m["data"]["qq"]), name=""))
|
|
346
362
|
except ActionFailed as e:
|
|
347
363
|
logger.error(f"获取 @ 用户信息失败: {e},此消息段将被忽略。")
|
|
348
364
|
except BaseException as e:
|
|
349
365
|
logger.error(f"获取 @ 用户信息失败: {e},此消息段将被忽略。")
|
|
366
|
+
|
|
367
|
+
message_str += "".join(at_parts)
|
|
350
368
|
else:
|
|
351
369
|
for m in m_group:
|
|
352
|
-
a = ComponentTypes[t](**m["data"])
|
|
370
|
+
a = ComponentTypes[t](**m["data"])
|
|
353
371
|
abm.message.append(a)
|
|
354
372
|
|
|
355
373
|
abm.timestamp = int(time.time())
|
|
@@ -361,7 +379,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
361
379
|
def run(self) -> Awaitable[Any]:
|
|
362
380
|
if not self.host or not self.port:
|
|
363
381
|
logger.warning(
|
|
364
|
-
"aiocqhttp: 未配置 ws_reverse_host 或 ws_reverse_port,将使用默认值:http://0.0.0.0:6199"
|
|
382
|
+
"aiocqhttp: 未配置 ws_reverse_host 或 ws_reverse_port,将使用默认值:http://0.0.0.0:6199",
|
|
365
383
|
)
|
|
366
384
|
self.host = "0.0.0.0"
|
|
367
385
|
self.port = 6199
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import os
|
|
3
|
+
import threading
|
|
3
4
|
import uuid
|
|
5
|
+
|
|
4
6
|
import aiohttp
|
|
5
7
|
import dingtalk_stream
|
|
6
|
-
import
|
|
8
|
+
from dingtalk_stream import AckMessage
|
|
7
9
|
|
|
10
|
+
from astrbot import logger
|
|
11
|
+
from astrbot.api.event import MessageChain
|
|
12
|
+
from astrbot.api.message_components import At, Image, Plain
|
|
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
|
-
from astrbot.api.message_components import Image, Plain, At
|
|
17
20
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
18
|
-
from .dingtalk_event import DingtalkMessageEvent
|
|
19
|
-
from ...register import register_platform_adapter
|
|
20
|
-
from astrbot import logger
|
|
21
|
-
from dingtalk_stream import AckMessage
|
|
22
|
-
from astrbot.core.utils.io import download_file
|
|
23
21
|
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
22
|
+
from astrbot.core.utils.io import download_file
|
|
23
|
+
|
|
24
|
+
from ...register import register_platform_adapter
|
|
25
|
+
from .dingtalk_event import DingtalkMessageEvent
|
|
24
26
|
|
|
25
27
|
|
|
26
28
|
class MyEventHandler(dingtalk_stream.EventHandler):
|
|
@@ -38,7 +40,10 @@ class MyEventHandler(dingtalk_stream.EventHandler):
|
|
|
38
40
|
@register_platform_adapter("dingtalk", "钉钉机器人官方 API 适配器")
|
|
39
41
|
class DingtalkPlatformAdapter(Platform):
|
|
40
42
|
def __init__(
|
|
41
|
-
self,
|
|
43
|
+
self,
|
|
44
|
+
platform_config: dict,
|
|
45
|
+
platform_settings: dict,
|
|
46
|
+
event_queue: asyncio.Queue,
|
|
42
47
|
) -> None:
|
|
43
48
|
super().__init__(event_queue)
|
|
44
49
|
|
|
@@ -64,12 +69,15 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
64
69
|
client = dingtalk_stream.DingTalkStreamClient(credential, logger=logger)
|
|
65
70
|
client.register_all_event_handler(MyEventHandler())
|
|
66
71
|
client.register_callback_handler(
|
|
67
|
-
dingtalk_stream.ChatbotMessage.TOPIC,
|
|
72
|
+
dingtalk_stream.ChatbotMessage.TOPIC,
|
|
73
|
+
self.client,
|
|
68
74
|
)
|
|
69
75
|
self.client_ = client # 用于 websockets 的 client
|
|
70
76
|
|
|
71
77
|
async def send_by_session(
|
|
72
|
-
self,
|
|
78
|
+
self,
|
|
79
|
+
session: MessageSesion,
|
|
80
|
+
message_chain: MessageChain,
|
|
73
81
|
):
|
|
74
82
|
raise NotImplementedError("钉钉机器人适配器不支持 send_by_session")
|
|
75
83
|
|
|
@@ -81,7 +89,8 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
81
89
|
)
|
|
82
90
|
|
|
83
91
|
async def convert_msg(
|
|
84
|
-
self,
|
|
92
|
+
self,
|
|
93
|
+
message: dingtalk_stream.ChatbotMessage,
|
|
85
94
|
) -> AstrBotMessage:
|
|
86
95
|
abm = AstrBotMessage()
|
|
87
96
|
abm.message = []
|
|
@@ -93,15 +102,19 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
93
102
|
else MessageType.FRIEND_MESSAGE
|
|
94
103
|
)
|
|
95
104
|
abm.sender = MessageMember(
|
|
96
|
-
user_id=message.sender_id,
|
|
105
|
+
user_id=message.sender_id,
|
|
106
|
+
nickname=message.sender_nick,
|
|
97
107
|
)
|
|
98
108
|
abm.self_id = message.chatbot_user_id
|
|
99
109
|
abm.message_id = message.message_id
|
|
100
110
|
abm.raw_message = message
|
|
101
111
|
|
|
102
112
|
if abm.type == MessageType.GROUP_MESSAGE:
|
|
103
|
-
|
|
104
|
-
|
|
113
|
+
# 处理所有被 @ 的用户(包括机器人自己,因 at_users 已包含)
|
|
114
|
+
if message.at_users:
|
|
115
|
+
for user in message.at_users:
|
|
116
|
+
if user.dingtalk_id:
|
|
117
|
+
abm.message.append(At(qq=user.dingtalk_id))
|
|
105
118
|
abm.group_id = message.conversation_id
|
|
106
119
|
if self.unique_session:
|
|
107
120
|
abm.session_id = abm.sender.user_id
|
|
@@ -136,7 +149,10 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
136
149
|
return abm # 别忘了返回转换后的消息对象
|
|
137
150
|
|
|
138
151
|
async def download_ding_file(
|
|
139
|
-
self,
|
|
152
|
+
self,
|
|
153
|
+
download_code: str,
|
|
154
|
+
robot_code: str,
|
|
155
|
+
ext: str,
|
|
140
156
|
) -> str:
|
|
141
157
|
"""下载钉钉文件
|
|
142
158
|
|
|
@@ -156,20 +172,22 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
156
172
|
}
|
|
157
173
|
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
|
|
158
174
|
f_path = os.path.join(temp_dir, f"dingtalk_file_{uuid.uuid4()}.{ext}")
|
|
159
|
-
async with
|
|
160
|
-
|
|
175
|
+
async with (
|
|
176
|
+
aiohttp.ClientSession() as session,
|
|
177
|
+
session.post(
|
|
161
178
|
"https://api.dingtalk.com/v1.0/robot/messageFiles/download",
|
|
162
179
|
headers=headers,
|
|
163
180
|
json=payload,
|
|
164
|
-
) as resp
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
181
|
+
) as resp,
|
|
182
|
+
):
|
|
183
|
+
if resp.status != 200:
|
|
184
|
+
logger.error(
|
|
185
|
+
f"下载钉钉文件失败: {resp.status}, {await resp.text()}",
|
|
186
|
+
)
|
|
187
|
+
return None
|
|
188
|
+
resp_data = await resp.json()
|
|
189
|
+
download_url = resp_data["data"]["downloadUrl"]
|
|
190
|
+
await download_file(download_url, f_path)
|
|
173
191
|
return f_path
|
|
174
192
|
|
|
175
193
|
async def get_access_token(self) -> str:
|
|
@@ -184,7 +202,7 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
184
202
|
) as resp:
|
|
185
203
|
if resp.status != 200:
|
|
186
204
|
logger.error(
|
|
187
|
-
f"获取钉钉机器人 access_token 失败: {resp.status}, {await resp.text()}"
|
|
205
|
+
f"获取钉钉机器人 access_token 失败: {resp.status}, {await resp.text()}",
|
|
188
206
|
)
|
|
189
207
|
return None
|
|
190
208
|
return (await resp.json())["data"]["accessToken"]
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
|
|
2
3
|
import dingtalk_stream
|
|
4
|
+
|
|
3
5
|
import astrbot.api.message_components as Comp
|
|
4
|
-
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
5
6
|
from astrbot import logger
|
|
7
|
+
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class DingtalkMessageEvent(AstrMessageEvent):
|
|
@@ -18,7 +20,9 @@ class DingtalkMessageEvent(AstrMessageEvent):
|
|
|
18
20
|
self.client = client
|
|
19
21
|
|
|
20
22
|
async def send_with_client(
|
|
21
|
-
self,
|
|
23
|
+
self,
|
|
24
|
+
client: dingtalk_stream.ChatbotHandler,
|
|
25
|
+
message: MessageChain,
|
|
22
26
|
):
|
|
23
27
|
for segment in message.chain:
|
|
24
28
|
if isinstance(segment, Comp.Plain):
|
|
@@ -69,7 +73,7 @@ class DingtalkMessageEvent(AstrMessageEvent):
|
|
|
69
73
|
else:
|
|
70
74
|
buffer.chain.extend(chain.chain)
|
|
71
75
|
if not buffer:
|
|
72
|
-
return
|
|
76
|
+
return None
|
|
73
77
|
buffer.squash_plain()
|
|
74
78
|
await self.send(buffer)
|
|
75
79
|
return await super().send_streaming(generator, use_fallback)
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
1
3
|
import discord
|
|
4
|
+
|
|
2
5
|
from astrbot import logger
|
|
3
|
-
import sys
|
|
4
6
|
|
|
5
7
|
if sys.version_info >= (3, 12):
|
|
6
8
|
from typing import override
|
|
@@ -12,7 +14,7 @@ else:
|
|
|
12
14
|
class DiscordBotClient(discord.Bot):
|
|
13
15
|
"""Discord客户端封装"""
|
|
14
16
|
|
|
15
|
-
def __init__(self, token: str, proxy: str = None):
|
|
17
|
+
def __init__(self, token: str, proxy: str | None = None):
|
|
16
18
|
self.token = token
|
|
17
19
|
self.proxy = proxy
|
|
18
20
|
|
|
@@ -41,7 +43,8 @@ class DiscordBotClient(discord.Bot):
|
|
|
41
43
|
await self.on_ready_once_callback()
|
|
42
44
|
except Exception as e:
|
|
43
45
|
logger.error(
|
|
44
|
-
f"[Discord] on_ready_once_callback 执行失败: {e}",
|
|
46
|
+
f"[Discord] on_ready_once_callback 执行失败: {e}",
|
|
47
|
+
exc_info=True,
|
|
45
48
|
)
|
|
46
49
|
|
|
47
50
|
def _create_message_data(self, message: discord.Message) -> dict:
|
|
@@ -84,7 +87,7 @@ class DiscordBotClient(discord.Bot):
|
|
|
84
87
|
return
|
|
85
88
|
|
|
86
89
|
logger.debug(
|
|
87
|
-
f"[Discord] 收到原始消息 from {message.author.name}: {message.content}"
|
|
90
|
+
f"[Discord] 收到原始消息 from {message.author.name}: {message.content}",
|
|
88
91
|
)
|
|
89
92
|
|
|
90
93
|
if self.on_message_received:
|
|
@@ -103,12 +106,12 @@ class DiscordBotClient(discord.Bot):
|
|
|
103
106
|
command_name = interaction_data.get("name", "")
|
|
104
107
|
if options := interaction_data.get("options", []):
|
|
105
108
|
params = " ".join(
|
|
106
|
-
[f"{opt['name']}:{opt.get('value', '')}" for opt in options]
|
|
109
|
+
[f"{opt['name']}:{opt.get('value', '')}" for opt in options],
|
|
107
110
|
)
|
|
108
111
|
return f"/{command_name} {params}"
|
|
109
112
|
return f"/{command_name}"
|
|
110
113
|
|
|
111
|
-
|
|
114
|
+
if interaction_type == discord.InteractionType.component:
|
|
112
115
|
custom_id = interaction_data.get("custom_id", "")
|
|
113
116
|
component_type = interaction_data.get("component_type", "")
|
|
114
117
|
return f"component:{custom_id}:{component_type}"
|