Compare commits

..

5 Commits

Author SHA1 Message Date
a07de626ad fix: 修复AI重复调用问题,统一消息处理流程
- _process_message 只保存用户消息并通知
- 新增 generate_ai_response action 统一处理AI回复
- 避免重复调用AI和重复发送消息
2026-04-12 00:34:30 +08:00
f3636dddd6 fix: 添加 event_id 去重机制,防止消息重复处理
- 添加 processed_events 集合追踪已处理消息
- _process_message 检查 event_id 是否已处理
- 限制集合大小防止内存过大
2026-04-12 00:26:04 +08:00
66fa5db3d7 fix: 移除前端 [Matrix] 来源标记显示 2026-04-12 00:16:16 +08:00
2c007c4801 fix: 移除消息来源标记[网页] 2026-04-12 00:12:57 +08:00
027830b0d6 fix: Matrix用户消息同步到网页端
- 新增 user_message action 回调
- handle_matrix_message 处理 user_message 通知
- 网页端 WebSocket 接收用户消息
2026-04-12 00:07:19 +08:00
20 changed files with 142 additions and 34 deletions

Binary file not shown.

86
main.py
View File

@@ -258,7 +258,7 @@ async def websocket_endpoint(websocket: WebSocket, user_id: str, db: Session = D
# 同步到Matrix如果有房间
if matrix_bot.is_running and matrix_bot.last_room_id:
await matrix_bot.send_message(matrix_bot.last_room_id, f"[网页] {message}")
await matrix_bot.send_message(matrix_bot.last_room_id, message)
# 获取对话历史
history = conv_service.get_conversation_history(conversation_id, limit=20)
@@ -422,7 +422,7 @@ async def update_config(data: dict, db: Session = Depends(get_db)):
# ==================== Matrix消息处理回调 ====================
async def handle_matrix_message(action: str, conversation_id: str = None, user_message: str = None, room_id: str = None, message_id: int = None):
async def handle_matrix_message(action: str, conversation_id: str = None, user_message: str = None, room_id: str = None, message_id: int = None, sender: str = None):
"""处理从Matrix收到的消息"""
if action == "new_conversation":
@@ -433,6 +433,88 @@ async def handle_matrix_message(action: str, conversation_id: str = None, user_m
})
return
if action == "user_message":
# 用户消息 - 同步到网页端
db = SessionLocal()
try:
conv_service = ConversationService(db)
conversation = conv_service.get_conversation(conversation_id)
if conversation:
# 发送用户消息通知到网页端
await manager.send_to_user(MAIN_USER_ID, {
"type": "user_message",
"conversation_id": conversation_id,
"message": {
"id": message_id,
"role": "user",
"content": user_message,
"source": "matrix",
"sender": sender,
"created_at": datetime.now().isoformat()
}
})
finally:
db.close()
return
if action == "generate_ai_response":
# 生成AI回复并同步
db = SessionLocal()
try:
conv_service = ConversationService(db)
conversation = conv_service.get_conversation(conversation_id)
if conversation:
# 发送"正在输入"状态
try:
await matrix_bot.client.room_typing(room_id, typing_state=True)
except:
pass
# 获取会话历史
history = conv_service.get_conversation_history(conversation_id, limit=20)
# 调用AI
ai_response = await ai_service.chat(history)
# 保存AI回复
assistant_msg = conv_service.add_message(
conversation_id=conversation.id,
role='assistant',
content=ai_response,
source='matrix'
)
# 发送到Matrix
await matrix_bot.send_message(room_id, ai_response)
# 关闭"正在输入"状态
try:
await matrix_bot.client.room_typing(room_id, typing_state=False)
except:
pass
# 同步到网页端WebSocket
await manager.send_to_user(MAIN_USER_ID, {
"type": "assistant_message",
"conversation_id": conversation_id,
"message": {
"id": assistant_msg.id,
"role": "assistant",
"content": ai_response,
"source": "matrix",
"created_at": assistant_msg.created_at.isoformat()
}
})
logger.info(f"Matrix AI回复已发送: {ai_response[:50]}")
except Exception as e:
logger.error(f"生成AI回复失败: {e}")
await matrix_bot.send_message(room_id, f"处理消息时出错: {str(e)}")
finally:
db.close()
return
if action == "chat":
db = SessionLocal()
try:

View File

