AstrBot 3.5.6__py3-none-any.whl → 4.7.0__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 +16 -4
- astrbot/api/all.py +2 -1
- astrbot/api/event/__init__.py +5 -6
- astrbot/api/event/filter/__init__.py +37 -34
- astrbot/api/platform/__init__.py +7 -8
- astrbot/api/provider/__init__.py +8 -7
- astrbot/api/star/__init__.py +3 -4
- astrbot/api/util/__init__.py +2 -2
- astrbot/cli/__init__.py +1 -0
- astrbot/cli/__main__.py +18 -197
- astrbot/cli/commands/__init__.py +6 -0
- astrbot/cli/commands/cmd_conf.py +209 -0
- astrbot/cli/commands/cmd_init.py +56 -0
- astrbot/cli/commands/cmd_plug.py +245 -0
- astrbot/cli/commands/cmd_run.py +62 -0
- astrbot/cli/utils/__init__.py +18 -0
- astrbot/cli/utils/basic.py +76 -0
- astrbot/cli/utils/plugin.py +246 -0
- astrbot/cli/utils/version_comparator.py +90 -0
- astrbot/core/__init__.py +17 -19
- astrbot/core/agent/agent.py +14 -0
- astrbot/core/agent/handoff.py +38 -0
- astrbot/core/agent/hooks.py +30 -0
- astrbot/core/agent/mcp_client.py +385 -0
- astrbot/core/agent/message.py +175 -0
- astrbot/core/agent/response.py +14 -0
- astrbot/core/agent/run_context.py +22 -0
- astrbot/core/agent/runners/__init__.py +3 -0
- astrbot/core/agent/runners/base.py +65 -0
- astrbot/core/agent/runners/coze/coze_agent_runner.py +367 -0
- astrbot/core/agent/runners/coze/coze_api_client.py +324 -0
- astrbot/core/agent/runners/dashscope/dashscope_agent_runner.py +403 -0
- astrbot/core/agent/runners/dify/dify_agent_runner.py +336 -0
- astrbot/core/agent/runners/dify/dify_api_client.py +195 -0
- astrbot/core/agent/runners/tool_loop_agent_runner.py +400 -0
- astrbot/core/agent/tool.py +285 -0
- astrbot/core/agent/tool_executor.py +17 -0
- astrbot/core/astr_agent_context.py +19 -0
- astrbot/core/astr_agent_hooks.py +36 -0
- astrbot/core/astr_agent_run_util.py +80 -0
- astrbot/core/astr_agent_tool_exec.py +246 -0
- astrbot/core/astrbot_config_mgr.py +275 -0
- astrbot/core/config/__init__.py +2 -2
- astrbot/core/config/astrbot_config.py +60 -20
- astrbot/core/config/default.py +1972 -453
- astrbot/core/config/i18n_utils.py +110 -0
- astrbot/core/conversation_mgr.py +285 -75
- astrbot/core/core_lifecycle.py +167 -62
- astrbot/core/db/__init__.py +305 -102
- astrbot/core/db/migration/helper.py +69 -0
- astrbot/core/db/migration/migra_3_to_4.py +357 -0
- astrbot/core/db/migration/migra_45_to_46.py +44 -0
- astrbot/core/db/migration/migra_webchat_session.py +131 -0
- astrbot/core/db/migration/shared_preferences_v3.py +48 -0
- astrbot/core/db/migration/sqlite_v3.py +497 -0
- astrbot/core/db/po.py +259 -55
- astrbot/core/db/sqlite.py +773 -528
- astrbot/core/db/vec_db/base.py +73 -0
- astrbot/core/db/vec_db/faiss_impl/__init__.py +3 -0
- astrbot/core/db/vec_db/faiss_impl/document_storage.py +392 -0
- astrbot/core/db/vec_db/faiss_impl/embedding_storage.py +93 -0
- astrbot/core/db/vec_db/faiss_impl/sqlite_init.sql +17 -0
- astrbot/core/db/vec_db/faiss_impl/vec_db.py +204 -0
- astrbot/core/event_bus.py +26 -22
- astrbot/core/exceptions.py +9 -0
- astrbot/core/file_token_service.py +98 -0
- astrbot/core/initial_loader.py +19 -10
- astrbot/core/knowledge_base/chunking/__init__.py +9 -0
- astrbot/core/knowledge_base/chunking/base.py +25 -0
- astrbot/core/knowledge_base/chunking/fixed_size.py +59 -0
- astrbot/core/knowledge_base/chunking/recursive.py +161 -0
- astrbot/core/knowledge_base/kb_db_sqlite.py +301 -0
- astrbot/core/knowledge_base/kb_helper.py +642 -0
- astrbot/core/knowledge_base/kb_mgr.py +330 -0
- astrbot/core/knowledge_base/models.py +120 -0
- astrbot/core/knowledge_base/parsers/__init__.py +13 -0
- astrbot/core/knowledge_base/parsers/base.py +51 -0
- astrbot/core/knowledge_base/parsers/markitdown_parser.py +26 -0
- astrbot/core/knowledge_base/parsers/pdf_parser.py +101 -0
- astrbot/core/knowledge_base/parsers/text_parser.py +42 -0
- astrbot/core/knowledge_base/parsers/url_parser.py +103 -0
- astrbot/core/knowledge_base/parsers/util.py +13 -0
- astrbot/core/knowledge_base/prompts.py +65 -0
- astrbot/core/knowledge_base/retrieval/__init__.py +14 -0
- astrbot/core/knowledge_base/retrieval/hit_stopwords.txt +767 -0
- astrbot/core/knowledge_base/retrieval/manager.py +276 -0
- astrbot/core/knowledge_base/retrieval/rank_fusion.py +142 -0
- astrbot/core/knowledge_base/retrieval/sparse_retriever.py +136 -0
- astrbot/core/log.py +21 -15
- astrbot/core/message/components.py +413 -287
- astrbot/core/message/message_event_result.py +35 -24
- astrbot/core/persona_mgr.py +192 -0
- astrbot/core/pipeline/__init__.py +14 -14
- 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 +13 -14
- astrbot/core/pipeline/content_safety_check/strategies/keywords.py +2 -1
- astrbot/core/pipeline/content_safety_check/strategies/strategy.py +6 -6
- astrbot/core/pipeline/context.py +7 -1
- astrbot/core/pipeline/context_utils.py +107 -0
- astrbot/core/pipeline/preprocess_stage/stage.py +63 -36
- astrbot/core/pipeline/process_stage/method/agent_request.py +48 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/internal.py +464 -0
- astrbot/core/pipeline/process_stage/method/agent_sub_stages/third_party.py +202 -0
- astrbot/core/pipeline/process_stage/method/star_request.py +26 -32
- astrbot/core/pipeline/process_stage/stage.py +21 -15
- astrbot/core/pipeline/process_stage/utils.py +125 -0
- astrbot/core/pipeline/rate_limit_check/stage.py +34 -36
- astrbot/core/pipeline/respond/stage.py +142 -101
- astrbot/core/pipeline/result_decorate/stage.py +124 -57
- astrbot/core/pipeline/scheduler.py +21 -16
- astrbot/core/pipeline/session_status_check/stage.py +37 -0
- astrbot/core/pipeline/stage.py +11 -76
- astrbot/core/pipeline/waking_check/stage.py +69 -33
- astrbot/core/pipeline/whitelist_check/stage.py +10 -7
- astrbot/core/platform/__init__.py +6 -6
- astrbot/core/platform/astr_message_event.py +107 -129
- astrbot/core/platform/astrbot_message.py +32 -12
- astrbot/core/platform/manager.py +62 -18
- astrbot/core/platform/message_session.py +30 -0
- astrbot/core/platform/platform.py +16 -24
- astrbot/core/platform/platform_metadata.py +9 -4
- astrbot/core/platform/register.py +12 -7
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_message_event.py +136 -60
- astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py +126 -46
- astrbot/core/platform/sources/dingtalk/dingtalk_adapter.py +63 -31
- astrbot/core/platform/sources/dingtalk/dingtalk_event.py +30 -26
- astrbot/core/platform/sources/discord/client.py +129 -0
- astrbot/core/platform/sources/discord/components.py +139 -0
- astrbot/core/platform/sources/discord/discord_platform_adapter.py +473 -0
- astrbot/core/platform/sources/discord/discord_platform_event.py +313 -0
- astrbot/core/platform/sources/lark/lark_adapter.py +27 -18
- astrbot/core/platform/sources/lark/lark_event.py +39 -13
- astrbot/core/platform/sources/misskey/misskey_adapter.py +770 -0
- astrbot/core/platform/sources/misskey/misskey_api.py +964 -0
- astrbot/core/platform/sources/misskey/misskey_event.py +163 -0
- astrbot/core/platform/sources/misskey/misskey_utils.py +550 -0
- astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py +149 -33
- 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 +14 -8
- astrbot/core/platform/sources/satori/satori_adapter.py +792 -0
- astrbot/core/platform/sources/satori/satori_event.py +432 -0
- astrbot/core/platform/sources/slack/client.py +164 -0
- astrbot/core/platform/sources/slack/slack_adapter.py +416 -0
- astrbot/core/platform/sources/slack/slack_event.py +253 -0
- astrbot/core/platform/sources/telegram/tg_adapter.py +100 -43
- astrbot/core/platform/sources/telegram/tg_event.py +136 -36
- astrbot/core/platform/sources/webchat/webchat_adapter.py +72 -22
- astrbot/core/platform/sources/webchat/webchat_event.py +46 -22
- astrbot/core/platform/sources/webchat/webchat_queue_mgr.py +35 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_adapter.py +926 -0
- astrbot/core/platform/sources/wechatpadpro/wechatpadpro_message_event.py +178 -0
- astrbot/core/platform/sources/wechatpadpro/xml_data_parser.py +159 -0
- astrbot/core/platform/sources/wecom/wecom_adapter.py +169 -27
- astrbot/core/platform/sources/wecom/wecom_event.py +162 -77
- astrbot/core/platform/sources/wecom/wecom_kf.py +279 -0
- astrbot/core/platform/sources/wecom/wecom_kf_message.py +196 -0
- astrbot/core/platform/sources/wecom_ai_bot/WXBizJsonMsgCrypt.py +297 -0
- astrbot/core/platform/sources/wecom_ai_bot/__init__.py +15 -0
- astrbot/core/platform/sources/wecom_ai_bot/ierror.py +19 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_adapter.py +472 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_api.py +417 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_event.py +152 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_queue_mgr.py +153 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_server.py +168 -0
- astrbot/core/platform/sources/wecom_ai_bot/wecomai_utils.py +209 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_adapter.py +306 -0
- astrbot/core/platform/sources/weixin_official_account/weixin_offacc_event.py +186 -0
- astrbot/core/platform_message_history_mgr.py +49 -0
- astrbot/core/provider/__init__.py +2 -3
- astrbot/core/provider/entites.py +8 -8
- astrbot/core/provider/entities.py +154 -98
- astrbot/core/provider/func_tool_manager.py +446 -458
- astrbot/core/provider/manager.py +345 -207
- astrbot/core/provider/provider.py +188 -73
- astrbot/core/provider/register.py +9 -7
- astrbot/core/provider/sources/anthropic_source.py +295 -115
- astrbot/core/provider/sources/azure_tts_source.py +224 -0
- astrbot/core/provider/sources/bailian_rerank_source.py +236 -0
- astrbot/core/provider/sources/dashscope_tts.py +138 -14
- astrbot/core/provider/sources/edge_tts_source.py +24 -19
- astrbot/core/provider/sources/fishaudio_tts_api_source.py +58 -13
- astrbot/core/provider/sources/gemini_embedding_source.py +61 -0
- astrbot/core/provider/sources/gemini_source.py +310 -132
- astrbot/core/provider/sources/gemini_tts_source.py +81 -0
- astrbot/core/provider/sources/groq_source.py +15 -0
- astrbot/core/provider/sources/gsv_selfhosted_source.py +151 -0
- astrbot/core/provider/sources/gsvi_tts_source.py +14 -7
- astrbot/core/provider/sources/minimax_tts_api_source.py +159 -0
- astrbot/core/provider/sources/openai_embedding_source.py +40 -0
- astrbot/core/provider/sources/openai_source.py +241 -145
- astrbot/core/provider/sources/openai_tts_api_source.py +18 -7
- astrbot/core/provider/sources/sensevoice_selfhosted_source.py +13 -11
- astrbot/core/provider/sources/vllm_rerank_source.py +71 -0
- astrbot/core/provider/sources/volcengine_tts.py +115 -0
- astrbot/core/provider/sources/whisper_api_source.py +18 -13
- astrbot/core/provider/sources/whisper_selfhosted_source.py +19 -12
- astrbot/core/provider/sources/xinference_rerank_source.py +116 -0
- astrbot/core/provider/sources/xinference_stt_provider.py +197 -0
- astrbot/core/provider/sources/zhipu_source.py +6 -73
- astrbot/core/star/__init__.py +43 -11
- astrbot/core/star/config.py +17 -18
- astrbot/core/star/context.py +362 -138
- astrbot/core/star/filter/__init__.py +4 -3
- astrbot/core/star/filter/command.py +111 -35
- astrbot/core/star/filter/command_group.py +46 -34
- 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 +45 -12
- astrbot/core/star/filter/regex.py +4 -2
- astrbot/core/star/register/__init__.py +19 -15
- astrbot/core/star/register/star.py +41 -13
- astrbot/core/star/register/star_handler.py +236 -86
- astrbot/core/star/session_llm_manager.py +280 -0
- astrbot/core/star/session_plugin_manager.py +170 -0
- astrbot/core/star/star.py +36 -43
- astrbot/core/star/star_handler.py +47 -85
- astrbot/core/star/star_manager.py +442 -260
- astrbot/core/star/star_tools.py +167 -45
- astrbot/core/star/updator.py +17 -20
- astrbot/core/umop_config_router.py +106 -0
- astrbot/core/updator.py +38 -13
- astrbot/core/utils/astrbot_path.py +39 -0
- astrbot/core/utils/command_parser.py +1 -1
- astrbot/core/utils/io.py +119 -60
- astrbot/core/utils/log_pipe.py +1 -1
- astrbot/core/utils/metrics.py +11 -10
- astrbot/core/utils/migra_helper.py +73 -0
- astrbot/core/utils/path_util.py +63 -62
- astrbot/core/utils/pip_installer.py +37 -15
- astrbot/core/utils/session_lock.py +29 -0
- astrbot/core/utils/session_waiter.py +19 -20
- astrbot/core/utils/shared_preferences.py +174 -34
- astrbot/core/utils/t2i/__init__.py +4 -1
- astrbot/core/utils/t2i/local_strategy.py +386 -238
- astrbot/core/utils/t2i/network_strategy.py +109 -49
- astrbot/core/utils/t2i/renderer.py +29 -14
- astrbot/core/utils/t2i/template/astrbot_powershell.html +184 -0
- astrbot/core/utils/t2i/template_manager.py +111 -0
- astrbot/core/utils/tencent_record_helper.py +115 -1
- astrbot/core/utils/version_comparator.py +10 -13
- astrbot/core/zip_updator.py +112 -65
- astrbot/dashboard/routes/__init__.py +20 -13
- astrbot/dashboard/routes/auth.py +20 -9
- astrbot/dashboard/routes/chat.py +297 -141
- astrbot/dashboard/routes/config.py +652 -55
- astrbot/dashboard/routes/conversation.py +107 -37
- astrbot/dashboard/routes/file.py +26 -0
- astrbot/dashboard/routes/knowledge_base.py +1244 -0
- astrbot/dashboard/routes/log.py +27 -2
- astrbot/dashboard/routes/persona.py +202 -0
- astrbot/dashboard/routes/plugin.py +197 -139
- astrbot/dashboard/routes/route.py +27 -7
- astrbot/dashboard/routes/session_management.py +354 -0
- astrbot/dashboard/routes/stat.py +85 -18
- astrbot/dashboard/routes/static_file.py +5 -2
- astrbot/dashboard/routes/t2i.py +233 -0
- astrbot/dashboard/routes/tools.py +184 -120
- astrbot/dashboard/routes/update.py +59 -36
- astrbot/dashboard/server.py +96 -36
- astrbot/dashboard/utils.py +165 -0
- astrbot-4.7.0.dist-info/METADATA +294 -0
- astrbot-4.7.0.dist-info/RECORD +274 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/WHEEL +1 -1
- astrbot/core/db/plugin/sqlite_impl.py +0 -112
- astrbot/core/db/sqlite_init.sql +0 -50
- astrbot/core/pipeline/platform_compatibility/stage.py +0 -56
- astrbot/core/pipeline/process_stage/method/llm_request.py +0 -606
- astrbot/core/platform/sources/gewechat/client.py +0 -806
- astrbot/core/platform/sources/gewechat/downloader.py +0 -55
- astrbot/core/platform/sources/gewechat/gewechat_event.py +0 -255
- astrbot/core/platform/sources/gewechat/gewechat_platform_adapter.py +0 -103
- astrbot/core/platform/sources/gewechat/xml_data_parser.py +0 -110
- astrbot/core/provider/sources/dashscope_source.py +0 -203
- astrbot/core/provider/sources/dify_source.py +0 -281
- astrbot/core/provider/sources/llmtuner_source.py +0 -132
- astrbot/core/rag/embedding/openai_source.py +0 -20
- astrbot/core/rag/knowledge_db_mgr.py +0 -94
- astrbot/core/rag/store/__init__.py +0 -9
- astrbot/core/rag/store/chroma_db.py +0 -42
- astrbot/core/utils/dify_api_client.py +0 -152
- astrbot-3.5.6.dist-info/METADATA +0 -249
- astrbot-3.5.6.dist-info/RECORD +0 -158
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/entry_points.txt +0 -0
- {astrbot-3.5.6.dist-info → astrbot-4.7.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,33 +1,42 @@
|
|
|
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。",
|
|
32
|
+
support_streaming_message=False,
|
|
27
33
|
)
|
|
28
34
|
class AiocqhttpAdapter(Platform):
|
|
29
35
|
def __init__(
|
|
30
|
-
self,
|
|
36
|
+
self,
|
|
37
|
+
platform_config: dict,
|
|
38
|
+
platform_settings: dict,
|
|
39
|
+
event_queue: asyncio.Queue,
|
|
31
40
|
) -> None:
|
|
32
41
|
super().__init__(event_queue)
|
|
33
42
|
|
|
@@ -41,6 +50,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
41
50
|
name="aiocqhttp",
|
|
42
51
|
description="适用于 OneBot 标准的消息平台适配器,支持反向 WebSockets。",
|
|
43
52
|
id=self.config.get("id"),
|
|
53
|
+
support_streaming_message=False,
|
|
44
54
|
)
|
|
45
55
|
|
|
46
56
|
self.bot = CQHttp(
|
|
@@ -48,7 +58,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
48
58
|
import_name="aiocqhttp",
|
|
49
59
|
api_timeout_sec=180,
|
|
50
60
|
access_token=platform_config.get(
|
|
51
|
-
"ws_reverse_token"
|
|
61
|
+
"ws_reverse_token",
|
|
52
62
|
), # 以防旧版本配置不存在
|
|
53
63
|
)
|
|
54
64
|
|
|
@@ -81,28 +91,32 @@ class AiocqhttpAdapter(Platform):
|
|
|
81
91
|
logger.info("aiocqhttp(OneBot v11) 适配器已连接。")
|
|
82
92
|
|
|
83
93
|
async def send_by_session(
|
|
84
|
-
self,
|
|
94
|
+
self,
|
|
95
|
+
session: MessageSesion,
|
|
96
|
+
message_chain: MessageChain,
|
|
85
97
|
):
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
await self.bot.send_private_msg(user_id=session.session_id, message=ret)
|
|
98
|
+
is_group = session.message_type == MessageType.GROUP_MESSAGE
|
|
99
|
+
if is_group:
|
|
100
|
+
session_id = session.session_id.split("_")[-1]
|
|
101
|
+
else:
|
|
102
|
+
session_id = session.session_id
|
|
103
|
+
await AiocqhttpMessageEvent.send_message(
|
|
104
|
+
bot=self.bot,
|
|
105
|
+
message_chain=message_chain,
|
|
106
|
+
event=None, # 这里不需要 event,因为是通过 session 发送的
|
|
107
|
+
is_group=is_group,
|
|
108
|
+
session_id=session_id,
|
|
109
|
+
)
|
|
99
110
|
await super().send_by_session(session, message_chain)
|
|
100
111
|
|
|
101
|
-
async def convert_message(self, event: Event) -> AstrBotMessage:
|
|
112
|
+
async def convert_message(self, event: Event) -> AstrBotMessage | None:
|
|
102
113
|
logger.debug(f"[aiocqhttp] RawMessage {event}")
|
|
103
114
|
|
|
104
115
|
if event["post_type"] == "message":
|
|
105
116
|
abm = await self._convert_handle_message_event(event)
|
|
117
|
+
if abm.sender.user_id == "2854196310":
|
|
118
|
+
# 屏蔽 QQ 管家的消息
|
|
119
|
+
return None
|
|
106
120
|
elif event["post_type"] == "notice":
|
|
107
121
|
abm = await self._convert_handle_notice_event(event)
|
|
108
122
|
elif event["post_type"] == "request":
|
|
@@ -116,7 +130,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
116
130
|
abm.self_id = str(event.self_id)
|
|
117
131
|
abm.sender = MessageMember(user_id=str(event.user_id), nickname=event.user_id)
|
|
118
132
|
abm.type = MessageType.OTHER_MESSAGE
|
|
119
|
-
if
|
|
133
|
+
if event.get("group_id"):
|
|
120
134
|
abm.type = MessageType.GROUP_MESSAGE
|
|
121
135
|
abm.group_id = str(event.group_id)
|
|
122
136
|
else:
|
|
@@ -142,7 +156,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
142
156
|
abm.self_id = str(event.self_id)
|
|
143
157
|
abm.sender = MessageMember(user_id=str(event.user_id), nickname=event.user_id)
|
|
144
158
|
abm.type = MessageType.OTHER_MESSAGE
|
|
145
|
-
if
|
|
159
|
+
if event.get("group_id"):
|
|
146
160
|
abm.group_id = str(event.group_id)
|
|
147
161
|
abm.type = MessageType.GROUP_MESSAGE
|
|
148
162
|
else:
|
|
@@ -165,14 +179,14 @@ class AiocqhttpAdapter(Platform):
|
|
|
165
179
|
|
|
166
180
|
if "sub_type" in event:
|
|
167
181
|
if event["sub_type"] == "poke" and "target_id" in event:
|
|
168
|
-
abm.message.append(
|
|
169
|
-
Poke(qq=str(event["target_id"]), type="poke")
|
|
170
|
-
) # noqa: F405
|
|
182
|
+
abm.message.append(Poke(qq=str(event["target_id"]), type="poke"))
|
|
171
183
|
|
|
172
184
|
return abm
|
|
173
185
|
|
|
174
186
|
async def _convert_handle_message_event(
|
|
175
|
-
self,
|
|
187
|
+
self,
|
|
188
|
+
event: Event,
|
|
189
|
+
get_reply=True,
|
|
176
190
|
) -> AstrBotMessage:
|
|
177
191
|
"""OneBot V11 消息类事件
|
|
178
192
|
|
|
@@ -182,11 +196,13 @@ class AiocqhttpAdapter(Platform):
|
|
|
182
196
|
abm = AstrBotMessage()
|
|
183
197
|
abm.self_id = str(event.self_id)
|
|
184
198
|
abm.sender = MessageMember(
|
|
185
|
-
str(event.sender["user_id"]),
|
|
199
|
+
str(event.sender["user_id"]),
|
|
200
|
+
event.sender.get("card") or event.sender.get("nickname", "N/A"),
|
|
186
201
|
)
|
|
187
202
|
if event["message_type"] == "group":
|
|
188
203
|
abm.type = MessageType.GROUP_MESSAGE
|
|
189
204
|
abm.group_id = str(event.group_id)
|
|
205
|
+
abm.group.group_name = event.get("group_name", "N/A")
|
|
190
206
|
elif event["message_type"] == "private":
|
|
191
207
|
abm.type = MessageType.FRIEND_MESSAGE
|
|
192
208
|
if self.unique_session and abm.type == MessageType.GROUP_MESSAGE:
|
|
@@ -205,21 +221,24 @@ class AiocqhttpAdapter(Platform):
|
|
|
205
221
|
|
|
206
222
|
message_str = ""
|
|
207
223
|
if not isinstance(event.message, list):
|
|
208
|
-
err = f"aiocqhttp: 无法识别的消息类型: {
|
|
224
|
+
err = f"aiocqhttp: 无法识别的消息类型: {event.message!s},此条消息将被忽略。如果您在使用 go-cqhttp,请将其配置文件中的 message.post-format 更改为 array。"
|
|
209
225
|
logger.critical(err)
|
|
210
226
|
try:
|
|
211
|
-
self.bot.send(event, err)
|
|
227
|
+
await self.bot.send(event, err)
|
|
212
228
|
except BaseException as e:
|
|
213
229
|
logger.error(f"回复消息失败: {e}")
|
|
214
|
-
return
|
|
230
|
+
return None
|
|
215
231
|
|
|
216
232
|
# 按消息段类型类型适配
|
|
217
233
|
for t, m_group in itertools.groupby(event.message, key=lambda x: x["type"]):
|
|
218
234
|
a = None
|
|
219
235
|
if t == "text":
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
236
|
+
current_text = "".join(m["data"]["text"] for m in m_group).strip()
|
|
237
|
+
if not current_text:
|
|
238
|
+
# 如果文本段为空,则跳过
|
|
239
|
+
continue
|
|
240
|
+
message_str += current_text
|
|
241
|
+
a = ComponentTypes[t](text=current_text)
|
|
223
242
|
abm.message.append(a)
|
|
224
243
|
|
|
225
244
|
elif t == "file":
|
|
@@ -259,7 +278,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
259
278
|
elif t == "reply":
|
|
260
279
|
for m in m_group:
|
|
261
280
|
if not get_reply:
|
|
262
|
-
a = ComponentTypes[t](**m["data"])
|
|
281
|
+
a = ComponentTypes[t](**m["data"])
|
|
263
282
|
abm.message.append(a)
|
|
264
283
|
else:
|
|
265
284
|
try:
|
|
@@ -267,8 +286,17 @@ class AiocqhttpAdapter(Platform):
|
|
|
267
286
|
action="get_msg",
|
|
268
287
|
message_id=int(m["data"]["id"]),
|
|
269
288
|
)
|
|
289
|
+
# 添加必要的 post_type 字段,防止 Event.from_payload 报错
|
|
290
|
+
reply_event_data["post_type"] = "message"
|
|
291
|
+
new_event = Event.from_payload(reply_event_data)
|
|
292
|
+
if not new_event:
|
|
293
|
+
logger.error(
|
|
294
|
+
f"无法从回复消息数据构造 Event 对象: {reply_event_data}",
|
|
295
|
+
)
|
|
296
|
+
continue
|
|
270
297
|
abm_reply = await self._convert_handle_message_event(
|
|
271
|
-
|
|
298
|
+
new_event,
|
|
299
|
+
get_reply=False,
|
|
272
300
|
)
|
|
273
301
|
|
|
274
302
|
reply_seg = Reply(
|
|
@@ -285,11 +313,63 @@ class AiocqhttpAdapter(Platform):
|
|
|
285
313
|
abm.message.append(reply_seg)
|
|
286
314
|
except BaseException as e:
|
|
287
315
|
logger.error(f"获取引用消息失败: {e}。")
|
|
288
|
-
a = ComponentTypes[t](**m["data"])
|
|
316
|
+
a = ComponentTypes[t](**m["data"])
|
|
289
317
|
abm.message.append(a)
|
|
318
|
+
elif t == "at":
|
|
319
|
+
first_at_self_processed = False
|
|
320
|
+
# Accumulate @ mention text for efficient concatenation
|
|
321
|
+
at_parts = []
|
|
322
|
+
|
|
323
|
+
for m in m_group:
|
|
324
|
+
try:
|
|
325
|
+
if m["data"]["qq"] == "all":
|
|
326
|
+
abm.message.append(At(qq="all", name="全体成员"))
|
|
327
|
+
continue
|
|
328
|
+
|
|
329
|
+
at_info = await self.bot.call_action(
|
|
330
|
+
action="get_group_member_info",
|
|
331
|
+
group_id=event.group_id,
|
|
332
|
+
user_id=int(m["data"]["qq"]),
|
|
333
|
+
no_cache=False,
|
|
334
|
+
)
|
|
335
|
+
if at_info:
|
|
336
|
+
nickname = at_info.get("card", "")
|
|
337
|
+
if nickname == "":
|
|
338
|
+
at_info = await self.bot.call_action(
|
|
339
|
+
action="get_stranger_info",
|
|
340
|
+
user_id=int(m["data"]["qq"]),
|
|
341
|
+
no_cache=False,
|
|
342
|
+
)
|
|
343
|
+
nickname = at_info.get("nick", "") or at_info.get(
|
|
344
|
+
"nickname",
|
|
345
|
+
"",
|
|
346
|
+
)
|
|
347
|
+
is_at_self = str(m["data"]["qq"]) in {abm.self_id, "all"}
|
|
348
|
+
|
|
349
|
+
abm.message.append(
|
|
350
|
+
At(
|
|
351
|
+
qq=m["data"]["qq"],
|
|
352
|
+
name=nickname,
|
|
353
|
+
),
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
if is_at_self and not first_at_self_processed:
|
|
357
|
+
# 第一个@是机器人,不添加到message_str
|
|
358
|
+
first_at_self_processed = True
|
|
359
|
+
else:
|
|
360
|
+
# 非第一个@机器人或@其他用户,添加到message_str
|
|
361
|
+
at_parts.append(f" @{nickname}({m['data']['qq']}) ")
|
|
362
|
+
else:
|
|
363
|
+
abm.message.append(At(qq=str(m["data"]["qq"]), name=""))
|
|
364
|
+
except ActionFailed as e:
|
|
365
|
+
logger.error(f"获取 @ 用户信息失败: {e},此消息段将被忽略。")
|
|
366
|
+
except BaseException as e:
|
|
367
|
+
logger.error(f"获取 @ 用户信息失败: {e},此消息段将被忽略。")
|
|
368
|
+
|
|
369
|
+
message_str += "".join(at_parts)
|
|
290
370
|
else:
|
|
291
371
|
for m in m_group:
|
|
292
|
-
a = ComponentTypes[t](**m["data"])
|
|
372
|
+
a = ComponentTypes[t](**m["data"])
|
|
293
373
|
abm.message.append(a)
|
|
294
374
|
|
|
295
375
|
abm.timestamp = int(time.time())
|
|
@@ -301,7 +381,7 @@ class AiocqhttpAdapter(Platform):
|
|
|
301
381
|
def run(self) -> Awaitable[Any]:
|
|
302
382
|
if not self.host or not self.port:
|
|
303
383
|
logger.warning(
|
|
304
|
-
"aiocqhttp: 未配置 ws_reverse_host 或 ws_reverse_port,将使用默认值:http://0.0.0.0:6199"
|
|
384
|
+
"aiocqhttp: 未配置 ws_reverse_host 或 ws_reverse_port,将使用默认值:http://0.0.0.0:6199",
|
|
305
385
|
)
|
|
306
386
|
self.host = "0.0.0.0"
|
|
307
387
|
self.port = 6199
|
|
@@ -1,25 +1,29 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
import threading
|
|
2
4
|
import uuid
|
|
5
|
+
|
|
3
6
|
import aiohttp
|
|
4
7
|
import dingtalk_stream
|
|
5
|
-
import
|
|
8
|
+
from dingtalk_stream import AckMessage
|
|
6
9
|
|
|
10
|
+
from astrbot import logger
|
|
11
|
+
from astrbot.api.event import MessageChain
|
|
12
|
+
from astrbot.api.message_components import At, Image, Plain
|
|
7
13
|
from astrbot.api.platform import (
|
|
8
|
-
Platform,
|
|
9
14
|
AstrBotMessage,
|
|
10
15
|
MessageMember,
|
|
11
16
|
MessageType,
|
|
17
|
+
Platform,
|
|
12
18
|
PlatformMetadata,
|
|
13
19
|
)
|
|
14
|
-
from astrbot.api.event import MessageChain
|
|
15
|
-
from astrbot.api.message_components import Image, Plain, At
|
|
16
20
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
17
|
-
from .
|
|
18
|
-
from ...register import register_platform_adapter
|
|
19
|
-
from astrbot import logger
|
|
20
|
-
from dingtalk_stream import AckMessage
|
|
21
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
21
22
|
from astrbot.core.utils.io import download_file
|
|
22
23
|
|
|
24
|
+
from ...register import register_platform_adapter
|
|
25
|
+
from .dingtalk_event import DingtalkMessageEvent
|
|
26
|
+
|
|
23
27
|
|
|
24
28
|
class MyEventHandler(dingtalk_stream.EventHandler):
|
|
25
29
|
async def process(self, event: dingtalk_stream.EventMessage):
|
|
@@ -33,10 +37,15 @@ class MyEventHandler(dingtalk_stream.EventHandler):
|
|
|
33
37
|
return AckMessage.STATUS_OK, "OK"
|
|
34
38
|
|
|
35
39
|
|
|
36
|
-
@register_platform_adapter(
|
|
40
|
+
@register_platform_adapter(
|
|
41
|
+
"dingtalk", "钉钉机器人官方 API 适配器", support_streaming_message=False
|
|
42
|
+
)
|
|
37
43
|
class DingtalkPlatformAdapter(Platform):
|
|
38
44
|
def __init__(
|
|
39
|
-
self,
|
|
45
|
+
self,
|
|
46
|
+
platform_config: dict,
|
|
47
|
+
platform_settings: dict,
|
|
48
|
+
event_queue: asyncio.Queue,
|
|
40
49
|
) -> None:
|
|
41
50
|
super().__init__(event_queue)
|
|
42
51
|
|
|
@@ -62,12 +71,23 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
62
71
|
client = dingtalk_stream.DingTalkStreamClient(credential, logger=logger)
|
|
63
72
|
client.register_all_event_handler(MyEventHandler())
|
|
64
73
|
client.register_callback_handler(
|
|
65
|
-
dingtalk_stream.ChatbotMessage.TOPIC,
|
|
74
|
+
dingtalk_stream.ChatbotMessage.TOPIC,
|
|
75
|
+
self.client,
|
|
66
76
|
)
|
|
67
77
|
self.client_ = client # 用于 websockets 的 client
|
|
68
78
|
|
|
79
|
+
def _id_to_sid(self, dingtalk_id: str | None) -> str | None:
|
|
80
|
+
if not dingtalk_id:
|
|
81
|
+
return dingtalk_id
|
|
82
|
+
prefix = "$:LWCP_v1:$"
|
|
83
|
+
if dingtalk_id.startswith(prefix):
|
|
84
|
+
return dingtalk_id[len(prefix) :]
|
|
85
|
+
return dingtalk_id
|
|
86
|
+
|
|
69
87
|
async def send_by_session(
|
|
70
|
-
self,
|
|
88
|
+
self,
|
|
89
|
+
session: MessageSesion,
|
|
90
|
+
message_chain: MessageChain,
|
|
71
91
|
):
|
|
72
92
|
raise NotImplementedError("钉钉机器人适配器不支持 send_by_session")
|
|
73
93
|
|
|
@@ -76,10 +96,12 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
76
96
|
name="dingtalk",
|
|
77
97
|
description="钉钉机器人官方 API 适配器",
|
|
78
98
|
id=self.config.get("id"),
|
|
99
|
+
support_streaming_message=False,
|
|
79
100
|
)
|
|
80
101
|
|
|
81
102
|
async def convert_msg(
|
|
82
|
-
self,
|
|
103
|
+
self,
|
|
104
|
+
message: dingtalk_stream.ChatbotMessage,
|
|
83
105
|
) -> AstrBotMessage:
|
|
84
106
|
abm = AstrBotMessage()
|
|
85
107
|
abm.message = []
|
|
@@ -91,15 +113,19 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
91
113
|
else MessageType.FRIEND_MESSAGE
|
|
92
114
|
)
|
|
93
115
|
abm.sender = MessageMember(
|
|
94
|
-
user_id=message.sender_id,
|
|
116
|
+
user_id=self._id_to_sid(message.sender_id),
|
|
117
|
+
nickname=message.sender_nick,
|
|
95
118
|
)
|
|
96
|
-
abm.self_id = message.chatbot_user_id
|
|
119
|
+
abm.self_id = self._id_to_sid(message.chatbot_user_id)
|
|
97
120
|
abm.message_id = message.message_id
|
|
98
121
|
abm.raw_message = message
|
|
99
122
|
|
|
100
123
|
if abm.type == MessageType.GROUP_MESSAGE:
|
|
101
|
-
|
|
102
|
-
|
|
124
|
+
# 处理所有被 @ 的用户(包括机器人自己,因 at_users 已包含)
|
|
125
|
+
if message.at_users:
|
|
126
|
+
for user in message.at_users:
|
|
127
|
+
if id := self._id_to_sid(user.dingtalk_id):
|
|
128
|
+
abm.message.append(At(qq=id))
|
|
103
129
|
abm.group_id = message.conversation_id
|
|
104
130
|
if self.unique_session:
|
|
105
131
|
abm.session_id = abm.sender.user_id
|
|
@@ -134,7 +160,10 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
134
160
|
return abm # 别忘了返回转换后的消息对象
|
|
135
161
|
|
|
136
162
|
async def download_ding_file(
|
|
137
|
-
self,
|
|
163
|
+
self,
|
|
164
|
+
download_code: str,
|
|
165
|
+
robot_code: str,
|
|
166
|
+
ext: str,
|
|
138
167
|
) -> str:
|
|
139
168
|
"""下载钉钉文件
|
|
140
169
|
|
|
@@ -152,21 +181,24 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
152
181
|
"downloadCode": download_code,
|
|
153
182
|
"robotCode": robot_code,
|
|
154
183
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
184
|
+
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
|
|
185
|
+
f_path = os.path.join(temp_dir, f"dingtalk_file_{uuid.uuid4()}.{ext}")
|
|
186
|
+
async with (
|
|
187
|
+
aiohttp.ClientSession() as session,
|
|
188
|
+
session.post(
|
|
158
189
|
"https://api.dingtalk.com/v1.0/robot/messageFiles/download",
|
|
159
190
|
headers=headers,
|
|
160
191
|
json=payload,
|
|
161
|
-
) as resp
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
192
|
+
) as resp,
|
|
193
|
+
):
|
|
194
|
+
if resp.status != 200:
|
|
195
|
+
logger.error(
|
|
196
|
+
f"下载钉钉文件失败: {resp.status}, {await resp.text()}",
|
|
197
|
+
)
|
|
198
|
+
return None
|
|
199
|
+
resp_data = await resp.json()
|
|
200
|
+
download_url = resp_data["data"]["downloadUrl"]
|
|
201
|
+
await download_file(download_url, f_path)
|
|
170
202
|
return f_path
|
|
171
203
|
|
|
172
204
|
async def get_access_token(self) -> str:
|
|
@@ -181,7 +213,7 @@ class DingtalkPlatformAdapter(Platform):
|
|
|
181
213
|
) as resp:
|
|
182
214
|
if resp.status != 200:
|
|
183
215
|
logger.error(
|
|
184
|
-
f"获取钉钉机器人 access_token 失败: {resp.status}, {await resp.text()}"
|
|
216
|
+
f"获取钉钉机器人 access_token 失败: {resp.status}, {await resp.text()}",
|
|
185
217
|
)
|
|
186
218
|
return None
|
|
187
219
|
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):
|
|
@@ -26,36 +30,36 @@ class DingtalkMessageEvent(AstrMessageEvent):
|
|
|
26
30
|
await asyncio.get_event_loop().run_in_executor(
|
|
27
31
|
None,
|
|
28
32
|
client.reply_markdown,
|
|
29
|
-
|
|
33
|
+
segment.text,
|
|
30
34
|
segment.text,
|
|
31
35
|
self.message_obj.raw_message,
|
|
32
36
|
)
|
|
33
37
|
elif isinstance(segment, Comp.Image):
|
|
34
38
|
markdown_str = ""
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
if not segment.file:
|
|
42
|
+
logger.warning("钉钉图片 segment 缺少 file 字段,跳过")
|
|
43
|
+
continue
|
|
44
|
+
if segment.file.startswith(("http://", "https://")):
|
|
45
|
+
image_url = segment.file
|
|
46
|
+
else:
|
|
47
|
+
image_url = await segment.register_to_file_service()
|
|
48
|
+
|
|
49
|
+
markdown_str = f"\n\n"
|
|
50
|
+
|
|
51
|
+
ret = await asyncio.get_event_loop().run_in_executor(
|
|
52
|
+
None,
|
|
53
|
+
client.reply_markdown,
|
|
54
|
+
"😄",
|
|
55
|
+
markdown_str,
|
|
56
|
+
self.message_obj.raw_message,
|
|
48
57
|
)
|
|
49
|
-
|
|
58
|
+
logger.debug(f"send image: {ret}")
|
|
50
59
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
"😄",
|
|
55
|
-
markdown_str,
|
|
56
|
-
self.message_obj.raw_message,
|
|
57
|
-
)
|
|
58
|
-
logger.debug(f"send image: {ret}")
|
|
60
|
+
except Exception as e:
|
|
61
|
+
logger.warning(f"钉钉图片处理失败: {e}, 跳过图片发送")
|
|
62
|
+
continue
|
|
59
63
|
|
|
60
64
|
async def send(self, message: MessageChain):
|
|
61
65
|
await self.send_with_client(self.client, message)
|
|
@@ -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)
|