mirror of
https://github.com/ikechan8370/chatgpt-plugin.git
synced 2025-12-16 21:37:11 +00:00
feat: api and api3 support proxy
This commit is contained in:
parent
24c80d159b
commit
f4a073372f
6 changed files with 69 additions and 27 deletions
|
|
@ -23,7 +23,7 @@ Node.js >= 18 / Node.js >= 14(with node-fetch)
|
|||
|
||||
> #### API模式和浏览器模式如何选择?
|
||||
>
|
||||
> * API模式会调用OpenAI官方提供的GPT-3 LLM API,只需要提供API Key。一般情况下,该种方式响应速度更快,可配置项多,且不会像chatGPT官网一样总出现不可用的现象,但其聊天效果明显较官网差。但注意GPT-3的API调用是收费的,新用户有18美元试用金可用于支付,价格为`$0.0200/ 1K tokens`。(问题和回答**加起来**算token)
|
||||
> * API模式会调用OpenAI官方提供的gpt-3.5-turbo API,ChatGPT官网同款模型,只需要提供API Key。一般情况下,该种方式响应速度更快,可配置项多,且不会像chatGPT官网一样总出现不可用的现象,但注意API调用是收费的,新用户有18美元试用金可用于支付,价格为`$0.0020/ 1K tokens`。(问题和回答**加起来**算token)
|
||||
> * API3模式会调用第三方提供的官网反代API,他会帮你绕过CF防护,需要提供ChatGPT的Token。效果与官网和浏览器一致,但稳定性不一定。设置token和API2方法一样。
|
||||
> * 浏览器模式通过在本地启动Chrome等浏览器模拟用户访问ChatGPT网站,使得获得和官方以及API2模式一模一样的回复质量,同时保证安全性。缺点是本方法对环境要求较高,需要提供桌面环境和一个可用的代理(能够访问ChatGPT的IP地址),且响应速度不如API,而且高峰期容易无法使用。一般作为API3的下位替代。
|
||||
> * 必应(Bing)将调用微软新必应接口进行对话。需要在必应网页能够正常使用新必应且设置有效的Bing登录Cookie方可使用。
|
||||
|
|
|
|||
34
apps/chat.js
34
apps/chat.js
|
|
@ -12,6 +12,15 @@ import { OfficialChatGPTClient } from '../utils/message.js'
|
|||
import fetch from 'node-fetch'
|
||||
import { deleteConversation, getConversations, getLatestMessageIdByConversationId } from '../utils/conversation.js'
|
||||
let version = Config.version
|
||||
let proxy
|
||||
if (Config.proxy) {
|
||||
try {
|
||||
proxy = (await import('https-proxy-agent')).default
|
||||
} catch (e) {
|
||||
console.warn('未安装https-proxy-agent,请在插件目录下执行pnpm add https-proxy-agent')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 每个对话保留的时长。单个对话内ai是保留上下文的。超时后销毁对话,再次对话创建新的对话。
|
||||
* 单位:秒
|
||||
|
|
@ -21,7 +30,20 @@ let version = Config.version
|
|||
*/
|
||||
// const CONVERSATION_PRESERVE_TIME = Config.conversationPreserveTime
|
||||
const 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.'
|
||||
const newFetch = (url, options = {}) => {
|
||||
const defaultOptions = Config.proxy
|
||||
? {
|
||||
agent: proxy(Config.proxy)
|
||||
}
|
||||
: {}
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
|
||||
return fetch(url, mergedOptions)
|
||||
}
|
||||
export class chatgpt extends plugin {
|
||||
constructor () {
|
||||
let toggleMode = Config.toggleMode
|
||||
|
|
@ -170,7 +192,7 @@ export class chatgpt extends plugin {
|
|||
await this.reply('指令格式错误,请同时加上对话id或@某人以删除他当前进行的对话', true)
|
||||
return false
|
||||
} else {
|
||||
let deleteResponse = await deleteConversation(conversationId)
|
||||
let deleteResponse = await deleteConversation(conversationId, newFetch)
|
||||
console.log(deleteResponse)
|
||||
let deleted = 0
|
||||
let qcs = await redis.keys('CHATGPT:QQ_CONVERSATION:*')
|
||||
|
|
@ -370,7 +392,7 @@ export class chatgpt extends plugin {
|
|||
if (conversationId) {
|
||||
let lastMessageId = await redis.get(`CHATGPT:CONVERSATION_LAST_MESSAGE_ID:${conversationId}`)
|
||||
if (!lastMessageId) {
|
||||
lastMessageId = await getLatestMessageIdByConversationId(conversationId)
|
||||
lastMessageId = await getLatestMessageIdByConversationId(conversationId, newFetch)
|
||||
}
|
||||
// let lastMessagePrompt = await redis.get(`CHATGPT:CONVERSATION_LAST_MESSAGE_PROMPT:${conversationId}`)
|
||||
// let conversationCreateTime = await redis.get(`CHATGPT:CONVERSATION_CREATE_TIME:${conversationId}`)
|
||||
|
|
@ -535,7 +557,7 @@ export class chatgpt extends plugin {
|
|||
quotes: quote,
|
||||
cache: cacheData,
|
||||
version
|
||||
},{retType: Config.quoteReply ? 'base64' : ''}), e.isGroup && Config.quoteReply)
|
||||
}, { retType: Config.quoteReply ? 'base64' : '' }), e.isGroup && Config.quoteReply)
|
||||
}
|
||||
|
||||
async sendMessage (prompt, conversation = {}, use, e) {
|
||||
|
|
@ -683,7 +705,7 @@ export class chatgpt extends plugin {
|
|||
systemMessage: promptPrefix,
|
||||
completionParams,
|
||||
assistantLabel: Config.assistantLabel,
|
||||
fetch
|
||||
fetch: newFetch
|
||||
})
|
||||
let option = {
|
||||
timeoutMs: 120000,
|
||||
|
|
@ -714,7 +736,7 @@ export class chatgpt extends plugin {
|
|||
async getAllConversations (e) {
|
||||
const use = await redis.get('CHATGPT:USE')
|
||||
if (use === 'api3') {
|
||||
let conversations = await getConversations(e.sender.user_id)
|
||||
let conversations = await getConversations(e.sender.user_id, newFetch)
|
||||
if (Config.debug) {
|
||||
logger.mark('all conversations: ', conversations)
|
||||
}
|
||||
|
|
@ -779,7 +801,7 @@ export class chatgpt extends plugin {
|
|||
return false
|
||||
}
|
||||
// 查询OpenAI API剩余试用额度
|
||||
fetch(`${Config.openAiBaseUrl}/dashboard/billing/credit_grants`, {
|
||||
newFetch(`${Config.openAiBaseUrl}/dashboard/billing/credit_grants`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export function supportGuoba () {
|
|||
link: 'https://github.com/ikechan8370/chatgpt-plugin',
|
||||
isV3: true,
|
||||
isV2: false,
|
||||
description: '基于OpenAI最新推出的chatgpt和微软的 New bing通过api进行问答的插件,需自备openai账号或有New bing访问权限的必应账号',
|
||||
description: '基于OpenAI最新推出的chatgpt和微软的 New bing通过api进行聊天的插件,需自备openai账号或有New bing访问权限的必应账号',
|
||||
// 显示图标,此为个性化配置
|
||||
// 图标可在 https://icon-sets.iconify.design 这里进行搜索
|
||||
icon: 'simple-icons:openai',
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
"type": "module",
|
||||
"author": "ikechan8370",
|
||||
"dependencies": {
|
||||
"@waylaidwanderer/chatgpt-api": "^1.22.5",
|
||||
"chatgpt": "^5.0.0",
|
||||
"@waylaidwanderer/chatgpt-api": "^1.23.0",
|
||||
"chatgpt": "^5.0.5",
|
||||
"delay": "^5.0.0",
|
||||
"keyv-file": "^0.2.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
import fetch from 'node-fetch'
|
||||
import { Config } from '../utils/config.js'
|
||||
|
||||
export async function getConversations (qq = '') {
|
||||
export async function getConversations (qq = '', fetchFn = fetch) {
|
||||
let accessToken = await redis.get('CHATGPT:TOKEN')
|
||||
if (!accessToken) {
|
||||
throw new Error('未绑定ChatGPT AccessToken,请使用#chatgpt设置token命令绑定token')
|
||||
}
|
||||
let response = await fetch(`${Config.apiBaseUrl}/conversations?offset=0&limit=20`, {
|
||||
let response = await fetchFn(`${Config.apiBaseUrl}/conversations?offset=0&limit=20`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -33,7 +33,7 @@ export async function getConversations (qq = '') {
|
|||
map[item.id] = cachedConversationLastMessage
|
||||
} else {
|
||||
// 缓存中没有,就去查官方api
|
||||
let conversationDetailResponse = await fetch(`${Config.apiBaseUrl}/api/conversation/${item.id}`, {
|
||||
let conversationDetailResponse = await fetchFn(`${Config.apiBaseUrl}/api/conversation/${item.id}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -96,12 +96,12 @@ export async function getConversations (qq = '') {
|
|||
return res
|
||||
}
|
||||
|
||||
export async function getLatestMessageIdByConversationId (conversationId) {
|
||||
export async function getLatestMessageIdByConversationId (conversationId, fetchFn = fetch) {
|
||||
let accessToken = await redis.get('CHATGPT:TOKEN')
|
||||
if (!accessToken) {
|
||||
throw new Error('未绑定ChatGPT AccessToken,请使用#chatgpt设置token命令绑定token')
|
||||
}
|
||||
let conversationDetailResponse = await fetch(`${Config.apiBaseUrl}/api/conversation/${conversationId}`, {
|
||||
let conversationDetailResponse = await fetchFn(`${Config.apiBaseUrl}/api/conversation/${conversationId}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
@ -124,12 +124,12 @@ export async function getLatestMessageIdByConversationId (conversationId) {
|
|||
}
|
||||
|
||||
// 调用chat.open.com删除某一个对话。该操作不可逆。
|
||||
export async function deleteConversation (conversationId) {
|
||||
export async function deleteConversation (conversationId, fetchFn = fetch) {
|
||||
let accessToken = await redis.get('CHATGPT:TOKEN')
|
||||
if (!accessToken) {
|
||||
throw new Error('未绑定ChatGPT AccessToken,请使用#chatgpt设置token命令绑定token')
|
||||
}
|
||||
let response = await fetch(`${Config.apiBaseUrl}/api/conversation/${conversationId}`, {
|
||||
let response = await fetchFn(`${Config.apiBaseUrl}/api/conversation/${conversationId}`, {
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
|
|
|
|||
|
|
@ -3,6 +3,15 @@ import { Config } from '../utils/config.js'
|
|||
import HttpsProxyAgent from 'https-proxy-agent'
|
||||
import _ from 'lodash'
|
||||
import fetch from 'node-fetch'
|
||||
let proxy
|
||||
if (Config.proxy) {
|
||||
try {
|
||||
proxy = (await import('https-proxy-agent')).default
|
||||
} catch (e) {
|
||||
console.warn('未安装https-proxy-agent,请在插件目录下执行pnpm add https-proxy-agent')
|
||||
}
|
||||
}
|
||||
|
||||
export class OfficialChatGPTClient {
|
||||
constructor (opts = {}) {
|
||||
const {
|
||||
|
|
@ -13,6 +22,20 @@ export class OfficialChatGPTClient {
|
|||
this._accessToken = accessToken
|
||||
this._apiReverseUrl = apiReverseUrl
|
||||
this._timeoutMs = timeoutMs
|
||||
this._fetch = (url, options = {}) => {
|
||||
const defaultOptions = Config.proxy
|
||||
? {
|
||||
agent: proxy(Config.proxy)
|
||||
}
|
||||
: {}
|
||||
|
||||
const mergedOptions = {
|
||||
...defaultOptions,
|
||||
...options
|
||||
}
|
||||
|
||||
return fetch(url, mergedOptions)
|
||||
}
|
||||
}
|
||||
|
||||
async sendMessage (prompt, opts = {}) {
|
||||
|
|
@ -59,10 +82,7 @@ export class OfficialChatGPTClient {
|
|||
},
|
||||
referrer: 'https://chat.openai.com/chat'
|
||||
}
|
||||
if (Config.proxy) {
|
||||
option.agent = new HttpsProxyAgent(Config.proxy)
|
||||
}
|
||||
const res = await fetch(url, option)
|
||||
const res = await this._fetch(url, option)
|
||||
const decoder = new TextDecoder('utf-8')
|
||||
const bodyBytes = await res.arrayBuffer()
|
||||
const bodyText = decoder.decode(bodyBytes)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue