feat: add support for bing(beta and WIP)

This commit is contained in:
ikechan8370 2023-02-14 11:24:09 +08:00
parent 83c058ab85
commit 3ff591d205
5 changed files with 91 additions and 19 deletions

View file

@ -5,9 +5,11 @@
* 支持问答图片截图 * 支持问答图片截图
* 仅需OpenAI Api Key开箱即用 * 仅需OpenAI Api Key开箱即用
* 提供基于浏览器的解决方案作为备选,有条件且希望得到更好回答质量可以选择使用浏览器模式。 * 提供基于浏览器的解决方案作为备选,有条件且希望得到更好回答质量可以选择使用浏览器模式。
* 支持新BingBeta
## 版本要求 ## 版本要求
Node.js >= 18 / Node.js >= 14(with node-fetch) Node.js >= 18 / Node.js >= 14(with node-fetch)
小白尽可能使用18版本以上的nodejs
## 安装 ## 安装
首先判断自己需要使用哪种模式本插件支持官方API、第三方API和浏览器两种模式。也可以选择**我全都要**通过qq发送命令`#chatgpt切换浏览器/API/API2`实时切换。对于轻量用户可以先使用API模式有较高要求再转为使用其他模式。 首先判断自己需要使用哪种模式本插件支持官方API、第三方API和浏览器两种模式。也可以选择**我全都要**通过qq发送命令`#chatgpt切换浏览器/API/API2`实时切换。对于轻量用户可以先使用API模式有较高要求再转为使用其他模式。
@ -15,9 +17,9 @@ Node.js >= 18 / Node.js >= 14(with node-fetch)
> API模式和浏览器模式如何选择 > API模式和浏览器模式如何选择
> >
> * API模式会调用OpenAI官方提供的GPT-3 LLM API只需要提供API Key。一般情况下该种方式响应速度更快可配置项多且不会像chatGPT官网一样总出现不可用的现象但其聊天效果明显较官网差。但注意GPT-3的API调用是收费的新用户有18美元试用金可用于支付价格为`$0.0200/1K tokens`.(问题和回答加起来算token) > * API模式会调用OpenAI官方提供的GPT-3 LLM API只需要提供API Key。一般情况下该种方式响应速度更快可配置项多且不会像chatGPT官网一样总出现不可用的现象但其聊天效果明显较官网差。但注意GPT-3的API调用是收费的新用户有18美元试用金可用于支付价格为`$0.0200/1K tokens`.(问题和回答加起来算token)
> * API2模式会调用第三方提供的基于OpenAI text-davinci-002-render模型官网同款的API需要提供ChatGPT的Token。效果比单纯的GPT-3 API好很多但同时将Token提供给了第三方API其中风险自行承担。 > * API2模式会调用第三方提供的基于OpenAI text-davinci-002-render模型官网同款的API需要提供ChatGPT的Token。效果比单纯的GPT-3 API好很多但同时将Token提供给了第三方API其中风险自行承担。#chatgpt设置token
> * 浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站使得获得和官方以及API2模式一模一样的回复质量同时保证安全性。缺点是本方法对环境要求较高需要提供桌面环境和一个可用的代理能够访问ChatGPT的IP地址且响应速度不如API而且高峰期容易无法使用。 > * 浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站使得获得和官方以及API2模式一模一样的回复质量同时保证安全性。缺点是本方法对环境要求较高需要提供桌面环境和一个可用的代理能够访问ChatGPT的IP地址且响应速度不如API而且高峰期容易无法使用。
> * 必应Bing将调用微软新必应接口进行对话。需要在必应网页能够正常使用新必应且设置有效的Bing 登录Cookie方可使用。#chatgpt设置必应token. ("_U" cookie from bing.com)
1. 进入 Yunzai根目录 1. 进入 Yunzai根目录
2. 检查 Node.js 版本 2. 检查 Node.js 版本
@ -74,7 +76,6 @@ git clone https://github.com/ikechan8370/chatgpt-plugin.git ./plugins/chatgpt-pl
## TODO ## TODO
* 更灵活的Conversation管理 * 更灵活的Conversation管理
* 支持Bing版本
* 版本号和归档 * 版本号和归档
* API2模式下自动获取/刷新Token * API2模式下自动获取/刷新Token

