From eff5688fb1f10fd7f2a7198f421c361bc60dbee4 Mon Sep 17 00:00:00 2001 From: Holegots Date: Mon, 19 Dec 2022 16:45:22 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=F0=9F=92=A5=20Update=20chatgpt=20api=20and?= =?UTF-8?q?=20update=20config=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 180 ++++++++++++++++++++++++++++------------------ package.json | 2 +- src/chatgpt.ts | 135 ++++++++++++---------------------- src/interface.ts | 24 ++----- src/main.ts | 4 ++ 5 files changed, 168 insertions(+), 177 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0a6da1a..71a2a2b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { - "chatgpt": "^2.2.0", + "chatgpt": "^3.3.1", "dotenv": "^16.0.3", "execa": "^6.1.0", "qrcode": "^1.5.1", @@ -1120,18 +1120,6 @@ "node": ">=0.4.0" } }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "optional": true, - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -1186,22 +1174,28 @@ } }, "node_modules/chatgpt": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-2.2.0.tgz", - "integrity": "sha512-rxJDPl528XC1QXEeyThh0SViIv7S2ETeXLsmC9n7N1RdokR/MKKUJbICKi0jHmbhiY/1iIjFD/kQgkmYXFxygw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-3.3.1.tgz", + "integrity": "sha512-xb+Bpza2jgBoAoYgJ8/UjZ9EAuPYrQVOoTKqERHUUxoSK7wrMe3B+TIj3UDNKtxcAj6ZX8mcD57r3QH3cvNAUA==", "dependencies": { + "delay": "^5.0.0", "eventsource-parser": "^0.0.5", "expiry-map": "^2.0.0", + "html-to-md": "^0.8.3", "p-timeout": "^6.0.0", + "puppeteer-extra": "^3.3.4", + "puppeteer-extra-plugin-recaptcha": "npm:@fisch0920/puppeteer-extra-plugin-recaptcha@^3.6.6", + "puppeteer-extra-plugin-stealth": "^2.11.1", + "random": "^4.1.0", "remark": "^14.0.2", "strip-markdown": "^5.0.0", "uuid": "^9.0.0" }, "engines": { - "node": ">=16.8" + "node": ">=18" }, - "optionalDependencies": { - "undici": "^5.13.0" + "peerDependencies": { + "puppeteer": "*" } }, "node_modules/cheerio": { @@ -1478,6 +1472,17 @@ "node": ">=10" } }, + "node_modules/delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -2109,6 +2114,11 @@ "node": ">=4" } }, + "node_modules/html-to-md": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/html-to-md/-/html-to-md-0.8.3.tgz", + "integrity": "sha512-Va+bB1YOdD6vMRDue9/l7YxbERgwOgsos4erUDRfRN6YE0B2Wbbw8uAj6xZJk9A9vrjVy7mG/WLlhDw6RXfgsA==" + }, "node_modules/htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -3968,6 +3978,31 @@ } } }, + "node_modules/puppeteer-extra-plugin-recaptcha": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-recaptcha/-/puppeteer-extra-plugin-recaptcha-3.6.6.tgz", + "integrity": "sha512-SVbmL+igGX8m0Qg9dn85trWDghbfUCTG/QUHYscYx5XgMZVVb0/v0a6MqbPdHoKmBx5BS2kLd6rorMlncMcXdw==", + "dependencies": { + "debug": "^4.1.1", + "merge-deep": "^3.0.2", + "puppeteer-extra-plugin": "^3.2.2" + }, + "engines": { + "node": ">=9.11.2" + }, + "peerDependencies": { + "playwright-extra": "*", + "puppeteer-extra": "*" + }, + "peerDependenciesMeta": { + "playwright-extra": { + "optional": true + }, + "puppeteer-extra": { + "optional": true + } + } + }, "node_modules/puppeteer-extra-plugin-stealth": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.1.tgz", @@ -4122,6 +4157,17 @@ } ] }, + "node_modules/random": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz", + "integrity": "sha512-6Ajb7XmMSE9EFAMGC3kg9mvE7fGlBip25mYYuSMzw/uUSrmGilvZo2qwX3RnTRjwXkwkS+4swse9otZ92VjAtQ==", + "dependencies": { + "seedrandom": "^3.0.5" + }, + "engines": { + "node": ">=14" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -4378,6 +4424,11 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "node_modules/semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -4517,15 +4568,6 @@ "rxjs": "^7.4.0" } }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "optional": true, - "engines": { - "node": ">=10.0.0" - } - }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -4825,18 +4867,6 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "node_modules/undici": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.13.0.tgz", - "integrity": "sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q==", - "optional": true, - "dependencies": { - "busboy": "^1.6.0" - }, - "engines": { - "node": ">=12.18" - } - }, "node_modules/unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", @@ -6393,15 +6423,6 @@ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==" }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "optional": true, - "requires": { - "streamsearch": "^1.1.0" - } - }, "camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", @@ -6437,16 +6458,21 @@ "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==" }, "chatgpt": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-2.2.0.tgz", - "integrity": "sha512-rxJDPl528XC1QXEeyThh0SViIv7S2ETeXLsmC9n7N1RdokR/MKKUJbICKi0jHmbhiY/1iIjFD/kQgkmYXFxygw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/chatgpt/-/chatgpt-3.3.1.tgz", + "integrity": "sha512-xb+Bpza2jgBoAoYgJ8/UjZ9EAuPYrQVOoTKqERHUUxoSK7wrMe3B+TIj3UDNKtxcAj6ZX8mcD57r3QH3cvNAUA==", "requires": { + "delay": "^5.0.0", "eventsource-parser": "^0.0.5", "expiry-map": "^2.0.0", + "html-to-md": "^0.8.3", "p-timeout": "^6.0.0", + "puppeteer-extra": "^3.3.4", + "puppeteer-extra-plugin-recaptcha": "npm:@fisch0920/puppeteer-extra-plugin-recaptcha@^3.6.6", + "puppeteer-extra-plugin-stealth": "^2.11.1", + "random": "^4.1.0", "remark": "^14.0.2", "strip-markdown": "^5.0.0", - "undici": "^5.13.0", "uuid": "^9.0.0" } }, @@ -6661,6 +6687,11 @@ "inherits": "^2.0.3" } }, + "delay": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz", + "integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==" + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -7140,6 +7171,11 @@ "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true }, + "html-to-md": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/html-to-md/-/html-to-md-0.8.3.tgz", + "integrity": "sha512-Va+bB1YOdD6vMRDue9/l7YxbERgwOgsos4erUDRfRN6YE0B2Wbbw8uAj6xZJk9A9vrjVy7mG/WLlhDw6RXfgsA==" + }, "htmlparser2": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz", @@ -8430,6 +8466,16 @@ "merge-deep": "^3.0.1" } }, + "puppeteer-extra-plugin-recaptcha": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-recaptcha/-/puppeteer-extra-plugin-recaptcha-3.6.6.tgz", + "integrity": "sha512-SVbmL+igGX8m0Qg9dn85trWDghbfUCTG/QUHYscYx5XgMZVVb0/v0a6MqbPdHoKmBx5BS2kLd6rorMlncMcXdw==", + "requires": { + "debug": "^4.1.1", + "merge-deep": "^3.0.2", + "puppeteer-extra-plugin": "^3.2.2" + } + }, "puppeteer-extra-plugin-stealth": { "version": "2.11.1", "resolved": "https://registry.npmjs.org/puppeteer-extra-plugin-stealth/-/puppeteer-extra-plugin-stealth-2.11.1.tgz", @@ -8495,6 +8541,14 @@ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" }, + "random": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/random/-/random-4.1.0.tgz", + "integrity": "sha512-6Ajb7XmMSE9EFAMGC3kg9mvE7fGlBip25mYYuSMzw/uUSrmGilvZo2qwX3RnTRjwXkwkS+4swse9otZ92VjAtQ==", + "requires": { + "seedrandom": "^3.0.5" + } + }, "readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -8679,6 +8733,11 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, + "seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==" + }, "semver": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", @@ -8781,12 +8840,6 @@ "xstate": "^4.26.1" } }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "optional": true - }, "string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -9014,15 +9067,6 @@ "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", "dev": true }, - "undici": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-5.13.0.tgz", - "integrity": "sha512-UDZKtwb2k7KRsK4SdXWG7ErXiL7yTGgLWvk2AXO1JMjgjh404nFo6tWSCM2xMpJwMPx3J8i/vfqEh1zOqvj82Q==", - "optional": true, - "requires": { - "busboy": "^1.6.0" - } - }, "unified": { "version": "10.1.2", "resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz", diff --git a/package.json b/package.json index cedd6d9..52e0918 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "author": "", "license": "ISC", "dependencies": { - "chatgpt": "^2.2.0", + "chatgpt": "^3.3.1", "dotenv": "^16.0.3", "execa": "^6.1.0", "qrcode": "^1.5.1", diff --git a/src/chatgpt.ts b/src/chatgpt.ts index 85bc144..abe7c9e 100644 --- a/src/chatgpt.ts +++ b/src/chatgpt.ts @@ -1,4 +1,4 @@ -import { ChatGPTAPI, ChatGPTConversation } from "chatgpt"; +import { ChatGPTAPI, ChatGPTAPIBrowser } from "chatgpt"; import { Message } from "wechaty"; import { config } from "./config.js"; import { execa } from "execa"; @@ -8,9 +8,6 @@ import { IChatGPTItem, IConversationItem, AccountWithUserInfo, - isAccountWithUserInfo, - isAccountWithSessionToken, - AccountWithSessionToken, IAccount, } from "./interface.js"; @@ -78,96 +75,58 @@ 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.account.email === account.email + // 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.find( + ( + item: any + ): item is IChatGPTItem & { + account: AccountWithUserInfo; + chatGpt: ChatGPTAPI; + } => item.account.email === account.email + ); + if (chatGPTItem) { + const account = chatGPTItem.account; + try { + chatGPTItem.chatGpt = new ChatGPTAPIBrowser({ + ...account, + proxyServer: config.openAIProxy, + }); + } 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` ); - 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, - clearanceToken: config.clearanceToken, - userAgent: config.userAgent, - }); - } 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 - ); } } resetConversation(talkid: string) { this.conversationsPool.delete(talkid); } async startPools() { - const sessionAccounts = config.chatGPTAccountPool.filter( - isAccountWithSessionToken - ); - const userAccounts = await Promise.all( - config.chatGPTAccountPool - .filter(isAccountWithUserInfo) - .map(async (account: AccountWithUserInfo) => { - const session_token = await this.getSessionToken( - account.email, - account.password - ); - return { - ...account, - session_token, - }; - }) + const chatGPTPools = await Promise.all( + config.chatGPTAccountPool.map(async (account) => { + const chatGpt = new ChatGPTAPIBrowser({ + ...account, + proxyServer: config.openAIProxy, + }); + await chatGpt.initSession(); + return { + chatGpt: chatGpt, + account: account, + }; + }) ); - this.chatGPTPools = [...sessionAccounts, ...userAccounts].map((account) => { - return { - chatGpt: new ChatGPTAPI({ - sessionToken: account.session_token, - clearanceToken: config.clearanceToken, - userAgent: config.userAgent, - }), - account, - }; - }); + this.chatGPTPools = chatGPTPools; if (this.chatGPTPools.length === 0) { throw new Error("⚠️ No chatgpt account in pool"); } @@ -199,7 +158,8 @@ export class ChatGPTPoole { if (!chatGPT) { throw new Error("⚠️ No chatgpt item in pool"); } - const conversation = chatGPT.chatGpt.getConversation(); + //TODO: Add conversation implementation + const conversation = chatGPT.chatGpt; const conversationItem = { conversation, account: chatGPT.account, @@ -220,7 +180,7 @@ export class ChatGPTPoole { const { conversation, account } = conversationItem; try { // TODO: Add Retry logic - const response = await conversation.sendMessage(message); + const { response } = await conversation.sendMessage(message); return response; } catch (err: any) { if (err.message.includes("ChatGPT failed to refresh auth token")) { @@ -250,7 +210,6 @@ export class ChatGPTPoole { } export class ChatGPTBot { // Record talkid with conversation id - conversations = new Map(); chatGPTPool = new ChatGPTPoole(); cache = new Cache("cache.json"); chatPrivateTiggerKeyword = config.chatPrivateTiggerKeyword; diff --git a/src/interface.ts b/src/interface.ts index 9245656..f51c366 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -1,34 +1,18 @@ -import { ChatGPTConversation, ChatGPTAPI } from "chatgpt"; +import { ChatGPTAPIBrowser } from "chatgpt"; export interface AccountWithUserInfo { password: string; email: string; } -export interface AccountWithSessionToken { - session_token: string; -} -export const isAccountWithUserInfo = ( - account: IAccount -): account is AccountWithUserInfo => { - return ( - !!(account as AccountWithUserInfo).email && - !!(account as AccountWithUserInfo).password - ); -}; -export const isAccountWithSessionToken = ( - account: IAccount -): account is AccountWithSessionToken => { - return !!(account as AccountWithSessionToken).session_token; -}; // Account will be one in the session token or email and password -export type IAccount = AccountWithUserInfo | AccountWithSessionToken; +export type IAccount = AccountWithUserInfo; export interface IChatGPTItem { - chatGpt: ChatGPTAPI; + chatGpt: ChatGPTAPIBrowser; account: IAccount; } export interface IConversationItem { - conversation: ChatGPTConversation; + conversation: ChatGPTAPIBrowser; account: IAccount; } diff --git a/src/main.ts b/src/main.ts index 82586af..6459549 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,6 +5,10 @@ const chatGPTBot = new ChatGPTBot(); const bot = WechatyBuilder.build({ name: "wechat-assistant", // generate xxxx.memory-card.json and save login data for the next login + puppetOptions: { + uos: true, // 开启uos协议 + }, + puppet: "wechaty-puppet-wechat", }); // get a Wechaty instance From f3aafbbf706c97699bbbb0f0b8d4cf40beca2a4a Mon Sep 17 00:00:00 2001 From: Holegots Date: Tue, 20 Dec 2022 17:22:50 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E2=9C=A8=20Add=20conversation=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cache.ts | 23 ----------------- src/chatgpt.ts | 55 +++++++++++++++++------------------------ src/generate_session.py | 50 ------------------------------------- src/interface.ts | 2 ++ src/main.ts | 3 +++ 5 files changed, 27 insertions(+), 106 deletions(-) delete mode 100644 src/cache.ts delete mode 100644 src/generate_session.py diff --git a/src/cache.ts b/src/cache.ts deleted file mode 100644 index 18440f3..0000000 --- a/src/cache.ts +++ /dev/null @@ -1,23 +0,0 @@ -// use local json file as cache -import fs from "fs"; -export class Cache { - private _cache: Record; - constructor(private _file: string) { - if (fs.existsSync(_file)) { - this._cache = JSON.parse(fs.readFileSync(_file, "utf-8")); - } else { - this._cache = {}; - } - } - get(key: string) { - return this._cache[key]; - } - 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 abe7c9e..37dff0d 100644 --- a/src/chatgpt.ts +++ b/src/chatgpt.ts @@ -1,8 +1,6 @@ import { ChatGPTAPI, ChatGPTAPIBrowser } from "chatgpt"; import { Message } from "wechaty"; import { config } from "./config.js"; -import { execa } from "execa"; -import { Cache } from "./cache.js"; import { ContactInterface, RoomInterface } from "wechaty/impls"; import { IChatGPTItem, @@ -46,34 +44,6 @@ const Commands = ["/reset", "/help"] as const; export class ChatGPTPoole { chatGPTPools: Array | [] = []; conversationsPool: Map = new Map(); - cache = new Cache("cache.json"); - async getSessionToken(email: string, password: string): Promise { - if (this.cache.get(email)) { - return this.cache.get(email); - } - 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], - { - env: { - https_proxy: config.openAIProxy || process.env.https_proxy, - }, - } - ); - if (exitCode !== 0) { - console.error(`${email} login failed: ${stderr}`); - return ""; - } - // The last line in stdout is the session token - const lines = stdout.split("\n"); - if (lines.length > 0) { - this.cache.set(email, lines[lines.length - 1]); - return lines[lines.length - 1]; - } - return ""; - } async resetAccount(account: IAccount) { // Remove all conversation information this.conversationsPool.forEach((item, key) => { @@ -167,6 +137,14 @@ export class ChatGPTPoole { this.conversationsPool.set(talkid, conversationItem); return conversationItem; } + setConversation(talkid: string, conversationId: string, messageId: string) { + const conversationItem = this.getConversation(talkid); + this.conversationsPool.set(talkid, { + ...conversationItem, + conversationId, + messageId, + }); + } // send message with talkid async sendMessage(message: string, talkid: string): Promise { if ( @@ -177,10 +155,20 @@ export class ChatGPTPoole { return this.command(message as typeof Commands[number], talkid); } const conversationItem = this.getConversation(talkid); - const { conversation, account } = conversationItem; + const { conversation, account, conversationId, messageId } = + conversationItem; try { // TODO: Add Retry logic - const { response } = await conversation.sendMessage(message); + const { + response, + conversationId: newConversationId, + messageId: newMessageId, + } = await conversation.sendMessage(message, { + conversationId, + messageId, + }); + // Update conversation information + this.setConversation(talkid, newConversationId, newMessageId); return response; } catch (err: any) { if (err.message.includes("ChatGPT failed to refresh auth token")) { @@ -211,9 +199,9 @@ export class ChatGPTPoole { export class ChatGPTBot { // Record talkid with conversation id chatGPTPool = new ChatGPTPoole(); - cache = new Cache("cache.json"); chatPrivateTiggerKeyword = config.chatPrivateTiggerKeyword; botName: string = ""; + ready = false; setBotName(botName: string) { this.botName = botName; } @@ -224,6 +212,7 @@ export class ChatGPTBot { console.debug(`Start GPT Bot Config is:${JSON.stringify(config)}`); await this.chatGPTPool.startPools(); console.debug(`🤖️ Start GPT Bot Success, ready to handle message!`); + this.ready = true; } // TODO: Add reset conversation id and ping pong async command(): Promise {} diff --git a/src/generate_session.py b/src/generate_session.py deleted file mode 100644 index 040169a..0000000 --- a/src/generate_session.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -import re -import os -import argparse - - -def email_type(value: str) -> str: - """validator on email""" - email_pattern = re.compile(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$") - if not email_pattern.match(value): - raise argparse.ArgumentTypeError(f"'{value}' is not a valid email") - return value - - -def login(email: str, password: str) -> str: - """generate ChatGPT session_token by email and password""" - chatbot = Chatbot( - config={ - "email": email, - "password": password, - "proxy": os.environ.get("http_proxy",None) or os.environ.get("openAIProxy", None) , - }, - conversation_id=None, - ) - chatbot.login( - email=email, - password=password, - ) - return chatbot.config.get('session_token') - - -def gen_argparser() -> argparse.Namespace: - """generate argparser""" - parser = argparse.ArgumentParser(description="generate ChatGPT seesion token") - parser.add_argument("email", type=email_type, help="email address of your ChatGPT account") - parser.add_argument("password", type=str, help="password of your ChatGPT account") - arguments = parser.parse_args() - return arguments - - -if __name__ == "__main__": - try: - from revChatGPT.revChatGPT import Chatbot - except: - raise RuntimeError("Could import revChatGPT. Please install it first. You can run `poetry install` to install it.") - args = gen_argparser() - print(args) - print(login(args.email, args.password)) diff --git a/src/interface.ts b/src/interface.ts index f51c366..7a08cc7 100644 --- a/src/interface.ts +++ b/src/interface.ts @@ -14,6 +14,8 @@ export interface IChatGPTItem { export interface IConversationItem { conversation: ChatGPTAPIBrowser; account: IAccount; + conversationId?: string; + messageId?: string; } export interface IConfig { diff --git a/src/main.ts b/src/main.ts index 6459549..a9d8a55 100644 --- a/src/main.ts +++ b/src/main.ts @@ -27,6 +27,9 @@ async function main() { await chatGPTBot.startGPTBot(); }) .on("message", async (message) => { + if (!chatGPTBot.ready) { + return; + } if (message.text().startsWith("/ping ")) { await message.say("pong"); return; From 331cf60645e374f02636163557797829edc47057 Mon Sep 17 00:00:00 2001 From: Holegots Date: Tue, 20 Dec 2022 18:35:46 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Adjust=20code=20struct?= =?UTF-8?q?ure?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/bot.ts | 154 ++++++++++++++++++++++++++++++++++++++++++++++++ src/chatgpt.ts | 155 ++----------------------------------------------- src/main.ts | 2 +- 3 files changed, 160 insertions(+), 151 deletions(-) create mode 100644 src/bot.ts diff --git a/src/bot.ts b/src/bot.ts new file mode 100644 index 0000000..844c091 --- /dev/null +++ b/src/bot.ts @@ -0,0 +1,154 @@ +import { ChatGPTPool } from "./chatgpt.js"; +import { config } from "./config.js"; +import { ContactInterface, RoomInterface } from "wechaty/impls"; +import { Message } from "wechaty"; +enum MessageType { + Unknown = 0, + + Attachment = 1, // Attach(6), + Audio = 2, // Audio(1), Voice(34) + Contact = 3, // ShareCard(42) + ChatHistory = 4, // ChatHistory(19) + Emoticon = 5, // Sticker: Emoticon(15), Emoticon(47) + Image = 6, // Img(2), Image(3) + Text = 7, // Text(1) + Location = 8, // Location(48) + MiniProgram = 9, // MiniProgram(33) + GroupNote = 10, // GroupNote(53) + Transfer = 11, // Transfers(2000) + RedEnvelope = 12, // RedEnvelopes(2001) + Recalled = 13, // Recalled(10002) + Url = 14, // Url(5) + Video = 15, // Video(4), Video(43) + Post = 16, // Moment, Channel, Tweet, etc +} + +const SINGLE_MESSAGE_MAX_SIZE = 500; +export class ChatGPTBot { + // Record talkid with conversation id + chatGPTPool = new ChatGPTPool(); + chatPrivateTiggerKeyword = config.chatPrivateTiggerKeyword; + botName: string = ""; + ready = false; + setBotName(botName: string) { + this.botName = botName; + } + get chatGroupTiggerKeyword(): string { + return `@${this.botName}`; + } + async startGPTBot() { + console.debug(`Start GPT Bot Config is:${JSON.stringify(config)}`); + await this.chatGPTPool.startPools(); + console.debug(`🤖️ Start GPT Bot Success, ready to handle message!`); + this.ready = true; + } + // TODO: Add reset conversation id and ping pong + async command(): Promise {} + // remove more times conversation and mention + cleanMessage(rawText: string, privateChat: boolean = false): string { + let text = rawText; + const item = rawText.split("- - - - - - - - - - - - - - -"); + if (item.length > 1) { + text = item[item.length - 1]; + } + text = text.replace( + privateChat ? this.chatPrivateTiggerKeyword : this.chatGroupTiggerKeyword, + "" + ); + // remove more text via - - - - - - - - - - - - - - - + return text; + } + async getGPTMessage(text: string, talkerId: string): Promise { + return await this.chatGPTPool.sendMessage(text, talkerId); + } + // The message is segmented according to its size + async trySay( + talker: RoomInterface | ContactInterface, + mesasge: string + ): Promise { + const messages: Array = []; + let message = mesasge; + while (message.length > SINGLE_MESSAGE_MAX_SIZE) { + messages.push(message.slice(0, SINGLE_MESSAGE_MAX_SIZE)); + message = message.slice(SINGLE_MESSAGE_MAX_SIZE); + } + messages.push(message); + for (const msg of messages) { + await talker.say(msg); + } + } + // Check whether the ChatGPT processing can be triggered + tiggerGPTMessage(text: string, privateChat: boolean = false): boolean { + const chatPrivateTiggerKeyword = this.chatPrivateTiggerKeyword; + let triggered = false; + if (privateChat) { + triggered = chatPrivateTiggerKeyword + ? text.includes(chatPrivateTiggerKeyword) + : true; + } else { + triggered = text.includes(this.chatGroupTiggerKeyword); + } + if (triggered) { + console.log(`🎯 Triggered ChatGPT: ${text}`); + } + return triggered; + } + // Filter out the message that does not need to be processed + isNonsense( + talker: ContactInterface, + messageType: MessageType, + text: string + ): boolean { + return ( + talker.self() || + // TODO: add doc support + messageType !== MessageType.Text || + talker.name() == "微信团队" || + // 语音(视频)消息 + text.includes("收到一条视频/语音聊天消息,请在手机上查看") || + // 红包消息 + text.includes("收到红包,请在手机上查看") || + // Transfer message + text.includes("收到转账,请在手机上查看") || + // 位置消息 + text.includes("/cgi-bin/mmwebwx-bin/webwxgetpubliclinkimg") + ); + } + + async onPrivateMessage(talker: ContactInterface, text: string) { + const talkerId = talker.id; + const gptMessage = await this.getGPTMessage(text, talkerId); + await this.trySay(talker, gptMessage); + } + + async onGroupMessage( + talker: ContactInterface, + text: string, + room: RoomInterface + ) { + const talkerId = room.id + talker.id; + const gptMessage = await this.getGPTMessage(text, talkerId); + const result = `${text}\n ------\n ${gptMessage}`; + await this.trySay(room, result); + } + async onMessage(message: Message) { + const talker = message.talker(); + const rawText = message.text(); + const room = message.room(); + const messageType = message.type(); + const privateChat = !room; + if (this.isNonsense(talker, messageType, rawText)) { + return; + } + if (this.tiggerGPTMessage(rawText, privateChat)) { + const text = this.cleanMessage(rawText, privateChat); + if (privateChat) { + return await this.onPrivateMessage(talker, text); + } else { + return await this.onGroupMessage(talker, text, room); + } + } else { + return; + } + } +} diff --git a/src/chatgpt.ts b/src/chatgpt.ts index 37dff0d..90da3b9 100644 --- a/src/chatgpt.ts +++ b/src/chatgpt.ts @@ -1,7 +1,7 @@ import { ChatGPTAPI, ChatGPTAPIBrowser } from "chatgpt"; -import { Message } from "wechaty"; + import { config } from "./config.js"; -import { ContactInterface, RoomInterface } from "wechaty/impls"; + import { IChatGPTItem, IConversationItem, @@ -9,28 +9,6 @@ import { IAccount, } from "./interface.js"; -enum MessageType { - Unknown = 0, - - Attachment = 1, // Attach(6), - Audio = 2, // Audio(1), Voice(34) - Contact = 3, // ShareCard(42) - ChatHistory = 4, // ChatHistory(19) - Emoticon = 5, // Sticker: Emoticon(15), Emoticon(47) - Image = 6, // Img(2), Image(3) - Text = 7, // Text(1) - Location = 8, // Location(48) - MiniProgram = 9, // MiniProgram(33) - GroupNote = 10, // GroupNote(53) - Transfer = 11, // Transfers(2000) - RedEnvelope = 12, // RedEnvelopes(2001) - Recalled = 13, // Recalled(10002) - Url = 14, // Url(5) - Video = 15, // Video(4), Video(43) - Post = 16, // Moment, Channel, Tweet, etc -} - -const SINGLE_MESSAGE_MAX_SIZE = 500; const ErrorCode2Message: Record = { "503": "OpenAI 服务器繁忙,请稍后再试| The OpenAI server is busy, please try again later", @@ -38,10 +16,12 @@ const ErrorCode2Message: Record = { "OpenAI 服务器限流,请稍后再试| The OpenAI server was limited, please try again later", "500": "OpenAI 服务器繁忙,请稍后再试| The OpenAI server is busy, please try again later", + "403": + "OpenAI 服务器拒绝访问,请稍后再试| The OpenAI server refused to access, please try again later", unknown: "未知错误,请看日志 | Error unknown, please see the log", }; const Commands = ["/reset", "/help"] as const; -export class ChatGPTPoole { +export class ChatGPTPool { chatGPTPools: Array | [] = []; conversationsPool: Map = new Map(); async resetAccount(account: IAccount) { @@ -196,128 +176,3 @@ export class ChatGPTPoole { return ErrorCode2Message.unknown; } } -export class ChatGPTBot { - // Record talkid with conversation id - chatGPTPool = new ChatGPTPoole(); - chatPrivateTiggerKeyword = config.chatPrivateTiggerKeyword; - botName: string = ""; - ready = false; - setBotName(botName: string) { - this.botName = botName; - } - get chatGroupTiggerKeyword(): string { - return `@${this.botName}`; - } - async startGPTBot() { - console.debug(`Start GPT Bot Config is:${JSON.stringify(config)}`); - await this.chatGPTPool.startPools(); - console.debug(`🤖️ Start GPT Bot Success, ready to handle message!`); - this.ready = true; - } - // TODO: Add reset conversation id and ping pong - async command(): Promise {} - // remove more times conversation and mention - cleanMessage(rawText: string, privateChat: boolean = false): string { - let text = rawText; - const item = rawText.split("- - - - - - - - - - - - - - -"); - if (item.length > 1) { - text = item[item.length - 1]; - } - text = text.replace( - privateChat ? this.chatPrivateTiggerKeyword : this.chatGroupTiggerKeyword, - "" - ); - // remove more text via - - - - - - - - - - - - - - - - return text; - } - async getGPTMessage(text: string, talkerId: string): Promise { - return await this.chatGPTPool.sendMessage(text, talkerId); - } - // The message is segmented according to its size - async trySay( - talker: RoomInterface | ContactInterface, - mesasge: string - ): Promise { - const messages: Array = []; - let message = mesasge; - while (message.length > SINGLE_MESSAGE_MAX_SIZE) { - messages.push(message.slice(0, SINGLE_MESSAGE_MAX_SIZE)); - message = message.slice(SINGLE_MESSAGE_MAX_SIZE); - } - messages.push(message); - for (const msg of messages) { - await talker.say(msg); - } - } - // Check whether the ChatGPT processing can be triggered - tiggerGPTMessage(text: string, privateChat: boolean = false): boolean { - const chatPrivateTiggerKeyword = this.chatPrivateTiggerKeyword; - let triggered = false; - if (privateChat) { - triggered = chatPrivateTiggerKeyword - ? text.includes(chatPrivateTiggerKeyword) - : true; - } else { - triggered = text.includes(this.chatGroupTiggerKeyword); - } - if (triggered) { - console.log(`🎯 Triggered ChatGPT: ${text}`); - } - return triggered; - } - // Filter out the message that does not need to be processed - isNonsense( - talker: ContactInterface, - messageType: MessageType, - text: string - ): boolean { - return ( - talker.self() || - messageType > MessageType.GroupNote || - talker.name() == "微信团队" || - // 语音(视频)消息 - text.includes("收到一条视频/语音聊天消息,请在手机上查看") || - // 红包消息 - text.includes("收到红包,请在手机上查看") || - // 位置消息 - text.includes("/cgi-bin/mmwebwx-bin/webwxgetpubliclinkimg") - ); - } - - async onPrivateMessage(talker: ContactInterface, text: string) { - const talkerId = talker.id; - const gptMessage = await this.getGPTMessage(text, talkerId); - await this.trySay(talker, gptMessage); - } - - async onGroupMessage( - talker: ContactInterface, - text: string, - room: RoomInterface - ) { - const talkerId = room.id + talker.id; - const gptMessage = await this.getGPTMessage(text, talkerId); - const result = `${text}\n ------\n ${gptMessage}`; - await this.trySay(room, result); - } - async onMessage(message: Message) { - const talker = message.talker(); - const rawText = message.text(); - const room = message.room(); - const messageType = message.type(); - const privateChat = !room; - if (this.isNonsense(talker, messageType, rawText)) { - return; - } - if (this.tiggerGPTMessage(rawText, privateChat)) { - const text = this.cleanMessage(rawText, privateChat); - if (privateChat) { - return await this.onPrivateMessage(talker, text); - } else { - return await this.onGroupMessage(talker, text, room); - } - } else { - return; - } - } -} diff --git a/src/main.ts b/src/main.ts index a9d8a55..d6a0696 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,6 +1,6 @@ import { WechatyBuilder } from "wechaty"; import QRCode from "qrcode"; -import { ChatGPTBot } from "./chatgpt.js"; +import { ChatGPTBot } from "./bot.js"; const chatGPTBot = new ChatGPTBot(); const bot = WechatyBuilder.build({ From 0d5bcbdd29e46bc989de82e2734ffae188426c5e Mon Sep 17 00:00:00 2001 From: Holegots Date: Tue, 20 Dec 2022 20:33:31 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=F0=9F=93=9D=20Update=20Doc=20and=20docker?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 5 ++- Dockerfile | 28 +++++++-------- README.md | 85 +++++++++++++-------------------------------- config.yaml.example | 5 +-- src/config.ts | 1 - src/main.ts | 2 +- 6 files changed, 42 insertions(+), 84 deletions(-) diff --git a/.env.example b/.env.example index e8491e2..5dd6b29 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,7 @@ CHAT_GPT_EMAIL= CHAT_GPT_PASSWORD= -CHAT_GPT_SESSION_TOKEN= CHAT_GPT_RETRY_TIMES= CHAT_PRIVATE_TRIGGER_KEYWORD= OPENAI_PROXY= -CF_CLEARANCE= -USER_AGENT= +NOPECHA_KEY= +CAPTCHA_TOKEN= diff --git a/Dockerfile b/Dockerfile index 1e04077..6cae43a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,15 @@ -FROM python:3 +FROM node:19 AS app + +# We don't need the standalone Chromium +RUN apt-get install -y wget \ + && wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ + && echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list \ + && apt-get update && apt-get -y install google-chrome-stable chromium xvfb\ + && rm -rf /var/lib/apt/lists/* \ + && echo "Chrome: " && google-chrome --version WORKDIR /app -ARG POETRY_VERSION=1.2.2 -RUN apt-get update && \ - curl -sL https://deb.nodesource.com/setup_16.x | bash - && \ - apt-get install -y nodejs && \ - rm -rf /var/cache/apk/* && \ - pip3 install --no-cache-dir poetry && \ - rm -rf ~/.cache/ -COPY package*.json ./ -COPY pyproject.toml ./ -COPY poetry.lock ./ -# Install dependencies -ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true -RUN poetry install && npm install && rm -rf ~/.npm/ +COPY package*.json . +RUN npm install COPY . . -CMD ["npm", "run", "dev"] +ENV WECHATY_PUPPET_WECHAT_ENDPOINT=/usr/bin/google-chrome +CMD xvfb-run --server-args="-screen 0 1024x768x24" npm run dev \ No newline at end of file diff --git a/README.md b/README.md index c89bb84..2b64e45 100644 --- a/README.md +++ b/README.md @@ -16,12 +16,19 @@ English | [中文文档](README_ZH.md) If you don't have a server or want to experience rapid deployment, you can use Railway to do so, see [Usage with Railway](#usage-with-railway). -# Update December 13, 2022 -Yesterday (2022.12.12), OpenAI upgraded the authentication measures. +### Update December 20, 2022 + +Thanks @transitive-bullshit, The ChatGPT API automates the work. +You should use password & username to login, and config [CAPTCHAs](#CAPTCHAS). +⚠️ There may be a problem with the Docker image because I don't have an X86 device and Qume doesn't work. + +### Update December 13, 2022 + +Yesterday (2022.12.12), OpenAI upgraded the authentication measures. It causes `⚠️ No chatgpt item in pool` when you use this project. -However, please rest assured that we are actively looking for an effective solution, +However, please rest assured that we are actively looking for an effective solution, If you have a good solution, feel free to contribute! @@ -30,7 +37,7 @@ If you have a good solution, feel free to contribute! - [x] Use ChatGPT On Wechat via wechaty - [x] Support OpenAI Accounts Pool - [x] Support use proxy to login -- [x] Add conversation Support (Everyone will have their own session) +- [x] Add conversation Support - [x] Add Dockerfile - [x] Publish to Docker.hub - [x] Add Railway deploy @@ -80,7 +87,7 @@ docker logs -f wechat-chatgpt npm install && poetry install ``` -## Usage with manual +## Config ### Copy config @@ -94,7 +101,7 @@ cp config.yaml.example config.yaml > If you don't have this OpenAI account and you live in China, you can get it [here](https://mirror.xyz/boxchen.eth/9O9CSqyKDj4BKUIil7NC1Sa1LJM-3hsPqaeW_QjfFBc). -#### **A:Use account and password** +#### Use account and password You need get OpenAI account and password. Your config.yaml should be like this: @@ -118,59 +125,23 @@ You can configure in `config.yaml`: openAIProxy: ``` -Or you can export to environment variable: - -```sh -export http_proxy= -``` - -#### **B: Use Session Token** - -If you cant use email and password to login your openai account or your network can't login, you can use session token. You need to follow these steps: - -1. Go to and log in or sign up. -2. Open dev tools. -3. Open Application > Cookies. - ![image](docs/images/session-token.png) -4. Copy the value for `\_\_Secure-next-auth.session-token`, save it to your config - -Your `config.yaml` should be like this: - -```yaml -chatGPTAccountPool: - - session_token: -``` - -#### **Configure Cloudflare Token** - -We also need Cloudflare token to build the connection successfully. Similar to getting the session token, you need to copy the value for `cf_clearance`, save it to your config file. - -![image](docs/images/cloudflare-token.png) - -Your `config.yaml` should be like this: - -```yaml -clearanceToken: -``` - -#### **Configure User Agent** - -We have provided a sample `userAgent` in `config.yaml.example`. But you can also set up your own user agent, which can be found in following steps: +### CAPTCHAS -1. Open dev tools -2. Open network -3. Find `User-Agent` in request headers -4. Copy the value for `User-Agent`, save it to your config +> The browser portions of this package use Puppeteer to automate as much as possible, including solving all CAPTCHAs. 🔥 -![image](docs/images/user-agent.png) +> Basic Cloudflare CAPTCHAs are handled by default, but if you want to automate the email + password Recaptchas, you'll need to sign up for one of these paid providers: -Your `config.yaml` should be like this: +> - [nopecha](https://nopecha.com/) - Uses AI to solve CAPTCHAS +> - Faster and cheaper +> - Set the `NOPECHA_KEY` env var to your nopecha API key +> - [Demo video](https://user-images.githubusercontent.com/552829/208235991-de4890f2-e7ba-4b42-bf55-4fcd792d4b19.mp4) of nopecha solving the login Recaptcha (41 seconds) +> - [2captcha](https://2captcha.com) - Uses real people to solve CAPTCHAS +> - More well-known solution that's been around longer +> - Set the `CAPTCHA_TOKEN` env var to your 2captcha API token -```yaml -userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" -``` +So you should config `NOPECHA_KEY` or `CAPTCHA_TOKEN` in your Environment Variables. -### Start Project +## Start Project ```sh npm run dev @@ -198,16 +169,10 @@ Some environment variables need to be configured: - **CHAT_GPT_PASSWORD** : Your OpenAI Account password, *if you have session_token, It's optional*. -- **CHAT_GPT_SESSION_TOKEN** : Your OpenAI Account session_token, *if you have email and password, It's optional*.See how to get a token [here](#b-use-session-token). - - **CHAT_GPT_RETRY_TIMES** : The number of times to retry when the OpenAI API returns 429 or 503. - **CHAT_PRIVATE_TRIGGER_KEYWORD** : If you hope only some keywords can trigger chatgpt on private chat, you can set it. -- **CF_CLEARANCE** : Your Cloudflare's clearance token. See how to get a token [here](#configure-cloudflare-token). - -- **USER_AGENT**: Your user-agent. See how to get [here](#configure-user-agent). - Click the Deploy button and your service will start deploying shortly.The following interface appears to indicate that the deployment has begun: ![railway-deploying](docs/images/railway-deploying.png) diff --git a/config.yaml.example b/config.yaml.example index 0d789df..eddb959 100644 --- a/config.yaml.example +++ b/config.yaml.example @@ -1,8 +1,5 @@ chatGPTAccountPool: - email: email password: password - session_token: session_token chatPrivateTiggerKeyword: "" -openAIProxy: "" -clearanceToken: "" -userAgent: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36" \ No newline at end of file +openAIProxy: "" \ No newline at end of file diff --git a/src/config.ts b/src/config.ts index f164fb2..df810aa 100644 --- a/src/config.ts +++ b/src/config.ts @@ -14,7 +14,6 @@ if (fs.existsSync("./config.yaml")) { { email: process.env.CHAT_GPT_EMAIL, password: process.env.CHAT_GPT_PASSWORD, - session_token: process.env.CHAT_GPT_SESSION_TOKEN, }, ], chatGptRetryTimes: Number(process.env.CHAT_GPT_RETRY_TIMES), diff --git a/src/main.ts b/src/main.ts index d6a0696..04dde5c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,7 +30,7 @@ async function main() { if (!chatGPTBot.ready) { return; } - if (message.text().startsWith("/ping ")) { + if (message.text().startsWith("/ping")) { await message.say("pong"); return; }