添加锅巴面板配置支持 (#184)

* 修复引用转发,默认bing模式并发

* 开启stream增加稳定性

* fix: remove queue element only in non-bing mode

* 使用chatgpt-api自带的超时逻辑,文字过多时启动切换到图片输出防止被吞

* Update chat.js

* 添加Bing专用的图片输出样式

* 添加chatgpt的新图片模式,临时处理切换api导致的对话异常

* 修改bing样式表

* 为图片添加外部页面缓存

* 为图片模式添加MathJax

* feat: add switch for qrcode

* 防止script攻击

* 修复网页模板错误

* 修复bing页面引用错误

* 缓存服务器异常时处理

* 添加默认配置加载

* 修复配置文件路径错误

* 删除重复的模板文件,修复二维码地址错误

* 修正图片渲染错误

* 修复引用渲染错误

* 二维码网址统一改为使用本地配置

* 添加关闭思考提示的配置项

* 修复在Windows上无法载入配置文件的问题

* 修复关闭qr的情况下渲染错误

* 改为使用base64传递返回数据

* 当异常过多时使用图片输出

* 添加锅巴面板配置支持

* 补充遗漏的默认配置

* 修复qr模式下引用未被传递的问题

---------

Co-authored-by: ikechan8370 <geyinchibuaa@gmail.com>
This commit is contained in:
HalcyonAlcedo 2023-02-22 18:51:05 +08:00 committed by GitHub
parent 53ff4b653c
commit a68103737f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 267 additions and 9 deletions

View file

@ -469,7 +469,7 @@ export class chatgpt extends plugin {
if (cacheres.ok) { if (cacheres.ok) {
cache = Object.assign({}, cache, await cacheres.json()) cache = Object.assign({}, cache, await cacheres.json())
} }
await e.runtime.render('chatgpt-plugin', use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index', { content: response, prompt: escapeHtml(prompt), senderName: e.sender.nickname, cache }) await e.runtime.render('chatgpt-plugin', use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index', { content: response, prompt: escapeHtml(prompt), senderName: e.sender.nickname, quote: quotemessage.length > 0, quotes: quotemessage, cache })
} catch (err) { } catch (err) {
logger.warn('error happened while uploading content to the cache server. QR Code will not be showed in this picture.') logger.warn('error happened while uploading content to the cache server. QR Code will not be showed in this picture.')
logger.error(err) logger.error(err)
@ -489,16 +489,23 @@ export class chatgpt extends plugin {
// 移除队列首位,释放锁 // 移除队列首位,释放锁
await redis.lPop('CHATGPT:CHAT_QUEUE', 0) await redis.lPop('CHATGPT:CHAT_QUEUE', 0)
} }
} catch (e) { } catch (err) {
logger.error(e) logger.error(err)
if (use !== 'bing') { if (use !== 'bing') {
// 异常了也要腾地方todo 大概率后面的也会异常,要不要一口气全杀了) // 异常了也要腾地方todo 大概率后面的也会异常,要不要一口气全杀了)
await redis.lPop('CHATGPT:CHAT_QUEUE', 0) await redis.lPop('CHATGPT:CHAT_QUEUE', 0)
} }
if (e === 'Error: {"detail":"Conversation not found"}') { if (err === 'Error: {"detail":"Conversation not found"}') {
await this.destroyConversations(e) await this.destroyConversations(err)
await this.reply('当前对话异常,已经清除,请重试', true, { recallMsg: e.isGroup ? 10 : 0 }) await this.reply('当前对话异常,已经清除,请重试', true, { recallMsg: e.isGroup ? 10 : 0 })
} else { await this.reply(`通信异常,请稍后重试:${e}`, true, { recallMsg: e.isGroup ? 10 : 0 }) } } else {
if (err.length < 200) {
await this.reply(`通信异常,请稍后重试:${err}`, true, { recallMsg: e.isGroup ? 10 : 0 })
} else {
//这里是否还需要上传到缓存服务器呐?多半是代理服务器的问题,本地也修不了,应该不用吧。
await e.runtime.render('chatgpt-plugin', use !== 'bing' ? 'content/ChatGPT/index' : 'content/Bing/index', { content: new Buffer.from(`通信异常,错误信息如下 ${err}`).toString("base64"), prompt: escapeHtml(prompt), senderName: e.sender.nickname, quote: false , quotes: [], cache: {file:'',cacheUrl:Config.cacheUrl} })
}
}
} }
} }

View file

@ -80,7 +80,7 @@ export default {
// 各个地方的默认超时时间 // 各个地方的默认超时时间
// defaultTimeoutMs: 120000, // defaultTimeoutMs: 120000,
// bing默认超时时间bing太慢了有的时候 // bing默认超时时间bing太慢了有的时候
//bingTimeoutMs: 360000, // bingTimeoutMs: 360000,
// 浏览器默认超时,浏览器可能需要更高的超时时间 // 浏览器默认超时,浏览器可能需要更高的超时时间
// chromeTimeoutMS: 120000 // chromeTimeoutMS: 120000
} }

