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,30 +1,31 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import asyncio
|
|
4
4
|
import logging
|
|
5
|
+
import os
|
|
5
6
|
import time
|
|
6
|
-
|
|
7
|
+
|
|
8
|
+
import botpy
|
|
7
9
|
import botpy.message
|
|
8
10
|
import botpy.types
|
|
9
11
|
import botpy.types.message
|
|
10
|
-
import os
|
|
11
|
-
|
|
12
12
|
from botpy import Client
|
|
13
|
+
|
|
14
|
+
from astrbot import logger
|
|
15
|
+
from astrbot.api.event import MessageChain
|
|
16
|
+
from astrbot.api.message_components import At, Image, Plain
|
|
13
17
|
from astrbot.api.platform import (
|
|
14
|
-
Platform,
|
|
15
18
|
AstrBotMessage,
|
|
16
19
|
MessageMember,
|
|
17
20
|
MessageType,
|
|
21
|
+
Platform,
|
|
18
22
|
PlatformMetadata,
|
|
19
23
|
)
|
|
20
|
-
from astrbot import
|
|
21
|
-
from astrbot.api.event import MessageChain
|
|
22
|
-
from typing import Union, List
|
|
23
|
-
from astrbot.api.message_components import Image, Plain, At
|
|
24
|
+
from astrbot.core.message.components import BaseMessageComponent
|
|
24
25
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
25
|
-
|
|
26
|
+
|
|
26
27
|
from ...register import register_platform_adapter
|
|
27
|
-
from
|
|
28
|
+
from .qqofficial_message_event import QQOfficialMessageEvent
|
|
28
29
|
|
|
29
30
|
# remove logger handler
|
|
30
31
|
for handler in logging.root.handlers[:]:
|
|
@@ -33,13 +34,14 @@ for handler in logging.root.handlers[:]:
|
|
|
33
34
|
|
|
34
35
|
# QQ 机器人官方框架
|
|
35
36
|
class botClient(Client):
|
|
36
|
-
def set_platform(self, platform:
|
|
37
|
+
def set_platform(self, platform: QQOfficialPlatformAdapter):
|
|
37
38
|
self.platform = platform
|
|
38
39
|
|
|
39
40
|
# 收到群消息
|
|
40
41
|
async def on_group_at_message_create(self, message: botpy.message.GroupMessage):
|
|
41
42
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
42
|
-
message,
|
|
43
|
+
message,
|
|
44
|
+
MessageType.GROUP_MESSAGE,
|
|
43
45
|
)
|
|
44
46
|
abm.session_id = (
|
|
45
47
|
abm.sender.user_id if self.platform.unique_session else message.group_openid
|
|
@@ -49,7 +51,8 @@ class botClient(Client):
|
|
|
49
51
|
# 收到频道消息
|
|
50
52
|
async def on_at_message_create(self, message: botpy.message.Message):
|
|
51
53
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
52
|
-
message,
|
|
54
|
+
message,
|
|
55
|
+
MessageType.GROUP_MESSAGE,
|
|
53
56
|
)
|
|
54
57
|
abm.session_id = (
|
|
55
58
|
abm.sender.user_id if self.platform.unique_session else message.channel_id
|
|
@@ -59,7 +62,8 @@ class botClient(Client):
|
|
|
59
62
|
# 收到私聊消息
|
|
60
63
|
async def on_direct_message_create(self, message: botpy.message.DirectMessage):
|
|
61
64
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
62
|
-
message,
|
|
65
|
+
message,
|
|
66
|
+
MessageType.FRIEND_MESSAGE,
|
|
63
67
|
)
|
|
64
68
|
abm.session_id = abm.sender.user_id
|
|
65
69
|
self._commit(abm)
|
|
@@ -67,7 +71,8 @@ class botClient(Client):
|
|
|
67
71
|
# 收到 C2C 消息
|
|
68
72
|
async def on_c2c_message_create(self, message: botpy.message.C2CMessage):
|
|
69
73
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
70
|
-
message,
|
|
74
|
+
message,
|
|
75
|
+
MessageType.FRIEND_MESSAGE,
|
|
71
76
|
)
|
|
72
77
|
abm.session_id = abm.sender.user_id
|
|
73
78
|
self._commit(abm)
|
|
@@ -80,14 +85,17 @@ class botClient(Client):
|
|
|
80
85
|
self.platform.meta(),
|
|
81
86
|
abm.session_id,
|
|
82
87
|
self.platform.client,
|
|
83
|
-
)
|
|
88
|
+
),
|
|
84
89
|
)
|
|
85
90
|
|
|
86
91
|
|
|
87
92
|
@register_platform_adapter("qq_official", "QQ 机器人官方 API 适配器")
|
|
88
93
|
class QQOfficialPlatformAdapter(Platform):
|
|
89
94
|
def __init__(
|
|
90
|
-
self,
|
|
95
|
+
self,
|
|
96
|
+
platform_config: dict,
|
|
97
|
+
platform_settings: dict,
|
|
98
|
+
event_queue: asyncio.Queue,
|
|
91
99
|
) -> None:
|
|
92
100
|
super().__init__(event_queue)
|
|
93
101
|
|
|
@@ -107,7 +115,8 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
107
115
|
)
|
|
108
116
|
else:
|
|
109
117
|
self.intents = botpy.Intents(
|
|
110
|
-
public_guild_messages=True,
|
|
118
|
+
public_guild_messages=True,
|
|
119
|
+
direct_message=guild_dm,
|
|
111
120
|
)
|
|
112
121
|
self.client = botClient(
|
|
113
122
|
intents=self.intents,
|
|
@@ -120,7 +129,9 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
120
129
|
self.test_mode = os.environ.get("TEST_MODE", "off") == "on"
|
|
121
130
|
|
|
122
131
|
async def send_by_session(
|
|
123
|
-
self,
|
|
132
|
+
self,
|
|
133
|
+
session: MessageSesion,
|
|
134
|
+
message_chain: MessageChain,
|
|
124
135
|
):
|
|
125
136
|
raise NotImplementedError("QQ 机器人官方 API 适配器不支持 send_by_session")
|
|
126
137
|
|
|
@@ -133,7 +144,7 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
133
144
|
|
|
134
145
|
@staticmethod
|
|
135
146
|
def _parse_from_qqofficial(
|
|
136
|
-
message:
|
|
147
|
+
message: botpy.message.Message | botpy.message.GroupMessage,
|
|
137
148
|
message_type: MessageType,
|
|
138
149
|
):
|
|
139
150
|
abm = AstrBotMessage()
|
|
@@ -142,10 +153,11 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
142
153
|
abm.raw_message = message
|
|
143
154
|
abm.message_id = message.id
|
|
144
155
|
abm.tag = "qq_official"
|
|
145
|
-
msg:
|
|
156
|
+
msg: list[BaseMessageComponent] = []
|
|
146
157
|
|
|
147
158
|
if isinstance(message, botpy.message.GroupMessage) or isinstance(
|
|
148
|
-
message,
|
|
159
|
+
message,
|
|
160
|
+
botpy.message.C2CMessage,
|
|
149
161
|
):
|
|
150
162
|
if isinstance(message, botpy.message.GroupMessage):
|
|
151
163
|
abm.sender = MessageMember(message.author.member_openid, "")
|
|
@@ -167,7 +179,8 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
167
179
|
abm.message = msg
|
|
168
180
|
|
|
169
181
|
elif isinstance(message, botpy.message.Message) or isinstance(
|
|
170
|
-
message,
|
|
182
|
+
message,
|
|
183
|
+
botpy.message.DirectMessage,
|
|
171
184
|
):
|
|
172
185
|
try:
|
|
173
186
|
abm.self_id = str(message.mentions[0].id)
|
|
@@ -175,7 +188,8 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
175
188
|
abm.self_id = ""
|
|
176
189
|
|
|
177
190
|
plain_content = message.content.replace(
|
|
178
|
-
"<@!" + str(abm.self_id) + ">",
|
|
191
|
+
"<@!" + str(abm.self_id) + ">",
|
|
192
|
+
"",
|
|
179
193
|
).strip()
|
|
180
194
|
|
|
181
195
|
if message.attachments:
|
|
@@ -189,7 +203,8 @@ class QQOfficialPlatformAdapter(Platform):
|
|
|
189
203
|
abm.message = msg
|
|
190
204
|
abm.message_str = plain_content
|
|
191
205
|
abm.sender = MessageMember(
|
|
192
|
-
str(message.author.id),
|
|
206
|
+
str(message.author.id),
|
|
207
|
+
str(message.author.username),
|
|
193
208
|
)
|
|
194
209
|
msg.append(At(qq="qq_official"))
|
|
195
210
|
msg.append(Plain(plain_content))
|
|
@@ -1,19 +1,21 @@
|
|
|
1
|
-
import botpy
|
|
2
|
-
import logging
|
|
3
1
|
import asyncio
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import botpy
|
|
4
5
|
import botpy.message
|
|
5
6
|
import botpy.types
|
|
6
7
|
import botpy.types.message
|
|
7
|
-
|
|
8
8
|
from botpy import Client
|
|
9
|
-
|
|
9
|
+
|
|
10
|
+
from astrbot import logger
|
|
10
11
|
from astrbot.api.event import MessageChain
|
|
12
|
+
from astrbot.api.platform import AstrBotMessage, MessageType, Platform, PlatformMetadata
|
|
11
13
|
from astrbot.core.platform.astr_message_event import MessageSesion
|
|
12
|
-
|
|
14
|
+
|
|
13
15
|
from ...register import register_platform_adapter
|
|
14
|
-
from .qo_webhook_server import QQOfficialWebhook
|
|
15
16
|
from ..qqofficial.qqofficial_platform_adapter import QQOfficialPlatformAdapter
|
|
16
|
-
from
|
|
17
|
+
from .qo_webhook_event import QQOfficialWebhookMessageEvent
|
|
18
|
+
from .qo_webhook_server import QQOfficialWebhook
|
|
17
19
|
|
|
18
20
|
# remove logger handler
|
|
19
21
|
for handler in logging.root.handlers[:]:
|
|
@@ -28,7 +30,8 @@ class botClient(Client):
|
|
|
28
30
|
# 收到群消息
|
|
29
31
|
async def on_group_at_message_create(self, message: botpy.message.GroupMessage):
|
|
30
32
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
31
|
-
message,
|
|
33
|
+
message,
|
|
34
|
+
MessageType.GROUP_MESSAGE,
|
|
32
35
|
)
|
|
33
36
|
abm.session_id = (
|
|
34
37
|
abm.sender.user_id if self.platform.unique_session else message.group_openid
|
|
@@ -38,7 +41,8 @@ class botClient(Client):
|
|
|
38
41
|
# 收到频道消息
|
|
39
42
|
async def on_at_message_create(self, message: botpy.message.Message):
|
|
40
43
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
41
|
-
message,
|
|
44
|
+
message,
|
|
45
|
+
MessageType.GROUP_MESSAGE,
|
|
42
46
|
)
|
|
43
47
|
abm.session_id = (
|
|
44
48
|
abm.sender.user_id if self.platform.unique_session else message.channel_id
|
|
@@ -48,7 +52,8 @@ class botClient(Client):
|
|
|
48
52
|
# 收到私聊消息
|
|
49
53
|
async def on_direct_message_create(self, message: botpy.message.DirectMessage):
|
|
50
54
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
51
|
-
message,
|
|
55
|
+
message,
|
|
56
|
+
MessageType.FRIEND_MESSAGE,
|
|
52
57
|
)
|
|
53
58
|
abm.session_id = abm.sender.user_id
|
|
54
59
|
self._commit(abm)
|
|
@@ -56,7 +61,8 @@ class botClient(Client):
|
|
|
56
61
|
# 收到 C2C 消息
|
|
57
62
|
async def on_c2c_message_create(self, message: botpy.message.C2CMessage):
|
|
58
63
|
abm = QQOfficialPlatformAdapter._parse_from_qqofficial(
|
|
59
|
-
message,
|
|
64
|
+
message,
|
|
65
|
+
MessageType.FRIEND_MESSAGE,
|
|
60
66
|
)
|
|
61
67
|
abm.session_id = abm.sender.user_id
|
|
62
68
|
self._commit(abm)
|
|
@@ -64,15 +70,22 @@ class botClient(Client):
|
|
|
64
70
|
def _commit(self, abm: AstrBotMessage):
|
|
65
71
|
self.platform.commit_event(
|
|
66
72
|
QQOfficialWebhookMessageEvent(
|
|
67
|
-
abm.message_str,
|
|
68
|
-
|
|
73
|
+
abm.message_str,
|
|
74
|
+
abm,
|
|
75
|
+
self.platform.meta(),
|
|
76
|
+
abm.session_id,
|
|
77
|
+
self,
|
|
78
|
+
),
|
|
69
79
|
)
|
|
70
80
|
|
|
71
81
|
|
|
72
82
|
@register_platform_adapter("qq_official_webhook", "QQ 机器人官方 API 适配器(Webhook)")
|
|
73
83
|
class QQOfficialWebhookPlatformAdapter(Platform):
|
|
74
84
|
def __init__(
|
|
75
|
-
self,
|
|
85
|
+
self,
|
|
86
|
+
platform_config: dict,
|
|
87
|
+
platform_settings: dict,
|
|
88
|
+
event_queue: asyncio.Queue,
|
|
76
89
|
) -> None:
|
|
77
90
|
super().__init__(event_queue)
|
|
78
91
|
|
|
@@ -83,7 +96,9 @@ class QQOfficialWebhookPlatformAdapter(Platform):
|
|
|
83
96
|
self.unique_session = platform_settings["unique_session"]
|
|
84
97
|
|
|
85
98
|
intents = botpy.Intents(
|
|
86
|
-
public_messages=True,
|
|
99
|
+
public_messages=True,
|
|
100
|
+
public_guild_messages=True,
|
|
101
|
+
direct_message=True,
|
|
87
102
|
)
|
|
88
103
|
self.client = botClient(
|
|
89
104
|
intents=intents, # 已经无用
|
|
@@ -93,7 +108,9 @@ class QQOfficialWebhookPlatformAdapter(Platform):
|
|
|
93
108
|
self.client.set_platform(self)
|
|
94
109
|
|
|
95
110
|
async def send_by_session(
|
|
96
|
-
self,
|
|
111
|
+
self,
|
|
112
|
+
session: MessageSesion,
|
|
113
|
+
message_chain: MessageChain,
|
|
97
114
|
):
|
|
98
115
|
raise NotImplementedError("QQ 机器人官方 API 适配器不支持 send_by_session")
|
|
99
116
|
|
|
@@ -106,7 +123,9 @@ class QQOfficialWebhookPlatformAdapter(Platform):
|
|
|
106
123
|
|
|
107
124
|
async def run(self):
|
|
108
125
|
self.webhook_helper = QQOfficialWebhook(
|
|
109
|
-
self.config,
|
|
126
|
+
self.config,
|
|
127
|
+
self._event_queue,
|
|
128
|
+
self.client,
|
|
110
129
|
)
|
|
111
130
|
await self.webhook_helper.initialize()
|
|
112
131
|
await self.webhook_helper.start_polling()
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import quart
|
|
2
|
-
import logging
|
|
3
1
|
import asyncio
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
import quart
|
|
5
|
+
from botpy import BotAPI, BotHttp, BotWebSocket, Client, ConnectionSession, Token
|
|
6
6
|
from cryptography.hazmat.primitives.asymmetric import ed25519
|
|
7
7
|
|
|
8
|
+
from astrbot.api import logger
|
|
9
|
+
|
|
8
10
|
# remove logger handler
|
|
9
11
|
for handler in logging.root.handlers[:]:
|
|
10
12
|
logging.root.removeHandler(handler)
|
|
@@ -27,7 +29,9 @@ class QQOfficialWebhook:
|
|
|
27
29
|
|
|
28
30
|
self.server = quart.Quart(__name__)
|
|
29
31
|
self.server.add_url_rule(
|
|
30
|
-
"/astrbot-qo-webhook/callback",
|
|
32
|
+
"/astrbot-qo-webhook/callback",
|
|
33
|
+
view_func=self.callback,
|
|
34
|
+
methods=["POST"],
|
|
31
35
|
)
|
|
32
36
|
self.client = botpy_client
|
|
33
37
|
self.event_queue = event_queue
|
|
@@ -62,7 +66,8 @@ class QQOfficialWebhook:
|
|
|
62
66
|
seed = await self.repeat_seed(self.secret)
|
|
63
67
|
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(seed)
|
|
64
68
|
msg = validation_payload.get("event_ts", "") + validation_payload.get(
|
|
65
|
-
"plain_token",
|
|
69
|
+
"plain_token",
|
|
70
|
+
"",
|
|
66
71
|
)
|
|
67
72
|
# sign
|
|
68
73
|
signature = private_key.sign(msg.encode()).hex()
|
|
@@ -99,7 +104,7 @@ class QQOfficialWebhook:
|
|
|
99
104
|
|
|
100
105
|
async def start_polling(self):
|
|
101
106
|
logger.info(
|
|
102
|
-
f"将在 {self.callback_server_host}:{self.port} 端口启动 QQ 官方机器人 webhook 适配器。"
|
|
107
|
+
f"将在 {self.callback_server_host}:{self.port} 端口启动 QQ 官方机器人 webhook 适配器。",
|
|
103
108
|
)
|
|
104
109
|
await self.server.run_task(
|
|
105
110
|
host=self.callback_server_host,
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import time
|
|
4
|
+
from xml.etree import ElementTree as ET
|
|
5
|
+
|
|
4
6
|
import websockets
|
|
5
|
-
from websockets.asyncio.client import connect
|
|
6
|
-
from typing import Optional
|
|
7
7
|
from aiohttp import ClientSession, ClientTimeout
|
|
8
|
-
from websockets.asyncio.client import ClientConnection
|
|
8
|
+
from websockets.asyncio.client import ClientConnection, connect
|
|
9
|
+
|
|
9
10
|
from astrbot.api import logger
|
|
10
11
|
from astrbot.api.event import MessageChain
|
|
12
|
+
from astrbot.api.message_components import (
|
|
13
|
+
At,
|
|
14
|
+
File,
|
|
15
|
+
Image,
|
|
16
|
+
Plain,
|
|
17
|
+
Record,
|
|
18
|
+
Reply,
|
|
19
|
+
)
|
|
11
20
|
from astrbot.api.platform import (
|
|
12
21
|
AstrBotMessage,
|
|
13
22
|
MessageMember,
|
|
@@ -17,15 +26,6 @@ from astrbot.api.platform import (
|
|
|
17
26
|
register_platform_adapter,
|
|
18
27
|
)
|
|
19
28
|
from astrbot.core.platform.astr_message_event import MessageSession
|
|
20
|
-
from astrbot.api.message_components import (
|
|
21
|
-
Plain,
|
|
22
|
-
Image,
|
|
23
|
-
At,
|
|
24
|
-
File,
|
|
25
|
-
Record,
|
|
26
|
-
Reply,
|
|
27
|
-
)
|
|
28
|
-
from xml.etree import ElementTree as ET
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@register_platform_adapter(
|
|
@@ -34,18 +34,23 @@ from xml.etree import ElementTree as ET
|
|
|
34
34
|
)
|
|
35
35
|
class SatoriPlatformAdapter(Platform):
|
|
36
36
|
def __init__(
|
|
37
|
-
self,
|
|
37
|
+
self,
|
|
38
|
+
platform_config: dict,
|
|
39
|
+
platform_settings: dict,
|
|
40
|
+
event_queue: asyncio.Queue,
|
|
38
41
|
) -> None:
|
|
39
42
|
super().__init__(event_queue)
|
|
40
43
|
self.config = platform_config
|
|
41
44
|
self.settings = platform_settings
|
|
42
45
|
|
|
43
46
|
self.api_base_url = self.config.get(
|
|
44
|
-
"satori_api_base_url",
|
|
47
|
+
"satori_api_base_url",
|
|
48
|
+
"http://localhost:5140/satori/v1",
|
|
45
49
|
)
|
|
46
50
|
self.token = self.config.get("satori_token", "")
|
|
47
51
|
self.endpoint = self.config.get(
|
|
48
|
-
"satori_endpoint",
|
|
52
|
+
"satori_endpoint",
|
|
53
|
+
"ws://localhost:5140/satori/v1/events",
|
|
49
54
|
)
|
|
50
55
|
self.auto_reconnect = self.config.get("satori_auto_reconnect", True)
|
|
51
56
|
self.heartbeat_interval = self.config.get("satori_heartbeat_interval", 10)
|
|
@@ -57,21 +62,25 @@ class SatoriPlatformAdapter(Platform):
|
|
|
57
62
|
id=self.config["id"],
|
|
58
63
|
)
|
|
59
64
|
|
|
60
|
-
self.ws:
|
|
61
|
-
self.session:
|
|
65
|
+
self.ws: ClientConnection | None = None
|
|
66
|
+
self.session: ClientSession | None = None
|
|
62
67
|
self.sequence = 0
|
|
63
68
|
self.logins = []
|
|
64
69
|
self.running = False
|
|
65
|
-
self.heartbeat_task:
|
|
70
|
+
self.heartbeat_task: asyncio.Task | None = None
|
|
66
71
|
self.ready_received = False
|
|
67
72
|
|
|
68
73
|
async def send_by_session(
|
|
69
|
-
self,
|
|
74
|
+
self,
|
|
75
|
+
session: MessageSession,
|
|
76
|
+
message_chain: MessageChain,
|
|
70
77
|
):
|
|
71
78
|
from .satori_event import SatoriPlatformEvent
|
|
72
79
|
|
|
73
80
|
await SatoriPlatformEvent.send_with_adapter(
|
|
74
|
-
self,
|
|
81
|
+
self,
|
|
82
|
+
message_chain,
|
|
83
|
+
session.session_id,
|
|
75
84
|
)
|
|
76
85
|
await super().send_by_session(session, message_chain)
|
|
77
86
|
|
|
@@ -85,10 +94,9 @@ class SatoriPlatformAdapter(Platform):
|
|
|
85
94
|
try:
|
|
86
95
|
if hasattr(ws, "closed"):
|
|
87
96
|
return ws.closed
|
|
88
|
-
|
|
97
|
+
if hasattr(ws, "close_code"):
|
|
89
98
|
return ws.close_code is not None
|
|
90
|
-
|
|
91
|
-
return False
|
|
99
|
+
return False
|
|
92
100
|
except AttributeError:
|
|
93
101
|
return False
|
|
94
102
|
|
|
@@ -240,7 +248,7 @@ class SatoriPlatformAdapter(Platform):
|
|
|
240
248
|
user_id = user.get("id", "")
|
|
241
249
|
user_name = user.get("name", "")
|
|
242
250
|
logger.info(
|
|
243
|
-
f"Satori 连接成功 - Bot {i + 1}: platform={platform}, user_id={user_id}, user_name={user_name}"
|
|
251
|
+
f"Satori 连接成功 - Bot {i + 1}: platform={platform}, user_id={user_id}, user_name={user_name}",
|
|
244
252
|
)
|
|
245
253
|
|
|
246
254
|
if "sn" in body:
|
|
@@ -282,7 +290,12 @@ class SatoriPlatformAdapter(Platform):
|
|
|
282
290
|
return
|
|
283
291
|
|
|
284
292
|
abm = await self.convert_satori_message(
|
|
285
|
-
message,
|
|
293
|
+
message,
|
|
294
|
+
user,
|
|
295
|
+
channel,
|
|
296
|
+
guild,
|
|
297
|
+
login,
|
|
298
|
+
timestamp,
|
|
286
299
|
)
|
|
287
300
|
if abm:
|
|
288
301
|
await self.handle_msg(abm)
|
|
@@ -295,10 +308,10 @@ class SatoriPlatformAdapter(Platform):
|
|
|
295
308
|
message: dict,
|
|
296
309
|
user: dict,
|
|
297
310
|
channel: dict,
|
|
298
|
-
guild:
|
|
311
|
+
guild: dict | None,
|
|
299
312
|
login: dict,
|
|
300
|
-
timestamp:
|
|
301
|
-
) ->
|
|
313
|
+
timestamp: int | None = None,
|
|
314
|
+
) -> AstrBotMessage | None:
|
|
302
315
|
try:
|
|
303
316
|
abm = AstrBotMessage()
|
|
304
317
|
abm.message_id = message.get("id", "")
|
|
@@ -438,7 +451,7 @@ class SatoriPlatformAdapter(Platform):
|
|
|
438
451
|
|
|
439
452
|
return prefixes
|
|
440
453
|
|
|
441
|
-
async def _extract_quote_element(self, content: str) ->
|
|
454
|
+
async def _extract_quote_element(self, content: str) -> dict | None:
|
|
442
455
|
"""提取<quote>标签信息"""
|
|
443
456
|
try:
|
|
444
457
|
# 处理命名空间前缀问题
|
|
@@ -451,7 +464,7 @@ class SatoriPlatformAdapter(Platform):
|
|
|
451
464
|
[
|
|
452
465
|
f'xmlns:{prefix}="http://temp.uri/{prefix}"'
|
|
453
466
|
for prefix in prefixes
|
|
454
|
-
]
|
|
467
|
+
],
|
|
455
468
|
)
|
|
456
469
|
|
|
457
470
|
# 包装内容
|
|
@@ -483,14 +496,17 @@ class SatoriPlatformAdapter(Platform):
|
|
|
483
496
|
inner_content += quote_element.text
|
|
484
497
|
for child in quote_element:
|
|
485
498
|
inner_content += ET.tostring(
|
|
486
|
-
child,
|
|
499
|
+
child,
|
|
500
|
+
encoding="unicode",
|
|
501
|
+
method="xml",
|
|
487
502
|
)
|
|
488
503
|
if child.tail:
|
|
489
504
|
inner_content += child.tail
|
|
490
505
|
|
|
491
506
|
# 构造移除了<quote>标签的内容
|
|
492
507
|
content_without_quote = content.replace(
|
|
493
|
-
ET.tostring(quote_element, encoding="unicode", method="xml"),
|
|
508
|
+
ET.tostring(quote_element, encoding="unicode", method="xml"),
|
|
509
|
+
"",
|
|
494
510
|
)
|
|
495
511
|
|
|
496
512
|
return {
|
|
@@ -506,7 +522,7 @@ class SatoriPlatformAdapter(Platform):
|
|
|
506
522
|
logger.error(f"提取<quote>标签时发生错误: {e}")
|
|
507
523
|
return None
|
|
508
524
|
|
|
509
|
-
async def _extract_quote_with_regex(self, content: str) ->
|
|
525
|
+
async def _extract_quote_with_regex(self, content: str) -> dict | None:
|
|
510
526
|
"""使用正则表达式提取quote标签信息"""
|
|
511
527
|
import re
|
|
512
528
|
|
|
@@ -529,7 +545,7 @@ class SatoriPlatformAdapter(Platform):
|
|
|
529
545
|
"content_without_quote": content_without_quote,
|
|
530
546
|
}
|
|
531
547
|
|
|
532
|
-
async def _convert_quote_message(self, quote: dict) ->
|
|
548
|
+
async def _convert_quote_message(self, quote: dict) -> AstrBotMessage | None:
|
|
533
549
|
"""转换引用消息"""
|
|
534
550
|
try:
|
|
535
551
|
quote_abm = AstrBotMessage()
|
|
@@ -587,7 +603,7 @@ class SatoriPlatformAdapter(Platform):
|
|
|
587
603
|
[
|
|
588
604
|
f'xmlns:{prefix}="http://temp.uri/{prefix}"'
|
|
589
605
|
for prefix in prefixes
|
|
590
|
-
]
|
|
606
|
+
],
|
|
591
607
|
)
|
|
592
608
|
|
|
593
609
|
# 包装内容
|
|
@@ -747,13 +763,15 @@ class SatoriPlatformAdapter(Platform):
|
|
|
747
763
|
|
|
748
764
|
try:
|
|
749
765
|
async with self.session.request(
|
|
750
|
-
method,
|
|
766
|
+
method,
|
|
767
|
+
url,
|
|
768
|
+
json=data,
|
|
769
|
+
headers=headers,
|
|
751
770
|
) as response:
|
|
752
771
|
if response.status == 200:
|
|
753
772
|
result = await response.json()
|
|
754
773
|
return result
|
|
755
|
-
|
|
756
|
-
return {}
|
|
774
|
+
return {}
|
|
757
775
|
except Exception as e:
|
|
758
776
|
logger.error(f"Satori HTTP 请求异常: {e}")
|
|
759
777
|
return {}
|