@@ -0,0 +1,7 @@
@tester:matrix.tphai.com AJFVRTHLJY matrix-ed25519 4mRjLhM8xbwjkwQP2T/iB3UZJoaADgP6cCVUiB8AtSk
@tester:matrix.tphai.com GVSFGGYNJL matrix-ed25519 8qV2own4G3m2nki+izFDBOrAxtbGl8RoneM3qUPkThU
@tester:matrix.tphai.com IMEQIQPXTR matrix-ed25519 6Yd4lmhP6jdkkNvh1rIw6TRK331ZUyiAt5G5hPeYqSE
@tester:matrix.tphai.com MIPPYHRVAS matrix-ed25519 s8Ol56sxLCjCOi0Gkv/Kj7LqVMp/8ZmuAJ6QA1rUi7o
@tester:matrix.tphai.com UKJGJYQQLT matrix-ed25519 opC9rhsz1nzrvQqNWMKTF5FxWIGuHTDfixx+q/Y8ea0
@tester:matrix.tphai.com UPMZGRLESG matrix-ed25519 86c6XPCIYHgesq83C2k5xhXNa0EYMnqTq4jFrTwJX8I
@huangzhuang_bro:matrix.tphai.com BQHGFLQEPR matrix-ed25519 IrEHmvqotfHKLyx1JRJp4RthUVyBT8qQX72qBifRRyQ

View File

@@ -0,0 +1,8 @@
@tester:matrix.tphai.com AJFVRTHLJY matrix-ed25519 4mRjLhM8xbwjkwQP2T/iB3UZJoaADgP6cCVUiB8AtSk
@tester:matrix.tphai.com BDTRXIGPBE matrix-ed25519 gjQNtLEpIEYCjmzUx5ma91G498n4UADh84KUmiReJUM
@tester:matrix.tphai.com GVSFGGYNJL matrix-ed25519 8qV2own4G3m2nki+izFDBOrAxtbGl8RoneM3qUPkThU
@tester:matrix.tphai.com IMEQIQPXTR matrix-ed25519 6Yd4lmhP6jdkkNvh1rIw6TRK331ZUyiAt5G5hPeYqSE
@tester:matrix.tphai.com MIPPYHRVAS matrix-ed25519 s8Ol56sxLCjCOi0Gkv/Kj7LqVMp/8ZmuAJ6QA1rUi7o
@tester:matrix.tphai.com UKJGJYQQLT matrix-ed25519 opC9rhsz1nzrvQqNWMKTF5FxWIGuHTDfixx+q/Y8ea0
@tester:matrix.tphai.com UPMZGRLESG matrix-ed25519 86c6XPCIYHgesq83C2k5xhXNa0EYMnqTq4jFrTwJX8I
@huangzhuang_bro:matrix.tphai.com BQHGFLQEPR matrix-ed25519 IrEHmvqotfHKLyx1JRJp4RthUVyBT8qQX72qBifRRyQ

View File

@@ -0,0 +1,4 @@
@huangzhuang_bro:matrix.tphai.com BQHGFLQEPR matrix-ed25519 IrEHmvqotfHKLyx1JRJp4RthUVyBT8qQX72qBifRRyQ
@tester:matrix.tphai.com AJFVRTHLJY matrix-ed25519 4mRjLhM8xbwjkwQP2T/iB3UZJoaADgP6cCVUiB8AtSk
@tester:matrix.tphai.com GVSFGGYNJL matrix-ed25519 8qV2own4G3m2nki+izFDBOrAxtbGl8RoneM3qUPkThU
@tester:matrix.tphai.com IMEQIQPXTR matrix-ed25519 6Yd4lmhP6jdkkNvh1rIw6TRK331ZUyiAt5G5hPeYqSE

View File

@@ -0,0 +1,5 @@
@tester:matrix.tphai.com AJFVRTHLJY matrix-ed25519 4mRjLhM8xbwjkwQP2T/iB3UZJoaADgP6cCVUiB8AtSk
@tester:matrix.tphai.com GVSFGGYNJL matrix-ed25519 8qV2own4G3m2nki+izFDBOrAxtbGl8RoneM3qUPkThU
@tester:matrix.tphai.com IMEQIQPXTR matrix-ed25519 6Yd4lmhP6jdkkNvh1rIw6TRK331ZUyiAt5G5hPeYqSE
@tester:matrix.tphai.com MIPPYHRVAS matrix-ed25519 s8Ol56sxLCjCOi0Gkv/Kj7LqVMp/8ZmuAJ6QA1rUi7o
@huangzhuang_bro:matrix.tphai.com BQHGFLQEPR matrix-ed25519 IrEHmvqotfHKLyx1JRJp4RthUVyBT8qQX72qBifRRyQ

View File

@@ -0,0 +1,6 @@
@huangzhuang_bro:matrix.tphai.com BQHGFLQEPR matrix-ed25519 IrEHmvqotfHKLyx1JRJp4RthUVyBT8qQX72qBifRRyQ
@tester:matrix.tphai.com AJFVRTHLJY matrix-ed25519 4mRjLhM8xbwjkwQP2T/iB3UZJoaADgP6cCVUiB8AtSk
@tester:matrix.tphai.com GVSFGGYNJL matrix-ed25519 8qV2own4G3m2nki+izFDBOrAxtbGl8RoneM3qUPkThU
@tester:matrix.tphai.com IMEQIQPXTR matrix-ed25519 6Yd4lmhP6jdkkNvh1rIw6TRK331ZUyiAt5G5hPeYqSE
@tester:matrix.tphai.com MIPPYHRVAS matrix-ed25519 s8Ol56sxLCjCOi0Gkv/Kj7LqVMp/8ZmuAJ6QA1rUi7o
@tester:matrix.tphai.com UKJGJYQQLT matrix-ed25519 opC9rhsz1nzrvQqNWMKTF5FxWIGuHTDfixx+q/Y8ea0

View File

@@ -26,6 +26,7 @@ class MatrixBot:
self.is_running: bool = False
self.on_message_callback: Optional[Callable] = None
self.last_room_id: str = ""
self.processed_events: set = set() # 已处理的事件ID防止重复处理
async def init_from_config(self):
"""从数据库配置初始化"""
@@ -171,6 +172,20 @@ class MatrixBot:
async def _process_message(self, room, sender, message_text, event_id):
"""处理消息的核心逻辑"""
# 检查是否已处理过此事件(防止重复处理)
if event_id in self.processed_events:
logger.debug(f"忽略已处理的事件: {event_id}")
return
# 标记为已处理
self.processed_events.add(event_id)
logger.info(f"处理消息: event_id={event_id}")
# 限制集合大小保留最近的100个
if len(self.processed_events) > 100:
# 清理旧的一半
self.processed_events = set(list(self.processed_events)[-50:])
# 忽略自己发送的消息
if sender == self.user_id:
logger.debug(f"忽略自己发送的消息: {sender}")
@@ -224,41 +239,23 @@ class MatrixBot:
}
)
# 发送"正在输入"状态
try:
await self.client.room_typing(room.room_id, typing_state=True)
except Exception as e:
logger.warning(f"发送输入状态失败: {e}")
# 获取AI回复 - 使用字典格式
messages = conv_service.get_conversation_history(conversation.conversation_id)
ai_response = await ai_service.chat(messages)
# 保存AI回复
conv_service.add_message(
conversation_id=conversation.id,
role='assistant',
content=ai_response,
source='matrix'
)
# 发送回复
await self.send_message(room.room_id, ai_response)
# 关闭"正在输入"状态
try:
await self.client.room_typing(room.room_id, typing_state=False)
except Exception as e:
logger.warning(f"关闭输入状态失败: {e}")
# 调用回调(用于网页同步)
# 立即通知网页端有新用户消息(通过回调)
if self.on_message_callback:
await self.on_message_callback(
action="chat",
action="user_message",
conversation_id=conversation.conversation_id,
user_message=message_text,
room_id=room.room_id,
message_id=user_msg.id
message_id=user_msg.id,
sender=sender
)
# 通知回调处理 AI 回复(不再在这里直接调用 AI
if self.on_message_callback:
await self.on_message_callback(
action="generate_ai_response",
conversation_id=conversation.conversation_id,
room_id=room.room_id
)
logger.info(f"Matrix回复已发送: {ai_response[:50]}")

View File

@@ -467,11 +467,10 @@
messageDiv.className = `message ${role}`;
const avatar = role === 'user' ? '👤' : '🤖';
const sourceLabel = source === 'matrix' ? ' [Matrix]' : '';
messageDiv.innerHTML = `
<div class="message-avatar">${avatar}</div>
<div class="message-content">${formatContent(content)}${sourceLabel}</div>
<div class="message-content">${formatContent(content)}</div>
`;
container.appendChild(messageDiv);