mirror of
https://github.com/Jerryplusy/crystelf-plugin.git
synced 2025-12-05 15:41:56 +00:00
Compare commits
5 Commits
5aecaebfdc
...
7489cf0197
| Author | SHA1 | Date | |
|---|---|---|---|
| 7489cf0197 | |||
| d1584e4402 | |||
| 1ed09b5c32 | |||
| 9487ffd184 | |||
| 75b6d1a348 |
27
apps/ai.js
27
apps/ai.js
@ -110,7 +110,7 @@ async function index(e) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractUserMessage(msg, nickname,e) {
|
async function extractUserMessage(msg, nickname, e) {
|
||||||
if (e.message) {
|
if (e.message) {
|
||||||
let text = [];
|
let text = [];
|
||||||
let at = [];
|
let at = [];
|
||||||
@ -118,8 +118,7 @@ function extractUserMessage(msg, nickname,e) {
|
|||||||
logger.info(message);
|
logger.info(message);
|
||||||
if (message.type === 'text') {
|
if (message.type === 'text') {
|
||||||
text.push(message.text);
|
text.push(message.text);
|
||||||
}
|
} else if (message.type === 'at') {
|
||||||
else if(message.type === 'at'){
|
|
||||||
at.push(message.qq);
|
at.push(message.qq);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -131,9 +130,18 @@ function extractUserMessage(msg, nickname,e) {
|
|||||||
}
|
}
|
||||||
if (at.length > 0) {
|
if (at.length > 0) {
|
||||||
at.forEach((at) => {
|
at.forEach((at) => {
|
||||||
|
if(at === e.bot.uin){
|
||||||
|
returnMessage += `[${e.sender?.nickname},id:${e.user_id}]@(at)了你,你的id是${at}\n`;
|
||||||
|
}
|
||||||
|
else{
|
||||||
returnMessage += `[${e.sender?.nickname},id:${e.user_id}]@(at)了一个人,id是${at}\n`;
|
returnMessage += `[${e.sender?.nickname},id:${e.user_id}]@(at)了一个人,id是${at}\n`;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
const imgUrls = await YunzaiUtils.getImages(e, 1, true);
|
||||||
|
if(imgUrls){
|
||||||
|
returnMessage += `[${e.sender?.nickname},id:${e.user_id}]发送了一张图片(你可能暂时无法查看)\n`;
|
||||||
|
}
|
||||||
return returnMessage;
|
return returnMessage;
|
||||||
}
|
}
|
||||||
logger.warn('[crystelf-ai] 字符串匹配失败,使用空字符串操作');
|
logger.warn('[crystelf-ai] 字符串匹配失败,使用空字符串操作');
|
||||||
@ -218,12 +226,17 @@ async function handleMixMode(userMessage, e, aiConfig) {
|
|||||||
recall: 0,
|
recall: 0,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
let resToSave = res;
|
let resMessage = {
|
||||||
resToSave.data += '[词库预设消息]';
|
type: 'message',
|
||||||
|
data: matchResult.text + ' [词库预设消息]',
|
||||||
|
at: false,
|
||||||
|
quote: false,
|
||||||
|
recall: 0,
|
||||||
|
};
|
||||||
const newChatHistory = [
|
const newChatHistory = [
|
||||||
...chatHistory,
|
...chatHistory,
|
||||||
{ role: 'user', content: userMessage },
|
{ role: 'user', content: userMessage },
|
||||||
{ role: 'assistant', content: resToSave },
|
{ role: 'assistant', content: JSON.stringify(resMessage) },
|
||||||
];
|
];
|
||||||
SessionManager.updateChatHistory(e.group_id, newChatHistory);
|
SessionManager.updateChatHistory(e.group_id, newChatHistory);
|
||||||
SessionManager.deactivateSession(e.group_id,e.user_id);
|
SessionManager.deactivateSession(e.group_id,e.user_id);
|
||||||
@ -248,7 +261,7 @@ async function callAiForResponse(userMessage, e, aiConfig) {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
//搜索相关记忆
|
//搜索相关记忆
|
||||||
const memories = await MemorySystem.searchMemories(e.user_id,[userMessage], 5);
|
const memories = await MemorySystem.searchMemories(e.user_id,e.msg||'',5);
|
||||||
logger.info(`[crystelf-ai] ${memories}`)
|
logger.info(`[crystelf-ai] ${memories}`)
|
||||||
//构建聊天历史
|
//构建聊天历史
|
||||||
const historyLen = aiConfig.chatHistory;
|
const historyLen = aiConfig.chatHistory;
|
||||||
|
|||||||
@ -5,50 +5,42 @@ import path from 'path';
|
|||||||
class MemorySystem {
|
class MemorySystem {
|
||||||
constructor() {
|
constructor() {
|
||||||
this.baseDir = path.join(process.cwd(), 'data', 'crystelf', 'memories');
|
this.baseDir = path.join(process.cwd(), 'data', 'crystelf', 'memories');
|
||||||
this.memories = new Map();
|
this.memories = new Map();//缓存个别加载的记忆
|
||||||
this.defaultTimeout = 30;
|
this.defaultTimeout = 30;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
try {
|
try {
|
||||||
const config = await ConfigControl.get('ai');
|
const config = await ConfigControl.get('ai');
|
||||||
this.defaultTimeout = config?.timeout || 30;
|
this.defaultTimeout = config?.timeout || 30;
|
||||||
await this.loadAllMemories();
|
if (!fs.existsSync(this.baseDir)) {
|
||||||
await this.cleanExpiredMemories();
|
fs.mkdirSync(this.baseDir, { recursive: true });
|
||||||
|
}
|
||||||
|
logger.info('[crystelf-ai] 记忆系统初始化完成');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[crystelf-ai] 记忆系统初始化失败: ${error.message}`);
|
logger.error(`[crystelf-ai] 记忆系统初始化失败: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadAllMemories() {
|
/**
|
||||||
|
* 动态加载单个用户的记忆
|
||||||
|
*/
|
||||||
|
async loadUserMemories(groupId, userId) {
|
||||||
try {
|
try {
|
||||||
if (!fs.existsSync(this.baseDir)) {
|
const filePath = path.join(this.baseDir, String(groupId), `${String(userId)}.json`);
|
||||||
fs.mkdirSync(this.baseDir, { recursive: true });
|
if (!fs.existsSync(filePath)) return {};
|
||||||
}
|
const data = await fs.promises.readFile(filePath, 'utf8');
|
||||||
|
const json = JSON.parse(data || '{}');
|
||||||
const groupDirs = fs.readdirSync(this.baseDir);
|
for (const [key, memory] of Object.entries(json)) {
|
||||||
for (const groupId of groupDirs) {
|
|
||||||
const groupPath = path.join(this.baseDir, String(groupId));
|
|
||||||
if (!fs.statSync(groupPath).isDirectory()) continue;
|
|
||||||
|
|
||||||
const userFiles = fs.readdirSync(groupPath);
|
|
||||||
for (const file of userFiles) {
|
|
||||||
if (!file.endsWith('.json')) continue;
|
|
||||||
const userId = path.basename(file, '.json');
|
|
||||||
const filePath = path.join(groupPath, file);
|
|
||||||
const data = fs.readFileSync(filePath, 'utf8');
|
|
||||||
const memoriesData = JSON.parse(data || '{}');
|
|
||||||
for (const [key, memory] of Object.entries(memoriesData)) {
|
|
||||||
this.memories.set(`${groupId}_${userId}_${key}`, memory);
|
this.memories.set(`${groupId}_${userId}_${key}`, memory);
|
||||||
}
|
}
|
||||||
}
|
return json;
|
||||||
}
|
|
||||||
logger.info(`[crystelf-ai] 加载了 ${this.memories.size} 条记忆`);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[crystelf-ai] 加载记忆失败: ${error.message}`);
|
logger.error(`[crystelf-ai] 加载用户记忆失败(${groupId}/${userId}): ${error.message}`);
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//保存指定用户记忆
|
||||||
async saveMemories(groupId, userId) {
|
async saveMemories(groupId, userId) {
|
||||||
try {
|
try {
|
||||||
const groupPath = path.join(this.baseDir, String(groupId));
|
const groupPath = path.join(this.baseDir, String(groupId));
|
||||||
@ -56,7 +48,6 @@ class MemorySystem {
|
|||||||
if (!fs.existsSync(groupPath)) {
|
if (!fs.existsSync(groupPath)) {
|
||||||
fs.mkdirSync(groupPath, { recursive: true });
|
fs.mkdirSync(groupPath, { recursive: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const userMemories = {};
|
const userMemories = {};
|
||||||
for (const [key, memory] of this.memories) {
|
for (const [key, memory] of this.memories) {
|
||||||
if (key.startsWith(`${groupId}_${userId}_`)) {
|
if (key.startsWith(`${groupId}_${userId}_`)) {
|
||||||
@ -64,14 +55,22 @@ class MemorySystem {
|
|||||||
userMemories[memoryId] = memory;
|
userMemories[memoryId] = memory;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
await fs.promises.writeFile(filePath, JSON.stringify(userMemories, null, 2), 'utf8');
|
||||||
fs.writeFileSync(filePath, JSON.stringify(userMemories, null, 2));
|
|
||||||
logger.info(`[crystelf-ai] 记忆已保存到 ${groupId}/${userId}.json`);
|
logger.info(`[crystelf-ai] 记忆已保存到 ${groupId}/${userId}.json`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error(`[crystelf-ai] 保存记忆失败: ${error.message}`);
|
logger.error(`[crystelf-ai] 保存记忆失败: ${error.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加新的记忆
|
||||||
|
* @param groupId
|
||||||
|
* @param userId
|
||||||
|
* @param data 内容
|
||||||
|
* @param keywords 关键词数组
|
||||||
|
* @param timeout 超时
|
||||||
|
* @returns {Promise<null|string>}
|
||||||
|
*/
|
||||||
async addMemory(groupId, userId, data, keywords = [], timeout = null) {
|
async addMemory(groupId, userId, data, keywords = [], timeout = null) {
|
||||||
try {
|
try {
|
||||||
const memoryId = this.generateMemoryId();
|
const memoryId = this.generateMemoryId();
|
||||||
@ -87,7 +86,6 @@ class MemorySystem {
|
|||||||
};
|
};
|
||||||
this.memories.set(`${groupId}_${userId}_${memoryId}`, memory);
|
this.memories.set(`${groupId}_${userId}_${memoryId}`, memory);
|
||||||
await this.saveMemories(groupId, userId);
|
await this.saveMemories(groupId, userId);
|
||||||
|
|
||||||
logger.info(`[crystelf-ai] 添加新记忆: ${groupId}/${userId}/${memoryId}`);
|
logger.info(`[crystelf-ai] 添加新记忆: ${groupId}/${userId}/${memoryId}`);
|
||||||
return memoryId;
|
return memoryId;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -96,48 +94,41 @@ class MemorySystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async searchMemories(userId, keywords = [], limit = 10) {
|
/**
|
||||||
|
* 搜索记忆
|
||||||
|
* @param userId 用户id
|
||||||
|
* @param input 输入
|
||||||
|
* @param limit 最大记忆
|
||||||
|
* @returns {Promise<*[]>}
|
||||||
|
*/
|
||||||
|
async searchMemories(userId, input = '', limit = 10) {
|
||||||
try {
|
try {
|
||||||
|
if(input === '') return null;
|
||||||
|
const keywords = this.extractKeywords(input);
|
||||||
const results = [];
|
const results = [];
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
let searchText = '';
|
//遍历所有群聊目录
|
||||||
if (keywords.length === 1 && keywords[0].length > 6) {
|
const groupDirs = fs.existsSync(this.baseDir) ? fs.readdirSync(this.baseDir) : [];
|
||||||
searchText = keywords[0].toLowerCase();
|
for (const groupId of groupDirs) {
|
||||||
const words = searchText.match(/[\u4e00-\u9fa5]{1,2}|[a-zA-Z0-9]+/g) || [];
|
const filePath = path.join(this.baseDir, String(groupId), `${String(userId)}.json`);
|
||||||
keywords = Array.from(new Set(words.filter(w => w.length > 1)));
|
if (!fs.existsSync(filePath)) continue;
|
||||||
}
|
const data = await fs.promises.readFile(filePath, 'utf8');
|
||||||
const userMemories = [];
|
const json = JSON.parse(data || '{}');
|
||||||
for (const [key, memory] of this.memories) {
|
for (const memory of Object.values(json)) {
|
||||||
const parts = key.split('_');
|
if (now > memory.expireAt) continue; // 跳过过期
|
||||||
if (parts.length < 3) continue;
|
const matchScore = this.calculateMatchScore(memory, keywords);
|
||||||
const uid = parts[1];
|
|
||||||
if (uid !== userId) continue;
|
|
||||||
if (now > memory.expireAt) continue;
|
|
||||||
userMemories.push(memory);
|
|
||||||
}
|
|
||||||
if (userMemories.length === 0) return [];
|
|
||||||
for (const memory of userMemories) {
|
|
||||||
let matchScore = 0;
|
|
||||||
for (const kw of keywords) {
|
|
||||||
if (memory.keywords.some(k => k.includes(kw) || kw.includes(k))) matchScore += 10;
|
|
||||||
else if (memory.data.includes(kw)) matchScore += 5;
|
|
||||||
}
|
|
||||||
if (searchText) {
|
|
||||||
for (const mk of memory.keywords) {
|
|
||||||
if (searchText.includes(mk)) matchScore += 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (matchScore > 0) {
|
if (matchScore > 0) {
|
||||||
memory.accessCount++;
|
memory.accessCount = (memory.accessCount || 0) + 1;
|
||||||
memory.lastAccessed = now;
|
memory.lastAccessed = now;
|
||||||
results.push({
|
results.push({
|
||||||
id: memory.id,
|
id: memory.id,
|
||||||
data: memory.data,
|
data: memory.data,
|
||||||
keywords: memory.keywords,
|
keywords: memory.keywords,
|
||||||
relevance: matchScore + this.calculateRelevance(memory, keywords)
|
relevance: matchScore
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
results.sort((a, b) => b.relevance - a.relevance);
|
results.sort((a, b) => b.relevance - a.relevance);
|
||||||
return results.slice(0, limit);
|
return results.slice(0, limit);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -146,44 +137,29 @@ class MemorySystem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateRelevance(memory, keywords) {
|
//提取关键词
|
||||||
|
extractKeywords(text) {
|
||||||
|
if (!text) return [];
|
||||||
|
text = text.toLowerCase();
|
||||||
|
const words = text.match(/[\u4e00-\u9fa5]{1,2}|[a-zA-Z0-9]+/g) || [];
|
||||||
|
return Array.from(new Set(words.filter(w => w.length > 0)));
|
||||||
|
}
|
||||||
|
|
||||||
|
//记忆匹配分数
|
||||||
|
calculateMatchScore(memory, keywords) {
|
||||||
let score = 0;
|
let score = 0;
|
||||||
for (const keyword of keywords) {
|
const text = (memory.data || '').toLowerCase();
|
||||||
if (memory.keywords.includes(keyword)) {
|
for (const kw of keywords) {
|
||||||
score += 10;
|
for (const mk of memory.keywords || []) {
|
||||||
|
if (mk.includes(kw) || kw.includes(mk)) score += 10;
|
||||||
}
|
}
|
||||||
if (memory.data.includes(keyword)) {
|
if (text.includes(kw)) score += 6;
|
||||||
score += 5;
|
|
||||||
}
|
}
|
||||||
}
|
score += Math.min((memory.accessCount || 0) * 0.2, 5);
|
||||||
score += Math.min(memory.accessCount * 0.1, 5);
|
|
||||||
const daysSinceCreated = (Date.now() - memory.createdAt) / (24 * 60 * 60 * 1000);
|
const daysSinceCreated = (Date.now() - memory.createdAt) / (24 * 60 * 60 * 1000);
|
||||||
score += Math.max(10 - daysSinceCreated * 0.1, 0);
|
score += Math.max(10 - daysSinceCreated * 0.1, 0);
|
||||||
return score;
|
return score;
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanExpiredMemories() {
|
|
||||||
try {
|
|
||||||
const now = Date.now();
|
|
||||||
let cleanedCount = 0;
|
|
||||||
|
|
||||||
for (const [memoryKey, memory] of this.memories) {
|
|
||||||
if (now > memory.expireAt) {
|
|
||||||
this.memories.delete(memoryKey);
|
|
||||||
cleanedCount++;
|
|
||||||
const [groupId, userId] = memoryKey.split('_');
|
|
||||||
await this.saveMemories(groupId, userId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cleanedCount > 0) {
|
|
||||||
logger.info(`[crystelf-ai] 清理了 ${cleanedCount} 条过期记忆`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.error(`[crystelf-ai] 清理过期记忆失败: ${error.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
generateMemoryId() {
|
generateMemoryId() {
|
||||||
return `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
return `mem_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -58,23 +58,27 @@ class Renderer {
|
|||||||
|
|
||||||
async renderMarkdown(markdown) {
|
async renderMarkdown(markdown) {
|
||||||
if (!this.isInitialized) await this.init();
|
if (!this.isInitialized) await this.init();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const page = await this.browser.newPage();
|
const page = await this.browser.newPage();
|
||||||
const html = this.getMarkdownTemplate(markdown, this.config?.markdownRenderer || {});
|
const html = this.getMarkdownTemplate(markdown, this.config?.markdownRenderer || {});
|
||||||
|
|
||||||
await page.setContent(html, { waitUntil: 'networkidle0' });
|
await page.setContent(html, { waitUntil: 'networkidle0' });
|
||||||
await page.waitForSelector('#render-complete', { timeout: 5000 });
|
await page.waitForSelector('#render-complete', { timeout: 5000 });
|
||||||
|
//计算页面尺寸
|
||||||
const rect = await page.evaluate(() => {
|
const rect = await page.evaluate(() => {
|
||||||
const body = document.body;
|
const body = document.body;
|
||||||
return { width: body.scrollWidth, height: body.scrollHeight };
|
const main = document.querySelector('.markdown-body') || body;
|
||||||
});
|
const rect = main.getBoundingClientRect();
|
||||||
await page.setViewport({
|
return {
|
||||||
width: Math.ceil(rect.width),
|
width: Math.min(Math.ceil(rect.width + 40), 1200),
|
||||||
height: Math.ceil(rect.height),
|
height: Math.ceil(rect.height + 40),
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await page.setViewport({
|
||||||
|
width: rect.width,
|
||||||
|
height: Math.min(rect.height, 3000),
|
||||||
|
deviceScaleFactor: 2,
|
||||||
|
});
|
||||||
const tempDir = path.join(process.cwd(), 'temp', 'html');
|
const tempDir = path.join(process.cwd(), 'temp', 'html');
|
||||||
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });
|
if (!fs.existsSync(tempDir)) fs.mkdirSync(tempDir, { recursive: true });
|
||||||
const filepath = path.join(tempDir, `markdown_${Date.now()}.png`);
|
const filepath = path.join(tempDir, `markdown_${Date.now()}.png`);
|
||||||
@ -225,21 +229,92 @@ class Renderer {
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC&display=swap" rel="stylesheet">
|
||||||
<style>
|
<style>
|
||||||
body { background-color: ${themeColor}; color: #e2e8f0; font-family: 'Noto Sans SC', sans-serif; font-size: ${fontSize}px; line-height: 1.6; margin: 0; padding: 20px; }
|
body {
|
||||||
h1, h2, h3, h4, h5, h6 { color: #f1f5f9; border-bottom: 1px solid #334155; padding-bottom: 5px; }
|
background-color: ${themeColor};
|
||||||
a { color: #38bdf8; text-decoration: none; }
|
color: #e2e8f0;
|
||||||
|
font-family: 'Noto Sans SC', sans-serif;
|
||||||
|
font-size: ${fontSize}px;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.markdown-body {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 900px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: #f1f5f9;
|
||||||
|
border-bottom: 1px solid #334155;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
margin-top: 1.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #38bdf8;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
a:hover { text-decoration: underline; }
|
a:hover { text-decoration: underline; }
|
||||||
code { background-color: #1e293b; padding: 2px 5px; border-radius: 5px; }
|
|
||||||
pre { background-color: #1e293b; padding: 15px; border-radius: 10px; overflow-x: auto; }
|
code {
|
||||||
blockquote { border-left: 4px solid #334155; padding-left: 15px; color: #9ca3af; }
|
background-color: #1e293b;
|
||||||
|
padding: 2px 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
background-color: #1e293b;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
blockquote {
|
||||||
|
border-left: 4px solid #334155;
|
||||||
|
padding-left: 15px;
|
||||||
|
color: #9ca3af;
|
||||||
|
margin: 1em 0;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
width: 100%;
|
||||||
|
margin: 1em 0;
|
||||||
|
background-color: #1e293b;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #334155;
|
||||||
|
padding: 10px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
th {
|
||||||
|
background-color: #0f172a;
|
||||||
|
color: #f1f5f9;
|
||||||
|
}
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background-color: #16213d;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: block;
|
||||||
|
margin: 10px auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
<div class="markdown-body">
|
||||||
${md.render(markdown)}
|
${md.render(markdown)}
|
||||||
<div id="render-complete"></div>
|
<div id="render-complete"></div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@ -86,8 +86,6 @@ class ResponseHandler {
|
|||||||
return null;
|
return null;
|
||||||
case 'recall':
|
case 'recall':
|
||||||
return this.handleRecallMessage(message);
|
return this.handleRecallMessage(message);
|
||||||
case 'function':
|
|
||||||
return this.handleFunctionMessage(message);
|
|
||||||
default:
|
default:
|
||||||
return this.handleNormalMessage(message);
|
return this.handleNormalMessage(message);
|
||||||
}
|
}
|
||||||
@ -114,28 +112,7 @@ class ResponseHandler {
|
|||||||
if (!validTypes.includes(message.type)) {
|
if (!validTypes.includes(message.type)) {
|
||||||
logger.info(`[crystelf-ai] ai返回未知的type类型:${message.type}`)
|
logger.info(`[crystelf-ai] ai返回未知的type类型:${message.type}`)
|
||||||
return false;
|
return false;
|
||||||
}
|
}return true;
|
||||||
/**
|
|
||||||
switch (message.type) {
|
|
||||||
case 'message':
|
|
||||||
case 'code':
|
|
||||||
case 'markdown':
|
|
||||||
case 'meme':
|
|
||||||
case 'ai-record':
|
|
||||||
case 'file':
|
|
||||||
case 'memory':
|
|
||||||
case 'at':
|
|
||||||
case 'poke':
|
|
||||||
case 'emoji-like':
|
|
||||||
case 'like':
|
|
||||||
return !!message.id;
|
|
||||||
case 'recall':
|
|
||||||
return !!message.seq;
|
|
||||||
case 'function':
|
|
||||||
return !!(message.data && message.data.name);
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}*/return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -169,20 +146,6 @@ class ResponseHandler {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 函数调用消息
|
|
||||||
* @param message 函数消息
|
|
||||||
* @returns {{type: string, data}}
|
|
||||||
*/
|
|
||||||
handleFunctionMessage(message) {
|
|
||||||
// TOdO 具体的函数调用逻辑
|
|
||||||
logger.info(`[crystelf-ai] 函数调用: ${message.data.name}(${message.data.params?.join(', ') || ''})`);
|
|
||||||
return {
|
|
||||||
type: 'function',
|
|
||||||
data: message.data
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
//普通消息
|
//普通消息
|
||||||
handleNormalMessage(message) {
|
handleNormalMessage(message) {
|
||||||
// 设置默认值
|
// 设置默认值
|
||||||
@ -202,6 +165,11 @@ class ResponseHandler {
|
|||||||
return processedMessage;
|
return processedMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//对上下文消息进行处理
|
||||||
|
handleChatHistory(message) {
|
||||||
|
let messageToHistory = [];
|
||||||
|
}
|
||||||
|
|
||||||
createErrorResponse(error) {
|
createErrorResponse(error) {
|
||||||
return [{
|
return [{
|
||||||
type: 'message',
|
type: 'message',
|
||||||
|
|||||||
@ -36,5 +36,32 @@ const Message = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取群聊聊天历史记录
|
||||||
|
* @param e
|
||||||
|
* @param group_id
|
||||||
|
* @param message_seq seq
|
||||||
|
* @param count 数量
|
||||||
|
* @param reverseOrder 倒序
|
||||||
|
* @param adapter 适配器
|
||||||
|
* @returns {Promise<*>}
|
||||||
|
*/
|
||||||
|
async getGroupHistory(e,group_id,message_seq,count = 20,reverseOrder = false,adapter = 'nc'){
|
||||||
|
if(adapter === 'nc') {
|
||||||
|
return await e.bot.sendApi('get_group_msg_history', {
|
||||||
|
group_id: group_id,
|
||||||
|
message_seq: message_seq,
|
||||||
|
count: count,
|
||||||
|
reverseOrder: reverseOrder,
|
||||||
|
})
|
||||||
|
} else if (adapter === 'lgr') {
|
||||||
|
return await e.bot.sendApi('get_group_msg_history', {
|
||||||
|
group_id: group_id,
|
||||||
|
message_id:message_seq,
|
||||||
|
count: count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
export default Message;
|
export default Message;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user