mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 21:37:11 +00:00
feat: bym
This commit is contained in:
parent
6997d1e024
commit
9fcc25a726
5 changed files with 219 additions and 46 deletions
93
apps/bym.js
Normal file
93
apps/bym.js
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
import ChatGPTConfig from '../config/config.js'
|
||||||
|
import { Chaite } from 'chaite'
|
||||||
|
import { intoUserMessage, toYunzai } from '../utils/message.js'
|
||||||
|
import common from '../../../lib/common/common.js'
|
||||||
|
|
||||||
|
export class bym extends plugin {
|
||||||
|
constructor () {
|
||||||
|
super({
|
||||||
|
name: 'ChatGPT-Plugin伪人模式',
|
||||||
|
dsc: 'ChatGPT-Plugin伪人模式',
|
||||||
|
event: 'message',
|
||||||
|
priority: -150,
|
||||||
|
rule: [
|
||||||
|
{
|
||||||
|
reg: '^#chatgpt伪人模式$',
|
||||||
|
fnc: 'bym'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
async bym (e) {
|
||||||
|
if (!ChatGPTConfig.bym.enable) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
let recall = false
|
||||||
|
let presetId = ChatGPTConfig.bym.defaultPreset
|
||||||
|
if (ChatGPTConfig.bym.presetMap && ChatGPTConfig.bym.presetMap.length > 0) {
|
||||||
|
const option = ChatGPTConfig.bym.presetMap.sort((a, b) => a.priority - b.priority)
|
||||||
|
.find(item => item.keywords.find(keyword => e.msg.includes(keyword)))
|
||||||
|
if (option) {
|
||||||
|
presetId = option.presetId
|
||||||
|
}
|
||||||
|
recall = !!option.recall
|
||||||
|
}
|
||||||
|
|
||||||
|
const presetManager = Chaite.getInstance().getChatPresetManager()
|
||||||
|
let preset = await presetManager.getInstance(presetId)
|
||||||
|
if (!preset) {
|
||||||
|
preset = await presetManager.getInstance(ChatGPTConfig.bym.defaultPreset)
|
||||||
|
}
|
||||||
|
if (!preset) {
|
||||||
|
logger.debug('未找到预设,请检查配置文件')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if (ChatGPTConfig.bym.presetPrefix) {
|
||||||
|
if (!preset.sendMessageOption.systemOverride) {
|
||||||
|
preset.sendMessageOption.systemOverride = ''
|
||||||
|
}
|
||||||
|
preset.sendMessageOption.systemOverride = ChatGPTConfig.bym.presetPrefix + preset.sendMessageOption.systemOverride
|
||||||
|
}
|
||||||
|
const userMessage = await intoUserMessage(e, {
|
||||||
|
handleReplyText: true,
|
||||||
|
handleReplyImage: true,
|
||||||
|
useRawMessage: true,
|
||||||
|
handleAtMsg: true,
|
||||||
|
excludeAtBot: false,
|
||||||
|
toggleMode: ChatGPTConfig.basic.toggleMode,
|
||||||
|
togglePrefix: ChatGPTConfig.basic.togglePrefix
|
||||||
|
})
|
||||||
|
// 伪人不记录历史
|
||||||
|
preset.sendMessageOption.disableHistoryRead = true
|
||||||
|
preset.sendMessageOption.disableHistorySave = true
|
||||||
|
// 设置多轮调用回掉
|
||||||
|
preset.sendMessageOption.onMessageWithToolCall = async content => {
|
||||||
|
const { msgs, forward } = await toYunzai(e, [content])
|
||||||
|
if (msgs.length > 0) {
|
||||||
|
await e.reply(msgs)
|
||||||
|
}
|
||||||
|
for (let forwardElement of forward) {
|
||||||
|
this.reply(forwardElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 发送
|
||||||
|
const response = await Chaite.getInstance().sendMessage(userMessage, e, {
|
||||||
|
...preset.sendMessageOption,
|
||||||
|
chatPreset: preset
|
||||||
|
})
|
||||||
|
const { msgs, forward } = await toYunzai(e, response.contents)
|
||||||
|
if (msgs.length > 0) {
|
||||||
|
// await e.reply(msgs, false, { recallMsg: recall })
|
||||||
|
for (let msg of msgs) {
|
||||||
|
await e.reply(msg, false, { recallMsg: recall ? 10 : 0 })
|
||||||
|
await common.sleep(Math.floor(Math.random() * 2000) + 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ChatGPTConfig.bym.sendReasoning) {
|
||||||
|
for (let forwardElement of forward) {
|
||||||
|
await e.reply(forwardElement, false, { recallMsg: recall ? 10 : 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
25
apps/chat.js
25
apps/chat.js
|
|
@ -1,6 +1,6 @@
|
||||||
import Config from '../config/config.js'
|
import Config from '../config/config.js'
|
||||||
import { Chaite, SendMessageOption } from 'chaite'
|
import { Chaite, SendMessageOption } from 'chaite'
|
||||||
import { getPreset, intoUserMessage } from '../utils/message.js'
|
import { getPreset, intoUserMessage, toYunzai } from '../utils/message.js'
|
||||||
|
|
||||||
export class Chat extends plugin {
|
export class Chat extends plugin {
|
||||||
constructor () {
|
constructor () {
|
||||||
|
|
@ -22,9 +22,18 @@ export class Chat extends plugin {
|
||||||
async chat (e) {
|
async chat (e) {
|
||||||
const state = await Chaite.getInstance().getUserStateStorage().getItem(e.sender.user_id + '')
|
const state = await Chaite.getInstance().getUserStateStorage().getItem(e.sender.user_id + '')
|
||||||
const sendMessageOptions = SendMessageOption.create(state?.settings)
|
const sendMessageOptions = SendMessageOption.create(state?.settings)
|
||||||
|
sendMessageOptions.onMessageWithToolCall = async content => {
|
||||||
|
const { msgs, forward } = await toYunzai(e, [content])
|
||||||
|
if (msgs.length > 0) {
|
||||||
|
await e.reply(msgs)
|
||||||
|
}
|
||||||
|
for (let forwardElement of forward) {
|
||||||
|
this.reply(forwardElement)
|
||||||
|
}
|
||||||
|
}
|
||||||
const preset = await getPreset(e, state?.settings.preset || Config.llm.defaultChatPresetId, Config.basic.toggleMode, Config.basic.togglePrefix)
|
const preset = await getPreset(e, state?.settings.preset || Config.llm.defaultChatPresetId, Config.basic.toggleMode, Config.basic.togglePrefix)
|
||||||
if (!preset) {
|
if (!preset) {
|
||||||
logger.debug('未找到预设,不进入对话')
|
logger.debug('不满足对话触发条件或未找到预设,不进入对话')
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
const userMessage = await intoUserMessage(e, {
|
const userMessage = await intoUserMessage(e, {
|
||||||
|
|
@ -40,10 +49,12 @@ export class Chat extends plugin {
|
||||||
...sendMessageOptions,
|
...sendMessageOptions,
|
||||||
chatPreset: preset
|
chatPreset: preset
|
||||||
})
|
})
|
||||||
const responseText = response.contents
|
const { msgs, forward } = await toYunzai(e, response.contents)
|
||||||
.filter(c => c.type === 'text')
|
if (msgs.length > 0) {
|
||||||
.map(c => (/** @type {import('chaite').TextContent} **/ c).text)
|
await e.reply(msgs, true)
|
||||||
.reduce((a, b) => a + b, '')
|
}
|
||||||
await this.reply(responseText)
|
for (let forwardElement of forward) {
|
||||||
|
this.reply(forwardElement)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import ChatGPTConfig from '../config/config.js'
|
import ChatGPTConfig from '../config/config.js'
|
||||||
import { createCRUDCommandRules, createSwitchCommandRules } from '../utils/command.js'
|
import { createCRUDCommandRules, createSwitchCommandRules } from '../utils/command.js'
|
||||||
import { Chaite } from 'chaite'
|
import { Chaite } from 'chaite'
|
||||||
import { resolve } from 'eslint-plugin-promise/rules/lib/promise-statics.js'
|
|
||||||
|
|
||||||
export class ChatGPTManagement extends plugin {
|
export class ChatGPTManagement extends plugin {
|
||||||
constructor () {
|
constructor () {
|
||||||
|
|
@ -20,6 +19,11 @@ export class ChatGPTManagement extends plugin {
|
||||||
{
|
{
|
||||||
reg: `^${cmdPrefix}结束(全部)?对话$`,
|
reg: `^${cmdPrefix}结束(全部)?对话$`,
|
||||||
fnc: 'destroyConversation'
|
fnc: 'destroyConversation'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
reg: `^${cmdPrefix}(bym|伪人)设置默认预设`,
|
||||||
|
fnc: 'setDefaultBymPreset',
|
||||||
|
permission: 'master'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
|
|
@ -49,7 +53,8 @@ export class ChatGPTManagement extends plugin {
|
||||||
...createCRUDCommandRules.bind(this)(cmdPrefix, '黑名单群', 'blackGroups', false),
|
...createCRUDCommandRules.bind(this)(cmdPrefix, '黑名单群', 'blackGroups', false),
|
||||||
...createCRUDCommandRules.bind(this)(cmdPrefix, '白名单群', 'whiteGroups', false),
|
...createCRUDCommandRules.bind(this)(cmdPrefix, '白名单群', 'whiteGroups', false),
|
||||||
...createCRUDCommandRules.bind(this)(cmdPrefix, '黑名单用户', 'blackUsers', false),
|
...createCRUDCommandRules.bind(this)(cmdPrefix, '黑名单用户', 'blackUsers', false),
|
||||||
...createCRUDCommandRules.bind(this)(cmdPrefix, '白名单用户', 'whiteUsers', false)
|
...createCRUDCommandRules.bind(this)(cmdPrefix, '白名单用户', 'whiteUsers', false),
|
||||||
|
createSwitchCommandRules(cmdPrefix, '(伪人|bym)', 'bym')
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +65,17 @@ export class ChatGPTManagement extends plugin {
|
||||||
this.reply(`token: ${token}, 有效期300秒`, true)
|
this.reply(`token: ${token}, 有效期300秒`, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async setDefaultBymPreset (e) {
|
||||||
|
const presetId = e.msg.replace(`${ChatGPTConfig.basic.commandPrefix}伪人设置默认预设`, '')
|
||||||
|
const preset = await Chaite.getInstance().getChatPresetManager().getInstance(presetId)
|
||||||
|
if (preset) {
|
||||||
|
ChatGPTConfig.bym.defaultPreset = presetId
|
||||||
|
this.reply(`伪人模式默认预设已切换为${presetId}(${preset.name})`)
|
||||||
|
} else {
|
||||||
|
this.reply(`未找到预设${presetId}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async destroyConversation (e) {
|
async destroyConversation (e) {
|
||||||
if (e.msg.includes('全部')) {
|
if (e.msg.includes('全部')) {
|
||||||
if (!e.isMaster) {
|
if (!e.isMaster) {
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,47 @@ class ChatGPTConfig {
|
||||||
commandPrefix: '^#chatgpt'
|
commandPrefix: '^#chatgpt'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 伪人模式,基于框架实现,因此机器人开启前缀后依然需要带上前缀。
|
||||||
|
* @type {{
|
||||||
|
* enable: boolean,
|
||||||
|
* hit: string[],
|
||||||
|
* probability: number,
|
||||||
|
* defaultPreset: string,
|
||||||
|
* presetPrefix?: string,
|
||||||
|
* presetMap: Array<{
|
||||||
|
* keywords: string[],
|
||||||
|
* presetId: string,
|
||||||
|
* priority: number,
|
||||||
|
* recall?: boolean
|
||||||
|
* }>,
|
||||||
|
* maxTokens: number,
|
||||||
|
* temperature: number,
|
||||||
|
* sendReasoning: boolean
|
||||||
|
* }}
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
bym = {
|
||||||
|
// 开关
|
||||||
|
enable: false,
|
||||||
|
// 伪人必定触发词
|
||||||
|
hit: ['bym'],
|
||||||
|
// 不包含伪人必定触发词时的概率
|
||||||
|
probability: 0.02,
|
||||||
|
// 伪人模式的默认预设
|
||||||
|
defaultPreset: '',
|
||||||
|
// 伪人模式的预设前缀,会加在在所有其他预设前。例如此处可以用于配置通用的伪人发言风格(随意、模仿群友等),presetMap中专心配置角色设定即可
|
||||||
|
presetPrefix: '',
|
||||||
|
// 包含关键词与预设的对应关系。包含特定触发词使用特定的预设,按照优先级排序
|
||||||
|
presetMap: [],
|
||||||
|
// 如果大于0,会覆盖preset中的maxToken,用于控制伪人模式发言长度
|
||||||
|
maxTokens: 0,
|
||||||
|
// 如果大于等于0,会覆盖preset中的temperature,用于控制伪人模式发言随机性
|
||||||
|
temperature: -1,
|
||||||
|
// 是否发送思考内容
|
||||||
|
sendReasoning: false
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 模型和对话相关配置
|
* 模型和对话相关配置
|
||||||
* @type {{
|
* @type {{
|
||||||
|
|
@ -119,43 +160,6 @@ class ChatGPTConfig {
|
||||||
|
|
||||||
constructor () {
|
constructor () {
|
||||||
this.version = '3.0.0'
|
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.watcher = null
|
||||||
this.configPath = ''
|
this.configPath = ''
|
||||||
}
|
}
|
||||||
|
|
@ -216,6 +220,7 @@ class ChatGPTConfig {
|
||||||
|
|
||||||
// 为所有嵌套对象创建Proxy
|
// 为所有嵌套对象创建Proxy
|
||||||
this.basic = createDeepProxy(this.basic, handler)
|
this.basic = createDeepProxy(this.basic, handler)
|
||||||
|
this.bym = createDeepProxy(this.bym, handler)
|
||||||
this.llm = createDeepProxy(this.llm, handler)
|
this.llm = createDeepProxy(this.llm, handler)
|
||||||
this.management = createDeepProxy(this.management, handler)
|
this.management = createDeepProxy(this.management, handler)
|
||||||
this.chaite = createDeepProxy(this.chaite, handler)
|
this.chaite = createDeepProxy(this.chaite, handler)
|
||||||
|
|
@ -248,6 +253,7 @@ class ChatGPTConfig {
|
||||||
const config = {
|
const config = {
|
||||||
version: this.version,
|
version: this.version,
|
||||||
basic: this.basic,
|
basic: this.basic,
|
||||||
|
bym: this.bym,
|
||||||
llm: this.llm,
|
llm: this.llm,
|
||||||
management: this.management,
|
management: this.management,
|
||||||
chaite: this.chaite
|
chaite: this.chaite
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { Chaite } from 'chaite'
|
import { Chaite } from 'chaite'
|
||||||
|
import common from '../../../lib/common/common.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将e中的消息转换为chaite的UserMessage
|
* 将e中的消息转换为chaite的UserMessage
|
||||||
|
|
@ -143,3 +144,49 @@ export function checkChatMsg (e, toggleMode, togglePrefix) {
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模型响应转为机器人格式
|
||||||
|
* @param e
|
||||||
|
* @param {import('chaite').MessageContent[]} contents
|
||||||
|
* @returns {Promise<{ msgs: (import('icqq').TextElem | import('icqq').ImageElem | import('icqq').AtElem | import('icqq').PttElem | string)[], forward: *[]}>}
|
||||||
|
*/
|
||||||
|
export async function toYunzai (e, contents) {
|
||||||
|
/**
|
||||||
|
* 要发送的消息
|
||||||
|
* @type {(import('icqq').TextElem | import('icqq').ImageElem | import('icqq').AtElem | import('icqq').PttElem | string)[]}
|
||||||
|
*/
|
||||||
|
const msgs = []
|
||||||
|
/**
|
||||||
|
* 要转发的
|
||||||
|
* @type {*[]}
|
||||||
|
*/
|
||||||
|
const forward = []
|
||||||
|
for (let content of contents) {
|
||||||
|
switch (content.type) {
|
||||||
|
case 'text': {
|
||||||
|
msgs.push((/** @type {import('chaite').TextContent} **/ content).text)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'image': {
|
||||||
|
msgs.push(segment.image((/** @type {import('chaite').ImageContent} **/ content).image))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'audio': {
|
||||||
|
msgs.push(segment.record((/** @type {import('chaite').AudioContent} **/ content).data))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
case 'reasoning': {
|
||||||
|
const reasoning = await common.makeForwardMsg(e, [(/** @type {import('chaite').ReasoningContent} **/ content).text], '思考过程')
|
||||||
|
forward.push(reasoning)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
logger.warn(`不支持的类型 ${content.type}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
msgs, forward
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue