From 95a4d7a69c5d2149f5dc2119fbe16a0817d1aa43 Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Sun, 2 Apr 2023 12:08:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E9=80=9A=E8=BF=87=E9=99=8D=E7=BA=A7mes?= =?UTF-8?q?sageType=E6=89=8B=E6=AE=B5=E5=A4=8D=E6=B4=BB=E9=99=90=E6=B5=81?= =?UTF-8?q?=E7=9A=84=E8=B4=A6=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/chat.js | 78 +++++++++++++++++++++++++++++------------ utils/SydneyAIClient.js | 16 ++++++--- utils/config.js | 2 +- 3 files changed, 69 insertions(+), 27 deletions(-) diff --git a/apps/chat.js b/apps/chat.js index d18b8a6..acb3ba7 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -1006,25 +1006,8 @@ export class chatgpt extends plugin { return await this.chatgptBrowserBased(prompt, conversation) } case 'bing': { - let bingToken = await redis.get('CHATGPT:BING_TOKEN') - if (!bingToken) { - throw new Error('未绑定Bing Cookie,请使用#chatgpt设置必应token命令绑定Bing Cookie') - } - const bingTokens = bingToken.split('|') - // 负载均衡 - if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') { - // sydney下不需要保证同一token - const select = Math.floor(Math.random() * bingTokens.length) - bingToken = bingTokens[select] - } else { - // bing 下,需要保证同一对话使用同一账号的token - if (!conversation.bingToken) { - const select = Math.floor(Math.random() * bingTokens.length) - bingToken = bingTokens[select] - } else if (bingTokens.indexOf(conversation.bingToken) > -1) { - bingToken = conversation.bingToken - } - } + let throttledTokens = [] + let { bingToken, allThrottled } = await getAvailableBingToken(conversation, throttledTokens) let cookies if (bingToken?.indexOf('=') > -1) { cookies = bingToken @@ -1064,11 +1047,21 @@ export class chatgpt extends plugin { let reply = '' let retry = 3 let errorMessage = '' + do { + let abtrs = await getAvailableBingToken(conversation, throttledTokens) + bingToken = abtrs.bingToken + allThrottled = abtrs.allThrottled + if (bingToken?.indexOf('=') > -1) { + cookies = bingToken + } + bingAIClient.opts.userToken = bingToken + bingAIClient.opts.cookies = cookies try { let opt = _.cloneDeep(conversation) || {} opt.toneStyle = Config.toneStyle opt.context = Config.sydneyContext + opt.messageType = allThrottled ? 'Chat' : 'SearchQuery' if (Config.enableGroupContext && e.isGroup) { try { opt.groupId = e.group_id @@ -1119,8 +1112,13 @@ export class chatgpt extends plugin { break } catch (error) { const message = error?.message || error?.data?.message || error || '出错了' - retry-- - errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message + if (message.indexOf('限流') > -1) { + throttledTokens.push(bingToken) + // 不减次数 + } else { + retry-- + errorMessage = message === 'Timed out waiting for response. Try enabling debug mode to see more information.' ? (reply ? `${reply}\n不行了,我的大脑过载了,处理不过来了!` : '必应的小脑瓜不好使了,不知道怎么回答!') : message + } } } while (retry > 0) if (errorMessage) { @@ -1139,7 +1137,7 @@ export class chatgpt extends plugin { invocationId: response.invocationId, conversationSignature: response.conversationSignature, parentMessageId: response.apology ? conversation.parentMessageId : response.messageId, - bingToken: bingToken + bingToken } } } @@ -1368,3 +1366,39 @@ export class chatgpt extends plugin { return await this.chatGPTApi.sendMessage(prompt, sendMessageOption) } } + +async function getAvailableBingToken (conversation, throttled = []) { + let allThrottled = false + let bingToken = await redis.get('CHATGPT:BING_TOKEN') + if (!bingToken) { + throw new Error('未绑定Bing Cookie,请使用#chatgpt设置必应token命令绑定Bing Cookie') + } + const bingTokens = bingToken.split('|') + // 负载均衡 + if (Config.toneStyle === 'Sydney' || Config.toneStyle === 'Custom') { + // sydney下不需要保证同一token + let notThrottled = bingTokens.filter(t => throttled.indexOf(t) === -1) + if (notThrottled.length > 0) { + bingToken = notThrottled[0] + } else { + // 全都被限流了,随便找一个算了 + allThrottled = true + const select = Math.floor(Math.random() * bingTokens.length) + bingToken = bingTokens[select] + } + // const select = Math.floor(Math.random() * bingTokens.length) + // bingToken = bingTokens[select] + } else { + // bing 下,需要保证同一对话使用同一账号的token + if (!conversation.bingToken) { + const select = Math.floor(Math.random() * bingTokens.length) + bingToken = bingTokens[select] + } else if (bingTokens.indexOf(conversation.bingToken) > -1) { + bingToken = conversation.bingToken + } + } + return { + bingToken, + allThrottled + } +} diff --git a/utils/SydneyAIClient.js b/utils/SydneyAIClient.js index 26d464c..14e1faf 100644 --- a/utils/SydneyAIClient.js +++ b/utils/SydneyAIClient.js @@ -217,8 +217,12 @@ export default class SydneyAIClient { abortController = new AbortController(), timeout = Config.defaultTimeoutMs, firstMessageTimeout = Config.sydneyFirstMessageTimeout, - groupId, nickname, qq, groupName, chats, botName, masterName + groupId, nickname, qq, groupName, chats, botName, masterName, + messageType = 'SearchQuery' } = opts + if (messageType === 'Chat') { + logger.warn('该Bing账户token已被限流,降级至使用非搜索模式。本次对话AI将无法使用Bing搜索返回的内容') + } if (typeof onProgress !== 'function') { onProgress = () => {} } @@ -382,8 +386,8 @@ export default class SydneyAIClient { author: 'user', inputMethod: 'Keyboard', text: message, - // messageType: 'Chat' - messageType: 'SearchQuery' + messageType + // messageType: 'SearchQuery' }, conversationSignature, participant: { @@ -621,6 +625,10 @@ export default class SydneyAIClient { if (event.item?.result) { if (event.item?.result?.exception?.indexOf('maximum context length') > -1) { reject('对话长度太长啦!超出8193token,请结束对话重新开始') + } else if (event.item?.result.value === 'Throttled') { + reject('该账户的SERP请求已被限流') + logger.warn('该账户的SERP请求已被限流') + logger.warn(JSON.stringify(event.item?.result)) } else { reject(`${event.item?.result.value}\n${event.item?.result.error}\n${event.item?.result.exception}`) } @@ -717,7 +725,7 @@ export default class SydneyAIClient { conversationExpiryTime, response: reply.text, details: reply, - apology: Config.sydneyApologyIgnored && apology + apology: Config.sydneyApologyIgnored && apology, } } catch (err) { await this.conversationsCache.set(conversationKey, conversation) diff --git a/utils/config.js b/utils/config.js index 62f8839..68a1e56 100644 --- a/utils/config.js +++ b/utils/config.js @@ -78,7 +78,7 @@ const defaultConfig = { maxNumUserMessagesInConversation: 20, sydneyApologyIgnored: true, enforceMaster: false, - version: 'v2.4.10' + version: 'v2.4.11' } const _path = process.cwd() let config = {}