Send.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. const API = require('../../../lib/API')
  2. const { BaseStdResponse } = require('../../../BaseStdResponse')
  3. const AccessControl = require('../../../lib/AccessControl')
  4. const db = require('../../../plugin/DataBase/db')
  5. const OneBotV11 = require('../../../plugin/OneBot/OneBotV11')
  6. const { ConversationService } = require('../../../lib/AIChat/ConversationService')
  7. const { checkQuota, consumeQuota } = require('../../../lib/AIChat/QuotaService')
  8. class SendMessage extends API {
  9. constructor() {
  10. super()
  11. this.setPath('/AIChat/Message')
  12. this.setMethod('POST')
  13. }
  14. async onRequest(req, res) {
  15. const { uuid, session, conversation_id, content, images = [] } = req.body
  16. if ([uuid, session].some(v => v === '' || v === null || v === undefined)) {
  17. return res.json({ ...BaseStdResponse.MISSING_PARAMETER })
  18. }
  19. if (!await AccessControl.checkSession(uuid, session)) {
  20. return res.status(401).json({ ...BaseStdResponse.ACCESS_DENIED })
  21. }
  22. const cleanContent = String(content || '').trim()
  23. const cleanImages = Array.isArray(images) ? images.filter(Boolean) : []
  24. if (!cleanContent && cleanImages.length === 0) {
  25. return res.json({ ...BaseStdResponse.MISSING_PARAMETER, msg: '请输入消息或上传图片' })
  26. }
  27. if (conversation_id) {
  28. const rows = await db.query(
  29. 'SELECT channel FROM ai_chat_conversation WHERE id = ? AND create_user = ? AND state = 1 LIMIT 1',
  30. [conversation_id, uuid]
  31. )
  32. if (!rows || rows.length !== 1) return res.json({ ...BaseStdResponse.ERR, msg: '会话不存在或无权发送' })
  33. if (rows[0].channel === 'wechat') return res.json({ ...BaseStdResponse.ERR, msg: '微信会话仅支持查看,请在微信内继续对话' })
  34. }
  35. const quotaCheck = await checkQuota({ uuid, channel: 'web' })
  36. if (!quotaCheck.allowed) {
  37. return res.json({ ...BaseStdResponse.ERR, msg: quotaCheck.message || '今日与小妍助理聊天次数已达上限,请明天再试' })
  38. }
  39. const saved = await ConversationService.addUserMessage({
  40. uuid,
  41. conversationId: conversation_id,
  42. content: cleanContent,
  43. images: cleanImages,
  44. channel: 'web'
  45. })
  46. if (!saved) return res.json({ ...BaseStdResponse.ERR, msg: '会话不存在或无权发送' })
  47. if (saved.missingContent) return res.json({ ...BaseStdResponse.MISSING_PARAMETER, msg: '请输入消息或上传图片' })
  48. if (saved.readonly) return res.json({ ...BaseStdResponse.ERR, msg: '微信会话仅支持查看,请在微信内继续对话' })
  49. const quota = await consumeQuota({ uuid, channel: 'web' })
  50. if (!quota.allowed) {
  51. await ConversationService.addSystemMessage({
  52. conversationId: saved.conversationId,
  53. content: quota.message || '今日与小妍助理聊天次数已达上限,请明天再试',
  54. status: 'error',
  55. errorMsg: 'quota exceeded',
  56. channel: 'web'
  57. })
  58. return res.json({ ...BaseStdResponse.ERR, msg: quota.message || '今日与小妍助理聊天次数已达上限,请明天再试' })
  59. }
  60. res.json({
  61. ...BaseStdResponse.OK,
  62. data: {
  63. message_id: saved.messageId,
  64. conversation_id: saved.conversationId,
  65. conversation_no: saved.conversationNo,
  66. conversation: saved.conversation,
  67. message: saved.message,
  68. quota: {
  69. limit: quota.limit,
  70. used: quota.used,
  71. remaining: quota.remaining,
  72. channel: 'web',
  73. vip: quota.vip
  74. }
  75. }
  76. })
  77. try {
  78. await OneBotV11.sendAiChatMessage({
  79. conversationId: saved.conversationId,
  80. conversationNo: saved.conversationNo,
  81. senderUuid: uuid,
  82. content: saved.content,
  83. images: saved.images,
  84. channel: 'web'
  85. })
  86. } catch (err) {
  87. this.logger.error(`AIChat OneBot forward failed: ${err.stack || err}`)
  88. await ConversationService.addSystemMessage({
  89. conversationId: saved.conversationId,
  90. content: '消息已保存,但暂时无法连接小妍助理,请稍后再试。',
  91. status: 'error',
  92. errorMsg: err.message || 'OneBot send failed',
  93. channel: 'web'
  94. })
  95. }
  96. }
  97. }
  98. module.exports.SendMessage = SendMessage