From a0c294ab45fb4aa7889299bbe8b33812096e0f62 Mon Sep 17 00:00:00 2001 From: Holegots Date: Fri, 9 Dec 2022 19:01:04 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20account=20relogin=20if=20refr?= =?UTF-8?q?esh=20token=20failed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 +-- README_ZH.md | 4 +-- src/cache.ts | 6 ++++- src/chatgpt.ts | 69 +++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 75 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 3460afe..2f422b9 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@

> Use ChatGPT On Wechat via wechaty - +English | [中文文档](README.zh-CN.md) ### 🏠 [Homepage](https://github.com/fuergaosi233/wechat-chatgpt) ## 🌟 Feature @@ -22,7 +22,7 @@ - [x] Add Dockerfile - [x] Publish to Docker.hub - [ ] Add Railway deploy -- [ ] Auto Reload OpenAI Accounts Pool +- [x] Auto Reload OpenAI Accounts Pool - [ ] Add sendmessage retry for 429/503 ## Use with docker (recommended) diff --git a/README_ZH.md b/README_ZH.md index 98b5648..61c71c8 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -10,7 +10,7 @@

> 在微信上迅速接入 ChatGPT,让它成为你最好的助手! - +[English](README.md) | 中文文档 ### 🏠 [主页](https://github.com/fuergaosi233/wechat-chatgpt/blob/main/README_ZH.md) ## 🌟 功能点 @@ -22,7 +22,7 @@ - [x] 加入 Dockerfile - [x] 发布到 Docker.hub - [ ] 通过 Railway 进行部署 -- [ ] 实现 OpenAI 账户池的热加载 +- [x] 实现 OpenAI 账户池的热加载 - [ ] 当 OpenAI 返回码为 429/503 时自动重试 ## 通过 Docker 使用(✅ 推荐) diff --git a/src/cache.ts b/src/cache.ts index bbe3dbf..18440f3 100644 --- a/src/cache.ts +++ b/src/cache.ts @@ -12,8 +12,12 @@ export class Cache { get(key: string) { return this._cache[key]; } - set(key: string, value: any) { + async set(key: string, value: any) { this._cache[key] = value; fs.writeFileSync(this._file, JSON.stringify(this._cache)); } + async delete(key: string) { + delete this._cache[key]; + fs.writeFileSync(this._file, JSON.stringify(this._cache)); + } } diff --git a/src/chatgpt.ts b/src/chatgpt.ts index 71281c8..3d7876d 100644 --- a/src/chatgpt.ts +++ b/src/chatgpt.ts @@ -10,6 +10,8 @@ import { AccountWithUserInfo, isAccountWithUserInfo, isAccountWithSessionToken, + AccountWithSessionToken, + IAccount, } from "./interface.js"; enum MessageType { @@ -53,7 +55,6 @@ export class ChatGPTPoole { } const cmd = `poetry run python3 src/generate_session.py ${email} ${password}`; const platform = process.platform; - const { stdout, stderr, exitCode } = await execa( platform === "win32" ? "powershell" : "sh", [platform === "win32" ? "/c" : "-c", cmd] @@ -70,6 +71,64 @@ export class ChatGPTPoole { } return ""; } + async resetAccount(account: IAccount) { + if (isAccountWithUserInfo(account)) { + // Remove all conversation information + this.conversationsPool.forEach((item, key) => { + if ((item.account as AccountWithUserInfo)?.email === account.email) { + this.conversationsPool.delete(key); + } + }); + // Relogin and generate a new session token + const chatGPTItem = this.chatGPTPools + .filter((item) => isAccountWithUserInfo(item.account)) + .find( + ( + item: any + ): item is IChatGPTItem & { + account: AccountWithUserInfo; + chatGpt: ChatGPTAPI; + } => item?.email === account.email + ); + if (chatGPTItem) { + await this.cache.delete(account.email); + try { + const session_token = await this.getSessionToken( + chatGPTItem.account?.email, + chatGPTItem.account?.password + ); + chatGPTItem.chatGpt = new ChatGPTAPI({ + sessionToken: session_token, + }); + } catch (err) { + //remove this object + this.chatGPTPools = this.chatGPTPools.filter( + (item) => + (item.account as AccountWithUserInfo)?.email !== account.email + ); + console.error( + `Try reset account: ${account.email} failed: ${err}, remove it from pool` + ); + } + } + } else if (isAccountWithSessionToken(account)) { + // Remove all conversation information + this.conversationsPool.forEach((item, key) => { + if ( + (item.account as AccountWithSessionToken)?.session_token === + account.session_token + ) { + this.conversationsPool.delete(key); + } + }); + // Remove this gptItem + this.chatGPTPools = this.chatGPTPools.filter( + (item) => + (item.account as AccountWithSessionToken)?.session_token !== + account.session_token + ); + } + } async startPools() { const sessionAccounts = config.chatGPTAccountPool.filter( isAccountWithSessionToken @@ -125,7 +184,7 @@ export class ChatGPTPoole { return conversationItem; } // send message with talkid - async sendMessage(message: string, talkid: string) { + async sendMessage(message: string, talkid: string): Promise { const conversationItem = this.getConversation(talkid); const { conversation, account } = conversationItem; try { @@ -133,6 +192,10 @@ export class ChatGPTPoole { const response = await conversation.sendMessage(message); return response; } catch (err: any) { + if (err.message.includes("ChatGPT failed to refresh auth token")) { + await this.resetAccount(account); + return this.sendMessage(message, talkid); + } console.error( `err is ${err.message}, account ${JSON.stringify(account)}` ); @@ -166,7 +229,7 @@ export class ChatGPTBot { return `@${this.botName}`; } async startGPTBot() { - console.debug(`Start GPT Bot Config is:${config}`); + console.debug(`Start GPT Bot Config is:${JSON.stringify(config)}`); await this.chatGPTPool.startPools(); console.debug(`🤖️ Start GPT Bot Success, ready to handle message!`); }