View file

@ -6,7 +6,7 @@ import mjAPI from 'mathjax-node'
import { uuid } from 'oicq/lib/common.js' import { uuid } from 'oicq/lib/common.js'
import delay from 'delay' import delay from 'delay'
import { ChatGPTAPI } from 'chatgpt' import { ChatGPTAPI } from 'chatgpt'
import { ChatGPTClient } from '@waylaidwanderer/chatgpt-api' import { ChatGPTClient, BingAIClient } from '@waylaidwanderer/chatgpt-api'
import { getMessageById, tryTimes, upsertMessage } from '../utils/common.js' import { getMessageById, tryTimes, upsertMessage } from '../utils/common.js'
import { ChatGPTPuppeteer } from '../utils/browser.js' import { ChatGPTPuppeteer } from '../utils/browser.js'
import { KeyvFile } from 'keyv-file' import { KeyvFile } from 'keyv-file'
@ -247,15 +247,25 @@ export class chatgpt extends plugin {
previousConversation = JSON.parse(previousConversation) previousConversation = JSON.parse(previousConversation)
conversation = { conversation = {
conversationId: previousConversation.conversation.conversationId, conversationId: previousConversation.conversation.conversationId,
parentMessageId: previousConversation.conversation.parentMessageId parentMessageId: previousConversation.conversation.parentMessageId,
clientId: previousConversation.clientId,
invocationId: previousConversation.invocationId,
conversationSignature: previousConversation.conversationSignature
} }
} }
const use = await redis.get('CHATGPT:USE')
try { try {
let chatMessage = await this.sendMessage(prompt, conversation) let chatMessage = await this.sendMessage(prompt, conversation, use)
previousConversation.conversation = { previousConversation.conversation = {
conversationId: chatMessage.conversationId, conversationId: chatMessage.conversationId
parentMessageId: chatMessage.id }
if (use === 'bing') {
previousConversation.clientId = chatMessage.clientId
previousConversation.invocationId = chatMessage.invocationId
previousConversation.conversationSignature = chatMessage.conversationSignature
} else {
// 或许这样切换回来不会404
previousConversation.conversation.parentMessageId = chatMessage.id
} }
console.log(chatMessage) console.log(chatMessage)
let response = chatMessage?.text let response = chatMessage?.text
@ -284,7 +294,7 @@ export class chatgpt extends plugin {
// !response.trimEnd().endsWith('') && !response.trimEnd().endsWith('!') && !response.trimEnd().endsWith(']') && !response.trimEnd().endsWith('】') // !response.trimEnd().endsWith('') && !response.trimEnd().endsWith('!') && !response.trimEnd().endsWith(']') && !response.trimEnd().endsWith('】')
// ) { // ) {
await this.reply('内容有点多,我正在奋笔疾书,请再等一会', true, { recallMsg: 5 }) await this.reply('内容有点多,我正在奋笔疾书,请再等一会', true, { recallMsg: 5 })
let responseAppend = await this.sendMessage('Continue', conversation) let responseAppend = await this.sendMessage('Continue', conversation, use)
previousConversation.conversation = { previousConversation.conversation = {
conversationId: responseAppend.conversationId, conversationId: responseAppend.conversationId,
parentMessageId: responseAppend.id parentMessageId: responseAppend.id
@ -325,8 +335,8 @@ export class chatgpt extends plugin {
} }
} }
async sendMessage (prompt, conversation = {}) { async sendMessage (prompt, conversation = {}, use) {
const use = await redis.get('CHATGPT:USE')
// console.log(use) // console.log(use)
if (use === 'browser') { if (use === 'browser') {
return await this.chatgptBrowserBased(prompt, conversation) return await this.chatgptBrowserBased(prompt, conversation)
@ -350,7 +360,7 @@ export class chatgpt extends plugin {
// (Optional) Set a custom name for ChatGPT // (Optional) Set a custom name for ChatGPT
chatGptLabel: Config.assistantLabel, chatGptLabel: Config.assistantLabel,
// (Optional) Set to true to enable `console.debug()` logging // (Optional) Set to true to enable `console.debug()` logging
debug: false debug: Config.debug
} }
const cacheOptions = { const cacheOptions = {
// Options for the Keyv cache, see https://www.npmjs.com/package/keyv // Options for the Keyv cache, see https://www.npmjs.com/package/keyv
@ -371,6 +381,23 @@ export class chatgpt extends plugin {
id: response.messageId, id: response.messageId,
parentMessageId: conversation?.parentMessageId parentMessageId: conversation?.parentMessageId
} }
} else if (use === 'bing') {
let bingToken = await redis.get('CHATGPT:BING_TOKEN')
if (!bingToken) {
throw new Error('未绑定Bing Cookie请使用#chatgpt设置Bing Cookie命令绑定Bing Cookie')
}
const bingAIClient = new BingAIClient({
userToken: bingToken, // "_U" cookie from bing.com
debug: Config.debug
})
let response = await bingAIClient.sendMessage(prompt, conversation)
return {
text: response.response,
conversationId: response.conversationId,
clientId: response.clientId,
invocationId: response.invocationId,
conversationSignature: response.conversationSignature
}
} else { } else {
let completionParams = {} let completionParams = {}
if (Config.model) { if (Config.model) {

View file

@ -63,8 +63,13 @@ let helpData = [
}, },
{ {
icon: 'switch', icon: 'switch',
title: '#chatgpt切换浏览器/API/API2', title: '#chatgpt切换浏览器/API/API2/Bing',
desc: '切换使用的后端为浏览器或OpenAI API/第三方API' desc: '切换使用的后端为浏览器或OpenAI API/第三方API/Bing'
},
{
icon: 'help',
title: '#chatgpt设置必应token',
desc: '设置ChatGPT或bing的Token'
}, },
{ {
icon: 'help', icon: 'help',

View file

@ -23,6 +23,11 @@ export class ChatgptManagement extends plugin {
fnc: 'setAccessToken', fnc: 'setAccessToken',
permission: 'master' permission: 'master'
}, },
{
reg: '#chatgpt设置必应token',
fnc: 'setBingAccessToken',
permission: 'master'
},
{ {
reg: '^#chatgpt切换浏览器$', reg: '^#chatgpt切换浏览器$',
fnc: 'useBrowserBasedSolution', fnc: 'useBrowserBasedSolution',
@ -38,6 +43,11 @@ export class ChatgptManagement extends plugin {
fnc: 'useReversedAPIBasedSolution', fnc: 'useReversedAPIBasedSolution',
permission: 'master' permission: 'master'
}, },
{
reg: '^#chatgpt切换(必应|Bing)$',
fnc: 'useReversedBingSolution',
permission: 'master'
},
{ {
reg: '^#chatgpt模式帮助$', reg: '^#chatgpt模式帮助$',
fnc: 'modeHelp' fnc: 'modeHelp'
@ -68,9 +78,28 @@ export class ChatgptManagement extends plugin {
return false return false
} }
async setBingAccessToken (e) {
this.setContext('saveBingToken')
await this.reply('请发送Bing Cookie Token.("_U" cookie from bing.com)', true)
return false
}
async saveBingToken () {
if (!this.e.msg) return
let token = this.e.msg
// todo 未知bing token是什么样的有号的可以加个校验在这
await redis.set('CHATGPT:BING_TOKEN', token)
await this.reply('Bing Token设置成功', true)
this.finish('saveBingToken')
}
async saveToken () { async saveToken () {
if (!this.e.msg) return if (!this.e.msg) return
let token = this.e.msg let token = this.e.msg
if (!token.startsWith('ey') || token.length < 20) {
await this.reply('ChatGPT AccessToken格式错误', true)
this.finish('saveToken')
}
await redis.set('CHATGPT:TOKEN', token) await redis.set('CHATGPT:TOKEN', token)
await this.reply('ChatGPT AccessToken设置成功', true) await this.reply('ChatGPT AccessToken设置成功', true)
this.finish('saveToken') this.finish('saveToken')
@ -91,12 +120,18 @@ export class ChatgptManagement extends plugin {
await this.reply('已切换到基于第三方Reversed API的解决方案如果已经对话过建议执行`#结束对话`避免引起404错误') await this.reply('已切换到基于第三方Reversed API的解决方案如果已经对话过建议执行`#结束对话`避免引起404错误')
} }
async useReversedBingSolution (e) {
await redis.set('CHATGPT:USE', 'bing')
await this.reply('已切换到基于第三方Reversed API的解决方案如果已经对话过务必执行`#结束对话`避免引起404错误')
}
async modeHelp () { async modeHelp () {
let mode = await redis.get('CHATGPT:USE') let mode = await redis.get('CHATGPT:USE')
const modeMap = { const modeMap = {
browser: '浏览器', browser: '浏览器',
apiReverse: 'API2', apiReverse: 'API2',
api: 'API' api: 'API',
bing: '必应'
} }
let modeText = modeMap[mode || 'api'] let modeText = modeMap[mode || 'api']
let message = ` API模式和浏览器模式如何选择 let message = ` API模式和浏览器模式如何选择
@ -104,11 +139,14 @@ export class ChatgptManagement extends plugin {
// eslint-disable-next-line no-irregular-whitespace // eslint-disable-next-line no-irregular-whitespace
API模式会调用OpenAI官方提供的GPT-3 LLM API只需要提供API Key一般情况下该种方式响应速度更快可配置项多且不会像chatGPT官网一样总出现不可用的现象但其聊天效果明显较官网差但注意GPT-3的API调用是收费的新用户有18美元试用金可用于支付价格为$0.0200/1K tokens.(问题和回答加起来算token) API模式会调用OpenAI官方提供的GPT-3 LLM API只需要提供API Key一般情况下该种方式响应速度更快可配置项多且不会像chatGPT官网一样总出现不可用的现象但其聊天效果明显较官网差但注意GPT-3的API调用是收费的新用户有18美元试用金可用于支付价格为$0.0200/1K tokens.(问题和回答加起来算token)
API2模式会调用第三方提供的基于OpenAI text-davinci-002-render模型官网同款的API需要提供ChatGPT的Token效果比单纯的GPT-3 API好很多但同时将Token提供给了第三方API其中风险自行承担 API2模式会调用第三方提供的基于OpenAI text-davinci-002-render模型官网同款的API需要提供ChatGPT的Token效果比单纯的GPT-3 API好很多但同时将Token提供给了第三方API其中风险自行承担#chatgpt设置token
浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站使得获得和官方以及API2模式一模一样的回复质量同时保证安全性缺点是本方法对环境要求较高需要提供桌面环境和一个可用的代理能够访问ChatGPT的IP地址且响应速度不如API而且高峰期容易无法使用 浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站使得获得和官方以及API2模式一模一样的回复质量同时保证安全性缺点是本方法对环境要求较高需要提供桌面环境和一个可用的代理能够访问ChatGPT的IP地址且响应速度不如API而且高峰期容易无法使用
您可以使用#chatgpt切换浏览器/API/API2来切换到指定模式 必应Bing将调用微软新必应接口进行对话需要在必应网页能够正常使用新必应且设置有效的Bing 登录Cookie方可使用#chatgpt设置必应token
您可以使用#chatgpt切换浏览器/API/API2/Bing来切换到指定模式
当前为${modeText}模式 当前为${modeText}模式
` `
await this.reply(message) await this.reply(message)

View file

@ -49,5 +49,6 @@ export const Config = {
// 可注册2captcha实现跳过验证码收费服务但很便宜。否则可能会遇到验证码而卡住。 // 可注册2captcha实现跳过验证码收费服务但很便宜。否则可能会遇到验证码而卡住。
'2captchaToken': '', '2captchaToken': '',
// http或socks5代理 // http或socks5代理
proxy: PROXY proxy: PROXY,
debug: false
} }