fix:正常获取Bot自身发送的消息id
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
from collections.abc import Awaitable, Callable
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Any, Optional, Sequence
|
||||
from typing import Any, Optional, Sequence, TYPE_CHECKING
|
||||
|
||||
import random
|
||||
|
||||
@@ -18,6 +18,9 @@ from .emoji_manager import _serialize_emoji_for_hook, emoji_manager, emoji_manag
|
||||
|
||||
logger = get_logger("emoji_maisaka_tool")
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.chat.message_receive.message import SessionMessage
|
||||
|
||||
EmojiSelector = Callable[
|
||||
[str, str, Sequence[str] | None, int],
|
||||
Awaitable[tuple[MaiEmoji | None, str]],
|
||||
@@ -35,6 +38,7 @@ class MaisakaEmojiSendResult:
|
||||
emotions: list[str] = field(default_factory=list)
|
||||
requested_emotion: str = ""
|
||||
matched_emotion: str = ""
|
||||
sent_message: Optional["SessionMessage"] = None
|
||||
|
||||
|
||||
def _get_runtime_manager() -> Any:
|
||||
@@ -309,6 +313,7 @@ async def send_emoji_for_maisaka(
|
||||
|
||||
try:
|
||||
target_session = chat_manager.get_session_by_session_id(stream_id)
|
||||
sent_message = None
|
||||
if target_session is not None and target_session.platform == CLI_PLATFORM_NAME:
|
||||
preview_message = (
|
||||
f"已发送表情包:{selected_emoji.description.strip()}"
|
||||
@@ -318,13 +323,14 @@ async def send_emoji_for_maisaka(
|
||||
render_cli_message(preview_message)
|
||||
sent = True
|
||||
else:
|
||||
sent = await send_service.emoji_to_stream(
|
||||
sent_message = await send_service.emoji_to_stream_with_message(
|
||||
emoji_base64=emoji_base64,
|
||||
stream_id=stream_id,
|
||||
storage_message=True,
|
||||
set_reply=False,
|
||||
reply_message=None,
|
||||
)
|
||||
sent = sent_message is not None
|
||||
except Exception as exc:
|
||||
return MaisakaEmojiSendResult(
|
||||
success=False,
|
||||
@@ -361,4 +367,5 @@ async def send_emoji_for_maisaka(
|
||||
emotions=emotions,
|
||||
requested_emotion=normalized_requested_emotion,
|
||||
matched_emotion=matched_emotion,
|
||||
sent_message=sent_message,
|
||||
)
|
||||
|
||||
@@ -4,7 +4,7 @@ from __future__ import annotations
|
||||
|
||||
from base64 import b64decode
|
||||
from datetime import datetime
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List, Optional, TYPE_CHECKING
|
||||
|
||||
from src.chat.utils.utils import process_llm_response
|
||||
from src.common.data_models.message_component_data_model import EmojiComponent, MessageSequence, TextComponent
|
||||
@@ -12,10 +12,12 @@ from src.config.config import global_config
|
||||
from src.core.tooling import ToolExecutionResult
|
||||
|
||||
from ..context_messages import SessionBackedMessage
|
||||
from ..message_adapter import format_speaker_content
|
||||
from ..message_adapter import build_visible_text_from_sequence, clone_message_sequence, format_speaker_content
|
||||
from ..planner_message_utils import build_planner_prefix, build_session_backed_text_message
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from src.chat.message_receive.message import SessionMessage
|
||||
|
||||
from ..reasoning_engine import MaisakaReasoningEngine
|
||||
from ..runtime import MaisakaHeartFlowChatting
|
||||
|
||||
@@ -136,6 +138,57 @@ class BuiltinToolRuntimeContext:
|
||||
|
||||
return self.engine._get_runtime_manager()
|
||||
|
||||
@staticmethod
|
||||
def _build_visible_text_from_sent_message(message: "SessionMessage") -> str:
|
||||
"""将已发送消息转换为 Maisaka 可见文本。"""
|
||||
|
||||
user_info = message.message_info.user_info
|
||||
speaker_name = user_info.user_cardname or user_info.user_nickname or user_info.user_id
|
||||
visible_message_id = None if message.is_notify else message.message_id
|
||||
legacy_sequence = MessageSequence([])
|
||||
legacy_sequence.text(
|
||||
format_speaker_content(
|
||||
speaker_name,
|
||||
"",
|
||||
message.timestamp,
|
||||
visible_message_id,
|
||||
)
|
||||
)
|
||||
for component in clone_message_sequence(message.raw_message).components:
|
||||
legacy_sequence.components.append(component)
|
||||
return build_visible_text_from_sequence(legacy_sequence).strip()
|
||||
|
||||
def append_sent_message_to_chat_history(
|
||||
self,
|
||||
message: "SessionMessage",
|
||||
*,
|
||||
source_kind: str = "guided_reply",
|
||||
) -> None:
|
||||
"""将真实已发送消息同步到 Maisaka 历史。"""
|
||||
|
||||
user_info = message.message_info.user_info
|
||||
speaker_name = user_info.user_cardname or user_info.user_nickname or user_info.user_id
|
||||
planner_prefix = build_planner_prefix(
|
||||
timestamp=message.timestamp,
|
||||
user_name=speaker_name,
|
||||
group_card=user_info.user_cardname or "",
|
||||
message_id=message.message_id,
|
||||
include_message_id=not message.is_notify and bool(message.message_id),
|
||||
)
|
||||
planner_components = clone_message_sequence(message.raw_message).components
|
||||
if planner_components and isinstance(planner_components[0], TextComponent):
|
||||
planner_components[0].text = f"{planner_prefix}{planner_components[0].text}"
|
||||
else:
|
||||
planner_components.insert(0, TextComponent(planner_prefix))
|
||||
|
||||
history_message = SessionBackedMessage.from_session_message(
|
||||
message,
|
||||
raw_message=MessageSequence(planner_components),
|
||||
visible_text=self._build_visible_text_from_sent_message(message),
|
||||
source_kind=source_kind,
|
||||
)
|
||||
self.runtime._chat_history.append(history_message)
|
||||
|
||||
def append_guided_reply_to_chat_history(self, reply_text: str) -> None:
|
||||
"""将引导回复写回 Maisaka 历史。"""
|
||||
|
||||
|
||||
@@ -144,13 +144,14 @@ async def handle_tool(
|
||||
combined_reply_text = "".join(reply_segments)
|
||||
try:
|
||||
sent = False
|
||||
sent_messages = []
|
||||
if tool_ctx.runtime.chat_stream.platform == CLI_PLATFORM_NAME:
|
||||
for segment in reply_segments:
|
||||
render_cli_message(segment)
|
||||
sent = True
|
||||
else:
|
||||
for index, segment in enumerate(reply_segments):
|
||||
sent = await send_service.text_to_stream(
|
||||
sent_message = await send_service.text_to_stream_with_message(
|
||||
text=segment,
|
||||
stream_id=tool_ctx.runtime.session_id,
|
||||
set_reply=set_quote if index == 0 else False,
|
||||
@@ -158,8 +159,10 @@ async def handle_tool(
|
||||
selected_expressions=reply_result.selected_expression_ids or None,
|
||||
typing=index > 0,
|
||||
)
|
||||
sent = sent_message is not None
|
||||
if not sent:
|
||||
break
|
||||
sent_messages.append(sent_message)
|
||||
except Exception:
|
||||
logger.exception(
|
||||
f"{tool_ctx.runtime.log_prefix} 发送文字消息时发生异常,目标消息编号={target_message_id}"
|
||||
@@ -183,7 +186,11 @@ async def handle_tool(
|
||||
target_user_info = target_message.message_info.user_info
|
||||
target_user_name = target_user_info.user_cardname or target_user_info.user_nickname or target_user_info.user_id
|
||||
|
||||
tool_ctx.append_guided_reply_to_chat_history(combined_reply_text)
|
||||
if tool_ctx.runtime.chat_stream.platform == CLI_PLATFORM_NAME:
|
||||
tool_ctx.append_guided_reply_to_chat_history(combined_reply_text)
|
||||
else:
|
||||
for sent_message in sent_messages:
|
||||
tool_ctx.append_sent_message_to_chat_history(sent_message)
|
||||
return tool_ctx.build_success_result(
|
||||
invocation.tool_name,
|
||||
"回复已生成并发送。",
|
||||
|
||||
@@ -346,10 +346,13 @@ async def handle_tool(
|
||||
f"描述={send_result.description!r} 情绪标签={send_result.emotions} "
|
||||
f"请求情绪={emotion!r} 命中情绪={send_result.matched_emotion!r}"
|
||||
)
|
||||
tool_ctx.append_sent_emoji_to_chat_history(
|
||||
emoji_base64=send_result.emoji_base64,
|
||||
success_message=_EMOJI_SUCCESS_MESSAGE,
|
||||
)
|
||||
if send_result.sent_message is not None:
|
||||
tool_ctx.append_sent_message_to_chat_history(send_result.sent_message)
|
||||
else:
|
||||
tool_ctx.append_sent_emoji_to_chat_history(
|
||||
emoji_base64=send_result.emoji_base64,
|
||||
success_message=_EMOJI_SUCCESS_MESSAGE,
|
||||
)
|
||||
structured_result["success"] = True
|
||||
return tool_ctx.build_success_result(
|
||||
invocation.tool_name,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""
|
||||
"""
|
||||
发送服务模块。
|
||||
|
||||
统一封装内部模块的出站消息发送逻辑:
|
||||
@@ -728,7 +728,7 @@ async def _send_via_platform_io(
|
||||
reply_message_id: Optional[str],
|
||||
storage_message: bool,
|
||||
show_log: bool,
|
||||
) -> bool:
|
||||
) -> Optional[SessionMessage]:
|
||||
"""通过 Platform IO 发送消息。
|
||||
|
||||
Args:
|
||||
@@ -753,7 +753,7 @@ async def _send_via_platform_io(
|
||||
)
|
||||
if before_send_result.aborted:
|
||||
logger.info(f"[SendService] 消息 {message.message_id} 在发送前被 Hook 中止")
|
||||
return False
|
||||
return None
|
||||
|
||||
before_kwargs = before_send_result.kwargs
|
||||
typing = _coerce_bool(before_kwargs.get("typing"), typing)
|
||||
@@ -769,13 +769,13 @@ async def _send_via_platform_io(
|
||||
except Exception as exc:
|
||||
logger.error(f"[SendService] 准备 Platform IO 发送管线失败: {exc}")
|
||||
logger.debug(traceback.format_exc())
|
||||
return False
|
||||
return None
|
||||
|
||||
try:
|
||||
route_key = platform_io_manager.build_route_key_from_message(message)
|
||||
except Exception as exc:
|
||||
logger.warning(f"[SendService] 根据消息构造 Platform IO 路由键失败: {exc}")
|
||||
return False
|
||||
return None
|
||||
|
||||
try:
|
||||
await _prepare_message_for_platform_io(
|
||||
@@ -792,7 +792,7 @@ async def _send_via_platform_io(
|
||||
except Exception as exc:
|
||||
logger.error(f"[SendService] Platform IO 发送异常: {exc}")
|
||||
logger.debug(traceback.format_exc())
|
||||
return False
|
||||
return None
|
||||
|
||||
sent = bool(delivery_batch.has_success)
|
||||
if sent:
|
||||
@@ -823,10 +823,34 @@ async def _send_via_platform_io(
|
||||
f"(drivers: {', '.join(successful_driver_ids)}) "
|
||||
f"message={_build_outbound_log_preview(message)}"
|
||||
)
|
||||
return True
|
||||
return message
|
||||
|
||||
_log_platform_io_failures(delivery_batch)
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
async def send_session_message_with_message(
|
||||
message: SessionMessage,
|
||||
*,
|
||||
typing: bool = False,
|
||||
set_reply: bool = False,
|
||||
reply_message_id: Optional[str] = None,
|
||||
storage_message: bool = True,
|
||||
show_log: bool = True,
|
||||
) -> Optional[SessionMessage]:
|
||||
"""统一发送一条内部消息,并返回最终发送成功的消息对象。"""
|
||||
if not message.message_id:
|
||||
logger.error("[SendService] 消息缺少 message_id,无法发送")
|
||||
raise ValueError("消息缺少 message_id,无法发送")
|
||||
|
||||
return await _send_via_platform_io(
|
||||
message,
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
reply_message_id=reply_message_id,
|
||||
storage_message=storage_message,
|
||||
show_log=show_log,
|
||||
)
|
||||
|
||||
|
||||
async def send_session_message(
|
||||
@@ -861,13 +885,16 @@ async def send_session_message(
|
||||
logger.error("[SendService] 消息缺少 message_id,无法发送")
|
||||
raise ValueError("消息缺少 message_id,无法发送")
|
||||
|
||||
return await _send_via_platform_io(
|
||||
message,
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
reply_message_id=reply_message_id,
|
||||
storage_message=storage_message,
|
||||
show_log=show_log,
|
||||
return (
|
||||
await send_session_message_with_message(
|
||||
message,
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
reply_message_id=reply_message_id,
|
||||
storage_message=storage_message,
|
||||
show_log=show_log,
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
@@ -882,6 +909,34 @@ async def _send_to_target(
|
||||
show_log: bool = True,
|
||||
selected_expressions: Optional[List[int]] = None,
|
||||
) -> bool:
|
||||
"""向指定目标构建并发送消息,并返回是否发送成功。"""
|
||||
return (
|
||||
await _send_to_target_with_message(
|
||||
message_sequence=message_sequence,
|
||||
stream_id=stream_id,
|
||||
display_message=display_message,
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
reply_message=reply_message,
|
||||
storage_message=storage_message,
|
||||
show_log=show_log,
|
||||
selected_expressions=selected_expressions,
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
async def _send_to_target_with_message(
|
||||
message_sequence: MessageSequence,
|
||||
stream_id: str,
|
||||
display_message: str = "",
|
||||
typing: bool = False,
|
||||
set_reply: bool = False,
|
||||
reply_message: Optional[MaiMessage] = None,
|
||||
storage_message: bool = True,
|
||||
show_log: bool = True,
|
||||
selected_expressions: Optional[List[int]] = None,
|
||||
) -> Optional[SessionMessage]:
|
||||
"""向指定目标构建并发送消息。
|
||||
|
||||
Args:
|
||||
@@ -901,7 +956,7 @@ async def _send_to_target(
|
||||
try:
|
||||
if set_reply and reply_message is None:
|
||||
logger.warning("[SendService] 使用引用回复,但未提供回复消息")
|
||||
return False
|
||||
return None
|
||||
|
||||
if show_log:
|
||||
logger.debug(f"[SendService] 发送{_describe_message_sequence(message_sequence)}消息到 {stream_id}")
|
||||
@@ -914,7 +969,7 @@ async def _send_to_target(
|
||||
selected_expressions=selected_expressions,
|
||||
)
|
||||
if outbound_message is None:
|
||||
return False
|
||||
return None
|
||||
|
||||
after_build_result, outbound_message = await _invoke_send_hook(
|
||||
"send_service.after_build_message",
|
||||
@@ -928,7 +983,7 @@ async def _send_to_target(
|
||||
)
|
||||
if after_build_result.aborted:
|
||||
logger.info(f"[SendService] 消息 {outbound_message.message_id} 在构建后被 Hook 中止")
|
||||
return False
|
||||
return None
|
||||
|
||||
after_build_kwargs = after_build_result.kwargs
|
||||
typing = _coerce_bool(after_build_kwargs.get("typing"), typing)
|
||||
@@ -936,7 +991,7 @@ async def _send_to_target(
|
||||
storage_message = _coerce_bool(after_build_kwargs.get("storage_message"), storage_message)
|
||||
show_log = _coerce_bool(after_build_kwargs.get("show_log"), show_log)
|
||||
|
||||
sent = await send_session_message(
|
||||
sent_message = await send_session_message_with_message(
|
||||
outbound_message,
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
@@ -944,16 +999,38 @@ async def _send_to_target(
|
||||
storage_message=storage_message,
|
||||
show_log=show_log,
|
||||
)
|
||||
if sent:
|
||||
if sent_message is not None:
|
||||
logger.debug(f"[SendService] 成功发送消息到 {stream_id}")
|
||||
return True
|
||||
return sent_message
|
||||
|
||||
logger.error("[SendService] 发送消息失败")
|
||||
return False
|
||||
return None
|
||||
except Exception as exc:
|
||||
logger.error(f"[SendService] 发送消息时出错: {exc}")
|
||||
traceback.print_exc()
|
||||
return False
|
||||
return None
|
||||
|
||||
|
||||
async def text_to_stream_with_message(
|
||||
text: str,
|
||||
stream_id: str,
|
||||
typing: bool = False,
|
||||
set_reply: bool = False,
|
||||
reply_message: Optional[MaiMessage] = None,
|
||||
storage_message: bool = True,
|
||||
selected_expressions: Optional[List[int]] = None,
|
||||
) -> Optional[SessionMessage]:
|
||||
"""向指定流发送文本消息,并返回发送成功后的消息对象。"""
|
||||
return await _send_to_target_with_message(
|
||||
message_sequence=MessageSequence(components=[TextComponent(text=text)]),
|
||||
stream_id=stream_id,
|
||||
display_message="",
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
reply_message=reply_message,
|
||||
storage_message=storage_message,
|
||||
selected_expressions=selected_expressions,
|
||||
)
|
||||
|
||||
|
||||
async def text_to_stream(
|
||||
@@ -979,15 +1056,36 @@ async def text_to_stream(
|
||||
Returns:
|
||||
bool: 发送成功时返回 ``True``。
|
||||
"""
|
||||
return await _send_to_target(
|
||||
message_sequence=MessageSequence(components=[TextComponent(text=text)]),
|
||||
return (
|
||||
await text_to_stream_with_message(
|
||||
text=text,
|
||||
stream_id=stream_id,
|
||||
typing=typing,
|
||||
set_reply=set_reply,
|
||||
reply_message=reply_message,
|
||||
storage_message=storage_message,
|
||||
selected_expressions=selected_expressions,
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
async def emoji_to_stream_with_message(
|
||||
emoji_base64: str,
|
||||
stream_id: str,
|
||||
storage_message: bool = True,
|
||||
set_reply: bool = False,
|
||||
reply_message: Optional[MaiMessage] = None,
|
||||
) -> Optional[SessionMessage]:
|
||||
"""向指定流发送表情消息,并返回发送成功后的消息对象。"""
|
||||
return await _send_to_target_with_message(
|
||||
message_sequence=_build_message_sequence_from_custom_message("emoji", emoji_base64),
|
||||
stream_id=stream_id,
|
||||
display_message="",
|
||||
typing=typing,
|
||||
typing=False,
|
||||
storage_message=storage_message,
|
||||
set_reply=set_reply,
|
||||
reply_message=reply_message,
|
||||
storage_message=storage_message,
|
||||
selected_expressions=selected_expressions,
|
||||
)
|
||||
|
||||
|
||||
@@ -1010,14 +1108,15 @@ async def emoji_to_stream(
|
||||
Returns:
|
||||
bool: 发送成功时返回 ``True``。
|
||||
"""
|
||||
return await _send_to_target(
|
||||
message_sequence=_build_message_sequence_from_custom_message("emoji", emoji_base64),
|
||||
stream_id=stream_id,
|
||||
display_message="",
|
||||
typing=False,
|
||||
storage_message=storage_message,
|
||||
set_reply=set_reply,
|
||||
reply_message=reply_message,
|
||||
return (
|
||||
await emoji_to_stream_with_message(
|
||||
emoji_base64=emoji_base64,
|
||||
stream_id=stream_id,
|
||||
storage_message=storage_message,
|
||||
set_reply=set_reply,
|
||||
reply_message=reply_message,
|
||||
)
|
||||
is not None
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user