mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
1,067 lines (972 loc) • 29.9 kB
JavaScript
const Manager = require('mm_machine').Manager;
const Drive = require('./drive');
const OpenAI = require('openai');
/**
* Cmd指令类
* @augments {Manager}
* @class
*/
class Cmd extends Manager {
/**
* 配置参数
* @type {object}
*/
static config = {
// 模块名称
name: '',
// 模块标题
title: 'Cmd指令管理',
// 文件名
filename: 'cmd.json',
// 模板目录
tpl_dir: __dirname,
// 自定义目录,加载项目自定义资源
dir: './app'.fullname(),
/**
* 搜索模式 dir按目录搜索 | file按文件名搜索
* @type {string}
*/
search_way: 'file',
// 数据库表名
table: 'wechat_message',
// 主键
key: 'message_id',
// 缓存前缀
cache_prefix: 'cmd_',
// 聊天间隔时长,单位:秒,600秒为10分钟
interval: 600,
// 是否启用AI对话, 如果为空则不使用
ai: 'deepseek',
// AI API密钥
api_key: 'sk-0521b7b3e9494a5e844b66ec4787f89d',
// API端点
base_url: 'https://api.deepseek.com',
// 模型名称
model: 'deepseek-chat',
// 最大对话轮数
max_turns: 50,
// 最大令牌数
max_tokens: 2048,
// 温度参数
temperature: 0.8
};
/**
* 消息对象
* @type {object}
*/
static msg = {
// 消息ID
'message_id': 0,
// 结束状态 0-正常 1-结束
'end': 0,
// 阶段 1-9阶段
'stage': 1,
// 微信/公众号消息ID
'msgid': '',
// 更新时间
'update_time': '',
// 创建时间
'create_time': '',
// 群组
'group': '',
// 消息类型 text-文本 image-图片 voice-语音 video-视频 file-文件 location-位置 link-链接 event-事件 other-其他
'msg_type': 'text',
// 媒体ID,图片、语音、视频、文件
'media_id': '',
// 发信人姓名
'name': '',
// 一般情况下,1永久会话/群、2临时会话/群
'type': 1,
// 指令
'cmd': '',
// 应用ID
'appid': '',
// 发信人
'from_user': '',
// 收信人
'to_user': '',
// 机器人,是否机器人回复
'robot': 'system',
// 会话ID,用于判断是否是同一个会话
'chatid': '',
// 发信人头像
'avatar': '',
// 除指令外,过滤、抽取后的词,多个词用空格分隔
'keyword': '',
// 表单,用于记录用户已填参数,json格式字符串
'form': '',
// 聊天内容
'content': '',
// 消息传递数据,json格式字符串
'data': '',
// 备注信息
'note': ''
};
/**
* 构造函数
* @param {object} config 配置参数
* @param {object} parent 父级模块
*/
constructor(config, parent) {
super({ ...Cmd.config, ...config }, parent);
this.list_before = [];
this.list_check = [];
this.list_main = [];
this.list_render = [];
this.list_after = [];
// 智能指令工具函数
this.tools = [];
}
}
/**
* Cmd驱动类
*/
Cmd.prototype.Drive = Drive;
/**
* 初始化
* @private
*/
Cmd.prototype._preset = function () {
// 初始化OpenAI客户端
this._initOpenAIClient();
};
/**
* 初始化OpenAI客户端
* @private
*/
Cmd.prototype._initOpenAIClient = function () {
var cg = this.config;
if (cg.ai && cg.api_key) {
try {
this.client = new OpenAI({
baseURL: cg.base_url,
apiKey: cg.api_key
});
} catch (error) {
this.log('error', 'AI客户端初始化失败', error.message);
this.client = null;
}
} else {
this.client = null;
}
};
/**
* 构建完整指令列表
*/
Cmd.prototype._fullList = async function () {
this.list_before.clear();
this.list_check.clear();
this.list_main.clear();
this.list_render.clear();
this.list_after.clear();
let infos = this.getInfos();
for (var i = 0; i < infos.length; i++) {
var info = infos[i];
if (info.state === 1) {
let mod = this.getMod(info.name);
if (mod) {
switch (mod.config.tense) {
case 'before':
this.list_before.push(info);
break;
case 'check':
this.list_check.push(info);
break;
case 'render':
this.list_render.push(info);
break;
case 'after':
this.list_after.push(info);
break;
default:
this.list_main.push(info);
break;
}
}
}
}
};
/**
* 下达指令
* @param {string} name 名称
* @param {number} state 状态
* @param {...any} params 参数集合
* @returns {object|string} 返回指令执行结果
*/
Cmd.prototype.cmd = async function (name, state, ...params) {
var obj = this.getMod(name);
if (!obj) {
return '错误: 指令不存在';
}
return await obj.call('cmd', state, ...params);
};
/**
* 新建消息对象
* @returns {object} 返回新建的消息对象
*/
Cmd.prototype.newMsg = function () {
return { ...Cmd.msg };
};
/**
* 更新后
* @param {string} dir_path 目录路径
*/
Cmd.prototype.updateAfter = async function (dir_path) {
// 构建完整指令列表
this._fullList();
// 构建指令工具函数
this.tools = this._buildTools();
};
/**
* 获取智能指令的工具函数
* @param {number} index 当前阶段ID
* @returns {Array} 返回工具函数数组
*/
Cmd.prototype._buildTools = function (index = 0) {
let tools = [];
for (var i = 0; i < this.list_main.length; i++) {
var info = this.list_main[i];
if (info.state === 1) {
let mod = this.getMod(info.name);
if (mod) {
let tool = this._buildTool(mod.config, index);
if (tool) {
tools.push(tool);
}
}
}
}
return tools;
};
/**
* 构建智能指令工具函数
* @param {object} o 指令配置项
* @param {number} index 当前阶段ID
* @returns {object|null} 返回构建的工具函数对象,若失败则返回null
*/
Cmd.prototype._buildTool = function (o, index = 0) {
try {
let description = o.title + "," + o.description;
if (o.example) {
description += ",例如:" + o.example;
}
let tool = {
// 类型
type: 'function',
// 函数定义
function: {
// 函数名称
name: o.name,
// 函数描述
description
},
// 启用结构化输出
strict: true
}
let pm = this._buildParams(o.stage, index);
if (pm) {
let { props, required } = pm;
tool.function.parameters = {
type: 'object',
properties: props,
required,
// 配合 strict 模式
additionalProperties: false
}
}
return tool;
} catch (error) {
this.log('error', `${o.name} 构建工具函数失败`, error);
return null;
}
};
/**
* 构建智能指令的参数
* @param {array} stage 指令阶段对象
* @param {number} index 当前阶段索引
* @returns {object} 返回当前阶段的参数对象
* @private
*/
Cmd.prototype._buildParams = function (stage, index) {
if (!stage || index < 0 || !stage.length || index >= stage.length) {
return null
}
let props = {};
let required = [];
var list = stage[index].param;
for (let i = 0; i < list.length; i++) {
let o = list[i];
let description = o.title + "," + o.desc;
if (o.example) {
description += ",例如:" + o.example;
}
if (o.name) {
props[o.name] = {
type: o.type || 'string',
description
};
if (o.required) {
required.push(o.name);
}
}
}
return {
props,
required
};
};
/**
* 调用AI模型(公开通用)
* @param {array} messages 消息对象数组
* @param {array} tools 工具函数数组
* @param {string} model 模型名称
* @returns {object|string} 返回AI模型的响应消息对象,若失败则返回错误信息字符串
*/
Cmd.prototype.postToAI = async function (messages, tools = [], model = '') {
if (!this.client) {
return '错误: AI模型未初始化';
}
try {
let cg = this.config;
let body = {
model: model || cg.model,
messages,
temperature: cg.temperature,
max_tokens: cg.max_tokens || 2048,
stream: false
};
if (tools && tools.length > 0) {
body.tools = tools;
body.tool_choice = 'auto';
}
return res = await this.client.chat.completions.create(body);
} catch (error) {
this.log('error', 'AI API调用失败', error.message);
// 根据错误类型提供更具体的提示
if (error.code === 'insufficient_quota' || error.status === 402) {
return '抱歉,API配额不足,请检查账户余额';
} else if (error.code === 'invalid_api_key' || error.status === 401) {
return 'API密钥无效,请检查配置';
} else if (error.status === 429) {
return '请求过于频繁,请稍后重试';
} else {
return '抱歉,AI服务暂时不可用,请稍后再试';
}
}
};
/**
* 发布消息到AI模型
* @param {object} msg 消息对象
* @param {object} db 数据库对象
* @returns {object|string} 返回AI模型的响应消息对象,若失败则返回错误信息字符串
* @private
*/
Cmd.prototype.callAI = async function (msg, db) {
if (!msg || !msg.content) {
return '错误: 消息内容不能为空';
}
msg.robot = this.config.ai;
let content = '';
var messages = await this._buildMessages(msg, db);
if (!messages || messages.length === 0) {
throw new Error('消息数组不能为空');
}
// 最大迭代次数限制,防止无限循环
const max_iterations = 10;
let iteration_count = 0;
let has_tool_calls = false;
do {
has_tool_calls = false;
iteration_count++;
// 调用AI模型,始终包含工具函数以确保AI返回标准格式
let res = await this.postToAI(messages, this.tools);
if (!res || !res.choices || res.choices.length === 0) {
throw new Error('AI模型响应为空');
}
let message = res.choices[0].message;
messages.push(message);
// 检查是否有工具调用
if (message.tool_calls && message.tool_calls.length > 0) {
has_tool_calls = true;
// 处理工具调用,返回工具调用信息
let rets = await this._handleToolCalls(msg, db, message.tool_calls);
if (!rets || rets.length === 0) {
this.log('warn', '处理工具调用失败', rets);
// 如果工具调用失败,移除工具调用相关的消息,然后让AI直接回复
// 保留系统消息和用户消息,移除AI的工具调用响应
messages = messages.filter(msg => msg.role !== 'assistant' || !msg.tool_calls);
// 重新调用AI,这次不包含工具函数,让AI直接回复
let direct_res = await this.postToAI(messages, []);
if (direct_res && direct_res.choices && direct_res.choices.length > 0) {
let direct_message = direct_res.choices[0].message;
content = direct_message.content || '抱歉,我无法处理这个请求';
// 添加直接回复到消息上下文
messages.push(direct_message);
has_tool_calls = false; // 结束循环
} else {
content = '抱歉,AI服务暂时不可用,请稍后再试';
has_tool_calls = false; // 结束循环
}
} else {
// 将工具调用结果添加到消息上下文
messages = messages.concat(rets);
}
} else {
// 没有工具调用,获取最终响应内容
content = message.content || '错误:AI未提供有效响应';
}
// 防止无限循环
if (iteration_count >= max_iterations) {
this.log('warn', '达到最大迭代次数限制', iteration_count);
if (!content) {
content = '错误:达到最大处理次数限制,请简化您的请求';
}
break;
}
} while (has_tool_calls && msg.robot);
// 保存指令执行日志
await this.saveHistory(msg, db, messages);
return content;
};
/**
* 构建消息数组
* @param {object} msg 消息对象
* @param {object} db 数据库对象
* @returns {array} 返回构建的消息数组
* @private
*/
Cmd.prototype._buildMessages = async function (msg, db) {
if (!msg || !msg.content) {
return [];
}
var messages = await this.getHistory(msg, db);
if (!messages || messages.length === 0) {
messages = [];
let system_prompt = this._getSystemPrompt();
messages.push({
role: 'system',
content: system_prompt
});
}
messages.push({
role: 'user',
content: msg.content
});
return messages;
};
/**
* 获取系统提示
* @returns {string} 返回系统提示字符串
* @private
*/
Cmd.prototype._getSystemPrompt = function () {
// 获取个性提示词
let personality_prompt = this._getPersonalityPrompt();
// 获取规范提示词
let standard_prompt = this._getStandardPrompt();
// 合并个性提示和规范提示
let system_prompt = `${personality_prompt}\n${standard_prompt}`;
return system_prompt;
};
/**
* 获取个性提示词
* @returns {string} 返回个性提示字符串
* @private
*/
Cmd.prototype._getPersonalityPrompt = function () {
let prompt = this.config.personality_prompt;
if (!prompt) {
prompt = `你是一个专业的智能指令转换专家,具备以下核心能力:
## 角色定位
- 智能指令解析专家,擅长将自然语言转换为结构化指令
- 上下文理解大师,能够基于会话历史和状态进行智能判断
- 多模态消息处理专家,支持文本、图片、语音、文件等多种消息类型
## 核心能力
1. **自然语言理解**:准确理解用户意图,识别关键指令和参数
2. **上下文感知**:利用消息历史、会话状态、表单数据等上下文信息
3. **指令映射**:将用户请求映射到合适的系统指令和工具函数
4. **参数提取**:自动提取和补全指令执行所需的参数
5. **多步骤规划**:处理复杂的多步骤指令执行场景
## 专业特性
- 响应速度快,判断准确,能够处理复杂的指令转换场景
- 具备良好的容错能力,能够处理模糊或不完整的用户输入
- 支持个性化定制,能够根据不同的用户和场景调整响应策略
## 工具调用失败处理策略
- 当系统工具调用失败时,基于自己的知识库直接回答用户问题
- 回复时要自然流畅,不要提及工具调用失败或技术细节
- 对于天气查询:直接提供一般性天气信息、季节特点或穿衣建议,就像自己知道这些信息一样
- 对于时间查询:直接提供当前时间或相关时间信息
- 对于计算问题:直接计算结果并给出答案
- 对于其他问题:基于常识和经验提供有用的回答
- 避免提及任何技术限制,保持回复的自然性和实用性`;
}
return prompt;
};
/**
* 获取规范提示词
* @returns {string} 返回规范提示字符串
* @private
*/
Cmd.prototype._getStandardPrompt = function () {
let prompt = this.config.standard_prompt;
if (!prompt) {
prompt = `
## 消息上下文信息
你可以利用以下消息上下文信息来更好地理解用户意图:
### 基础消息属性
- **消息类型**: 文本、图片、语音、视频、文件、位置、链接、事件等
- **消息内容**: 原始消息文本、媒体文件URL、文件信息等
- **时间信息**: 消息发送时间、创建时间、更新时间
### 会话上下文
- **会话类型**: 群组、私聊、临时会话、公众号会话
- **参与者信息**: 发信人ID、收信人ID、机器人标识、群组成员
- **会话状态**: 会话ID、会话阶段、结束状态、活跃状态
### 历史状态
- **关键词历史**: 用户历史关键词、常用指令模式
- **表单数据**: 已填写的表单字段、待补充的参数
- **备注信息**: 用户备注、会话标签、自定义属性
## 智能指令映射规则
### 1. 消息类型映射规则
- **文本消息**:
- 优先匹配文本处理指令(搜索、翻译、计算等)
- 识别指令关键词(如"搜索"、"查询"、"计算"等)
- 支持自然语言指令(如"帮我找一下资料")
- **媒体消息**(图片/语音/视频):
- 优先匹配媒体处理指令(识别、分析、转换等)
- 结合文本内容进行多模态理解
- 支持媒体文件的分析和提取
- **文件消息**:
- 优先匹配文件处理指令(上传、下载、解析等)
- 根据文件类型选择合适工具(文档、表格、图片等)
- 支持文件内容的智能提取
- **位置消息**:
- 优先匹配位置相关指令(导航、周边搜索、地理信息)
- 结合位置坐标和描述信息
### 2. 会话上下文映射规则
- **群组消息**:
- 考虑群组特定的指令和权限
- 支持群组管理功能(成员管理、公告等)
- 适应群组交流场景
- **私聊消息**:
- 考虑个人化指令和偏好设置
- 支持个性化服务和定制功能
- 保护用户隐私信息
- **临时会话**:
- 考虑临时性指令和一次性服务
- 快速响应,简化流程
- 支持会话状态的快速清理
### 3. 历史状态映射规则
- **阶段信息**:
- 根据当前阶段选择合适的指令流程
- 支持多步骤指令的连续执行
- 维护阶段间的上下文连贯性
- **表单数据**:
- 利用已有的表单数据自动补全参数
- 减少用户重复输入
- 支持表单数据的智能验证
- **关键词历史**:
- 基于历史关键词理解用户意图
- 识别用户的使用习惯和偏好
- 支持个性化推荐和智能补全
## 指令处理流程
### 1. 意图识别阶段
- 分析用户输入的自然语言
- 识别关键指令词和参数
- 结合上下文信息理解真实意图
### 2. 指令映射阶段
- 将用户意图映射到系统指令
- 选择合适的工具函数
- 确定指令执行优先级
### 3. 参数处理阶段
- 提取和验证指令参数
- 补全缺失的参数信息
- 处理参数格式转换
### 4. 执行规划阶段
- 规划指令执行步骤
- 处理多步骤指令场景
- 考虑异常情况和回退策略
## 特别注意
### 智能判断原则
- 充分利用所有可用上下文信息
- 优先考虑最相关的指令映射
- 支持模糊匹配和容错处理
### 用户体验优化
- 减少用户输入,智能补全参数
- 提供清晰的指令反馈
- 支持指令的撤销和重做
### 错误处理机制
- 识别和处理无效指令
- 提供友好的错误提示
- 支持指令的重新解释和修正
### 工具调用失败处理
- 当工具调用失败时,基于自己的知识直接回答用户问题
- 回复时要自然流畅,不要提及工具调用失败或技术细节
- 对于常见问题(如天气、时间、计算等),直接提供一般性信息或建议,就像自己知道这些信息一样
- 避免重复工具调用过程,直接给出有用的回答
- 如果无法提供准确信息,可以给出替代方案或建议,但不要暴露技术限制
### 性能考虑
- 快速响应,减少等待时间
- 优化指令映射算法
- 支持并发指令处理`;
}
return prompt;
};
/**
* 执行指令
* @param {string} cmd 指令字符串
* @param {string} method 方法名
* @param {...*} args 指令参数
* @returns {string} 返回指令执行结果
* @private
*/
Cmd.prototype._exec = async function (tense, cmd, method, ...args) {
var list = this['list_' + tense];
if (!list) {
throw new Error(` 不存在时态: ${tense}`);
}
// 查找指令
for (var i = 0; i < list.length; i++) {
var info = list[i];
if (info.name === cmd) {
let mod = this.getMod(info.name);
if (mod) {
return await mod.call(method, ...args);
}
}
}
};
/**
* 推送工具调用消息
* @param {*} ret 工具调用执行结果
* @param {string} tool_call_id 工具调用ID
* @returns {object} 返回工具调用消息对象
* @private
*/
Cmd.prototype.getMessage = function (ret, tool_call_id) {
if (typeof ret === 'object') {
if (ret.error) {
return {
role: 'tool',
tool_call_id,
content: JSON.stringify({ error: ret.error.message }),
}
}
ret = JSON.stringify(ret);
}
else if (typeof ret === 'string') {
if (this._isError(ret)) {
// 记录工具调用信息
return {
role: 'tool',
tool_call_id,
content: JSON.stringify({ error: ret })
}
}
}
// 记录工具调用信息
return {
role: 'tool',
tool_call_id,
content: ret
}
};
/**
* 判断是否为错误信息
* @param {string} content 内容字符串
* @returns {boolean} 返回是否为错误信息
* @private
*/
Cmd.prototype._isError = function (content) {
return content.startsWith('错误') || content.startsWith('不能为');
};
/**
* 处理工具调用
* @description 处理消息中的工具调用,执行对应的指令并返回结果
* @param {object} msg 消息对象,包含工具调用信息
* @param {object} db 数据管理器,用于存储和检索数据
* @param {Array} tool_calls 工具调用数组,每个元素包含工具调用的信息
* @returns {string} 返回工具调用处理结果,格式为JSON字符串
* @private
*/
Cmd.prototype._handleToolCalls = async function (msg, db, tool_calls) {
var messages = [];
for (var i = 0; i < tool_calls.length; i++) {
var tool = tool_calls[i];
var cmd = tool.function.name;
var form = {};
try {
// 解析函数参数
var args = tool.function.arguments;
if (args) {
if (typeof args === 'string') {
form = JSON.parse(args);
} else {
form = args;
}
}
msg.form = form;
var ret = await this._exec('main', cmd, 'main', msg, db);
if (ret) {
messages.push(this.getMessage(ret, tool.id));
}
} catch (error) {
this.log('error', `解析工具调用参数失败: ${cmd} `, error.message);
messages.push({
role: 'tool',
tool_call_id: tool.id,
content: JSON.stringify({ error: `工具调用参数解析失败: ${error.message} ` })
});
}
}
return messages;
};
/**
* 获取指令执行日志
* @param {object} msg 消息对象,包含指令信息
* @param {object} db 数据管理器,用于存储和检索数据
* @returns {Array} 返回指令执行日志数组
*/
Cmd.prototype.getHistory = async function (msg, db) {
var key = this._getKey(msg);
var logs = await $.cache.get(key);
if (!logs) {
logs = [];
}
else if (typeof logs === 'string') {
logs = JSON.parse(logs);
}
return logs;
};
/**
* 保存指令执行日志
* @param {object} msg 消息对象,包含指令信息
* @param {object} db 数据管理器,用于存储和检索数据
* @param {Array} logs 指令执行日志数组
* @private
*/
Cmd.prototype.saveHistory = async function (msg, db, logs) {
var key = this._getKey(msg);
await $.cache.set(key, logs, this.config.interval);
};
/**
* 获取指令执行日志缓存键
* @param {object} msg 消息内容
* @returns {string} 返回指令执行日志缓存键
*/
Cmd.prototype._getKey = function (msg) {
return this.config.cache_prefix + msg.form + "_" + msg.to_user + "_" + msg.group;
};
/**
* 执行指令
* @param {object} msg 消息对象,包含指令信息
* @param {object} db 数据管理器,用于存储和检索数据
* @returns {string} 返回指令执行结果,格式为JSON字符串
* @private
*/
Cmd.prototype.run = async function (msg, db) {
// 判断是否含有消息和消息正文
if (!msg || !msg.content) {
return;
}
// 初始化数据库配置
db.table = this.config.table;
db.key = this.config.key;
// 统一的指令处理流程
var ret = await this._run(msg, db);
// 记录执行结果
db.ret = ret;
return ret;
};
/**
* 指令行为主函数
* @param {object} msg 消息
* @param {object} db 数据管理器,如: { next: async function{}, ret: {} }
* @returns {object} 执行结果
* @private
*/
Cmd.prototype._run = async function (msg, db) {
return null;
var cg = this.config;
if (cg.ai) {
msg.robot = cg.ai;
let ret = await this.callAI(msg, db);
return ret;
}
else {
let ret = await this._exec('main', msg.content, 'main', msg, db);
return ret;
}
};
/**
* 执行指令前处理
* @param {object} msg 消息对象
* @param {object} db 数据库对象
* @returns {object|string} 返回指令执行结果
*/
Cmd.prototype._runBefore = async function (msg, db) {
db.logs = await this._getLogs(msg, db);
await this._fullMsg(msg, db);
return await this.runSub(msg, db, 'before');
};
/**
* 执行主指令
* @param {object} msg 消息
* @param {object} db 数据管理器
* @returns {object} 返回执行结果
* @private
*/
Cmd.prototype._runMain = async function (msg, db) {
// 先判断本次内容是否为指令
var ret_main = await this.runSub(msg, db, 'main');
if (ret_main) {
return ret_main;
}
// 获取历史未完成的指令,拼接本次内容并执行
var log = await this.getContext(msg, db);
if (!log) {
return null;
}
return await this.runSub(m, db, 'main');
};
/**
* 执行后时态指令
* @param {object} msg 消息
* @param {object} db 数据管理器
* @returns {object|string} 返回指令执行结果
*/
Cmd.prototype._runAfter = async function (msg, db) {
var ret = await this.runSub(msg, db, 'after');
return ret;
};
/**
* 补全消息
* @param {object} msg 消息对象
* @param {object} db 数据管理器
* @returns {object} 返回合并后的消息对象
*/
Cmd.prototype._fullMsg = function (msg, db) {
};
/**
* 执行指令
* @param {object} msg 消息对象
* @param {object} db 数据库对象
* @param {string} tense 时态
* @returns {object|string} 返回指令执行结果
*/
Cmd.prototype.runSub = async function (msg, db, tense = 'main') {
var list = this['list_' + tense];
if (!list) {
return;
}
var ret;
for (var i = 0, info; info = list[i++];) {
if (info.state === 1) {
let mod = this.getMod(info.name);
if (mod) {
ret = await mod.run(msg, db);
if (ret) {
db.ret = ret;
// 如果匹配指令则将该消息定义为使用该指令
msg.cmd = info.name;
if (mod.config.end) {
break;
}
}
}
}
}
return ret;
};
/**
* 获取对话历史
* @param {object} msg 当前消息
* @param {object} db 数据库对象
* @returns {Array} 返回历史消息数组
*/
Cmd.prototype._getLogs = async function (msg, db) {
// 先尝试从缓存中获取对话历史
var logs = await this._getLogsFromCache(msg);
if (logs.length > 0) {
return logs;
}
// 如果缓存中没有,从数据库查询
logs = await this._getLogsFromDB(msg, db);
if (logs.length > 0) {
// 缓存查询结果
await this._setLogsToCache(msg, logs);
}
return logs;
};
/**
* 从数据库中获取对话历史
* @param {object} msg 当前消息
* @param {object} db 数据库对象
* @returns {Array} 返回历史消息数组
* @private
*/
Cmd.prototype._getLogsFromDB = async function (msg, db) {
var cg = this.config;
var max_turns = cg.max_turns;
// 基于实际数据库字段查询对话记录
var query = {
from_user: msg.from_user,
to_user: msg.to_user,
group: msg.group,
type: msg.type
};
// 如果当前消息有chatid,优先基于chatid查询同一个对话会话
if (msg.chatid) {
query.chatid = msg.chatid;
}
var db1 = db.new(cg.table, cg.key);
var logs = await db1.getList(query, '`time_create` desc', max_turns * 2);
return logs;
};
/**
* 从缓存中获取对话历史
* @param {object} msg 当前消息
* @returns {Array} 返回历史消息数组
* @private
*/
Cmd.prototype._getLogsFromCache = async function (msg) {
var logs = await $.cache.get(msg.chatid || this._getKey(msg));
if (!logs) {
logs = [];
}
else if (typeof logs === 'string') {
logs = JSON.parse(logs);
}
return logs;
};
/* 导出指令 */
exports.Cmd = Cmd;
/**
* Cmd事件池
*/
if (!$.pool.cmd) {
$.pool.cmd = {};
}
/**
* Cmd管理器,用于创建缓存
* @param {string} scope 作用域
* @param {string} title 标题
* @returns {object} 返回一个缓存类
*/
function cmdAdmin(scope, title) {
var sc = scope || $.val.scope + '';
var obj = $.pool.cmd[sc];
if (!obj) {
$.pool.cmd[sc] = new Cmd({
name: sc,
title: title || sc
});
obj = $.pool.cmd[sc];
}
return obj;
}
/**
* @module 导出Cmd管理器
*/
if ($.admin) {
$.admin.cmd = cmdAdmin;
}