This commit is contained in:
ikechan8370 2025-03-13 15:54:32 +08:00
parent 116479e34e
commit 54806ee6fb
6 changed files with 265 additions and 15 deletions

View file

@ -1,9 +1,9 @@
import Config from '../config/config.js'
import { Chaite, SendMessageOption } from 'chaite'
import { getPreset, intoUserMessage } from '../utils/message.js'
export class Chat extends plugin {
constructor () {
let toggleMode = Config.basic.toggleMode
let prefix = Config.basic.togglePrefix
super({
name: 'ChatGPT-Plugin对话',
dsc: 'ChatGPT-Plugin对话',
@ -11,24 +11,39 @@ export class Chat extends plugin {
priority: 0,
rule: [
{
reg: toggleMode === 'at' ? '^[^#][sS]*' : `^#?(图片)?${prefix}[^gpt][sS]*`,
fnc: 'chat'
reg: '^[^#][sS]*',
fnc: 'chat',
log: false
}
]
})
}
async chat (e) {
const state = await Chaite.getInstance().getUserStateStorage().getItem(e.sender.user_id + '')
const userSettings = state.settings
const sendMessageOptions = SendMessageOption.create({
model: userSettings.model,
temperature: userSettings.temperature,
max_tokens: userSettings.maxToken,
systemOverride: userSettings.systemOverride,
const sendMessageOptions = SendMessageOption.create(state.settings)
const preset = await getPreset(e, state.settings.preset, Config.basic.toggleMode, Config.basic.togglePrefix)
if (!preset) {
logger.debug('未找到预设,不进入对话')
return false
}
const userMessage = await intoUserMessage(e, {
handleReplyText: false,
handleReplyImage: true,
useRawMessage: false,
handleAtMsg: true,
excludeAtBot: false,
toggleMode: Config.basic.toggleMode,
togglePrefix: Config.basic.togglePrefix
})
Chaite.getInstance().sendMessage(msg, e, )
const response = await Chaite.getInstance().sendMessage(userMessage, e, {
...sendMessageOptions,
chatPreset: preset
})
const responseText = response.contents
.filter(c => c.type === 'text')
.map(c => (/** @type {import('chaite').TextContent} **/ c).text)
.reduce((a, b) => a + b, '')
await this.reply(responseText)
}
}

39
apps/management.js Normal file
View file

@ -0,0 +1,39 @@
import ChatGPTConfig from '../config/config.js'
import { createCRUDCommandRules, createSwitchCommandRules } from '../utils/command.js'
export class ChatGPTManagement extends plugin {
constructor () {
const cmdPrefix = ChatGPTConfig.basic.commandPrefix
super({
name: 'ChatGPT-Plugin管理',
dsc: 'ChatGPT-Plugin管理',
event: 'message',
priority: 20,
rule: [
{
reg: `^${cmdPrefix}管理面板$`,
fnc: 'managementPanel',
permission: 'master'
},
{
reg: `^${cmdPrefix}结束(全部)?对话$`,
fnc: 'destroyConversation'
},
...createCRUDCommandRules(cmdPrefix, '渠道', 'channels'),
...createCRUDCommandRules(cmdPrefix, '预设', 'presets'),
...createCRUDCommandRules(cmdPrefix, '工具', 'tools'),
...createCRUDCommandRules(cmdPrefix, '处理器', 'processors'),
createSwitchCommandRules(cmdPrefix, '(预设切换|其他人切换预设)', 'customPreset', 1),
createSwitchCommandRules(cmdPrefix, '(调试|debug)(模式)?', 'debug'),
...createCRUDCommandRules(cmdPrefix, '预设切换黑名单', 'blackCustomPreset', false),
...createCRUDCommandRules(cmdPrefix, '预设切换白名单', 'whiteCustomPreset', false),
...createCRUDCommandRules(cmdPrefix, '输入屏蔽词', 'blackPromptWords', false),
...createCRUDCommandRules(cmdPrefix, '输出屏蔽词', 'blackResponseWords', false),
...createCRUDCommandRules(cmdPrefix, '黑名单群', 'blackGroups', false),
...createCRUDCommandRules(cmdPrefix, '白名单群', 'whiteGroups', false),
...createCRUDCommandRules(cmdPrefix, '黑名单用户', 'blackUsers', false),
...createCRUDCommandRules(cmdPrefix, '白名单用户', 'whiteUsers', false)
]
})
}
}

View file

@ -18,7 +18,9 @@ class ChatGPTConfig {
// 触发前缀,仅在前缀触发时有效
togglePrefix: '#chat',
// 是否开启调试模式
debug: false
debug: false,
// 一般命令的开头
commandPrefix: '^#chatgpt'
}
/**

View file

@ -24,7 +24,7 @@ export class LowDBChatPresetsStorage extends ChaiteStorage {
* @returns {Promise<import('chaite').ChatPreset>}
*/
async getItem (key) {
return this.collection.findOne({ id: key })
}
/**

49
utils/command.js Normal file
View file

@ -0,0 +1,49 @@
/**
* 模板
* @param cmdPrefix
* @param name
* @param variable
* @param detail
* @returns {{reg: string, fnc: string}[]}
*/
export function createCRUDCommandRules (cmdPrefix, name, variable, detail = true) {
// make the first letter of variable capable
variable = variable.charAt(0).toUpperCase() + variable.slice(1)
const rules = [
{
reg: cmdPrefix + `${name}列表$`,
fnc: `list${variable}`
},
{
reg: cmdPrefix + `(编辑|修改)${name}`,
fnc: `edit${variable}`
},
{
reg: cmdPrefix + `(添加|新增)${name}$`,
fnc: `add${variable}`
},
{
reg: cmdPrefix + `删除${name}`,
fnc: `remove${variable}`
}
]
if (detail) {
rules.push({
reg: cmdPrefix + `${name}详情$`,
fnc: `detail${variable}`
})
}
return rules
}
const switchCommandPreset = {
0: ['开启', '关闭'],
1: ['允许', '禁止']
}
export function createSwitchCommandRules (cmdPrefix, name, variable, preset = 0) {
variable = variable.charAt(0).toUpperCase() + variable.slice(1)
return {
reg: cmdPrefix + `(${switchCommandPreset[preset][0]}|${switchCommandPreset[preset][1]})${name}$`,
fnc: `switch${variable}`
}
}

145
utils/message.js Normal file
View file

@ -0,0 +1,145 @@
import { Chaite } from 'chaite'
/**
* 将e中的消息转换为chaite的UserMessage
*
* @param e
* @param {{
* handleReplyText: boolean,
* handleReplyImage: boolean,
* useRawMessage: boolean,
* handleAtMsg: boolean,
* excludeAtBot: boolean,
* toggleMode: 'at' | 'prefix',
* togglePrefix: string
* }} options
* @returns {Promise<import('chaite').UserMessage>}
*/
export async function intoUserMessage (e, options = {}) {
const {
handleReplyText = false,
handleReplyImage = true,
useRawMessage = false,
handleAtMsg = true,
excludeAtBot = false,
toggleMode = 'at',
togglePrefix = null
} = options
const contents = []
let text = ''
if (e.source && (handleReplyImage || handleReplyText)) {
let seq = e.isGroup ? e.source.seq : e.source.time
let reply = e.isGroup
? (await e.group.getChatHistory(seq, 1)).pop()?.message
: (await e.friend.getChatHistory(seq, 1)).pop()?.message
if (reply) {
for (let val of reply) {
if (val.type === 'image' && handleReplyImage) {
contents.push({
type: 'image',
url: val.url
})
} else if (val.type === 'text' && handleReplyText) {
text = `本条消息对以下消息进行了引用回复:${val.text}\n\n本条消息内容:\n`
}
}
}
}
if (useRawMessage) {
text += e.raw_message
} else {
for (let val of e.message) {
switch (val.type) {
case 'at': {
if (handleAtMsg) {
const { qq, text: atCard } = val
if ((toggleMode === 'at' || excludeAtBot) && qq === e.bot.uin) {
break
}
text += ` @${atCard || qq} `
}
break
}
case 'text': {
text += val.text
break
}
default:
}
}
}
e.message?.filter(element => element.type === 'image').forEach(element => {
contents.push({
type: 'image',
url: element.url
})
})
if (toggleMode === 'prefix') {
const regex = new RegExp(`^#?(图片)?${togglePrefix}[^gpt]`)
text = text.replace(regex, '')
}
if (text) {
contents.push({
type: 'text',
content: text
})
}
return {
role: 'user',
content: contents
}
}
/**
* 找到本次对话使用的预设
* @param e
* @param {string} presetId
* @param {'at' | 'prefix'} toggleMode
* @param {string} togglePrefix
* @returns {Promise<import('chaite').ChatPreset | null>}
*/
export async function getPreset (e, presetId, toggleMode, togglePrefix) {
const isValidChat = checkChatMsg(e, toggleMode, togglePrefix)
const manager = Chaite.getInstance().getChatPresetManager()
const presets = await manager.getAllPresets()
const prefixHitPresets = presets.filter(p => e.msg.startsWith(p.prefix))
if (!isValidChat && prefixHitPresets.length === 0) {
return null
}
let preset
// 如果不是at且不满足通用前缀查看是否满足其他预设
if (!isValidChat) {
// 找到其中prefix最长的
if (prefixHitPresets.length > 1) {
preset = prefixHitPresets.sort((a, b) => b.prefix.length - a.prefix.length)[0]
} else {
preset = prefixHitPresets[0]
}
} else {
// 命中at或通用前缀直接走用户默认预设
preset = await manager.getInstance(presetId)
}
// 如果没找到再查一次
if (!preset) {
preset = await manager.getInstance(presetId)
}
return preset
}
/**
*
* @param e
* @param {'at' | 'prefix'} toggleMode
* @param {string} togglePrefix
* @returns {boolean}
*/
export function checkChatMsg (e, toggleMode, togglePrefix) {
if (toggleMode === 'at' && e.atBot) {
return true
}
const prefixReg = new RegExp(`^#?(图片)?${togglePrefix}[^gpt][sS]*`)
if (toggleMode === 'prefix' && e.msg.startsWith(prefixReg)) {
return true
}
return false
}