From eee1285e2fb5da2a2023d135fbba8f5da1700674 Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Sun, 16 Mar 2025 22:13:52 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E5=AF=B9=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/chat.js | 4 +- apps/management.js | 17 ++- config/config.js | 152 ++++++++++++++++++++++++++- index.js | 2 + models/chaite/channel_storage.js | 8 +- models/chaite/chat_preset_storage.js | 11 +- models/chaite/cloud.js | 32 ++++-- models/chaite/processors_storage.js | 8 +- models/chaite/tools_storage.js | 8 +- package.json | 1 + utils/message.js | 4 +- 11 files changed, 221 insertions(+), 26 deletions(-) diff --git a/apps/chat.js b/apps/chat.js index 1e3a34b..a4fc0bd 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -21,8 +21,8 @@ export class Chat extends plugin { async chat (e) { const state = await Chaite.getInstance().getUserStateStorage().getItem(e.sender.user_id + '') - const sendMessageOptions = SendMessageOption.create(state.settings) - const preset = await getPreset(e, state.settings.preset, Config.basic.toggleMode, Config.basic.togglePrefix) + const sendMessageOptions = SendMessageOption.create(state?.settings) + const preset = await getPreset(e, state?.settings.preset || Config.llm.defaultChatPresetId, Config.basic.toggleMode, Config.basic.togglePrefix) if (!preset) { logger.debug('未找到预设,不进入对话') return false diff --git a/apps/management.js b/apps/management.js index ba6c93e..9e17fe0 100644 --- a/apps/management.js +++ b/apps/management.js @@ -1,6 +1,7 @@ import ChatGPTConfig from '../config/config.js' import { createCRUDCommandRules, createSwitchCommandRules } from '../utils/command.js' -import { Chaite } from '../../../../../../WebstormProjects/node-chaite/src/index.js' +import { Chaite } from 'chaite' +import { resolve } from 'eslint-plugin-promise/rules/lib/promise-statics.js' export class ChatGPTManagement extends plugin { constructor () { @@ -22,7 +23,19 @@ export class ChatGPTManagement extends plugin { } ] }) - this.rules.push(...[ + this.initCommand(cmdPrefix) + } + + async initCommand (cmdPrefix) { + const waitForChaite = async () => { + while (!Chaite.getInstance()) { + await new Promise(resolve => setTimeout(resolve, 1000)) + } + return Chaite.getInstance() + } + + await waitForChaite() + this.rule.push(...[ ...createCRUDCommandRules.bind(this)(cmdPrefix, '渠道', 'channels'), ...createCRUDCommandRules.bind(this)(cmdPrefix, '预设', 'presets'), ...createCRUDCommandRules.bind(this)(cmdPrefix, '工具', 'tools'), diff --git a/config/config.js b/config/config.js index f14ffd4..ab81a48 100644 --- a/config/config.js +++ b/config/config.js @@ -1,3 +1,7 @@ +import fs from 'fs' +import path from 'path' +import yaml from 'js-yaml' + class ChatGPTConfig { /** * 版本号 @@ -105,13 +109,159 @@ class ChatGPTConfig { cloudBaseUrl: '', // 云端API Key cloudApiKey: '', - // jwt key,非必要勿修改 + // jwt key,非必要勿修改,修改需重启 authKey: '', // 管理面板监听地址 host: '', // 管理面板监听端口 port: 48370 } + + constructor () { + this.version = '3.0.0' + this.basic = { + toggleMode: 'at', + togglePrefix: '#chat', + debug: false, + commandPrefix: '^#chatgpt' + } + this.llm = { + defaultModel: '', + embeddingModel: 'gemini-embedding-exp-03-07', + dimensions: 0, + defaultChatPresetId: '', + enableCustomPreset: false, + customPresetUserWhiteList: [], + customPresetUserBlackList: [], + promptBlockWords: [], + responseBlockWords: [], + blockStrategy: 'full', + blockWordMask: '***' + } + this.management = { + blackGroups: [], + whiteGroups: [], + blackUsers: [], + whiteUsers: [], + defaultRateLimit: 0 + } + this.chaite = { + dataDir: 'data', + processorsDirPath: 'utils/processors', + toolsDirPath: 'utils/tools', + cloudBaseUrl: '', + cloudApiKey: '', + authKey: '', + host: '', + port: 48370 + } + + this.watcher = null + this.configPath = '' + } + + /** + * Start config file sync + * call once! + * @param {string} configDir Directory containing config files + */ + startSync (configDir) { + const jsonPath = path.join(configDir, 'config.json') + const yamlPath = path.join(configDir, 'config.yaml') + + // Determine which config file to use + if (fs.existsSync(jsonPath)) { + this.configPath = jsonPath + } else if (fs.existsSync(yamlPath)) { + this.configPath = yamlPath + } else { + this.configPath = jsonPath + this.saveToFile() + } + + // Load initial config + this.loadFromFile() + + // Watch for file changes + this.watcher = fs.watchFile(this.configPath, (curr, prev) => { + if (curr.mtime !== prev.mtime) { + this.loadFromFile() + } + }) + + const createDeepProxy = (obj, handler) => { + if (obj === null || typeof obj !== 'object') return obj + + for (let key of Object.keys(obj)) { + if (typeof obj[key] === 'object' && obj[key] !== null) { + obj[key] = createDeepProxy(obj[key], handler) + } + } + + return new Proxy(obj, handler) + } + + // 创建处理器 + const handler = { + set: (target, prop, value) => { + if (prop !== 'watcher' && prop !== 'configPath') { + target[prop] = typeof value === 'object' && value !== null + ? createDeepProxy(value, handler) + : value + this.saveToFile() + } + return true + } + } + + // 为所有嵌套对象创建Proxy + this.basic = createDeepProxy(this.basic, handler) + this.llm = createDeepProxy(this.llm, handler) + this.management = createDeepProxy(this.management, handler) + this.chaite = createDeepProxy(this.chaite, handler) + + // 返回最外层的Proxy + return new Proxy(this, handler) + } + + /** + * Load config from file + */ + loadFromFile () { + try { + const content = fs.readFileSync(this.configPath, 'utf8') + const config = this.configPath.endsWith('.json') + ? JSON.parse(content) + : yaml.load(content) + + Object.assign(this, config) + } catch (error) { + console.error('Failed to load config:', error) + } + } + + /** + * Save config to file + */ + saveToFile () { + try { + const config = { + version: this.version, + basic: this.basic, + llm: this.llm, + management: this.management, + chaite: this.chaite + } + + const content = this.configPath.endsWith('.json') + ? JSON.stringify(config, null, 2) + : yaml.dump(config) + + fs.writeFileSync(this.configPath, content, 'utf8') + } catch (error) { + console.error('Failed to save config:', error) + } + } } export default new ChatGPTConfig() diff --git a/index.js b/index.js index 246273f..1267282 100644 --- a/index.js +++ b/index.js @@ -35,6 +35,8 @@ for (let i in files) { global.chatgpt = { } + +ChatGPTConfig.startSync('./plugins/chatgpt-plugin/data') initChaite() logger.info('chatgpt-plugin加载成功') logger.info(`当前版本${ChatGPTConfig.version}`) diff --git a/models/chaite/channel_storage.js b/models/chaite/channel_storage.js index 641c4a6..2785694 100644 --- a/models/chaite/channel_storage.js +++ b/models/chaite/channel_storage.js @@ -1,4 +1,4 @@ -import { ChaiteStorage } from 'chaite' +import { ChaiteStorage, Channel } from 'chaite' export class LowDBChannelStorage extends ChaiteStorage { /** @@ -21,7 +21,8 @@ export class LowDBChannelStorage extends ChaiteStorage { * @returns {Promise} */ async getItem (key) { - return this.collection.findOne({ id: key }) + const obj = await this.collection.findOne({ id: key }) + return new Channel({}).fromString(JSON.stringify(obj)) } /** @@ -53,7 +54,8 @@ export class LowDBChannelStorage extends ChaiteStorage { * @returns {Promise} */ async listItems () { - return this.collection.findAll() + const list = await this.collection.findAll() + return list.map(item => new Channel({}).fromString(JSON.stringify(item))) } async clear () { diff --git a/models/chaite/chat_preset_storage.js b/models/chaite/chat_preset_storage.js index 9708d6e..5df0c08 100644 --- a/models/chaite/chat_preset_storage.js +++ b/models/chaite/chat_preset_storage.js @@ -1,4 +1,4 @@ -import { ChaiteStorage } from 'chaite' +import { ChaiteStorage, ChatPreset } from 'chaite' /** * @extends {ChaiteStorage} @@ -24,7 +24,11 @@ export class LowDBChatPresetsStorage extends ChaiteStorage { * @returns {Promise} */ async getItem (key) { - return this.collection.findOne({ id: key }) + const obj = await this.collection.findOne({ id: key }) + if (!obj) { + return null + } + return new ChatPreset({}).fromString(JSON.stringify(obj)) } /** @@ -56,7 +60,8 @@ export class LowDBChatPresetsStorage extends ChaiteStorage { * @returns {Promise} */ async listItems () { - return this.collection.findAll() + const list = await this.collection.findAll() + return list.map(item => new ChatPreset({}).fromString(JSON.stringify(item))) } async clear () { diff --git a/models/chaite/cloud.js b/models/chaite/cloud.js index 0a29c21..a1e06e8 100644 --- a/models/chaite/cloud.js +++ b/models/chaite/cloud.js @@ -126,7 +126,11 @@ export async function initChaite () { chaite.setCloudService(ChatGPTConfig.chaite.cloudBaseUrl) logger.info('Chaite.Cloud 初始化完成') ChatGPTConfig.chaite.cloudApiKey && await chaite.auth(ChatGPTConfig.chaite.cloudApiKey) - await initRagManager(ChatGPTConfig.llm.embeddingModel, ChatGPTConfig.dimensions) + await initRagManager(ChatGPTConfig.llm.embeddingModel, ChatGPTConfig.llm.dimensions) + if (!ChatGPTConfig.chaite.authKey) { + ChatGPTConfig.chaite.authKey = Chaite.getInstance().getFrontendAuthHandler().generateToken(0, true) + } + chaite.getGlobalConfig().setAuthKey(ChatGPTConfig.chaite.authKey) // 监听Chaite配置变化,同步需要同步的配置 chaite.on('config-change', obj => { const { key, newVal, oldVal } = obj @@ -139,19 +143,33 @@ export async function initChaite () { chaite.setUpdateConfigCallback(config => { logger.debug('chatgpt-plugin config updated') Object.keys(config).forEach(key => { - ChatGPTConfig[key] = config[key] - // 回传部分需要同步的配置,以防不一致 - if (key === 'serverAuthKey') { - chaite.getGlobalConfig().setAuthKey(config[key]) + if (typeof config[key] === 'object' && config[key] !== null && ChatGPTConfig[key]) { + deepMerge(ChatGPTConfig[key], config[key]) + } else { + ChatGPTConfig[key] = config[key] } }) + // 回传部分需要同步的配置,以防不一致 + chaite.getGlobalConfig().setAuthKey(ChatGPTConfig.chaite.authKey) }) // 授予Chaite获取插件配置的能力以便通过api放出 chaite.setGetConfig(async () => { return ChatGPTConfig }) logger.info('Chaite.RAGManager 初始化完成') - const token = chaite.getFrontendAuthHandler().generateToken() - logger.info(token) chaite.runApiServer() } + +function deepMerge (target, source) { + for (const key in source) { + if (Object.prototype.hasOwnProperty.call(source, key)) { + if (typeof source[key] === 'object' && source[key] !== null && target[key]) { + // 如果是对象且目标属性存在,递归合并 + deepMerge(target[key], source[key]) + } else { + // 否则直接赋值 + target[key] = source[key] + } + } + } +} diff --git a/models/chaite/processors_storage.js b/models/chaite/processors_storage.js index 149cb55..36c14bf 100644 --- a/models/chaite/processors_storage.js +++ b/models/chaite/processors_storage.js @@ -1,4 +1,4 @@ -import { ChaiteStorage } from 'chaite' +import { ChaiteStorage, ProcessorDTO } from 'chaite' /** * @extends {ChaiteStorage} @@ -24,7 +24,8 @@ export class LowDBProcessorsStorage extends ChaiteStorage { * @returns {Promise} */ async getItem (key) { - return this.collection.findOne({ id: key }) + const obj = await this.collection.findOne({ id: key }) + return new ProcessorDTO({}).fromString(JSON.stringify(obj)) } /** @@ -56,7 +57,8 @@ export class LowDBProcessorsStorage extends ChaiteStorage { * @returns {Promise} */ async listItems () { - return this.collection.findAll() + const list = await this.collection.findAll() + return list.map(item => new ProcessorDTO({}).fromString(JSON.stringify(item))) } async clear () { diff --git a/models/chaite/tools_storage.js b/models/chaite/tools_storage.js index 453b1ef..91453ad 100644 --- a/models/chaite/tools_storage.js +++ b/models/chaite/tools_storage.js @@ -1,4 +1,4 @@ -import { ChaiteStorage } from 'chaite' +import { ChaiteStorage, ToolDTO } from 'chaite' /** * @extends {ChaiteStorage} @@ -24,7 +24,8 @@ export class LowDBToolsStorage extends ChaiteStorage { * @returns {Promise} */ async getItem (key) { - return this.collection.findOne({ id: key }) + const obj = await this.collection.findOne({ id: key }) + return new ToolDTO({}).fromString(JSON.stringify(obj)) } /** @@ -56,7 +57,8 @@ export class LowDBToolsStorage extends ChaiteStorage { * @returns {Promise} */ async listItems () { - return this.collection.findAll() + const list = await this.collection.findAll() + return list.map(item => new ToolDTO({}).fromString(JSON.stringify(item))) } async clear () { diff --git a/package.json b/package.json index 8d7ba6d..45b3cd2 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "author": "ikechan8370", "dependencies": { "chaite": "/Users/geyinchi/WebstormProjects/node-chaite", + "js-yaml": "^4.1.0", "keyv": "^5.3.1", "keyv-file": "^5.1.2", "lowdb": "^7.0.1", diff --git a/utils/message.js b/utils/message.js index 5fa8c71..6187623 100644 --- a/utils/message.js +++ b/utils/message.js @@ -81,7 +81,7 @@ export async function intoUserMessage (e, options = {}) { if (text) { contents.push({ type: 'text', - content: text + text }) } return { @@ -134,7 +134,7 @@ export async function getPreset (e, presetId, toggleMode, togglePrefix) { * @returns {boolean} */ export function checkChatMsg (e, toggleMode, togglePrefix) { - if (toggleMode === 'at' && e.atBot) { + if (toggleMode === 'at' && (e.atBot || e.isPrivate)) { return true } const prefixReg = new RegExp(`^#?(图片)?${togglePrefix}[^gpt][sS]*`)