const API = require('../../../lib/API') const { BaseStdResponse } = require('../../../BaseStdResponse') const AccessControl = require('../../../lib/AccessControl') const db = require('../../../plugin/DataBase/db') const OneBotV11 = require('../../../plugin/OneBot/OneBotV11') const { ConversationService } = require('../../../lib/AIChat/ConversationService') const { checkQuota, consumeQuota } = require('../../../lib/AIChat/QuotaService') class SendMessage extends API { constructor() { super() this.setPath('/AIChat/Message') this.setMethod('POST') } async onRequest(req, res) { const { uuid, session, conversation_id, content, images = [] } = req.body if ([uuid, session].some(v => v === '' || v === null || v === undefined)) { return res.json({ ...BaseStdResponse.MISSING_PARAMETER }) } if (!await AccessControl.checkSession(uuid, session)) { return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED }) } const cleanContent = String(content || '').trim() const cleanImages = Array.isArray(images) ? images.filter(Boolean) : [] if (!cleanContent && cleanImages.length === 0) { return res.json({ ...BaseStdResponse.MISSING_PARAMETER, msg: '请输入消息或上传图片' }) } if (conversation_id) { const rows = await db.query( 'SELECT channel FROM ai_chat_conversation WHERE id = ? AND create_user = ? AND state = 1 LIMIT 1', [conversation_id, uuid] ) if (!rows || rows.length !== 1) return res.json({ ...BaseStdResponse.ERR, msg: '会话不存在或无权发送' }) if (rows[0].channel === 'wechat') return res.json({ ...BaseStdResponse.ERR, msg: '微信会话仅支持查看,请在微信内继续对话' }) } const quotaCheck = await checkQuota({ uuid, channel: 'web' }) if (!quotaCheck.allowed) { return res.json({ ...BaseStdResponse.ERR, msg: quotaCheck.message || '今日与小妍助理聊天次数已达上限,请明天再试' }) } const saved = await ConversationService.addUserMessage({ uuid, conversationId: conversation_id, content: cleanContent, images: cleanImages, channel: 'web' }) if (!saved) return res.json({ ...BaseStdResponse.ERR, msg: '会话不存在或无权发送' }) if (saved.missingContent) return res.json({ ...BaseStdResponse.MISSING_PARAMETER, msg: '请输入消息或上传图片' }) if (saved.readonly) return res.json({ ...BaseStdResponse.ERR, msg: '微信会话仅支持查看,请在微信内继续对话' }) const quota = await consumeQuota({ uuid, channel: 'web' }) if (!quota.allowed) { await ConversationService.addSystemMessage({ conversationId: saved.conversationId, content: quota.message || '今日与小妍助理聊天次数已达上限,请明天再试', status: 'error', errorMsg: 'quota exceeded', channel: 'web' }) return res.json({ ...BaseStdResponse.ERR, msg: quota.message || '今日与小妍助理聊天次数已达上限,请明天再试' }) } res.json({ ...BaseStdResponse.OK, data: { message_id: saved.messageId, conversation_id: saved.conversationId, conversation_no: saved.conversationNo, conversation: saved.conversation, message: saved.message, quota: { limit: quota.limit, used: quota.used, remaining: quota.remaining, channel: 'web', vip: quota.vip } } }) try { await OneBotV11.sendAiChatMessage({ conversationId: saved.conversationId, conversationNo: saved.conversationNo, senderUuid: uuid, content: saved.content, images: saved.images, channel: 'web' }) } catch (err) { this.logger.error(`AIChat OneBot forward failed: ${err.stack || err}`) await ConversationService.addSystemMessage({ conversationId: saved.conversationId, content: '消息已保存,但暂时无法连接小妍助理,请稍后再试。', status: 'error', errorMsg: err.message || 'OneBot send failed', channel: 'web' }) } } } module.exports.SendMessage = SendMessage