diff --git a/apps/chat.js b/apps/chat.js index 4779acf..bcc6848 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -1,9 +1,10 @@ import plugin from '../../../lib/plugins/plugin.js' -import { ChatGPTAPI } from 'chatgpt' +import { ChatGPTAPI, getOpenAIAuth } from 'chatgpt' import _ from 'lodash' import { Config } from '../config/index.js' import showdown from 'showdown' import mjAPI from 'mathjax-node' +import puppeteer from '../utils/browser.js' // import showdownKatex from 'showdown-katex' const SESSION_TOKEN = Config.token const blockWords = '屏蔽词1,屏蔽词2,屏蔽词3' @@ -74,12 +75,6 @@ export class chatgpt extends plugin { } ] }) - this.chatGPTApi = new ChatGPTAPI({ - sessionToken: SESSION_TOKEN, - markdown: true, - userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36' - }) - logger.info('chatgpt插件已加载') } /** @@ -178,17 +173,64 @@ export class chatgpt extends plugin { if (e.isGroup && !e.atme) { return } - // let question = _.trimStart(e.msg, '#chatgpt') - let question = e.msg.trimStart() - // await e.runtime.render('chatgpt-plugin', 'content/index', { content: "", question }) - // return - try { - await this.chatGPTApi.ensureAuth() - } catch (e) { - logger.error(e) - await this.reply(`OpenAI认证失败,请检查Token:${e}`, true) - return + let api = await redis.get('CHATGPT:API_OPTION') + if (!api) { + let browser = await puppeteer.getBrowser() + const openAIAuth = await getOpenAIAuth((Config.username && Config.password) + ? { + email: Config.username, + password: Config.password, + browser + } + : { + browser + }) + const userAgent = await browser.userAgent() + let config = { markdown: true, userAgent } + let option + if (Config.username && Config.password) { + option = { ...Object.assign(config, openAIAuth) } + this.chatGPTApi = new ChatGPTAPI(option) + } else { + config.sessionToken = Config.token + option = { ...Object.assign(config, openAIAuth) } + this.chatGPTApi = new ChatGPTAPI(option) + } + try { + await this.chatGPTApi.ensureAuth() + await redis.set('CHATGPT:API_OPTION', JSON.stringify(option)) + } catch (e) { + logger.error(e) + await this.reply(`OpenAI认证失败,请检查Token:${e}`, true) + return + } + } else { + let option = JSON.parse(api) + this.chatGPTApi = new ChatGPTAPI(option) + try { + await this.chatGPTApi.ensureAuth() + } catch (e) { + let browser = await puppeteer.getBrowser() + const openAIAuth = await getOpenAIAuth({ + email: Config.username, + password: Config.password, + browserW + }) + const userAgent = await browser.userAgent() + let config = { markdown: true, userAgent } + let option = { ...Object.assign(config, openAIAuth) } + this.chatGPTApi = new ChatGPTAPI(option) + try { + await this.chatGPTApi.ensureAuth() + await redis.set('CHATGPT:API_OPTION', JSON.stringify(option)) + } catch (e) { + logger.error(e) + await this.reply(`OpenAI认证失败,请检查Token:${e}`, true) + return + } + } } + let question = e.msg.trimStart() await this.reply('我正在思考如何回复你,请稍等', true, { recallMsg: 5 }) let c logger.info(`chatgpt question: ${question}`) diff --git a/config/index.js b/config/index.js index f4f2957..b0ea2b3 100644 --- a/config/index.js +++ b/config/index.js @@ -1,5 +1,23 @@ +// Token,如不需要手动配置不填 const SESSION_TOKEN = '' +// CFtoken,每小时刷新一般不用填 +const CF_CLEARANCE = '' + +const PROXY = '' + export const Config = { - token: SESSION_TOKEN + token: SESSION_TOKEN, + cfClearance: CF_CLEARANCE, + proxy: PROXY, + username: '', + password: '', + // 改为true后,全局默认以图片形式回复,并自动发出Continue命令补全回答 + defaultUsePicture: true, + // 每个人发起的对话保留时长。超过这个时长没有进行对话,再进行对话将开启新的对话。单位:秒 + conversationPreserveTime: 600, + // UA: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36', + headless: false, + // 为空使用默认puppeteer的chromium,也可以传递自己本机安装的Chrome可执行文件地址,提高通过率 + chromePath: 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe' } diff --git a/utils/browser.js b/utils/browser.js new file mode 100644 index 0000000..3300cc7 --- /dev/null +++ b/utils/browser.js @@ -0,0 +1,88 @@ +import lodash from 'lodash' +import cfg from '../../../lib/config/config.js' +import { Config } from '../config/index.js' +import StealthPlugin from 'puppeteer-extra-plugin-stealth' +let puppeteer = {} + +class Puppeteer { + constructor () { + let args = [ + '--exclude-switches', + '--no-sandbox', + 'enable-automation', + // '--shm-size=1gb' + ] + if (Config.proxy) { + args.push(`--proxy-server=${Config.proxy}`) + } + this.browser = false + this.lock = false + this.config = { + headless: Config.headless, + args + } + + if (Config.chromePath) { + this.config.executablePath = Config.chromePath + } + + this.html = {} + } + + async initPupp () { + if (!lodash.isEmpty(puppeteer)) return puppeteer + puppeteer = (await import('puppeteer-extra')).default + const pluginStealth = StealthPlugin() + puppeteer.use(pluginStealth) + return puppeteer + } + + /** + * 初始化chromium + */ + async browserInit () { + await this.initPupp() + if (this.browser) return this.browser + if (this.lock) return false + this.lock = true + + logger.mark('puppeteer Chromium 启动中...') + + /** 初始化puppeteer */ + this.browser = await puppeteer.launch(this.config).catch((err) => { + logger.error(err.toString()) + if (String(err).includes('correct Chromium')) { + logger.error('没有正确安装Chromium,可以尝试执行安装命令:node ./node_modules/puppeteer/install.js') + } + }) + + this.lock = false + + if (!this.browser) { + logger.error('puppeteer Chromium 启动失败') + return false + } + + logger.mark('puppeteer Chromium 启动成功') + + /** 监听Chromium实例是否断开 */ + this.browser.on('disconnected', (e) => { + logger.error('Chromium实例关闭或崩溃!') + this.browser = false + }) + + return this.browser + } +} + +class ChatGPTPuppeteer extends Puppeteer { + async getBrowser () { + if (this.browser) { + return this.browser + } else { + return await this.browserInit() + } + } +} + +export default new ChatGPTPuppeteer()