Merge branch 'dev' of https://github.com/MoFox-Studio/MoFox_Bot into dev
This commit is contained in:
180
src/plugin_system/apis/cross_context_api.py
Normal file
180
src/plugin_system/apis/cross_context_api.py
Normal file
@@ -0,0 +1,180 @@
|
||||
"""
|
||||
跨群聊上下文API
|
||||
"""
|
||||
|
||||
import time
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.config.config import global_config
|
||||
from src.chat.utils.chat_message_builder import (
|
||||
get_raw_msg_before_timestamp_with_chat,
|
||||
build_readable_messages_with_id,
|
||||
)
|
||||
from src.chat.message_receive.chat_stream import get_chat_manager, ChatStream
|
||||
|
||||
logger = get_logger("cross_context_api")
|
||||
|
||||
|
||||
def get_context_groups(chat_id: str) -> Optional[List[List[str]]]:
|
||||
"""
|
||||
获取当前聊天所在的共享组的其他聊天ID
|
||||
"""
|
||||
current_stream = get_chat_manager().get_stream(chat_id)
|
||||
if not current_stream:
|
||||
return None
|
||||
|
||||
is_group = current_stream.group_info is not None
|
||||
if is_group:
|
||||
assert current_stream.group_info is not None
|
||||
current_chat_raw_id = current_stream.group_info.group_id
|
||||
else:
|
||||
current_chat_raw_id = current_stream.user_info.user_id
|
||||
current_type = "group" if is_group else "private"
|
||||
|
||||
for group in global_config.cross_context.groups:
|
||||
# 检查当前聊天的ID和类型是否在组的chat_ids中
|
||||
if [current_type, str(current_chat_raw_id)] in group.chat_ids:
|
||||
# 返回组内其他聊天的 [type, id] 列表
|
||||
return [chat_info for chat_info in group.chat_ids if chat_info != [current_type, str(current_chat_raw_id)]]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def build_cross_context_normal(chat_stream: ChatStream, other_chat_infos: List[List[str]]) -> str:
|
||||
"""
|
||||
构建跨群聊/私聊上下文 (Normal模式)
|
||||
"""
|
||||
cross_context_messages = []
|
||||
for chat_type, chat_raw_id in other_chat_infos:
|
||||
is_group = chat_type == "group"
|
||||
stream_id = get_chat_manager().get_stream_id(chat_stream.platform, chat_raw_id, is_group=is_group)
|
||||
if not stream_id:
|
||||
continue
|
||||
|
||||
try:
|
||||
messages = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=stream_id,
|
||||
timestamp=time.time(),
|
||||
limit=5, # 可配置
|
||||
)
|
||||
if messages:
|
||||
chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id
|
||||
formatted_messages, _ = build_readable_messages_with_id(messages, timestamp_mode="relative")
|
||||
cross_context_messages.append(f'[以下是来自"{chat_name}"的近期消息]\n{formatted_messages}')
|
||||
except Exception as e:
|
||||
logger.error(f"获取聊天 {chat_raw_id} 的消息失败: {e}")
|
||||
continue
|
||||
|
||||
if not cross_context_messages:
|
||||
return ""
|
||||
|
||||
return "# 跨上下文参考\n" + "\n\n".join(cross_context_messages) + "\n"
|
||||
|
||||
|
||||
async def build_cross_context_s4u(
|
||||
chat_stream: ChatStream,
|
||||
other_chat_infos: List[List[str]],
|
||||
target_user_info: Optional[Dict[str, Any]],
|
||||
) -> str:
|
||||
"""
|
||||
构建跨群聊/私聊上下文 (S4U模式)
|
||||
"""
|
||||
cross_context_messages = []
|
||||
if target_user_info:
|
||||
user_id = target_user_info.get("user_id")
|
||||
|
||||
if user_id:
|
||||
for chat_type, chat_raw_id in other_chat_infos:
|
||||
is_group = chat_type == "group"
|
||||
stream_id = get_chat_manager().get_stream_id(chat_stream.platform, chat_raw_id, is_group=is_group)
|
||||
if not stream_id:
|
||||
continue
|
||||
|
||||
try:
|
||||
messages = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=stream_id,
|
||||
timestamp=time.time(),
|
||||
limit=20, # 获取更多消息以供筛选
|
||||
)
|
||||
user_messages = [msg for msg in messages if msg.get("user_id") == user_id][-5:]
|
||||
|
||||
if user_messages:
|
||||
chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id
|
||||
user_name = (
|
||||
target_user_info.get("person_name") or target_user_info.get("user_nickname") or user_id
|
||||
)
|
||||
formatted_messages, _ = build_readable_messages_with_id(
|
||||
user_messages, timestamp_mode="relative"
|
||||
)
|
||||
cross_context_messages.append(
|
||||
f'[以下是"{user_name}"在"{chat_name}"的近期发言]\n{formatted_messages}'
|
||||
)
|
||||
except Exception as e:
|
||||
logger.error(f"获取用户 {user_id} 在聊天 {chat_raw_id} 的消息失败: {e}")
|
||||
continue
|
||||
|
||||
if not cross_context_messages:
|
||||
return ""
|
||||
|
||||
return "# 跨上下文参考\n" + "\n\n".join(cross_context_messages) + "\n"
|
||||
|
||||
|
||||
async def get_chat_history_by_group_name(group_name: str) -> str:
|
||||
"""
|
||||
根据互通组名字获取聊天记录
|
||||
"""
|
||||
target_group = None
|
||||
for group in global_config.cross_context.groups:
|
||||
if group.name == group_name:
|
||||
target_group = group
|
||||
break
|
||||
|
||||
if not target_group:
|
||||
return f"找不到名为 {group_name} 的互通组。"
|
||||
|
||||
if not target_group.chat_ids:
|
||||
return f"互通组 {group_name} 中没有配置任何聊天。"
|
||||
|
||||
chat_infos = target_group.chat_ids
|
||||
chat_manager = get_chat_manager()
|
||||
|
||||
cross_context_messages = []
|
||||
for chat_type, chat_raw_id in chat_infos:
|
||||
is_group = chat_type == "group"
|
||||
|
||||
found_stream = None
|
||||
for stream in chat_manager.streams.values():
|
||||
if is_group:
|
||||
if stream.group_info and stream.group_info.group_id == chat_raw_id:
|
||||
found_stream = stream
|
||||
break
|
||||
else: # private
|
||||
if stream.user_info and stream.user_info.user_id == chat_raw_id and not stream.group_info:
|
||||
found_stream = stream
|
||||
break
|
||||
|
||||
if not found_stream:
|
||||
logger.warning(f"在已加载的聊天流中找不到ID为 {chat_raw_id} 的聊天。")
|
||||
continue
|
||||
|
||||
stream_id = found_stream.stream_id
|
||||
|
||||
try:
|
||||
messages = get_raw_msg_before_timestamp_with_chat(
|
||||
chat_id=stream_id,
|
||||
timestamp=time.time(),
|
||||
limit=5, # 可配置
|
||||
)
|
||||
if messages:
|
||||
chat_name = get_chat_manager().get_stream_name(stream_id) or chat_raw_id
|
||||
formatted_messages, _ = build_readable_messages_with_id(messages, timestamp_mode="relative")
|
||||
cross_context_messages.append(f'[以下是来自"{chat_name}"的近期消息]\n{formatted_messages}')
|
||||
except Exception as e:
|
||||
logger.error(f"获取聊天 {chat_raw_id} 的消息失败: {e}")
|
||||
continue
|
||||
|
||||
if not cross_context_messages:
|
||||
return f"无法从互通组 {group_name} 中获取任何聊天记录。"
|
||||
|
||||
return "# 跨上下文参考\n" + "\n\n".join(cross_context_messages) + "\n"
|
||||
@@ -12,7 +12,6 @@ import traceback
|
||||
from typing import Tuple, Any, Dict, List, Optional
|
||||
from rich.traceback import install
|
||||
from src.common.logger import get_logger
|
||||
from src.config.api_ada_configs import TaskConfig
|
||||
from src.chat.replyer.default_generator import DefaultReplyer
|
||||
from src.chat.message_receive.chat_stream import ChatStream
|
||||
from src.chat.utils.utils import process_llm_response
|
||||
@@ -107,9 +106,7 @@ async def generate_reply(
|
||||
"""
|
||||
try:
|
||||
# 获取回复器
|
||||
replyer = get_replyer(
|
||||
chat_stream, chat_id, request_type=request_type
|
||||
)
|
||||
replyer = get_replyer(chat_stream, chat_id, request_type=request_type)
|
||||
if not replyer:
|
||||
logger.error("[GeneratorAPI] 无法获取回复器")
|
||||
return False, [], None
|
||||
|
||||
@@ -30,7 +30,6 @@
|
||||
|
||||
import traceback
|
||||
import time
|
||||
import difflib
|
||||
import asyncio
|
||||
from typing import Optional, Union, Dict, Any
|
||||
from src.common.logger import get_logger
|
||||
@@ -41,8 +40,6 @@ from maim_message import UserInfo
|
||||
from src.chat.message_receive.chat_stream import ChatStream
|
||||
from src.chat.message_receive.uni_message_sender import HeartFCSender
|
||||
from src.chat.message_receive.message import MessageSending, MessageRecv
|
||||
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, replace_user_references_async
|
||||
from src.person_info.person_info import get_person_info_manager
|
||||
from maim_message import Seg
|
||||
from src.config.config import global_config
|
||||
|
||||
@@ -51,6 +48,7 @@ logger = get_logger("send_api")
|
||||
# 适配器命令响应等待池
|
||||
_adapter_response_pool: Dict[str, asyncio.Future] = {}
|
||||
|
||||
|
||||
def message_dict_to_message_recv(message_dict: Dict[str, Any]) -> Optional[MessageRecv]:
|
||||
"""查找要回复的消息
|
||||
|
||||
@@ -97,10 +95,11 @@ def message_dict_to_message_recv(message_dict: Dict[str, Any]) -> Optional[Messa
|
||||
}
|
||||
|
||||
message_recv = MessageRecv(message_dict)
|
||||
|
||||
|
||||
logger.info(f"[SendAPI] 找到匹配的回复消息,发送者: {message_dict.get('user_nickname', '')}")
|
||||
return message_recv
|
||||
|
||||
|
||||
def put_adapter_response(request_id: str, response_data: dict) -> None:
|
||||
"""将适配器响应放入响应池"""
|
||||
if request_id in _adapter_response_pool:
|
||||
@@ -192,7 +191,7 @@ async def _send_to_target(
|
||||
anchor_message.update_chat_stream(target_stream)
|
||||
reply_to_platform_id = (
|
||||
f"{anchor_message.message_info.platform}:{anchor_message.message_info.user_info.user_id}"
|
||||
)
|
||||
)
|
||||
else:
|
||||
anchor_message = None
|
||||
reply_to_platform_id = None
|
||||
@@ -234,7 +233,6 @@ async def _send_to_target(
|
||||
return False
|
||||
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# 公共API函数 - 预定义类型的发送函数
|
||||
# =============================================================================
|
||||
@@ -274,7 +272,9 @@ async def text_to_stream(
|
||||
)
|
||||
|
||||
|
||||
async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False) -> bool:
|
||||
async def emoji_to_stream(
|
||||
emoji_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False
|
||||
) -> bool:
|
||||
"""向指定流发送表情包
|
||||
|
||||
Args:
|
||||
@@ -285,10 +285,14 @@ async def emoji_to_stream(emoji_base64: str, stream_id: str, storage_message: bo
|
||||
Returns:
|
||||
bool: 是否发送成功
|
||||
"""
|
||||
return await _send_to_target("emoji", emoji_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply)
|
||||
return await _send_to_target(
|
||||
"emoji", emoji_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply
|
||||
)
|
||||
|
||||
|
||||
async def image_to_stream(image_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False) -> bool:
|
||||
async def image_to_stream(
|
||||
image_base64: str, stream_id: str, storage_message: bool = True, set_reply: bool = False
|
||||
) -> bool:
|
||||
"""向指定流发送图片
|
||||
|
||||
Args:
|
||||
@@ -299,11 +303,17 @@ async def image_to_stream(image_base64: str, stream_id: str, storage_message: bo
|
||||
Returns:
|
||||
bool: 是否发送成功
|
||||
"""
|
||||
return await _send_to_target("image", image_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply)
|
||||
return await _send_to_target(
|
||||
"image", image_base64, stream_id, "", typing=False, storage_message=storage_message, set_reply=set_reply
|
||||
)
|
||||
|
||||
|
||||
async def command_to_stream(
|
||||
command: Union[str, dict], stream_id: str, storage_message: bool = True, display_message: str = "", set_reply: bool = False
|
||||
command: Union[str, dict],
|
||||
stream_id: str,
|
||||
storage_message: bool = True,
|
||||
display_message: str = "",
|
||||
set_reply: bool = False,
|
||||
) -> bool:
|
||||
"""向指定流发送命令
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class EventManager:
|
||||
event = BaseEvent(event_name, allowed_subscribers, allowed_triggers)
|
||||
self._events[event_name] = event
|
||||
logger.debug(f"事件 {event_name} 注册成功")
|
||||
|
||||
|
||||
# 检查是否有缓存的订阅需要处理
|
||||
self._process_pending_subscriptions(event_name)
|
||||
|
||||
|
||||
@@ -200,7 +200,7 @@ class PluginManager:
|
||||
|
||||
# 检查并调用 on_plugin_loaded 钩子(如果存在)
|
||||
if hasattr(plugin_instance, "on_plugin_loaded") and callable(
|
||||
getattr(plugin_instance, "on_plugin_loaded")
|
||||
plugin_instance.on_plugin_loaded
|
||||
):
|
||||
logger.debug(f"为插件 '{plugin_name}' 调用 on_plugin_loaded 钩子")
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user