233
guoba.support.js Normal file
View file

@ -0,0 +1,233 @@
import { Config } from './utils/config.js'
// 支持锅巴
export function supportGuoba() {
return {
// 插件信息,将会显示在前端页面
// 如果你的插件没有在插件库里,那么需要填上补充信息
// 如果存在的话,那么填不填就无所谓了,填了就以你的信息为准
pluginInfo: {
name: 'chatgpt-plugin',
title: 'ChatGPT-Plugin',
author: '@ikechan8370',
authorLink: 'https://github.com/ikechan8370',
link: 'https://github.com/ikechan8370/chatgpt-plugin',
isV3: true,
isV2: false,
description: '基于OpenAI最新推出的chatgpt和微软的 New bing通过api进行问答的插件需自备openai账号或有New bing访问权限的必应账号',
// 显示图标,此为个性化配置
// 图标可在 https://icon-sets.iconify.design 这里进行搜索
icon: 'simple-icons:openai',
// 图标颜色,例:#FF0000 或 rgb(255, 0, 0)
iconColor: '#00c3ff',
},
// 配置项信息
configInfo: {
// 配置项 schemas
schemas: [
{
field: 'defaultUsePicture',
label: '全局图片模式',
bottomHelpMessage: '全局默认以图片形式回复并自动发出Continue命令补全回答。长回复可能会有bug。',
component: 'Switch',
},
{
field: 'autoUsePicture',
label: '长文本自动转图片',
bottomHelpMessage: '字数大于阈值会自动用图片发送,即使是文本模式。',
component: 'Switch',
},
{
field: 'autoUsePictureThreshold',
label: '自动转图片阈值',
helpMessage: '长文本自动转图片开启后才生效',
bottomHelpMessage: '自动转图片的字数阈值。',
component: 'InputNumber',
componentProps: {
min: 0,
},
},
{
field: 'conversationPreserveTime',
label: '对话保留时长',
helpMessage: '单位:秒',
bottomHelpMessage: '每个人发起的对话保留时长。超过这个时长没有进行对话,再进行对话将开启新的对话。',
component: 'InputNumber',
componentProps: {
min: 0,
},
},
{
field: 'toggleMode',
label: '触发方式',
bottomHelpMessage: 'at模式下只有at机器人才会回复。#chat模式下不需要at但需要添加前缀#chat。',
component: 'Select',
componentProps: {
options: [
{label: 'at', value: 'at'},
{label: '#chat', value: 'prefix'},
],
},
},
{
field: 'showQRCode',
label: '启用二维码',
bottomHelpMessage: '在图片模式中启用二维码。该对话内容将被发送至第三方服务器以进行渲染展示,如果不希望对话内容被上传到第三方服务器请关闭此功能。',
component: 'Switch',
},
{
field: 'cacheUrl',
label: '渲染服务器地址',
bottomHelpMessage: '用于缓存图片模式会话内容并渲染的服务器地址。',
component: 'Input',
},
{
field: 'proxy',
label: '代理服务器地址',
bottomHelpMessage: '数据通过代理服务器发送http或socks5代理。',
component: 'Input',
},
{
field: 'debug',
label: '调试信息',
bottomHelpMessage: '将输出更多调试信息,如果不希望控制台刷屏的话,可以关闭。',
component: 'Switch',
},
{
label: '以下为服务超时配置。',
component: 'Divider',
},
{
field: 'defaultTimeoutMs',
label: '默认超时时间',
helpMessage: '单位:毫秒',
bottomHelpMessage: '各个地方的默认超时时间。',
component: 'InputNumber',
componentProps: {
min: 0,
},
},
{
field: 'bingTimeoutMs',
label: 'Bing超时时间',
helpMessage: '单位:毫秒',
bottomHelpMessage: 'bing默认超时时间bing太慢了有的时候。',
component: 'InputNumber',
componentProps: {
min: 0,
},
},
{
field: 'chromeTimeoutMS',
label: '浏览器超时时间',
helpMessage: '单位:毫秒',
bottomHelpMessage: '浏览器默认超时,浏览器可能需要更高的超时时间。',
component: 'InputNumber',
componentProps: {
min: 0,
},
},
{
label: '以下为API方式(默认)的配置',
component: 'Divider',
},
{
field: 'apiKey',
label: 'OpenAI API Key',
bottomHelpMessage: 'OpenAI的ApiKey用于访问OpenAI的API接口。',
component: 'InputPassword',
},
{
field: 'model',
label: '模型',
bottomHelpMessage: '模型名称如无特殊需求保持默认即可会使用chatgpt-api库提供的当前可用的最适合的默认值。保底可用的是 text-davinci-003。当发现新的可用的chatGPT模型会更新这里的值。',
component: 'Input',
},
{
label: '以下为API2方式的配置',
component: 'Divider',
},
{
field: 'plus',
label: 'ChatGPT Plus',
bottomHelpMessage: 'ChatGPT Plus访问如果购买了ChatGPT Plus请开启响应更快。',
component: 'Switch',
},
{
field: 'reverseProxy',
label: '第三方API接口',
bottomHelpMessage: '使用第三方API。github开源的有几个没特别要求保持默认就好。',
component: 'Input',
},
{
label: '以下为API3方式的配置。',
component: 'Divider',
},
{
field: 'api',
label: 'ChatGPT API反代服务器地址',
bottomHelpMessage: 'ChatGPT的API反代服务器用于绕过Cloudflare访问ChatGPT API',
component: 'Input',
},
{
field: 'apiBaseUrl',
label: 'apiBaseUrl地址',
bottomHelpMessage: 'apiBaseUrl地址',
component: 'Input',
},
{
label: '以下为浏览器方式的配置',
component: 'Divider',
},
{
field: 'username',
label: '用户名',
bottomHelpMessage: 'OpenAI用户名。',
component: 'Input',
},
{
field: 'password',
label: '用户名',
bottomHelpMessage: 'OpenAI密码。',
component: 'InputPassword',
},
{
field: 'UA',
label: '浏览器UA',
bottomHelpMessage: '模拟浏览器UA无特殊需求保持默认即可。',
component: 'Input',
},
{
field: 'headless',
label: '无头模式',
bottomHelpMessage: '无界面的服务器可以开启,但遇到验证码时可能无法使用。(实测很容易卡住,几乎不可用)。',
component: 'Switch',
},
{
field: 'chromePath',
label: 'Chrome路径',
bottomHelpMessage: '为空使用默认puppeteer的chromium也可以传递自己本机安装的Chrome可执行文件地址提高通过率。windows可以是C:\\Program Files\\Google\\Chrome\\Application\\chrome.exelinux通过which查找路径。',
component: 'Input',
},
{
field: '2captchaToken',
label: '验证码平台Token',
bottomHelpMessage: '可注册2captcha实现跳过验证码收费服务但很便宜。否则可能会遇到验证码而卡住。',
component: 'InputPassword',
},
],
// 获取配置数据方法(用于前端填充显示数据)
getConfigData() {
return Config
},
// 设置配置的方法(前端点确定后调用的方法)
setConfigData(data, {Result}) {
for (let [keyPath, value] of Object.entries(data)) {
if (Config[keyPath] != value)
Config[keyPath] = value
}
return Result.ok({}, '保存成功~')
},
},
}
}

