mirror of https://github.com/Mai-with-u/MaiBot.git
feat: 增强虚拟身份模式支持,添加群 ID 处理和历史记录管理
parent
6444bc0d3e
commit
2f61df1cc5
|
|
@ -18,6 +18,9 @@ logger = get_logger("sender")
|
||||||
# WebUI 聊天室的消息广播器(延迟导入避免循环依赖)
|
# WebUI 聊天室的消息广播器(延迟导入避免循环依赖)
|
||||||
_webui_chat_broadcaster = None
|
_webui_chat_broadcaster = None
|
||||||
|
|
||||||
|
# 虚拟群 ID 前缀(与 chat_routes.py 保持一致)
|
||||||
|
VIRTUAL_GROUP_ID_PREFIX = "webui_virtual_group_"
|
||||||
|
|
||||||
|
|
||||||
def get_webui_chat_broadcaster():
|
def get_webui_chat_broadcaster():
|
||||||
"""获取 WebUI 聊天室广播器"""
|
"""获取 WebUI 聊天室广播器"""
|
||||||
|
|
@ -32,16 +35,24 @@ def get_webui_chat_broadcaster():
|
||||||
return _webui_chat_broadcaster
|
return _webui_chat_broadcaster
|
||||||
|
|
||||||
|
|
||||||
|
def is_webui_virtual_group(group_id: str) -> bool:
|
||||||
|
"""检查是否是 WebUI 虚拟群"""
|
||||||
|
return group_id and group_id.startswith(VIRTUAL_GROUP_ID_PREFIX)
|
||||||
|
|
||||||
|
|
||||||
async def _send_message(message: MessageSending, show_log=True) -> bool:
|
async def _send_message(message: MessageSending, show_log=True) -> bool:
|
||||||
"""合并后的消息发送函数,包含WS发送和日志记录"""
|
"""合并后的消息发送函数,包含WS发送和日志记录"""
|
||||||
message_preview = truncate_message(message.processed_plain_text, max_length=200)
|
message_preview = truncate_message(message.processed_plain_text, max_length=200)
|
||||||
platform = message.message_info.platform
|
platform = message.message_info.platform
|
||||||
|
group_id = message.message_info.group_info.group_id if message.message_info.group_info else None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 检查是否是 WebUI 平台的消息
|
# 检查是否是 WebUI 平台的消息,或者是 WebUI 虚拟群的消息
|
||||||
chat_manager, webui_platform = get_webui_chat_broadcaster()
|
chat_manager, webui_platform = get_webui_chat_broadcaster()
|
||||||
if platform == webui_platform and chat_manager is not None:
|
is_webui_message = (platform == webui_platform) or is_webui_virtual_group(group_id)
|
||||||
# WebUI 聊天室消息,通过 WebSocket 广播
|
|
||||||
|
if is_webui_message and chat_manager is not None:
|
||||||
|
# WebUI 聊天室消息(包括虚拟身份模式),通过 WebSocket 广播
|
||||||
import time
|
import time
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
|
|
||||||
|
|
@ -51,6 +62,7 @@ async def _send_message(message: MessageSending, show_log=True) -> bool:
|
||||||
"content": message.processed_plain_text,
|
"content": message.processed_plain_text,
|
||||||
"message_type": "text",
|
"message_type": "text",
|
||||||
"timestamp": time.time(),
|
"timestamp": time.time(),
|
||||||
|
"group_id": group_id, # 包含群 ID 以便前端区分不同的聊天标签
|
||||||
"sender": {
|
"sender": {
|
||||||
"name": global_config.bot.nickname,
|
"name": global_config.bot.nickname,
|
||||||
"avatar": None,
|
"avatar": None,
|
||||||
|
|
@ -63,7 +75,10 @@ async def _send_message(message: MessageSending, show_log=True) -> bool:
|
||||||
# 无需手动保存
|
# 无需手动保存
|
||||||
|
|
||||||
if show_log:
|
if show_log:
|
||||||
logger.info(f"已将消息 '{message_preview}' 发往 WebUI 聊天室")
|
if is_webui_virtual_group(group_id):
|
||||||
|
logger.info(f"已将消息 '{message_preview}' 发往 WebUI 虚拟群 (平台: {platform})")
|
||||||
|
else:
|
||||||
|
logger.info(f"已将消息 '{message_preview}' 发往 WebUI 聊天室")
|
||||||
return True
|
return True
|
||||||
|
|
||||||
# 直接调用API发送消息
|
# 直接调用API发送消息
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,9 @@
|
||||||
"""本地聊天室路由 - WebUI 与麦麦直接对话"""
|
"""本地聊天室路由 - WebUI 与麦麦直接对话
|
||||||
|
|
||||||
|
支持两种模式:
|
||||||
|
1. WebUI 模式:使用 WebUI 平台独立身份聊天
|
||||||
|
2. 虚拟身份模式:使用真实平台用户的身份,在虚拟群聊中与麦麦对话
|
||||||
|
"""
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import uuid
|
import uuid
|
||||||
|
|
@ -7,7 +12,7 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.common.database.database_model import Messages
|
from src.common.database.database_model import Messages, PersonInfo
|
||||||
from src.config.config import global_config
|
from src.config.config import global_config
|
||||||
from src.chat.message_receive.bot import chat_bot
|
from src.chat.message_receive.bot import chat_bot
|
||||||
|
|
||||||
|
|
@ -19,10 +24,25 @@ router = APIRouter(prefix="/api/chat", tags=["LocalChat"])
|
||||||
WEBUI_CHAT_GROUP_ID = "webui_local_chat"
|
WEBUI_CHAT_GROUP_ID = "webui_local_chat"
|
||||||
WEBUI_CHAT_PLATFORM = "webui"
|
WEBUI_CHAT_PLATFORM = "webui"
|
||||||
|
|
||||||
|
# 虚拟身份模式的群 ID 前缀
|
||||||
|
VIRTUAL_GROUP_ID_PREFIX = "webui_virtual_group_"
|
||||||
|
|
||||||
# 固定的 WebUI 用户 ID 前缀
|
# 固定的 WebUI 用户 ID 前缀
|
||||||
WEBUI_USER_ID_PREFIX = "webui_user_"
|
WEBUI_USER_ID_PREFIX = "webui_user_"
|
||||||
|
|
||||||
|
|
||||||
|
class VirtualIdentityConfig(BaseModel):
|
||||||
|
"""虚拟身份配置"""
|
||||||
|
|
||||||
|
enabled: bool = False # 是否启用虚拟身份模式
|
||||||
|
platform: Optional[str] = None # 目标平台(如 qq, discord 等)
|
||||||
|
person_id: Optional[str] = None # PersonInfo 的 person_id
|
||||||
|
user_id: Optional[str] = None # 原始平台用户 ID
|
||||||
|
user_nickname: Optional[str] = None # 用户昵称
|
||||||
|
group_id: Optional[str] = None # 虚拟群 ID(自动生成或用户指定)
|
||||||
|
group_name: Optional[str] = None # 虚拟群名(用户自定义)
|
||||||
|
|
||||||
|
|
||||||
class ChatHistoryMessage(BaseModel):
|
class ChatHistoryMessage(BaseModel):
|
||||||
"""聊天历史消息"""
|
"""聊天历史消息"""
|
||||||
|
|
||||||
|
|
@ -41,12 +61,25 @@ class ChatHistoryManager:
|
||||||
def __init__(self, max_messages: int = 200):
|
def __init__(self, max_messages: int = 200):
|
||||||
self.max_messages = max_messages
|
self.max_messages = max_messages
|
||||||
|
|
||||||
def _message_to_dict(self, msg: Messages) -> Dict[str, Any]:
|
def _message_to_dict(self, msg: Messages, group_id: Optional[str] = None) -> Dict[str, Any]:
|
||||||
"""将数据库消息转换为前端格式"""
|
"""将数据库消息转换为前端格式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
msg: 数据库消息对象
|
||||||
|
group_id: 群 ID,用于判断是否是虚拟群
|
||||||
|
"""
|
||||||
# 判断是否是机器人消息
|
# 判断是否是机器人消息
|
||||||
# WebUI 用户的 user_id 以 "webui_" 开头,其他都是机器人消息
|
|
||||||
user_id = msg.user_id or ""
|
user_id = msg.user_id or ""
|
||||||
is_bot = not user_id.startswith("webui_") and not user_id.startswith(WEBUI_USER_ID_PREFIX)
|
|
||||||
|
# 对于虚拟群,通过比较机器人 QQ 账号来判断
|
||||||
|
# 对于普通 WebUI 群,检查 user_id 是否以 webui_ 开头
|
||||||
|
if group_id and group_id.startswith(VIRTUAL_GROUP_ID_PREFIX):
|
||||||
|
# 虚拟群:user_id 等于机器人 QQ 账号的是机器人消息
|
||||||
|
bot_qq = str(global_config.bot.qq_account)
|
||||||
|
is_bot = user_id == bot_qq
|
||||||
|
else:
|
||||||
|
# 普通 WebUI 群:不以 webui_ 开头的是机器人消息
|
||||||
|
is_bot = not user_id.startswith("webui_") and not user_id.startswith(WEBUI_USER_ID_PREFIX)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"id": msg.message_id,
|
"id": msg.message_id,
|
||||||
|
|
@ -58,32 +91,44 @@ class ChatHistoryManager:
|
||||||
"is_bot": is_bot,
|
"is_bot": is_bot,
|
||||||
}
|
}
|
||||||
|
|
||||||
def get_history(self, limit: int = 50) -> List[Dict[str, Any]]:
|
def get_history(self, limit: int = 50, group_id: Optional[str] = None) -> List[Dict[str, Any]]:
|
||||||
"""从数据库获取最近的历史记录"""
|
"""从数据库获取最近的历史记录
|
||||||
|
|
||||||
|
Args:
|
||||||
|
limit: 获取的消息数量
|
||||||
|
group_id: 群 ID,默认为 WEBUI_CHAT_GROUP_ID
|
||||||
|
"""
|
||||||
|
target_group_id = group_id if group_id else WEBUI_CHAT_GROUP_ID
|
||||||
try:
|
try:
|
||||||
# 查询 WebUI 平台的消息,按时间排序
|
# 查询指定群的消息,按时间排序
|
||||||
messages = (
|
messages = (
|
||||||
Messages.select()
|
Messages.select()
|
||||||
.where(Messages.chat_info_group_id == WEBUI_CHAT_GROUP_ID)
|
.where(Messages.chat_info_group_id == target_group_id)
|
||||||
.order_by(Messages.time.desc())
|
.order_by(Messages.time.desc())
|
||||||
.limit(limit)
|
.limit(limit)
|
||||||
)
|
)
|
||||||
|
|
||||||
# 转换为列表并反转(使最旧的消息在前)
|
# 转换为列表并反转(使最旧的消息在前)
|
||||||
result = [self._message_to_dict(msg) for msg in messages]
|
# 传递 group_id 以便正确判断虚拟群中的机器人消息
|
||||||
|
result = [self._message_to_dict(msg, target_group_id) for msg in messages]
|
||||||
result.reverse()
|
result.reverse()
|
||||||
|
|
||||||
logger.debug(f"从数据库加载了 {len(result)} 条聊天记录")
|
logger.debug(f"从数据库加载了 {len(result)} 条聊天记录 (group_id={target_group_id})")
|
||||||
return result
|
return result
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"从数据库加载聊天记录失败: {e}")
|
logger.error(f"从数据库加载聊天记录失败: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def clear_history(self) -> int:
|
def clear_history(self, group_id: Optional[str] = None) -> int:
|
||||||
"""清空 WebUI 聊天历史记录"""
|
"""清空聊天历史记录
|
||||||
|
|
||||||
|
Args:
|
||||||
|
group_id: 群 ID,默认清空 WebUI 默认聊天室
|
||||||
|
"""
|
||||||
|
target_group_id = group_id if group_id else WEBUI_CHAT_GROUP_ID
|
||||||
try:
|
try:
|
||||||
deleted = Messages.delete().where(Messages.chat_info_group_id == WEBUI_CHAT_GROUP_ID).execute()
|
deleted = Messages.delete().where(Messages.chat_info_group_id == target_group_id).execute()
|
||||||
logger.info(f"已清空 {deleted} 条 WebUI 聊天记录")
|
logger.info(f"已清空 {deleted} 条聊天记录 (group_id={target_group_id})")
|
||||||
return deleted
|
return deleted
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"清空聊天记录失败: {e}")
|
logger.error(f"清空聊天记录失败: {e}")
|
||||||
|
|
@ -132,27 +177,57 @@ chat_manager = ChatConnectionManager()
|
||||||
|
|
||||||
|
|
||||||
def create_message_data(
|
def create_message_data(
|
||||||
content: str, user_id: str, user_name: str, message_id: Optional[str] = None, is_at_bot: bool = True
|
content: str,
|
||||||
|
user_id: str,
|
||||||
|
user_name: str,
|
||||||
|
message_id: Optional[str] = None,
|
||||||
|
is_at_bot: bool = True,
|
||||||
|
virtual_config: Optional[VirtualIdentityConfig] = None,
|
||||||
) -> Dict[str, Any]:
|
) -> Dict[str, Any]:
|
||||||
"""创建符合麦麦消息格式的消息数据"""
|
"""创建符合麦麦消息格式的消息数据
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content: 消息内容
|
||||||
|
user_id: 用户 ID
|
||||||
|
user_name: 用户昵称
|
||||||
|
message_id: 消息 ID(可选,自动生成)
|
||||||
|
is_at_bot: 是否 @ 机器人
|
||||||
|
virtual_config: 虚拟身份配置(可选,启用后使用真实平台身份)
|
||||||
|
"""
|
||||||
if message_id is None:
|
if message_id is None:
|
||||||
message_id = str(uuid.uuid4())
|
message_id = str(uuid.uuid4())
|
||||||
|
|
||||||
|
# 确定使用的平台、群信息和用户信息
|
||||||
|
if virtual_config and virtual_config.enabled:
|
||||||
|
# 虚拟身份模式:使用真实平台身份
|
||||||
|
platform = virtual_config.platform or WEBUI_CHAT_PLATFORM
|
||||||
|
group_id = virtual_config.group_id or f"{VIRTUAL_GROUP_ID_PREFIX}{uuid.uuid4().hex[:8]}"
|
||||||
|
group_name = virtual_config.group_name or "WebUI虚拟群聊"
|
||||||
|
actual_user_id = virtual_config.user_id or user_id
|
||||||
|
actual_user_name = virtual_config.user_nickname or user_name
|
||||||
|
else:
|
||||||
|
# 标准 WebUI 模式
|
||||||
|
platform = WEBUI_CHAT_PLATFORM
|
||||||
|
group_id = WEBUI_CHAT_GROUP_ID
|
||||||
|
group_name = "WebUI本地聊天室"
|
||||||
|
actual_user_id = user_id
|
||||||
|
actual_user_name = user_name
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"message_info": {
|
"message_info": {
|
||||||
"platform": WEBUI_CHAT_PLATFORM,
|
"platform": platform,
|
||||||
"message_id": message_id,
|
"message_id": message_id,
|
||||||
"time": time.time(),
|
"time": time.time(),
|
||||||
"group_info": {
|
"group_info": {
|
||||||
"group_id": WEBUI_CHAT_GROUP_ID,
|
"group_id": group_id,
|
||||||
"group_name": "WebUI本地聊天室",
|
"group_name": group_name,
|
||||||
"platform": WEBUI_CHAT_PLATFORM,
|
"platform": platform,
|
||||||
},
|
},
|
||||||
"user_info": {
|
"user_info": {
|
||||||
"user_id": user_id,
|
"user_id": actual_user_id,
|
||||||
"user_nickname": user_name,
|
"user_nickname": actual_user_name,
|
||||||
"user_cardname": user_name,
|
"user_cardname": actual_user_name,
|
||||||
"platform": WEBUI_CHAT_PLATFORM,
|
"platform": platform,
|
||||||
},
|
},
|
||||||
"additional_config": {
|
"additional_config": {
|
||||||
"at_bot": is_at_bot,
|
"at_bot": is_at_bot,
|
||||||
|
|
@ -180,12 +255,15 @@ def create_message_data(
|
||||||
async def get_chat_history(
|
async def get_chat_history(
|
||||||
limit: int = Query(default=50, ge=1, le=200),
|
limit: int = Query(default=50, ge=1, le=200),
|
||||||
user_id: Optional[str] = Query(default=None), # 保留参数兼容性,但不用于过滤
|
user_id: Optional[str] = Query(default=None), # 保留参数兼容性,但不用于过滤
|
||||||
|
group_id: Optional[str] = Query(default=None), # 可选:指定群 ID 获取历史
|
||||||
):
|
):
|
||||||
"""获取聊天历史记录
|
"""获取聊天历史记录
|
||||||
|
|
||||||
所有 WebUI 用户共享同一个聊天室,因此返回所有历史记录
|
所有 WebUI 用户共享同一个聊天室,因此返回所有历史记录
|
||||||
|
如果指定了 group_id,则获取该虚拟群的历史记录
|
||||||
"""
|
"""
|
||||||
history = chat_history.get_history(limit)
|
target_group_id = group_id if group_id else WEBUI_CHAT_GROUP_ID
|
||||||
|
history = chat_history.get_history(limit, target_group_id)
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"messages": history,
|
"messages": history,
|
||||||
|
|
@ -193,10 +271,92 @@ async def get_chat_history(
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/platforms")
|
||||||
|
async def get_available_platforms():
|
||||||
|
"""获取可用平台列表
|
||||||
|
|
||||||
|
从 PersonInfo 表中获取所有已知的平台
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
from peewee import fn
|
||||||
|
|
||||||
|
# 查询所有不同的平台
|
||||||
|
platforms = (
|
||||||
|
PersonInfo.select(PersonInfo.platform, fn.COUNT(PersonInfo.id).alias("count"))
|
||||||
|
.group_by(PersonInfo.platform)
|
||||||
|
.order_by(fn.COUNT(PersonInfo.id).desc())
|
||||||
|
)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for p in platforms:
|
||||||
|
if p.platform: # 排除空平台
|
||||||
|
result.append({"platform": p.platform, "count": p.count})
|
||||||
|
|
||||||
|
return {"success": True, "platforms": result}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取平台列表失败: {e}")
|
||||||
|
return {"success": False, "error": str(e), "platforms": []}
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/persons")
|
||||||
|
async def get_persons_by_platform(
|
||||||
|
platform: str = Query(..., description="平台名称"),
|
||||||
|
search: Optional[str] = Query(default=None, description="搜索关键词"),
|
||||||
|
limit: int = Query(default=50, ge=1, le=200),
|
||||||
|
):
|
||||||
|
"""获取指定平台的用户列表
|
||||||
|
|
||||||
|
Args:
|
||||||
|
platform: 平台名称(如 qq, discord 等)
|
||||||
|
search: 搜索关键词(匹配昵称、用户名、user_id)
|
||||||
|
limit: 返回数量限制
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 构建查询
|
||||||
|
query = PersonInfo.select().where(PersonInfo.platform == platform)
|
||||||
|
|
||||||
|
# 搜索过滤
|
||||||
|
if search:
|
||||||
|
query = query.where(
|
||||||
|
(PersonInfo.person_name.contains(search))
|
||||||
|
| (PersonInfo.nickname.contains(search))
|
||||||
|
| (PersonInfo.user_id.contains(search))
|
||||||
|
)
|
||||||
|
|
||||||
|
# 按最后交互时间排序,优先显示活跃用户
|
||||||
|
from peewee import Case
|
||||||
|
|
||||||
|
query = query.order_by(Case(None, [(PersonInfo.last_know.is_null(), 1)], 0), PersonInfo.last_know.desc())
|
||||||
|
query = query.limit(limit)
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for person in query:
|
||||||
|
result.append(
|
||||||
|
{
|
||||||
|
"person_id": person.person_id,
|
||||||
|
"user_id": person.user_id,
|
||||||
|
"person_name": person.person_name,
|
||||||
|
"nickname": person.nickname,
|
||||||
|
"is_known": person.is_known,
|
||||||
|
"platform": person.platform,
|
||||||
|
"display_name": person.person_name or person.nickname or person.user_id,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return {"success": True, "persons": result, "total": len(result)}
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"获取用户列表失败: {e}")
|
||||||
|
return {"success": False, "error": str(e), "persons": []}
|
||||||
|
|
||||||
|
|
||||||
@router.delete("/history")
|
@router.delete("/history")
|
||||||
async def clear_chat_history():
|
async def clear_chat_history(group_id: Optional[str] = Query(default=None)):
|
||||||
"""清空聊天历史记录"""
|
"""清空聊天历史记录
|
||||||
deleted = chat_history.clear_history()
|
|
||||||
|
Args:
|
||||||
|
group_id: 可选,指定要清空的群 ID,默认清空 WebUI 默认聊天室
|
||||||
|
"""
|
||||||
|
deleted = chat_history.clear_history(group_id)
|
||||||
return {
|
return {
|
||||||
"success": True,
|
"success": True,
|
||||||
"message": f"已清空 {deleted} 条聊天记录",
|
"message": f"已清空 {deleted} 条聊天记录",
|
||||||
|
|
@ -208,12 +368,22 @@ async def websocket_chat(
|
||||||
websocket: WebSocket,
|
websocket: WebSocket,
|
||||||
user_id: Optional[str] = Query(default=None),
|
user_id: Optional[str] = Query(default=None),
|
||||||
user_name: Optional[str] = Query(default="WebUI用户"),
|
user_name: Optional[str] = Query(default="WebUI用户"),
|
||||||
|
platform: Optional[str] = Query(default=None),
|
||||||
|
person_id: Optional[str] = Query(default=None),
|
||||||
|
group_name: Optional[str] = Query(default=None),
|
||||||
|
group_id: Optional[str] = Query(default=None), # 前端传递的稳定 group_id
|
||||||
):
|
):
|
||||||
"""WebSocket 聊天端点
|
"""WebSocket 聊天端点
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
user_id: 用户唯一标识(由前端生成并持久化)
|
user_id: 用户唯一标识(由前端生成并持久化)
|
||||||
user_name: 用户显示昵称(可修改)
|
user_name: 用户显示昵称(可修改)
|
||||||
|
platform: 虚拟身份模式的平台(可选)
|
||||||
|
person_id: 虚拟身份模式的用户 person_id(可选)
|
||||||
|
group_name: 虚拟身份模式的群名(可选)
|
||||||
|
group_id: 虚拟身份模式的群 ID(可选,由前端生成并持久化)
|
||||||
|
|
||||||
|
虚拟身份模式可通过 URL 参数直接配置,或通过消息中的 set_virtual_identity 配置
|
||||||
"""
|
"""
|
||||||
# 生成会话 ID(每次连接都是新的)
|
# 生成会话 ID(每次连接都是新的)
|
||||||
session_id = str(uuid.uuid4())
|
session_id = str(uuid.uuid4())
|
||||||
|
|
@ -225,23 +395,60 @@ async def websocket_chat(
|
||||||
# 确保 user_id 有正确的前缀
|
# 确保 user_id 有正确的前缀
|
||||||
user_id = f"{WEBUI_USER_ID_PREFIX}{user_id}"
|
user_id = f"{WEBUI_USER_ID_PREFIX}{user_id}"
|
||||||
|
|
||||||
|
# 当前会话的虚拟身份配置(可通过消息动态更新)
|
||||||
|
current_virtual_config: Optional[VirtualIdentityConfig] = None
|
||||||
|
|
||||||
|
# 如果 URL 参数中提供了虚拟身份信息,自动配置
|
||||||
|
if platform and person_id:
|
||||||
|
try:
|
||||||
|
person = PersonInfo.get_or_none(PersonInfo.person_id == person_id)
|
||||||
|
if person:
|
||||||
|
# 使用前端传递的 group_id,如果没有则生成一个稳定的
|
||||||
|
virtual_group_id = group_id or f"{VIRTUAL_GROUP_ID_PREFIX}{platform}_{person.user_id}"
|
||||||
|
current_virtual_config = VirtualIdentityConfig(
|
||||||
|
enabled=True,
|
||||||
|
platform=person.platform,
|
||||||
|
person_id=person.person_id,
|
||||||
|
user_id=person.user_id,
|
||||||
|
user_nickname=person.person_name or person.nickname or person.user_id,
|
||||||
|
group_id=virtual_group_id,
|
||||||
|
group_name=group_name or "WebUI虚拟群聊",
|
||||||
|
)
|
||||||
|
logger.info(f"虚拟身份模式已通过 URL 参数激活: {current_virtual_config.user_nickname} @ {current_virtual_config.platform}, group_id={virtual_group_id}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"通过 URL 参数配置虚拟身份失败: {e}")
|
||||||
|
|
||||||
await chat_manager.connect(websocket, session_id, user_id)
|
await chat_manager.connect(websocket, session_id, user_id)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# 发送会话信息(包含用户 ID,前端需要保存)
|
# 构建会话信息
|
||||||
await chat_manager.send_message(
|
session_info_data = {
|
||||||
session_id,
|
"type": "session_info",
|
||||||
{
|
"session_id": session_id,
|
||||||
"type": "session_info",
|
"user_id": user_id,
|
||||||
"session_id": session_id,
|
"user_name": user_name,
|
||||||
"user_id": user_id,
|
"bot_name": global_config.bot.nickname,
|
||||||
"user_name": user_name,
|
}
|
||||||
"bot_name": global_config.bot.nickname,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
# 发送历史记录
|
# 如果有虚拟身份配置,添加到会话信息中
|
||||||
history = chat_history.get_history(50)
|
if current_virtual_config and current_virtual_config.enabled:
|
||||||
|
session_info_data["virtual_mode"] = True
|
||||||
|
session_info_data["group_id"] = current_virtual_config.group_id
|
||||||
|
session_info_data["virtual_identity"] = {
|
||||||
|
"platform": current_virtual_config.platform,
|
||||||
|
"user_id": current_virtual_config.user_id,
|
||||||
|
"user_nickname": current_virtual_config.user_nickname,
|
||||||
|
"group_name": current_virtual_config.group_name,
|
||||||
|
}
|
||||||
|
|
||||||
|
# 发送会话信息(包含用户 ID,前端需要保存)
|
||||||
|
await chat_manager.send_message(session_id, session_info_data)
|
||||||
|
|
||||||
|
# 发送历史记录(根据模式选择不同的群)
|
||||||
|
if current_virtual_config and current_virtual_config.enabled:
|
||||||
|
history = chat_history.get_history(50, current_virtual_config.group_id)
|
||||||
|
else:
|
||||||
|
history = chat_history.get_history(50)
|
||||||
if history:
|
if history:
|
||||||
await chat_manager.send_message(
|
await chat_manager.send_message(
|
||||||
session_id,
|
session_id,
|
||||||
|
|
@ -252,11 +459,16 @@ async def websocket_chat(
|
||||||
)
|
)
|
||||||
|
|
||||||
# 发送欢迎消息(不保存到历史)
|
# 发送欢迎消息(不保存到历史)
|
||||||
|
if current_virtual_config and current_virtual_config.enabled:
|
||||||
|
welcome_msg = f"已以 {current_virtual_config.user_nickname} 的身份连接到「{current_virtual_config.group_name}」,开始与 {global_config.bot.nickname} 对话吧!"
|
||||||
|
else:
|
||||||
|
welcome_msg = f"已连接到本地聊天室,可以开始与 {global_config.bot.nickname} 对话了!"
|
||||||
|
|
||||||
await chat_manager.send_message(
|
await chat_manager.send_message(
|
||||||
session_id,
|
session_id,
|
||||||
{
|
{
|
||||||
"type": "system",
|
"type": "system",
|
||||||
"content": f"已连接到本地聊天室,可以开始与 {global_config.bot.nickname} 对话了!",
|
"content": welcome_msg,
|
||||||
"timestamp": time.time(),
|
"timestamp": time.time(),
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -275,6 +487,14 @@ async def websocket_chat(
|
||||||
message_id = str(uuid.uuid4())
|
message_id = str(uuid.uuid4())
|
||||||
timestamp = time.time()
|
timestamp = time.time()
|
||||||
|
|
||||||
|
# 确定发送者信息(根据是否使用虚拟身份)
|
||||||
|
if current_virtual_config and current_virtual_config.enabled:
|
||||||
|
sender_name = current_virtual_config.user_nickname or current_user_name
|
||||||
|
sender_user_id = current_virtual_config.user_id or user_id
|
||||||
|
else:
|
||||||
|
sender_name = current_user_name
|
||||||
|
sender_user_id = user_id
|
||||||
|
|
||||||
# 广播用户消息给所有连接(包括发送者)
|
# 广播用户消息给所有连接(包括发送者)
|
||||||
# 注意:用户消息会在 chat_bot.message_process 中自动保存到数据库
|
# 注意:用户消息会在 chat_bot.message_process 中自动保存到数据库
|
||||||
await chat_manager.broadcast(
|
await chat_manager.broadcast(
|
||||||
|
|
@ -284,10 +504,11 @@ async def websocket_chat(
|
||||||
"message_id": message_id,
|
"message_id": message_id,
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
"sender": {
|
"sender": {
|
||||||
"name": current_user_name,
|
"name": sender_name,
|
||||||
"user_id": user_id,
|
"user_id": sender_user_id,
|
||||||
"is_bot": False,
|
"is_bot": False,
|
||||||
},
|
},
|
||||||
|
"virtual_mode": current_virtual_config.enabled if current_virtual_config else False,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -298,6 +519,7 @@ async def websocket_chat(
|
||||||
user_name=current_user_name,
|
user_name=current_user_name,
|
||||||
message_id=message_id,
|
message_id=message_id,
|
||||||
is_at_bot=True,
|
is_at_bot=True,
|
||||||
|
virtual_config=current_virtual_config,
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -352,6 +574,133 @@ async def websocket_chat(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
elif data.get("type") == "set_virtual_identity":
|
||||||
|
# 设置或更新虚拟身份配置
|
||||||
|
virtual_data = data.get("config", {})
|
||||||
|
if virtual_data.get("enabled"):
|
||||||
|
# 验证必要字段
|
||||||
|
if not virtual_data.get("platform") or not virtual_data.get("person_id"):
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"content": "虚拟身份配置缺少必要字段: platform 和 person_id",
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 获取用户信息
|
||||||
|
try:
|
||||||
|
person = PersonInfo.get_or_none(PersonInfo.person_id == virtual_data.get("person_id"))
|
||||||
|
if not person:
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"content": f"找不到用户: {virtual_data.get('person_id')}",
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 生成虚拟群 ID
|
||||||
|
custom_group_id = virtual_data.get("group_id")
|
||||||
|
if custom_group_id:
|
||||||
|
group_id = f"{VIRTUAL_GROUP_ID_PREFIX}{custom_group_id}"
|
||||||
|
else:
|
||||||
|
group_id = f"{VIRTUAL_GROUP_ID_PREFIX}{session_id[:8]}"
|
||||||
|
|
||||||
|
current_virtual_config = VirtualIdentityConfig(
|
||||||
|
enabled=True,
|
||||||
|
platform=person.platform,
|
||||||
|
person_id=person.person_id,
|
||||||
|
user_id=person.user_id,
|
||||||
|
user_nickname=person.person_name or person.nickname or person.user_id,
|
||||||
|
group_id=group_id,
|
||||||
|
group_name=virtual_data.get("group_name", "WebUI虚拟群聊"),
|
||||||
|
)
|
||||||
|
|
||||||
|
# 发送虚拟身份已激活的消息
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "virtual_identity_set",
|
||||||
|
"config": {
|
||||||
|
"enabled": True,
|
||||||
|
"platform": current_virtual_config.platform,
|
||||||
|
"user_id": current_virtual_config.user_id,
|
||||||
|
"user_nickname": current_virtual_config.user_nickname,
|
||||||
|
"group_id": current_virtual_config.group_id,
|
||||||
|
"group_name": current_virtual_config.group_name,
|
||||||
|
},
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# 加载虚拟群的历史记录
|
||||||
|
virtual_history = chat_history.get_history(50, current_virtual_config.group_id)
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "history",
|
||||||
|
"messages": virtual_history,
|
||||||
|
"group_id": current_virtual_config.group_id,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# 发送系统消息
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "system",
|
||||||
|
"content": f"已切换到虚拟身份模式:以 {current_virtual_config.user_nickname} 的身份在「{current_virtual_config.group_name}」与 {global_config.bot.nickname} 对话",
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"设置虚拟身份失败: {e}")
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "error",
|
||||||
|
"content": f"设置虚拟身份失败: {str(e)}",
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# 禁用虚拟身份模式
|
||||||
|
current_virtual_config = None
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "virtual_identity_set",
|
||||||
|
"config": {"enabled": False},
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
# 重新加载默认聊天室历史
|
||||||
|
default_history = chat_history.get_history(50, WEBUI_CHAT_GROUP_ID)
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "history",
|
||||||
|
"messages": default_history,
|
||||||
|
"group_id": WEBUI_CHAT_GROUP_ID,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
await chat_manager.send_message(
|
||||||
|
session_id,
|
||||||
|
{
|
||||||
|
"type": "system",
|
||||||
|
"content": "已切换回 WebUI 独立用户模式",
|
||||||
|
"timestamp": time.time(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
except WebSocketDisconnect:
|
except WebSocketDisconnect:
|
||||||
logger.info(f"WebSocket 断开: session={session_id}, user={user_id}")
|
logger.info(f"WebSocket 断开: session={session_id}, user={user_id}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue