97 lines
4.4 KiB
JavaScript
97 lines
4.4 KiB
JavaScript
// 消息路由 + AI 分析
|
||
const express = require('express');
|
||
const { authMiddleware } = require('../middleware/auth');
|
||
const { broadcastToRoomExcludeSelf, broadcastToRoom } = require('../ws');
|
||
const { buildSystemPrompt } = require('../ai/prompt');
|
||
const { validate, normalize } = require('../ai/validator');
|
||
const { callDeepSeek } = require('../ai/client');
|
||
const { getHistory, addToHistory, hasPendingAction, handleAIResult, executePendingAction } = require('../handlers');
|
||
const db = require('../db');
|
||
const { timestamp } = require('../utils');
|
||
|
||
const router = express.Router();
|
||
|
||
router.get('/:roomId/messages', authMiddleware, (req, res) => {
|
||
const msgs = db.prepare('SELECT * FROM messages WHERE room_id = ? ORDER BY id ASC').all(req.params.roomId);
|
||
res.json(msgs);
|
||
});
|
||
|
||
router.post('/:roomId/messages', authMiddleware, async (req, res) => {
|
||
const { text, attachments } = req.body;
|
||
console.log('📨 收到消息, text:', (text||'').substring(0,30), 'attachments:', JSON.stringify(attachments));
|
||
const roomId = req.params.roomId;
|
||
const username = req.user.username;
|
||
const now = timestamp();
|
||
|
||
const result = db.prepare('INSERT INTO messages (room_id, user, text, attachments, timestamp) VALUES (?,?,?,?,?)').run(
|
||
roomId, username, text || '', attachments ? JSON.stringify(attachments) : null, now
|
||
);
|
||
const msg = { id: result.lastInsertRowid, room_id: roomId, user: username, text: text || '', attachments: attachments || [], timestamp: now };
|
||
|
||
const lastMsg = db.prepare('SELECT text, timestamp FROM messages WHERE room_id = ? ORDER BY id DESC LIMIT 1').get(roomId);
|
||
broadcastToRoomExcludeSelf(roomId, username, {
|
||
type: 'new_message', message: msg,
|
||
room_preview: { room_id: roomId, last_message: lastMsg ? lastMsg.text : '', last_time: lastMsg ? lastMsg.timestamp : '' }
|
||
});
|
||
addToHistory(roomId, 'user', `${username}: ${text || '图片'}`);
|
||
res.json(msg);
|
||
|
||
if (text && text.trim() === '确认' && hasPendingAction(roomId, username)) {
|
||
executePendingAction(roomId, username);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const history = getHistory(roomId);
|
||
const historyMessages = history.map(e => ({ role: e.role, content: e.content }));
|
||
const aiResponse = await analyzeWithDeepSeek(text || '', historyMessages, username, req.user.isAdmin, roomId);
|
||
if (aiResponse) {
|
||
var responses = Array.isArray(aiResponse) ? aiResponse : [aiResponse];
|
||
responses.forEach(function(r) {
|
||
if (r.action !== 'ignore') handleAIResult(r, roomId, username, text || '', attachments || []);
|
||
});
|
||
}
|
||
} catch (e) {
|
||
console.error('AI 分析失败:', e.message);
|
||
var errText = '❌ ' + (e.message || 'AI 处理失败,请稍后重试');
|
||
var errResult = db.prepare('INSERT INTO messages (room_id, user, text, timestamp) VALUES (?,?,?,?)').run(roomId, '小财', errText, timestamp());
|
||
var errMsg = { id: errResult.lastInsertRowid, room_id: roomId, user: '小财', text: errText, attachments: [], timestamp: timestamp() };
|
||
broadcastToRoom(roomId, { type: 'new_message', message: errMsg });
|
||
}
|
||
});
|
||
|
||
async function analyzeWithDeepSeek(text, historyMessages, username, isAdmin, roomId) {
|
||
var systemPrompt = buildSystemPrompt(username, isAdmin);
|
||
var messages = [
|
||
{ role: 'system', content: systemPrompt }
|
||
];
|
||
historyMessages.forEach(function(m) { messages.push(m); });
|
||
messages.push({ role: 'system', content: '当前服务器时间:' + new Date().toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai', hour12: false }) });
|
||
const content = await callDeepSeek(messages, username);
|
||
console.log('🤖 AI 返回:', content.substring(0, 200));
|
||
try {
|
||
var cleaned = content.replace(/```json|```/g, '').trim();
|
||
if (!cleaned) { console.warn('⚠️ AI 返回空内容'); return { action: 'ignore' }; }
|
||
var parsed = JSON.parse(cleaned);
|
||
// 支持批量:AI 可能返回数组
|
||
if (Array.isArray(parsed)) {
|
||
parsed.forEach(function(item, i) {
|
||
normalize(item);
|
||
var v = validate(item);
|
||
if (!v.valid) console.warn('⚠️ AI 返回[' + i + ']校验失败:', v.error);
|
||
});
|
||
} else {
|
||
normalize(parsed);
|
||
var v = validate(parsed);
|
||
if (!v.valid) console.warn('⚠️ AI 返回校验失败:', v.error);
|
||
}
|
||
return parsed;
|
||
} catch (e) {
|
||
// AI 返回非 JSON(如纯文本回复图片消息)→ 降级为 chat
|
||
console.warn('⚠️ AI 返回非 JSON,降级为 chat:', content.substring(0, 50));
|
||
return { action: 'chat', reply: content };
|
||
}
|
||
}
|
||
|
||
module.exports = router;
|