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,17 +1,26 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import base64
|
|
3
|
+
import os
|
|
4
|
+
import random
|
|
5
|
+
import uuid
|
|
6
|
+
|
|
7
|
+
import aiofiles
|
|
1
8
|
import botpy
|
|
2
9
|
import botpy.message
|
|
3
10
|
import botpy.types
|
|
4
11
|
import botpy.types.message
|
|
5
|
-
import asyncio
|
|
6
|
-
from astrbot.core.utils.io import file_to_base64, download_image_by_url
|
|
7
|
-
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
8
|
-
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
|
9
|
-
from astrbot.api.message_components import Plain, Image
|
|
10
12
|
from botpy import Client
|
|
11
13
|
from botpy.http import Route
|
|
12
|
-
from astrbot.api import logger
|
|
13
14
|
from botpy.types import message
|
|
14
|
-
import
|
|
15
|
+
from botpy.types.message import Media
|
|
16
|
+
|
|
17
|
+
from astrbot.api import logger
|
|
18
|
+
from astrbot.api.event import AstrMessageEvent, MessageChain
|
|
19
|
+
from astrbot.api.message_components import Image, Plain, Record
|
|
20
|
+
from astrbot.api.platform import AstrBotMessage, PlatformMetadata
|
|
21
|
+
from astrbot.core.utils.astrbot_path import get_astrbot_data_path
|
|
22
|
+
from astrbot.core.utils.io import download_image_by_url, file_to_base64
|
|
23
|
+
from astrbot.core.utils.tencent_record_helper import wav_to_tencent_silk
|
|
15
24
|
|
|
16
25
|
|
|
17
26
|
class QQOfficialMessageEvent(AstrMessageEvent):
|
|
@@ -28,16 +37,15 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
28
37
|
self.send_buffer = None
|
|
29
38
|
|
|
30
39
|
async def send(self, message: MessageChain):
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
else:
|
|
34
|
-
self.send_buffer.chain.extend(message.chain)
|
|
40
|
+
self.send_buffer = message
|
|
41
|
+
await self._post_send()
|
|
35
42
|
|
|
36
43
|
async def send_streaming(self, generator, use_fallback: bool = False):
|
|
37
44
|
"""流式输出仅支持消息列表私聊"""
|
|
38
45
|
stream_payload = {"state": 1, "id": None, "index": 0, "reset": False}
|
|
39
46
|
last_edit_time = 0 # 上次编辑消息的时间
|
|
40
47
|
throttle_interval = 1 # 编辑消息的间隔时间 (秒)
|
|
48
|
+
ret = None
|
|
41
49
|
try:
|
|
42
50
|
async for chain in generator:
|
|
43
51
|
source = self.message_obj.raw_message
|
|
@@ -68,9 +76,9 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
68
76
|
|
|
69
77
|
return await super().send_streaming(generator, use_fallback)
|
|
70
78
|
|
|
71
|
-
async def _post_send(self, stream: dict = None):
|
|
79
|
+
async def _post_send(self, stream: dict | None = None):
|
|
72
80
|
if not self.send_buffer:
|
|
73
|
-
return
|
|
81
|
+
return None
|
|
74
82
|
|
|
75
83
|
source = self.message_obj.raw_message
|
|
76
84
|
assert isinstance(
|
|
@@ -87,10 +95,16 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
87
95
|
plain_text,
|
|
88
96
|
image_base64,
|
|
89
97
|
image_path,
|
|
98
|
+
record_file_path,
|
|
90
99
|
) = await QQOfficialMessageEvent._parse_to_qqofficial(self.send_buffer)
|
|
91
100
|
|
|
92
|
-
if
|
|
93
|
-
|
|
101
|
+
if (
|
|
102
|
+
not plain_text
|
|
103
|
+
and not image_base64
|
|
104
|
+
and not image_path
|
|
105
|
+
and not record_file_path
|
|
106
|
+
):
|
|
107
|
+
return None
|
|
94
108
|
|
|
95
109
|
payload = {
|
|
96
110
|
"content": plain_text,
|
|
@@ -100,21 +114,44 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
100
114
|
if not isinstance(source, (botpy.message.Message, botpy.message.DirectMessage)):
|
|
101
115
|
payload["msg_seq"] = random.randint(1, 10000)
|
|
102
116
|
|
|
117
|
+
ret = None
|
|
118
|
+
|
|
103
119
|
match type(source):
|
|
104
120
|
case botpy.message.GroupMessage:
|
|
105
121
|
if image_base64:
|
|
106
122
|
media = await self.upload_group_and_c2c_image(
|
|
107
|
-
image_base64,
|
|
123
|
+
image_base64,
|
|
124
|
+
1,
|
|
125
|
+
group_openid=source.group_openid,
|
|
126
|
+
)
|
|
127
|
+
payload["media"] = media
|
|
128
|
+
payload["msg_type"] = 7
|
|
129
|
+
if record_file_path: # group record msg
|
|
130
|
+
media = await self.upload_group_and_c2c_record(
|
|
131
|
+
record_file_path,
|
|
132
|
+
3,
|
|
133
|
+
group_openid=source.group_openid,
|
|
108
134
|
)
|
|
109
135
|
payload["media"] = media
|
|
110
136
|
payload["msg_type"] = 7
|
|
111
137
|
ret = await self.bot.api.post_group_message(
|
|
112
|
-
group_openid=source.group_openid,
|
|
138
|
+
group_openid=source.group_openid,
|
|
139
|
+
**payload,
|
|
113
140
|
)
|
|
114
141
|
case botpy.message.C2CMessage:
|
|
115
142
|
if image_base64:
|
|
116
143
|
media = await self.upload_group_and_c2c_image(
|
|
117
|
-
image_base64,
|
|
144
|
+
image_base64,
|
|
145
|
+
1,
|
|
146
|
+
openid=source.author.user_openid,
|
|
147
|
+
)
|
|
148
|
+
payload["media"] = media
|
|
149
|
+
payload["msg_type"] = 7
|
|
150
|
+
if record_file_path: # c2c record
|
|
151
|
+
media = await self.upload_group_and_c2c_record(
|
|
152
|
+
record_file_path,
|
|
153
|
+
3,
|
|
154
|
+
openid=source.author.user_openid,
|
|
118
155
|
)
|
|
119
156
|
payload["media"] = media
|
|
120
157
|
payload["msg_type"] = 7
|
|
@@ -126,14 +163,16 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
126
163
|
)
|
|
127
164
|
else:
|
|
128
165
|
ret = await self.post_c2c_message(
|
|
129
|
-
openid=source.author.user_openid,
|
|
166
|
+
openid=source.author.user_openid,
|
|
167
|
+
**payload,
|
|
130
168
|
)
|
|
131
169
|
logger.debug(f"Message sent to C2C: {ret}")
|
|
132
170
|
case botpy.message.Message:
|
|
133
171
|
if image_path:
|
|
134
172
|
payload["file_image"] = image_path
|
|
135
173
|
ret = await self.bot.api.post_message(
|
|
136
|
-
channel_id=source.channel_id,
|
|
174
|
+
channel_id=source.channel_id,
|
|
175
|
+
**payload,
|
|
137
176
|
)
|
|
138
177
|
case botpy.message.DirectMessage:
|
|
139
178
|
if image_path:
|
|
@@ -147,7 +186,10 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
147
186
|
return ret
|
|
148
187
|
|
|
149
188
|
async def upload_group_and_c2c_image(
|
|
150
|
-
self,
|
|
189
|
+
self,
|
|
190
|
+
image_base64: str,
|
|
191
|
+
file_type: int,
|
|
192
|
+
**kwargs,
|
|
151
193
|
) -> botpy.types.message.Media:
|
|
152
194
|
payload = {
|
|
153
195
|
"file_data": image_base64,
|
|
@@ -158,7 +200,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
158
200
|
payload["openid"] = kwargs["openid"]
|
|
159
201
|
route = Route("POST", "/v2/users/{openid}/files", openid=kwargs["openid"])
|
|
160
202
|
return await self.bot.api._http.request(route, json=payload)
|
|
161
|
-
|
|
203
|
+
if "group_openid" in kwargs:
|
|
162
204
|
payload["group_openid"] = kwargs["group_openid"]
|
|
163
205
|
route = Route(
|
|
164
206
|
"POST",
|
|
@@ -167,21 +209,73 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
167
209
|
)
|
|
168
210
|
return await self.bot.api._http.request(route, json=payload)
|
|
169
211
|
|
|
212
|
+
async def upload_group_and_c2c_record(
|
|
213
|
+
self,
|
|
214
|
+
file_source: str,
|
|
215
|
+
file_type: int,
|
|
216
|
+
srv_send_msg: bool = False,
|
|
217
|
+
**kwargs,
|
|
218
|
+
) -> Media | None:
|
|
219
|
+
"""上传媒体文件"""
|
|
220
|
+
# 构建基础payload
|
|
221
|
+
payload = {"file_type": file_type, "srv_send_msg": srv_send_msg}
|
|
222
|
+
|
|
223
|
+
# 处理文件数据
|
|
224
|
+
if os.path.exists(file_source):
|
|
225
|
+
# 读取本地文件
|
|
226
|
+
async with aiofiles.open(file_source, "rb") as f:
|
|
227
|
+
file_content = await f.read()
|
|
228
|
+
# use base64 encode
|
|
229
|
+
payload["file_data"] = base64.b64encode(file_content).decode("utf-8")
|
|
230
|
+
else:
|
|
231
|
+
# 使用URL
|
|
232
|
+
payload["url"] = file_source
|
|
233
|
+
|
|
234
|
+
# 添加接收者信息和确定路由
|
|
235
|
+
if "openid" in kwargs:
|
|
236
|
+
payload["openid"] = kwargs["openid"]
|
|
237
|
+
route = Route("POST", "/v2/users/{openid}/files", openid=kwargs["openid"])
|
|
238
|
+
elif "group_openid" in kwargs:
|
|
239
|
+
payload["group_openid"] = kwargs["group_openid"]
|
|
240
|
+
route = Route(
|
|
241
|
+
"POST",
|
|
242
|
+
"/v2/groups/{group_openid}/files",
|
|
243
|
+
group_openid=kwargs["group_openid"],
|
|
244
|
+
)
|
|
245
|
+
else:
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
# 使用底层HTTP请求
|
|
250
|
+
result = await self.bot.api._http.request(route, json=payload)
|
|
251
|
+
|
|
252
|
+
if result:
|
|
253
|
+
return Media(
|
|
254
|
+
file_uuid=result.get("file_uuid"),
|
|
255
|
+
file_info=result.get("file_info"),
|
|
256
|
+
ttl=result.get("ttl", 0),
|
|
257
|
+
file_id=result.get("id", ""),
|
|
258
|
+
)
|
|
259
|
+
except Exception as e:
|
|
260
|
+
logger.error(f"上传请求错误: {e}")
|
|
261
|
+
|
|
262
|
+
return None
|
|
263
|
+
|
|
170
264
|
async def post_c2c_message(
|
|
171
265
|
self,
|
|
172
266
|
openid: str,
|
|
173
267
|
msg_type: int = 0,
|
|
174
|
-
content: str = None,
|
|
175
|
-
embed: message.Embed = None,
|
|
176
|
-
ark: message.Ark = None,
|
|
177
|
-
message_reference: message.Reference = None,
|
|
178
|
-
media: message.Media = None,
|
|
179
|
-
msg_id: str = None,
|
|
268
|
+
content: str | None = None,
|
|
269
|
+
embed: message.Embed | None = None,
|
|
270
|
+
ark: message.Ark | None = None,
|
|
271
|
+
message_reference: message.Reference | None = None,
|
|
272
|
+
media: message.Media | None = None,
|
|
273
|
+
msg_id: str | None = None,
|
|
180
274
|
msg_seq: str = 1,
|
|
181
|
-
event_id: str = None,
|
|
182
|
-
markdown: message.MarkdownPayload = None,
|
|
183
|
-
keyboard: message.Keyboard = None,
|
|
184
|
-
stream: dict = None,
|
|
275
|
+
event_id: str | None = None,
|
|
276
|
+
markdown: message.MarkdownPayload | None = None,
|
|
277
|
+
keyboard: message.Keyboard | None = None,
|
|
278
|
+
stream: dict | None = None,
|
|
185
279
|
) -> message.Message:
|
|
186
280
|
payload = locals()
|
|
187
281
|
payload.pop("self", None)
|
|
@@ -193,6 +287,7 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
193
287
|
plain_text = ""
|
|
194
288
|
image_base64 = None # only one img supported
|
|
195
289
|
image_file_path = None
|
|
290
|
+
record_file_path = None
|
|
196
291
|
for i in message.chain:
|
|
197
292
|
if isinstance(i, Plain):
|
|
198
293
|
plain_text += i.text
|
|
@@ -208,6 +303,27 @@ class QQOfficialMessageEvent(AstrMessageEvent):
|
|
|
208
303
|
else:
|
|
209
304
|
image_base64 = file_to_base64(i.file)
|
|
210
305
|
image_base64 = image_base64.removeprefix("base64://")
|
|
306
|
+
elif isinstance(i, Record):
|
|
307
|
+
if i.file:
|
|
308
|
+
record_wav_path = await i.convert_to_file_path() # wav 路径
|
|
309
|
+
temp_dir = os.path.join(get_astrbot_data_path(), "temp")
|
|
310
|
+
record_tecent_silk_path = os.path.join(
|
|
311
|
+
temp_dir,
|
|
312
|
+
f"{uuid.uuid4()}.silk",
|
|
313
|
+
)
|
|
314
|
+
try:
|
|
315
|
+
duration = await wav_to_tencent_silk(
|
|
316
|
+
record_wav_path,
|
|
317
|
+
record_tecent_silk_path,
|
|
318
|
+
)
|
|
319
|
+
if duration > 0:
|
|
320
|
+
record_file_path = record_tecent_silk_path
|
|
321
|
+
else:
|
|
322
|
+
record_file_path = None
|
|
323
|
+
logger.error("转换音频格式时出错:音频时长不大于0")
|
|
324
|
+
except Exception as e:
|
|
325
|
+
logger.error(f"处理语音时出错: {e}")
|
|
326
|
+
record_file_path = None
|
|
211
327
|
else:
|
|
212
328
|
logger.debug(f"qq_official 忽略 {i.type}")
|
|
213
|
-
return plain_text, image_base64, image_file_path
|
|
329
|
+
return plain_text, image_base64, image_file_path, record_file_path
|
|
@@ -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)
|
|
@@ -15,18 +17,21 @@ class QQOfficialWebhook:
|
|
|
15
17
|
self.appid = config["appid"]
|
|
16
18
|
self.secret = config["secret"]
|
|
17
19
|
self.port = config.get("port", 6196)
|
|
20
|
+
self.is_sandbox = config.get("is_sandbox", False)
|
|
18
21
|
self.callback_server_host = config.get("callback_server_host", "0.0.0.0")
|
|
19
22
|
|
|
20
23
|
if isinstance(self.port, str):
|
|
21
24
|
self.port = int(self.port)
|
|
22
25
|
|
|
23
|
-
self.http: BotHttp = BotHttp(timeout=300)
|
|
26
|
+
self.http: BotHttp = BotHttp(timeout=300, is_sandbox=self.is_sandbox)
|
|
24
27
|
self.api: BotAPI = BotAPI(http=self.http)
|
|
25
28
|
self.token = Token(self.appid, self.secret)
|
|
26
29
|
|
|
27
30
|
self.server = quart.Quart(__name__)
|
|
28
31
|
self.server.add_url_rule(
|
|
29
|
-
"/astrbot-qo-webhook/callback",
|
|
32
|
+
"/astrbot-qo-webhook/callback",
|
|
33
|
+
view_func=self.callback,
|
|
34
|
+
methods=["POST"],
|
|
30
35
|
)
|
|
31
36
|
self.client = botpy_client
|
|
32
37
|
self.event_queue = event_queue
|
|
@@ -61,7 +66,8 @@ class QQOfficialWebhook:
|
|
|
61
66
|
seed = await self.repeat_seed(self.secret)
|
|
62
67
|
private_key = ed25519.Ed25519PrivateKey.from_private_bytes(seed)
|
|
63
68
|
msg = validation_payload.get("event_ts", "") + validation_payload.get(
|
|
64
|
-
"plain_token",
|
|
69
|
+
"plain_token",
|
|
70
|
+
"",
|
|
65
71
|
)
|
|
66
72
|
# sign
|
|
67
73
|
signature = private_key.sign(msg.encode()).hex()
|
|
@@ -98,7 +104,7 @@ class QQOfficialWebhook:
|
|
|
98
104
|
|
|
99
105
|
async def start_polling(self):
|
|
100
106
|
logger.info(
|
|
101
|
-
f"将在 {self.callback_server_host}:{self.port} 端口启动 QQ 官方机器人 webhook 适配器。"
|
|
107
|
+
f"将在 {self.callback_server_host}:{self.port} 端口启动 QQ 官方机器人 webhook 适配器。",
|
|
102
108
|
)
|
|
103
109
|
await self.server.run_task(
|
|
104
110
|
host=self.callback_server_host,
|