View file

@ -1,4 +1,5 @@
import fs from 'fs' import fs from 'fs'
import lodash from 'lodash'
const defaultConfig = { const defaultConfig = {
blockWords: ['屏蔽词1', '屏蔽词b'], blockWords: ['屏蔽词1', '屏蔽词b'],
promptBlockWords: ['屏蔽词1', '屏蔽词b'], promptBlockWords: ['屏蔽词1', '屏蔽词b'],
@ -20,6 +21,7 @@ const defaultConfig = {
thinkingTips: true, thinkingTips: true,
username: '', username: '',
password: '', password: '',
UA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36',
headless: false, headless: false,
chromePath: '', chromePath: '',
'2captchaToken': '', '2captchaToken': '',
@ -38,5 +40,21 @@ if (fs.existsSync(`${_path}/plugins/chatgpt-plugin/config/config.js`)) {
const fullPath = fs.realpathSync(`${_path}/plugins/chatgpt-plugin/config/index.js`); const fullPath = fs.realpathSync(`${_path}/plugins/chatgpt-plugin/config/index.js`);
config = (await import(`file://${fullPath}`)).Config; config = (await import(`file://${fullPath}`)).Config;
} }
config = Object.assign({}, defaultConfig, config)
export const Config = Object.assign({}, defaultConfig, config); export const Config = new Proxy(config, {
set(target, property, value) {
target[property] = value;
const change = lodash.transform(target, function(result, value, key) {
if (!lodash.isEqual(value, defaultConfig[key])) {
result[key] = (lodash.isObject(value) && lodash.isObject(defaultConfig[key])) ? changes(value, defaultConfig[key]) : value;
}
});
try {
fs.writeFileSync(`${_path}/plugins/chatgpt-plugin/config/config.js`, `export default ${JSON.stringify(change, '', '\t')}`, { flag: 'w' })
} catch (err) {
console.error(err)
return false;
}
return true;
},
});