From 233fc7417c096109ed11615587276916e9e81149 Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Wed, 8 Feb 2023 21:09:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8F=90=E4=BE=9B=E4=BA=86=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E7=9A=84=E5=8F=AF=E9=80=89=E9=85=8D=E7=BD=AE=E9=A1=B9?= =?UTF-8?q?=EF=BC=8C=E5=8C=85=E6=8B=AC=E7=A1=AE=E8=AE=A4=E5=BC=80=E5=85=B3?= =?UTF-8?q?=E3=80=81=E6=9A=97=E7=A4=BA=E6=8C=87=E4=BB=A4=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E3=80=81=E8=A7=A6=E5=8F=91=E6=96=B9=E5=BC=8F=E3=80=81=E6=A8=A1?= =?UTF-8?q?=E5=9E=8B=E5=90=8D=E7=A7=B0=E5=92=8C=E5=B1=8F=E8=94=BD=E8=AF=8D?= =?UTF-8?q?=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/chat.js | 60 +++++++++++++++++++++++++++++++++------------- apps/help.js | 7 +++++- apps/management.js | 32 +++++++++++++++++++++++++ config/index.js | 18 +++++++++++--- 4 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 apps/management.js diff --git a/apps/chat.js b/apps/chat.js index 9dbde61..bc19a1f 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -9,7 +9,7 @@ import { ChatGPTAPI } from 'chatgpt' import { getMessageById, upsertMessage } from '../utils/common.js' // import puppeteer from '../utils/browser.js' // import showdownKatex from 'showdown-katex' -const blockWords = '屏蔽词1,屏蔽词2,屏蔽词3' +const blockWords = Config.blockWords const converter = new showdown.Converter({ extensions: [ // showdownKatex({ @@ -38,6 +38,7 @@ mjAPI.start() export class chatgpt extends plugin { constructor () { + let toggleMode = Config.toggleMode super({ /** 功能名称 */ name: 'chatgpt', @@ -50,7 +51,7 @@ export class chatgpt extends plugin { rule: [ { /** 命令正则匹配 */ - reg: '^[^#][sS]*', + reg: toggleMode === 'at' ? '^[^#][sS]*' : '#chat[sS]*', /** 执行方法 */ fnc: 'chatgpt' }, @@ -85,6 +86,7 @@ export class chatgpt extends plugin { } ] }) + this.toggleMode = toggleMode } /** @@ -177,29 +179,49 @@ export class chatgpt extends plugin { * @param e oicq传递的事件参数e */ async chatgpt (e) { - if (!e.msg || e.msg.startsWith('#')) { - return + let prompt + if (this.toggleMode === 'at') { + if (!e.msg || e.msg.startsWith('#')) { + return false + } + if (e.isGroup && !e.atme) { + return false + } + prompt = e.msg.trim() + } else { + prompt = _.trimStart(e.msg.trimStart(), '#chat').trim() + if (prompt.length === 0) { + return false + } } - if (e.isGroup && !e.atme) { - return + let completionParams = {} + if (Config.model) { + completionParams.model = Config.model } + this.chatGPTApi = new ChatGPTAPI({ apiKey: Config.apiKey, debug: false, upsertMessage, - getMessageById + getMessageById, + completionParams, + assistantLabel: Config.assistantLabel }) - let prompt = e.msg.trimStart() - let randomId = uuid() // 队列队尾插入,开始排队 await redis.rPush('CHATGPT:CHAT_QUEUE', [randomId]) + let confirm = await redis.get('CHATGPT:CONFIRM') + let confirmOn = confirm === 'on' if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) { - await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 8 }) + if (confirmOn) { + await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 8 }) + } } else { - let length = await redis.lLen('CHATGPT:CHAT_QUEUE') - 1 - await this.reply(`我正在思考如何回复你,请稍等,当前队列前方还有${length}个问题`, true, { recallMsg: 8 }) + if (confirmOn) { + let length = await redis.lLen('CHATGPT:CHAT_QUEUE') - 1 + await this.reply(`我正在思考如何回复你,请稍等,当前队列前方还有${length}个问题`, true, { recallMsg: 8 }) + } // 开始排队 while (true) { if (await redis.lIndex('CHATGPT:CHAT_QUEUE', 0) === randomId) { @@ -234,10 +256,15 @@ export class chatgpt extends plugin { parentMessageId: previousConversation.conversation.parentMessageId } } - + const currentDate = new Date().toISOString().split('T')[0] + let defaultPropmtPrefix = 'You answer as concisely as possible for each response (e.g. don’t be verbose). It is very important that you answer as concisely as possible, so please remember this. If you are generating a list, do not have too many items. Keep the number of items short.' + let promptPrefix = `You are ${Config.assistantLabel}, a large language model trained by OpenAI. ${Config.promptPrefixOverride || defaultPropmtPrefix} + Knowledge cutoff: 2021-09 + Current date: ${currentDate}` try { let option = { - timeoutMs: 120000 + timeoutMs: 120000, + promptPrefix } if (conversation) { option = Object.assign(option, conversation) @@ -254,7 +281,7 @@ export class chatgpt extends plugin { const blockWord = blockWords.split(',').find(word => response.toLowerCase().includes(word.toLowerCase())) if (blockWord) { await this.reply('返回内容存在敏感词,我不想回答你', true) - return + return false } let userSetting = await redis.get(`CHATGPT:USER:${e.sender.user_id}`) if (userSetting) { @@ -274,7 +301,8 @@ export class chatgpt extends plugin { // ) { await this.reply('内容有点多,我正在奋笔疾书,请再等一会', true, { recallMsg: 5 }) option = { - timeoutMs: 120000 + timeoutMs: 120000, + promptPrefix } option = Object.assign(option, previousConversation.conversation) const responseAppend = await this.chatGPTApi.sendMessage('Continue', option) diff --git a/apps/help.js b/apps/help.js index a75d79c..cab7fe5 100644 --- a/apps/help.js +++ b/apps/help.js @@ -23,7 +23,7 @@ let helpData = [ icon: 'text', title: '#chatgpt文本模式', desc: '机器人以文本形式回答,默认选项' - }, + } ] }, @@ -55,6 +55,11 @@ let helpData = [ title: '#移出chat队列首位', desc: '移出当前对话等待队列中的首位。若前方对话卡死可使用本命令。' }, + { + icon: 'destroy-other', + title: '#chatgpt开启/关闭问题确认', + desc: '开启或关闭机器人收到消息后的确认回复消息。私聊无效。' + }, { icon: 'help', title: '#chatgpt帮助', diff --git a/apps/management.js b/apps/management.js new file mode 100644 index 0000000..b9466a6 --- /dev/null +++ b/apps/management.js @@ -0,0 +1,32 @@ +import plugin from '../../../lib/plugins/plugin.js' + +export class ChatgptManagement extends plugin { + constructor (e) { + super({ + name: 'ChatGPT-Plugin管理', + dsc: 'ChatGPT-Plugin管理', + event: 'message', + priority: 500, + rule: [ + { + reg: '#chatgpt开启(问题)?确认', + fnc: 'turnOnConfirm' + }, + { + reg: '#chatgpt关闭(问题)?确认', + fnc: 'turnOffConfirm' + } + ] + }) + } + + async turnOnConfirm (e) { + await redis.set('CHATGPT:CONFIRM', 'on') + await this.reply('已开启消息确认', true) + } + + async turnOffConfirm (e) { + await redis.set('CHATGPT:CONFIRM', 'off') + await this.reply('已关闭消息确认', true) + } +} diff --git a/config/index.js b/config/index.js index dcbfa14..19a062b 100644 --- a/config/index.js +++ b/config/index.js @@ -1,12 +1,24 @@ -const PROXY = 'http://127.0.0.1:7890' +const PROXY = '' const API_KEY = '' export const Config = { + // 模型名称。如无特殊需求保持默认即可,会使用chatgpt-api库提供的当前可用的最适合的默认值。 + model: '', + // 如果回答包括屏蔽词,就不返回。例如:'屏蔽词1,屏蔽词2,屏蔽词3' + blockWords: '', apiKey: API_KEY, // 暂时不支持proxy proxy: PROXY, // 改为true后,全局默认以图片形式回复,并自动发出Continue命令补全回答 - defaultUsePicture: true, + defaultUsePicture: false, // 每个人发起的对话保留时长。超过这个时长没有进行对话,再进行对话将开启新的对话。单位:秒 - conversationPreserveTime: 0 + conversationPreserveTime: 0, + // 触发方式 可选值:at 或 prefix 。at模式下只有at机器人才会回复。prefix模式下不需要at,但需要添加前缀#chat + toggleMode: 'at', + // 默认完整值:`You are ${this._assistantLabel}, a large language model trained by OpenAI. You answer as concisely as possible for each response (e.g. don’t be verbose). It is very important that you answer as concisely as possible, so please remember this. If you are generating a list, do not have too many items. Keep the number of items short. Current date: ${currentDate}\n\n + // 此项配置会覆盖掉中间部分 + // 你可以在这里写入你希望AI回答的风格,比如希望优先回答中文,去掉尽可能简洁的要求等 + promptPrefixOverride: '', + // AI认为的自己的名字。 + assistantLabel: 'ChatGPT' }