From bd7aac0517065cdf68cfc315be10a7b5f788d390 Mon Sep 17 00:00:00 2001 From: ikechan8370 Date: Sat, 9 Mar 2024 23:37:47 +0800 Subject: [PATCH] fix: remove useless thing --- apps/button.js | 2 +- apps/chat.js | 76 +- apps/entertainment.js | 1 - apps/management.js | 27 +- apps/prompts.js | 4 +- apps/update.js | 10 - apps/vocal.js | 2 +- config/config.example.json | 7 - package.json | 4 - utils/SydneyAIClient.js | 22 +- utils/bard.js | 590 +++++------ utils/bingCaptcha.js | 2 +- utils/browser.js | 932 +----------------- utils/chat.js | 1 - utils/config.js | 2 +- utils/jwt.js | 2 +- utils/openai-auth.js | 281 ------ utils/poe/credential.js | 48 - .../graphql/AddHumanMessageMutation.graphql | 52 - .../graphql/AddMessageBreakMutation.graphql | 17 - .../graphql/AutoSubscriptionMutation.graphql | 7 - utils/poe/graphql/BioFragment.graphql | 8 - .../poe/graphql/ChatAddedSubscription.graphql | 5 - utils/poe/graphql/ChatFragment.graphql | 6 - utils/poe/graphql/ChatPaginationQuery.graphql | 26 - utils/poe/graphql/ChatViewQuery.graphql | 8 - .../DeleteHumanMessagesMutation.graphql | 7 - utils/poe/graphql/HandleFragment.graphql | 8 - .../LoginWithVerificationCodeMutation.graphql | 13 - .../graphql/MessageAddedSubscription.graphql | 5 - .../MessageDeletedSubscription.graphql | 6 - utils/poe/graphql/MessageFragment.graphql | 13 - .../graphql/MessageRemoveVoteMutation.graphql | 7 - .../graphql/MessageSetVoteMutation.graphql | 7 - ...ndVerificationCodeForLoginMutation.graphql | 12 - .../poe/graphql/ShareMessagesMutation.graphql | 9 - ...SignupWithVerificationCodeMutation.graphql | 13 - .../graphql/StaleChatUpdateMutation.graphql | 7 - .../graphql/SummarizePlainPostQuery.graphql | 3 - .../graphql/SummarizeQuotePostQuery.graphql | 3 - .../graphql/SummarizeSharePostQuery.graphql | 3 - utils/poe/graphql/UserSnippetFragment.graphql | 14 - utils/poe/graphql/ViewerInfoQuery.graphql | 21 - utils/poe/graphql/ViewerStateFragment.graphql | 30 - .../ViewerStateUpdatedSubscription.graphql | 5 - utils/poe/index.js | 299 ------ utils/poe/websocket 2.js | 65 -- utils/poe/websocket.js | 65 -- utils/slack/slackClient.js | 170 ---- utils/tools/WebsiteTool.js | 9 +- utils/tts/voicevox.js | 25 +- utils/wordcloud/tokenizer.js | 22 +- utils/wordcloud/wordcloud.js | 6 +- 53 files changed, 350 insertions(+), 2639 deletions(-) delete mode 100644 utils/openai-auth.js delete mode 100644 utils/poe/credential.js delete mode 100644 utils/poe/graphql/AddHumanMessageMutation.graphql delete mode 100644 utils/poe/graphql/AddMessageBreakMutation.graphql delete mode 100644 utils/poe/graphql/AutoSubscriptionMutation.graphql delete mode 100644 utils/poe/graphql/BioFragment.graphql delete mode 100644 utils/poe/graphql/ChatAddedSubscription.graphql delete mode 100644 utils/poe/graphql/ChatFragment.graphql delete mode 100644 utils/poe/graphql/ChatPaginationQuery.graphql delete mode 100644 utils/poe/graphql/ChatViewQuery.graphql delete mode 100644 utils/poe/graphql/DeleteHumanMessagesMutation.graphql delete mode 100644 utils/poe/graphql/HandleFragment.graphql delete mode 100644 utils/poe/graphql/LoginWithVerificationCodeMutation.graphql delete mode 100644 utils/poe/graphql/MessageAddedSubscription.graphql delete mode 100644 utils/poe/graphql/MessageDeletedSubscription.graphql delete mode 100644 utils/poe/graphql/MessageFragment.graphql delete mode 100644 utils/poe/graphql/MessageRemoveVoteMutation.graphql delete mode 100644 utils/poe/graphql/MessageSetVoteMutation.graphql delete mode 100644 utils/poe/graphql/SendVerificationCodeForLoginMutation.graphql delete mode 100644 utils/poe/graphql/ShareMessagesMutation.graphql delete mode 100644 utils/poe/graphql/SignupWithVerificationCodeMutation.graphql delete mode 100644 utils/poe/graphql/StaleChatUpdateMutation.graphql delete mode 100644 utils/poe/graphql/SummarizePlainPostQuery.graphql delete mode 100644 utils/poe/graphql/SummarizeQuotePostQuery.graphql delete mode 100644 utils/poe/graphql/SummarizeSharePostQuery.graphql delete mode 100644 utils/poe/graphql/UserSnippetFragment.graphql delete mode 100644 utils/poe/graphql/ViewerInfoQuery.graphql delete mode 100644 utils/poe/graphql/ViewerStateFragment.graphql delete mode 100644 utils/poe/graphql/ViewerStateUpdatedSubscription.graphql delete mode 100644 utils/poe/index.js delete mode 100644 utils/poe/websocket 2.js delete mode 100644 utils/poe/websocket.js delete mode 100644 utils/slack/slackClient.js diff --git a/apps/button.js b/apps/button.js index a483ff3..7bff6ff 100644 --- a/apps/button.js +++ b/apps/button.js @@ -1,5 +1,5 @@ import plugin from '../../../lib/plugins/plugin.js' -import {Config} from "../utils/config.js"; +import { Config } from '../utils/config.js' const PLUGIN_CHAT = 'ChatGpt 对话' const PLUGIN_MANAGEMENT = 'ChatGPT-Plugin 管理' diff --git a/apps/chat.js b/apps/chat.js index 73e4f85..405f24b 100644 --- a/apps/chat.js +++ b/apps/chat.js @@ -5,7 +5,6 @@ import { Config, defaultOpenAIAPI } from '../utils/config.js' import { v4 as uuid } from 'uuid' import { ChatGPTAPI } from '../utils/openai/chatgpt-api.js' import SydneyAIClient from '../utils/SydneyAIClient.js' -import { PoeClient } from '../utils/poe/index.js' import AzureTTS from '../utils/tts/microsoft-azure.js' import VoiceVoxTTS from '../utils/tts/voicevox.js' import { @@ -31,7 +30,6 @@ import { renderUrl } from '../utils/common.js' -import { ChatGPTPuppeteer } from '../utils/browser.js' import { KeyvFile } from 'keyv-file' import { OfficialChatGPTClient } from '../utils/message.js' import fetch from 'node-fetch' @@ -39,7 +37,7 @@ import { deleteConversation, getConversations, getLatestMessageIdByConversationI import { convertSpeaker, speakers } from '../utils/tts.js' import ChatGLMClient from '../utils/chatglm.js' import { convertFaces } from '../utils/face.js' -import { originalValues, ConversationManager } from '../model/conversation.js' +import { ConversationManager, originalValues } from '../model/conversation.js' import BingDrawClient from '../utils/BingDraw.js' import XinghuoClient from '../utils/xinghuo/xinghuo.js' import Bard from '../utils/bard.js' @@ -795,10 +793,6 @@ export class chatgpt extends plugin { key = `CHATGPT:CONVERSATIONS_CHATGLM:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}` break } - case 'browser': { - key = `CHATGPT:CONVERSATIONS_BROWSER:${(e.isGroup && Config.groupMerge) ? e.group_id.toString() : e.sender.user_id}` - break - } case 'claude2': { key = `CHATGPT:CLAUDE2_CONVERSATION:${e.sender.user_id}` break @@ -877,7 +871,7 @@ export class chatgpt extends plugin { // 字数超限直接返回 return false } - if (use !== 'api3' && use !== 'poe') { + if (use !== 'api3') { previousConversation.conversation = { conversationId: chatMessage.conversationId } @@ -1534,49 +1528,9 @@ export class chatgpt extends plugin { user: e.sender.user_id, cache: cacheOptions }) - let sendMessageResult = await this.chatGPTApi.sendMessage(prompt, conversation) - return sendMessageResult - } else if (use === 'poe') { - const cookie = await redis.get('CHATGPT:POE_TOKEN') - if (!cookie) { - throw new Error('未绑定Poe Cookie,请使用#chatgpt设置Poe token命令绑定cookie') - } - let client = new PoeClient({ - quora_cookie: cookie, - proxy: Config.proxy - }) - await client.setCredentials() - await client.getChatId() - let ai = 'a2' // todo - await client.sendMsg(ai, prompt) - const response = await client.getResponse(ai) - return { - text: response.data - } + return await this.chatGPTApi.sendMessage(prompt, conversation) } else if (use === 'claude') { // slack已经不可用,移除 - // let client = new SlackClaudeClient({ - // slackUserToken: Config.slackUserToken, - // slackChannelId: Config.slackChannelId - // }) - // let conversationId = await redis.get(`CHATGPT:SLACK_CONVERSATION:${e.sender.user_id}`) - // if (!conversationId) { - // // 如果是新对话 - // if (Config.slackClaudeEnableGlobalPreset && (useCast?.slack || Config.slackClaudeGlobalPreset)) { - // // 先发送设定 - // let prompt = (useCast?.slack || Config.slackClaudeGlobalPreset) - // let emotion = await AzureTTS.getEmotionPrompt(e) - // if (emotion) { - // prompt = prompt + '\n' + emotion - // } - // await client.sendMessage(prompt, e) - // logger.info('claudeFirst:', prompt) - // } - // } - // let text = await client.sendMessage(prompt, e) - // return { - // text - // } const client = new ClaudeAPIClient({ key: Config.claudeApiKey, model: Config.claudeApiModel || 'claude-3-sonnet-20240229', @@ -2408,30 +2362,6 @@ export class chatgpt extends plugin { this.reply('总额度:$' + hardLimit + '\n已经使用额度:$' + totalUsage / 100 + '\n当前剩余额度:$' + left + '\n到期日期(UTC):' + expiresAt) } - /** - * #chatgpt - * @param prompt 问题 - * @param conversation 对话 - */ - async chatgptBrowserBased (prompt, conversation) { - let option = { markdown: true } - if (Config['2captchaToken']) { - option.captchaToken = Config['2captchaToken'] - } - // option.debug = true - option.email = Config.username - option.password = Config.password - this.chatGPTApi = new ChatGPTPuppeteer(option) - logger.info(`chatgpt prompt: ${prompt}`) - let sendMessageOption = { - timeoutMs: 120000 - } - if (conversation) { - sendMessageOption = Object.assign(sendMessageOption, conversation) - } - return await this.chatGPTApi.sendMessage(prompt, sendMessageOption) - } - /** * 其他模式 * @param e diff --git a/apps/entertainment.js b/apps/entertainment.js index d7fcbc7..b0ba5b3 100644 --- a/apps/entertainment.js +++ b/apps/entertainment.js @@ -4,7 +4,6 @@ import { generateHello } from '../utils/randomMessage.js' import { generateVitsAudio } from '../utils/tts.js' import fs from 'fs' import { emojiRegex, googleRequestUrl } from '../utils/emoj/index.js' -import fetch from 'node-fetch' import { getImageOcrText, getImg, makeForwardMsg, mkdirs, renderUrl } from '../utils/common.js' import uploadRecord from '../utils/uploadRecord.js' import { makeWordcloud } from '../utils/wordcloud/wordcloud.js' diff --git a/apps/management.js b/apps/management.js index 5e2c22a..c89b901 100644 --- a/apps/management.js +++ b/apps/management.js @@ -99,13 +99,8 @@ export class ChatgptManagement extends plugin { permission: 'master' }, { - reg: '^#chatgpt切换(Poe|poe)$', - fnc: 'useClaudeBasedSolution', - permission: 'master' - }, - { - reg: '^#chatgpt切换(Claude|claude|slack)$', - fnc: 'useSlackClaudeBasedSolution', + reg: '^#chatgpt切换(Claude|claude)$', + fnc: 'useClaudeAPIBasedSolution', permission: 'master' }, { @@ -923,23 +918,13 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务, } } - async useClaudeBasedSolution (e) { - let use = await redis.get('CHATGPT:USE') - if (use !== 'poe') { - await redis.set('CHATGPT:USE', 'poe') - await this.reply('已切换到基于Quora\'s POE的解决方案') - } else { - await this.reply('当前已经是POE模式了') - } - } - - async useSlackClaudeBasedSolution () { + async useClaudeAPIBasedSolution () { let use = await redis.get('CHATGPT:USE') if (use !== 'claude') { await redis.set('CHATGPT:USE', 'claude') - await this.reply('已切换到基于slack claude机器人的解决方案') + await this.reply('已切换到基于ClaudeAPI的解决方案') } else { - await this.reply('当前已经是claude模式了') + await this.reply('当前已经是Claude模式了') } } @@ -949,7 +934,7 @@ azure语音:Azure 语音是微软 Azure 平台提供的一项语音服务, await redis.set('CHATGPT:USE', 'claude2') await this.reply('已切换到基于claude.ai的解决方案') } else { - await this.reply('当前已经是claude2模式了') + await this.reply('当前已经是claude.ai模式了') } } diff --git a/apps/prompts.js b/apps/prompts.js index 1f60f14..b360db7 100644 --- a/apps/prompts.js +++ b/apps/prompts.js @@ -1,10 +1,8 @@ import plugin from '../../../lib/plugins/plugin.js' -import fs from 'fs' -import _ from 'lodash' import { Config } from '../utils/config.js' import { getMasterQQ, limitString, makeForwardMsg, maskQQ, getUin } from '../utils/common.js' import { deleteOnePrompt, getPromptByName, readPrompts, saveOnePrompt } from '../utils/prompts.js' -import AzureTTS from "../utils/tts/microsoft-azure.js"; +import AzureTTS from '../utils/tts/microsoft-azure.js' export class help extends plugin { constructor (e) { super({ diff --git a/apps/update.js b/apps/update.js index 4de2b6d..f22c8a6 100644 --- a/apps/update.js +++ b/apps/update.js @@ -3,21 +3,11 @@ import plugin from '../../../lib/plugins/plugin.js' import { createRequire } from 'module' import _ from 'lodash' import { Restart } from '../../other/restart.js' -import fs from 'fs' import {} from '../utils/common.js' -const _path = process.cwd() const require = createRequire(import.meta.url) const { exec, execSync } = require('child_process') -const checkAuth = async function (e) { - if (!e.isMaster) { - e.reply('只有主人才能命令ChatGPT哦~(*/ω\*)') - return false - } - return true -} - // 是否在更新中 let uping = false diff --git a/apps/vocal.js b/apps/vocal.js index 74ce9d8..805b64f 100644 --- a/apps/vocal.js +++ b/apps/vocal.js @@ -1,7 +1,7 @@ import plugin from '../../../lib/plugins/plugin.js' import { SunoClient } from '../client/SunoClient.js' import { Config } from '../utils/config.js' -import { downloadFile, maskEmail } from '../utils/common.js' +import { maskEmail } from '../utils/common.js' import common from '../../../lib/common/common.js' import lodash from 'lodash' diff --git a/config/config.example.json b/config/config.example.json index 8b2dbc1..df1b8a9 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -89,13 +89,6 @@ "whitelist": [], "blacklist": [], "ttsRegex": "/匹配规则/匹配模式", - "slackUserToken": "", - "slackBotUserToken": "", - "slackSigningSecret": "", - "slackClaudeUserId": "", - "slackClaudeEnableGlobalPreset": true, - "slackClaudeGlobalPreset": "", - "slackClaudeSpecifiedChannel": "", "cloudTranscode": "https://silk.201666.xyz", "cloudRender": false, "cloudMode": "url", diff --git a/package.json b/package.json index 44f2ee1..1055f02 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "@fastify/static": "^6.9.0", "@fastify/websocket": "^8.2.0", "@google/generative-ai": "^0.1.1", - "@slack/bolt": "^3.13.2", "asn1.js": "^5.0.0", "diff": "^5.1.0", "emoji-strip": "^1.0.1", @@ -40,9 +39,6 @@ "node-silk": "^0.1.0", "nodejs-pptx": "^1.2.4", "pdfjs-dist": "^3.11.174", - "puppeteer-extra": "^3.3.6", - "puppeteer-extra-plugin-recaptcha": "^3.6.8", - "puppeteer-extra-plugin-stealth": "^2.11.2", "sharp": "^0.32.3", "xlsx": "^0.18.5" }, diff --git a/utils/SydneyAIClient.js b/utils/SydneyAIClient.js index bee3eef..02c2706 100644 --- a/utils/SydneyAIClient.js +++ b/utils/SydneyAIClient.js @@ -1,23 +1,23 @@ import fetch, { - Headers, - Request, - Response, + // Headers, + // Request, + // Response, FormData } from 'node-fetch' import crypto from 'crypto' import WebSocket from 'ws' -import { Config, pureSydneyInstruction } from './config.js' +import { Config } from './config.js' import { formatDate, getMasterQQ, isCN, getUserData, limitString } from './common.js' import moment from 'moment' import { getProxy } from './proxy.js' import common from '../../../lib/common/common.js' - -if (!globalThis.fetch) { - globalThis.fetch = fetch - globalThis.Headers = Headers - globalThis.Request = Request - globalThis.Response = Response -} +// +// if (!globalThis.fetch) { +// globalThis.fetch = fetch +// globalThis.Headers = Headers +// globalThis.Request = Request +// globalThis.Response = Response +// } // workaround for ver 7.x and ver 5.x let proxy = getProxy() diff --git a/utils/bard.js b/utils/bard.js index dff940c..a28e060 100644 --- a/utils/bard.js +++ b/utils/bard.js @@ -1,373 +1,373 @@ // https://github.com/EvanZhouDev/bard-ai class Bard { - static JSON = "json"; - static MD = "markdown"; + static JSON = 'json' + static MD = 'markdown' // ID derived from Cookie - SNlM0e; + SNlM0e // HTTPS Headers - #headers; + #headers // Resolution status of initialization call - #initPromise; + #initPromise - #bardURL = "https://bard.google.com"; + #bardURL = 'https://bard.google.com' // Wether or not to log events to console - #verbose = false; + #verbose = false // Fetch function - #fetch = fetch; + #fetch = fetch - constructor(cookie, config) { - // Register some settings - if (config?.verbose == true) this.#verbose = true; - if (config?.fetch) this.#fetch = config.fetch; - // 可变更访问地址,利用反向代理绕过区域限制 - if (config?.bardURL) this.#bardURL = config.bardURL; + constructor (cookie, config) { + // Register some settings + if (config?.verbose == true) this.#verbose = true + if (config?.fetch) this.#fetch = config.fetch + // 可变更访问地址,利用反向代理绕过区域限制 + if (config?.bardURL) this.#bardURL = config.bardURL - // If a Cookie is provided, initialize - if (cookie) { - this.#initPromise = this.#init(cookie); - } else { - throw new Error("Please provide a Cookie when initializing Bard."); - } - this.cookie = cookie; + // If a Cookie is provided, initialize + if (cookie) { + this.#initPromise = this.#init(cookie) + } else { + throw new Error('Please provide a Cookie when initializing Bard.') + } + this.cookie = cookie } // You can also choose to initialize manually - async #init(cookie) { - this.#verbose && console.log("🚀 Starting intialization"); - // Assign headers - this.#headers = { - Host: this.#bardURL.match(/^https?:\/\/([^\/]+)\/?$/)[1], - "X-Same-Domain": "1", - "User-Agent": - "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36", - "Content-Type": "application/x-www-form-urlencoded;charset=UTF-8", - Origin: this.#bardURL, - Referer: this.#bardURL, - Cookie: (typeof cookie === "object") ? (Object.entries(cookie).map(([key, val]) => `${key}=${val};`).join("")) : ("__Secure-1PSID=" + cookie), - }; + async #init (cookie) { + this.#verbose && console.log('🚀 Starting intialization') + // Assign headers + this.#headers = { + Host: this.#bardURL.match(/^https?:\/\/([^\/]+)\/?$/)[1], + 'X-Same-Domain': '1', + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36', + 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8', + Origin: this.#bardURL, + Referer: this.#bardURL, + Cookie: (typeof cookie === 'object') ? (Object.entries(cookie).map(([key, val]) => `${key}=${val};`).join('')) : ('__Secure-1PSID=' + cookie) + } - let responseText; - // Attempt to retrieve SNlM0e - try { - this.#verbose && - console.log("🔒 Authenticating your Google account"); - responseText = await this.#fetch(this.#bardURL, { - method: "GET", - headers: this.#headers, - credentials: "include", - }) - .then((response) => response.text()) - } catch (e) { - // Failure to get server - throw new Error( - "Could not fetch Google Bard. You may be disconnected from internet: " + + let responseText + // Attempt to retrieve SNlM0e + try { + this.#verbose && + console.log('🔒 Authenticating your Google account') + responseText = await this.#fetch(this.#bardURL, { + method: 'GET', + headers: this.#headers, + credentials: 'include' + }) + .then((response) => response.text()) + } catch (e) { + // Failure to get server + throw new Error( + 'Could not fetch Google Bard. You may be disconnected from internet: ' + e - ); - } + ) + } - try { - const SNlM0e = responseText.match(/SNlM0e":"(.*?)"/)[1]; - // Assign SNlM0e and return it - this.SNlM0e = SNlM0e; - this.#verbose && console.log("✅ Initialization finished\n"); - return SNlM0e; - } catch { - throw new Error( - "Could not use your Cookie. Make sure that you copied correctly the Cookie with name __Secure-1PSID exactly. If you are sure your cookie is correct, you may also have reached your rate limit." - ); - } + try { + const SNlM0e = responseText.match(/SNlM0e":"(.*?)"/)[1] + // Assign SNlM0e and return it + this.SNlM0e = SNlM0e + this.#verbose && console.log('✅ Initialization finished\n') + return SNlM0e + } catch { + throw new Error( + 'Could not use your Cookie. Make sure that you copied correctly the Cookie with name __Secure-1PSID exactly. If you are sure your cookie is correct, you may also have reached your rate limit.' + ) + } } - async #uploadImage(name, buffer) { - this.#verbose && console.log("🖼️ Starting image processing"); - let size = buffer.byteLength; - let formBody = [ - `${encodeURIComponent("File name")}=${encodeURIComponent([name])}`, - ]; + async #uploadImage (name, buffer) { + this.#verbose && console.log('🖼️ Starting image processing') + let size = buffer.byteLength + let formBody = [ + `${encodeURIComponent('File name')}=${encodeURIComponent([name])}` + ] - try { - this.#verbose && - console.log("💻 Finding Google server destination"); - let response = await this.#fetch( - "https://content-push.googleapis.com/upload/", - { - method: "POST", - headers: { - "X-Goog-Upload-Command": "start", - "X-Goog-Upload-Protocol": "resumable", - "X-Goog-Upload-Header-Content-Length": size, - "X-Tenant-Id": "bard-storage", - "Push-Id": "feeds/mcudyrk2a4khkz", - }, - body: formBody, - credentials: "include", - } - ); + try { + this.#verbose && + console.log('💻 Finding Google server destination') + let response = await this.#fetch( + 'https://content-push.googleapis.com/upload/', + { + method: 'POST', + headers: { + 'X-Goog-Upload-Command': 'start', + 'X-Goog-Upload-Protocol': 'resumable', + 'X-Goog-Upload-Header-Content-Length': size, + 'X-Tenant-Id': 'bard-storage', + 'Push-Id': 'feeds/mcudyrk2a4khkz' + }, + body: formBody, + credentials: 'include' + } + ) - const uploadUrl = response.headers.get("X-Goog-Upload-URL"); - this.#verbose && console.log("📤 Sending your image"); - response = await this.#fetch(uploadUrl, { - method: "POST", - headers: { - "X-Goog-Upload-Command": "upload, finalize", - "X-Goog-Upload-Offset": 0, - "X-Tenant-Id": "bard-storage", - }, - body: buffer, - credentials: "include", - }); + const uploadUrl = response.headers.get('X-Goog-Upload-URL') + this.#verbose && console.log('📤 Sending your image') + response = await this.#fetch(uploadUrl, { + method: 'POST', + headers: { + 'X-Goog-Upload-Command': 'upload, finalize', + 'X-Goog-Upload-Offset': 0, + 'X-Tenant-Id': 'bard-storage' + }, + body: buffer, + credentials: 'include' + }) - const imageFileLocation = await response.text(); + const imageFileLocation = await response.text() - this.#verbose && console.log("✅ Image finished working\n"); - return imageFileLocation; - } catch (e) { - throw new Error( - "Could not fetch Google Bard. You may be disconnected from internet: " + + this.#verbose && console.log('✅ Image finished working\n') + return imageFileLocation + } catch (e) { + throw new Error( + 'Could not fetch Google Bard. You may be disconnected from internet: ' + e - ); - } + ) + } } // Query Bard - async #query(message, config) { - let formatMarkdown = (text, images) => { - if (!images) return text; + async #query (message, config) { + let formatMarkdown = (text, images) => { + if (!images) return text - for (let imageData of images) { - const formattedTag = `!${imageData.tag}(${imageData.url})`; - text = text.replace( - new RegExp(`(?!\\!)\\[${imageData.tag.slice(1, -1)}\\]`), - formattedTag - ); - } - - return text; + for (let imageData of images) { + const formattedTag = `!${imageData.tag}(${imageData.url})` + text = text.replace( + new RegExp(`(?!\\!)\\[${imageData.tag.slice(1, -1)}\\]`), + formattedTag + ) } - let { ids, imageBuffer } = config; + return text + } - // Wait until after init - await this.#initPromise; + let { ids, imageBuffer } = config - this.#verbose && console.log("🔎 Starting Bard Query"); + // Wait until after init + await this.#initPromise - // If user has not run init - if (!this.SNlM0e) { - throw new Error( - "Please initialize Bard first. If you haven't passed in your Cookie into the class, run Bard.init(cookie)." - ); - } + this.#verbose && console.log('🔎 Starting Bard Query') - this.#verbose && console.log("🏗️ Building Request"); - // HTTPS parameters - const params = { - bl: "boq_assistant-bard-web-server_20230711.08_p0", - _reqID: ids?._reqID ?? "0", - rt: "c", - }; + // If user has not run init + if (!this.SNlM0e) { + throw new Error( + "Please initialize Bard first. If you haven't passed in your Cookie into the class, run Bard.init(cookie)." + ) + } - // If IDs are provided, but doesn't have every one of the expected IDs, error - const messageStruct = [ - [message], - null, - [null, null, null], - ]; + this.#verbose && console.log('🏗️ Building Request') + // HTTPS parameters + const params = { + bl: 'boq_assistant-bard-web-server_20230711.08_p0', + _reqID: ids?._reqID ?? '0', + rt: 'c' + } - if (imageBuffer) { - let imageLocation = await this.#uploadImage( - `bard-ai_upload`, - imageBuffer - ); - messageStruct[0].push(0, null, [ - [[imageLocation, 1], "bard-ai_upload"], - ]); - } + // If IDs are provided, but doesn't have every one of the expected IDs, error + const messageStruct = [ + [message], + null, + [null, null, null] + ] - if (ids) { - const { conversationID, responseID, choiceID } = ids; - messageStruct[2] = [conversationID, responseID, choiceID]; - } + if (imageBuffer) { + let imageLocation = await this.#uploadImage( + 'bard-ai_upload', + imageBuffer + ) + messageStruct[0].push(0, null, [ + [[imageLocation, 1], 'bard-ai_upload'] + ]) + } - // HTTPs data - const data = { - "f.req": JSON.stringify([null, JSON.stringify(messageStruct)]), - at: this.SNlM0e, - }; + if (ids) { + const { conversationID, responseID, choiceID } = ids + messageStruct[2] = [conversationID, responseID, choiceID] + } - // URL that we are submitting to - const url = new URL( - "/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate", - this.#bardURL - ); + // HTTPs data + const data = { + 'f.req': JSON.stringify([null, JSON.stringify(messageStruct)]), + at: this.SNlM0e + } - // Append parameters to the URL - for (const key in params) { - url.searchParams.append(key, params[key]); - } + // URL that we are submitting to + const url = new URL( + '/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate', + this.#bardURL + ) - // Encode the data - const formBody = Object.entries(data) - .map( - ([property, value]) => + // Append parameters to the URL + for (const key in params) { + url.searchParams.append(key, params[key]) + } + + // Encode the data + const formBody = Object.entries(data) + .map( + ([property, value]) => `${encodeURIComponent(property)}=${encodeURIComponent( value )}` - ) - .join("&"); + ) + .join('&') - this.#verbose && console.log("💭 Sending message to Bard"); - // Send the fetch request - const chatData = await this.#fetch(url.toString(), { - method: "POST", - headers: this.#headers, - body: formBody, - credentials: "include", + this.#verbose && console.log('💭 Sending message to Bard') + // Send the fetch request + const chatData = await this.#fetch(url.toString(), { + method: 'POST', + headers: this.#headers, + body: formBody, + credentials: 'include' + }) + .then((response) => { + return response.text() }) - .then((response) => { - return response.text(); - }) - .then((text) => { - return JSON.parse(text.split("\n")[3])[0][2]; - }) - .then((rawData) => JSON.parse(rawData)); + .then((text) => { + return JSON.parse(text.split('\n')[3])[0][2] + }) + .then((rawData) => JSON.parse(rawData)) - this.#verbose && console.log("🧩 Parsing output"); - // Get first Bard-recommended answer - const answer = chatData[4][0]; + this.#verbose && console.log('🧩 Parsing output') + // Get first Bard-recommended answer + const answer = chatData[4][0] - // Text of that answer - const text = answer[1][0]; + // Text of that answer + const text = answer[1][0] - // Get data about images in that answer - const images = + // Get data about images in that answer + const images = answer[4]?.map((x) => ({ - tag: x[2], - url: x[3][0][0], - info: { - raw: x[0][0][0], - source: x[1][0][0], - alt: x[0][4], - website: x[1][1], - favicon: x[1][3], - }, - })) ?? []; + tag: x[2], + url: x[3][0][0], + info: { + raw: x[0][0][0], + source: x[1][0][0], + alt: x[0][4], + website: x[1][1], + favicon: x[1][3] + } + })) ?? [] - this.#verbose && console.log("✅ All done!\n"); - // Put everything together and return - return { - content: formatMarkdown(text, images), - images: images, - ids: { - conversationID: chatData[1][0], - responseID: chatData[1][1], - choiceID: answer[0], - _reqID: String(parseInt(ids?._reqID ?? 0) + 100000), - }, - }; + this.#verbose && console.log('✅ All done!\n') + // Put everything together and return + return { + content: formatMarkdown(text, images), + images, + ids: { + conversationID: chatData[1][0], + responseID: chatData[1][1], + choiceID: answer[0], + _reqID: String(parseInt(ids?._reqID ?? 0) + 100000) + } + } } - async #parseConfig(config) { - let result = { - useJSON: false, - imageBuffer: undefined, // Returns as {extension, filename} - ids: undefined, - }; + async #parseConfig (config) { + let result = { + useJSON: false, + imageBuffer: undefined, // Returns as {extension, filename} + ids: undefined + } - // Verify that format is one of the two types - if (config?.format) { - switch (config.format) { - case Bard.JSON: - result.useJSON = true; - break; - case Bard.MD: - result.useJSON = false; - break; - default: - throw new Error( - "Format can obly be Bard.JSON for JSON output or Bard.MD for Markdown output." - ); - } + // Verify that format is one of the two types + if (config?.format) { + switch (config.format) { + case Bard.JSON: + result.useJSON = true + break + case Bard.MD: + result.useJSON = false + break + default: + throw new Error( + 'Format can obly be Bard.JSON for JSON output or Bard.MD for Markdown output.' + ) } + } - // Verify that the image passed in is either a path to a jpeg, jpg, png, or webp, or that it is a Buffer - if (config?.image) { - if ( - config.image instanceof ArrayBuffer - ) { - result.imageBuffer = config.image; - } else if ( - typeof config.image === "string" && + // Verify that the image passed in is either a path to a jpeg, jpg, png, or webp, or that it is a Buffer + if (config?.image) { + if ( + config.image instanceof ArrayBuffer + ) { + result.imageBuffer = config.image + } else if ( + typeof config.image === 'string' && /\.(jpeg|jpg|png|webp)$/.test(config.image) - ) { - let fs; + ) { + let fs - try { - fs = await import("fs") - } catch { - throw new Error( - "Loading from an image file path is not supported in a browser environment.", - ); - } + try { + fs = await import('fs') + } catch { + throw new Error( + 'Loading from an image file path is not supported in a browser environment.' + ) + } - result.imageBuffer = fs.readFileSync( - config.image, - ).buffer; - } else { - throw new Error( - "Provide your image as a file path to a .jpeg, .jpg, .png, or .webp, or a Buffer." - ); - } + result.imageBuffer = fs.readFileSync( + config.image + ).buffer + } else { + throw new Error( + 'Provide your image as a file path to a .jpeg, .jpg, .png, or .webp, or a Buffer.' + ) } + } - // Verify that all values in IDs exist - if (config?.ids) { - if (config.ids.conversationID && config.ids.responseID && config.ids.choiceID && config.ids._reqID) { - result.ids = config.ids; - } else { - throw new Error( - "Please provide the IDs exported exactly as given." - ); - } + // Verify that all values in IDs exist + if (config?.ids) { + if (config.ids.conversationID && config.ids.responseID && config.ids.choiceID && config.ids._reqID) { + result.ids = config.ids + } else { + throw new Error( + 'Please provide the IDs exported exactly as given.' + ) } - return result; + } + return result } // Ask Bard a question! - async ask(message, config) { - let { useJSON, imageBuffer, ids } = await this.#parseConfig(config); - let response = await this.#query(message, { imageBuffer, ids }); - return useJSON ? response : response.content; + async ask (message, config) { + let { useJSON, imageBuffer, ids } = await this.#parseConfig(config) + let response = await this.#query(message, { imageBuffer, ids }) + return useJSON ? response : response.content } - createChat(ids) { - let bard = this; - class Chat { - ids = ids; + createChat (ids) { + let bard = this + class Chat { + ids = ids - async ask(message, config) { - let { useJSON, imageBuffer } = await bard.#parseConfig(config); - let response = await bard.#query(message, { - imageBuffer, - ids: this.ids, - }); - this.ids = response.ids; - return useJSON ? response : response.content; - } - - export() { - return this.ids; - } + async ask (message, config) { + let { useJSON, imageBuffer } = await bard.#parseConfig(config) + let response = await bard.#query(message, { + imageBuffer, + ids: this.ids + }) + this.ids = response.ids + return useJSON ? response : response.content } - return new Chat(); + export () { + return this.ids + } + } + + return new Chat() } } -export default Bard; +export default Bard diff --git a/utils/bingCaptcha.js b/utils/bingCaptcha.js index d0ebf11..5805e55 100644 --- a/utils/bingCaptcha.js +++ b/utils/bingCaptcha.js @@ -1,7 +1,7 @@ import fetch from 'node-fetch' // this file is deprecated -import {Config} from './config.js' +import { Config } from './config.js' import HttpsProxyAgent from 'https-proxy-agent' const newFetch = (url, options = {}) => { diff --git a/utils/browser.js b/utils/browser.js index 5866790..cd0fb37 100644 --- a/utils/browser.js +++ b/utils/browser.js @@ -1,10 +1,5 @@ import lodash from 'lodash' -import { Config } from '../utils/config.js' -import StealthPlugin from 'puppeteer-extra-plugin-stealth' -import { getOpenAIAuth } from './openai-auth.js' -import { v4 as uuidv4 } from 'uuid' -import common from '../../../lib/common/common.js' -const chatUrl = 'https://chat.openai.com/chat' +import { Config } from './config.js' let puppeteer = {} class Puppeteer { @@ -48,19 +43,9 @@ class Puppeteer { async initPupp () { if (!lodash.isEmpty(puppeteer)) return puppeteer - puppeteer = (await import('puppeteer-extra')).default - const pluginStealth = StealthPlugin() - puppeteer.use(pluginStealth) - if (Config['2captchaToken']) { - const pluginCaptcha = (await import('puppeteer-extra-plugin-recaptcha')).default - puppeteer.use(pluginCaptcha({ - provider: { - id: '2captcha', - token: Config['2captchaToken'] // REPLACE THIS WITH YOUR OWN 2CAPTCHA API KEY ⚡ - }, - visualFeedback: true - })) - } + puppeteer = (await import('puppeteer')).default + // const pluginStealth = StealthPlugin() + // puppeteer.use(pluginStealth) return puppeteer } @@ -109,25 +94,10 @@ export class ChatGPTPuppeteer extends Puppeteer { constructor (opts = {}) { super() const { - email, - password, - markdown = true, - debug = false, - isGoogleLogin = false, - minimize = true, - captchaToken, - executablePath + debug = false } = opts - this._email = email - this._password = password - - this._markdown = !!markdown this._debug = !!debug - this._isGoogleLogin = !!isGoogleLogin - this._minimize = !!minimize - this._captchaToken = captchaToken - this._executablePath = executablePath } async getBrowser () { @@ -138,394 +108,6 @@ export class ChatGPTPuppeteer extends Puppeteer { } } - async init () { - // if (this.inited) { - // return true - // } - logger.info('init chatgpt browser') - try { - // this.browser = await getBrowser({ - // captchaToken: this._captchaToken, - // executablePath: this._executablePath - // }) - this.browser = await this.getBrowser() - this._page = - (await this.browser.pages())[0] || (await this.browser.newPage()) - await maximizePage(this._page) - this._page.on('request', this._onRequest.bind(this)) - this._page.on('response', this._onResponse.bind(this)) - // bypass cloudflare and login - let preCookies = await redis.get('CHATGPT:RAW_COOKIES') - if (preCookies) { - await this._page.setCookie(...JSON.parse(preCookies)) - } - // const url = this._page.url().replace(/\/$/, '') - // bypass annoying popup modals - await this._page.evaluateOnNewDocument(() => { - window.localStorage.setItem('oai/apps/hasSeenOnboarding/chat', 'true') - const chatGPTUpdateDates = ['2022-12-15', '2022-12-19', '2023-01-09', '2023-01-30', '2023-02-10'] - chatGPTUpdateDates.forEach(date => { - window.localStorage.setItem( - `oai/apps/hasSeenReleaseAnnouncement/${date}`, - 'true' - ) - }) - }) - await this._page.goto(chatUrl, { - waitUntil: 'networkidle2' - }) - let timeout = 30000 - try { - while (timeout > 0 && (await this._page.title()).toLowerCase().indexOf('moment') > -1) { - // if meet captcha - if (Config['2captchaToken']) { - await this._page.solveRecaptchas() - } - await common.sleep(300) - timeout = timeout - 300 - } - } catch (e) { - // navigation后获取title会报错,报错说明已经在navigation了正合我意。 - } - if (timeout < 0) { - logger.error('wait for cloudflare navigation timeout. 可能遇见验证码') - throw new Error('wait for cloudflare navigation timeout. 可能遇见验证码') - } - try { - await this._page.waitForNavigation({ timeout: 3000 }) - } catch (e) {} - - if (!await this.getIsAuthenticated()) { - await redis.del('CHATGPT:RAW_COOKIES') - logger.info('需要登录,准备进行自动化登录') - await getOpenAIAuth({ - email: this._email, - password: this._password, - browser: this.browser, - page: this._page, - isGoogleLogin: this._isGoogleLogin - }) - logger.info('登录完成') - } else { - logger.info('无需登录') - } - } catch (err) { - if (this.browser) { - await this.browser.close() - } - - this.browser = null - this._page = null - - throw err - } - - const url = this._page.url().replace(/\/$/, '') - - if (url !== chatUrl) { - await this._page.goto(chatUrl, { - waitUntil: 'networkidle2' - }) - } - - // dismiss welcome modal (and other modals) - do { - const modalSelector = '[data-headlessui-state="open"]' - - if (!(await this._page.$(modalSelector))) { - break - } - - try { - await this._page.click(`${modalSelector} button:last-child`) - } catch (err) { - // "next" button not found in welcome modal - break - } - - await common.sleep(300) - } while (true) - - if (!await this.getIsAuthenticated()) { - return false - } - - if (this._minimize) { - await minimizePage(this._page) - } - - return true - } - - _onRequest = (request) => { - const url = request.url() - if (!isRelevantRequest(url)) { - return - } - - const method = request.method() - let body - - if (method === 'POST') { - body = request.postData() - - try { - body = JSON.parse(body) - } catch (_) { - } - - // if (url.endsWith('/conversation') && typeof body === 'object') { - // const conversationBody: types.ConversationJSONBody = body - // const conversationId = conversationBody.conversation_id - // const parentMessageId = conversationBody.parent_message_id - // const messageId = conversationBody.messages?.[0]?.id - // const prompt = conversationBody.messages?.[0]?.content?.parts?.[0] - - // // TODO: store this info for the current sendMessage request - // } - } - - if (this._debug) { - console.log('\nrequest', { - url, - method, - headers: request.headers(), - body - }) - } - } - - _onResponse = async (response) => { - const request = response.request() - - const url = response.url() - if (!isRelevantRequest(url)) { - return - } - - const status = response.status() - - let body - try { - body = await response.json() - } catch (_) { - } - - if (this._debug) { - console.log('\nresponse', { - url, - ok: response.ok(), - status, - statusText: response.statusText(), - headers: response.headers(), - body, - request: { - method: request.method(), - headers: request.headers(), - body: request.postData() - } - }) - } - - if (url.endsWith('/conversation')) { - if (status === 403) { - await this.handle403Error() - } - } else if (url.endsWith('api/auth/session')) { - if (status === 403) { - await this.handle403Error() - } else { - const session = body - if (session?.accessToken) { - this._accessToken = session.accessToken - } - } - } - } - - async handle403Error () { - console.log(`ChatGPT "${this._email}" session expired; refreshing...`) - try { - await maximizePage(this._page) - await this._page.reload({ - waitUntil: 'networkidle2', - timeout: Config.chromeTimeoutMS // 2 minutes - }) - if (this._minimize) { - await minimizePage(this._page) - } - } catch (err) { - console.error( - `ChatGPT "${this._email}" error refreshing session`, - err.toString() - ) - } - } - - async getIsAuthenticated () { - try { - const inputBox = await this._getInputBox() - return !!inputBox - } catch (err) { - // can happen when navigating during login - return false - } - } - - async sendMessage ( - message, - opts = {} - ) { - const { - conversationId, - parentMessageId = uuidv4(), - messageId = uuidv4(), - action = 'next', - // TODO - timeoutMs, - // onProgress, - onConversationResponse - } = opts - - const inputBox = await this._getInputBox() - if (!inputBox || !this._accessToken) { - console.log(`chatgpt re-authenticating ${this._email}`) - let isAuthenticated = false - - try { - isAuthenticated = await this.init() - } catch (err) { - console.warn( - `chatgpt error re-authenticating ${this._email}`, - err.toString() - ) - throw err - } - let timeout = 100000 - if (isAuthenticated) { - while (!this._accessToken) { - // wait for async response hook result - await common.sleep(300) - timeout = timeout - 300 - if (timeout < 0) { - const error = new Error('Not signed in') - error.statusCode = 401 - throw error - } - } - } else if (!this._accessToken) { - const error = new Error('Not signed in') - error.statusCode = 401 - throw error - } - } - - const url = 'https://chat.openai.com/backend-api/conversation' - const body = { - action, - messages: [ - { - id: messageId, - role: 'user', - content: { - content_type: 'text', - parts: [message] - } - } - ], - model: Config.plus ? Config.useGPT4 ? 'gpt-4' : 'text-davinci-002-render-sha' : 'text-davinci-002-render-sha', - parent_message_id: parentMessageId - } - - if (conversationId) { - body.conversation_id = conversationId - } - - // console.log('>>> EVALUATE', url, this._accessToken, body) - const result = await this._page.evaluate( - browserPostEventStream, - url, - this._accessToken, - body, - timeoutMs - ) - // console.log('<<< EVALUATE', result) - - if (result.error) { - const error = new Error(result.error.message) - error.statusCode = result.error.statusCode - error.statusText = result.error.statusText - - if (error.statusCode === 403) { - await this.handle403Error() - } - - throw error - } - - // TODO: support sending partial response events - if (onConversationResponse) { - onConversationResponse(result.conversationResponse) - } - - return { - text: result.response, - conversationId: result.conversationResponse.conversation_id, - id: messageId, - parentMessageId - } - - // const lastMessage = await this.getLastMessage() - - // await inputBox.focus() - // const paragraphs = message.split('\n') - // for (let i = 0; i < paragraphs.length; i++) { - // await inputBox.type(paragraphs[i], { delay: 0 }) - // if (i < paragraphs.length - 1) { - // await this._page.keyboard.down('Shift') - // await inputBox.press('Enter') - // await this._page.keyboard.up('Shift') - // } else { - // await inputBox.press('Enter') - // } - // } - - // const responseP = new Promise(async (resolve, reject) => { - // try { - // do { - // await common.sleep(1000) - - // // TODO: this logic needs some work because we can have repeat messages... - // const newLastMessage = await this.getLastMessage() - // if ( - // newLastMessage && - // lastMessage?.toLowerCase() !== newLastMessage?.toLowerCase() - // ) { - // return resolve(newLastMessage) - // } - // } while (true) - // } catch (err) { - // return reject(err) - // } - // }) - - // if (timeoutMs) { - // return pTimeout(responseP, { - // milliseconds: timeoutMs - // }) - // } else { - // return responseP - // } - } - - async resetThread () { - try { - await this._page.click('nav > a:nth-child(1)') - } catch (err) { - // ignore for now - } - } - async close () { if (this.browser) { await this.browser.close() @@ -533,510 +115,6 @@ export class ChatGPTPuppeteer extends Puppeteer { this._page = null this.browser = null } - - protected - - async _getInputBox () { - // [data-id="root"] - return this._page?.$('textarea') - } } export default new ChatGPTPuppeteer() - -export async function minimizePage (page) { - const session = await page.target().createCDPSession() - const goods = await session.send('Browser.getWindowForTarget') - const { windowId } = goods - await session.send('Browser.setWindowBounds', { - windowId, - bounds: { windowState: 'minimized' } - }) -} - -export async function maximizePage (page) { - const session = await page.target().createCDPSession() - const goods = await session.send('Browser.getWindowForTarget') - const { windowId } = goods - await session.send('Browser.setWindowBounds', { - windowId, - bounds: { windowState: 'normal' } - }) -} - -export function isRelevantRequest (url) { - let pathname - - try { - const parsedUrl = new URL(url) - pathname = parsedUrl.pathname - url = parsedUrl.toString() - } catch (_) { - return false - } - - if (!url.startsWith('https://chat.openai.com')) { - return false - } - - if ( - !pathname.startsWith('/backend-api/') && - !pathname.startsWith('/api/auth/session') - ) { - return false - } - - if (pathname.endsWith('backend-api/moderations')) { - return false - } - - return true -} - -/** - * This function is injected into the ChatGPT webapp page using puppeteer. It - * has to be fully self-contained, so we copied a few third-party sources and - * included them in here. - */ -export async function browserPostEventStream ( - url, - accessToken, - body, - timeoutMs -) { - // Workaround for https://github.com/esbuild-kit/tsx/issues/113 - globalThis.__name = () => undefined - - const BOM = [239, 187, 191] - - let conversationResponse - let conversationId = body?.conversation_id - let messageId = body?.messages?.[0]?.id - let response = '' - - try { - console.log('browserPostEventStream', url, accessToken, body) - - let abortController = null - if (timeoutMs) { - abortController = new AbortController() - } - - const res = await fetch(url, { - method: 'POST', - body: JSON.stringify(body), - signal: abortController?.signal, - headers: { - accept: 'text/event-stream', - 'x-openai-assistant-app-id': '', - authorization: `Bearer ${accessToken}`, - 'content-type': 'application/json' - } - }) - - console.log('browserPostEventStream response', res) - - if (!res.ok) { - return { - error: { - message: `ChatGPTAPI error ${res.status || res.statusText}`, - statusCode: res.status, - statusText: res.statusText - }, - response: null, - conversationId, - messageId - } - } - - const responseP = new Promise( - async (resolve, reject) => { - function onMessage (data) { - if (data === '[DONE]') { - return resolve({ - error: null, - response, - conversationId, - messageId, - conversationResponse - }) - } - try { - const _checkJson = JSON.parse(data) - } catch (error) { - console.log('warning: parse error.') - return - } - try { - const convoResponseEvent = - JSON.parse(data) - conversationResponse = convoResponseEvent - if (convoResponseEvent.conversation_id) { - conversationId = convoResponseEvent.conversation_id - } - - if (convoResponseEvent.message?.id) { - messageId = convoResponseEvent.message.id - } - - const partialResponse = - convoResponseEvent.message?.content?.parts?.[0] - if (partialResponse) { - response = partialResponse - } - } catch (err) { - console.warn('fetchSSE onMessage unexpected error', err) - reject(err) - } - } - - const parser = createParser((event) => { - if (event.type === 'event') { - onMessage(event.data) - } - }) - - for await (const chunk of streamAsyncIterable(res.body)) { - const str = new TextDecoder().decode(chunk) - parser.feed(str) - } - } - ) - - if (timeoutMs) { - if (abortController) { - // This will be called when a timeout occurs in order for us to forcibly - // ensure that the underlying HTTP request is aborted. - responseP.cancel = () => { - abortController.abort() - } - } - console.log({ pTimeout }) - return await pTimeout(responseP, { - milliseconds: timeoutMs, - message: 'ChatGPT timed out waiting for response' - }) - } else { - return await responseP - } - } catch (err) { - const errMessageL = err.toString().toLowerCase() - - if ( - response && - (errMessageL === 'error: typeerror: terminated' || - errMessageL === 'typeerror: terminated') - ) { - // OpenAI sometimes forcefully terminates the socket from their end before - // the HTTP request has resolved cleanly. In my testing, these cases tend to - // happen when OpenAI has already send the last `response`, so we can ignore - // the `fetch` error in this case. - return { - error: null, - response, - conversationId, - messageId, - conversationResponse - } - } - - return { - error: { - message: err.toString(), - statusCode: err.statusCode || err.status || err.response?.statusCode, - statusText: err.statusText || err.response?.statusText - }, - response: null, - conversationId, - messageId, - conversationResponse - } - } - // async function pTimeout (promise, option) { - // return await pTimeout(promise, option) - // } - async function * streamAsyncIterable (stream) { - const reader = stream.getReader() - try { - while (true) { - const { done, value } = await reader.read() - if (done) { - return - } - yield value - } - } finally { - reader.releaseLock() - } - } - - // @see https://github.com/rexxars/eventsource-parser - function createParser (onParse) { - // Processing state - let isFirstChunk - let buffer - let startingPosition - let startingFieldLength - - // Event state - let eventId - let eventName - let data - - reset() - return { feed, reset } - - function reset () { - isFirstChunk = true - buffer = '' - startingPosition = 0 - startingFieldLength = -1 - - eventId = undefined - eventName = undefined - data = '' - } - - function feed (chunk) { - buffer = buffer ? buffer + chunk : chunk - - // Strip any UTF8 byte order mark (BOM) at the start of the stream. - // Note that we do not strip any non - UTF8 BOM, as eventsource streams are - // always decoded as UTF8 as per the specification. - if (isFirstChunk && hasBom(buffer)) { - buffer = buffer.slice(BOM.length) - } - - isFirstChunk = false - - // Set up chunk-specific processing state - const length = buffer.length - let position = 0 - let discardTrailingNewline = false - - // Read the current buffer byte by byte - while (position < length) { - // EventSource allows for carriage return + line feed, which means we - // need to ignore a linefeed character if the previous character was a - // carriage return - // @todo refactor to reduce nesting, consider checking previous byte? - // @todo but consider multiple chunks etc - if (discardTrailingNewline) { - if (buffer[position] === '\n') { - ++position - } - discardTrailingNewline = false - } - - let lineLength = -1 - let fieldLength = startingFieldLength - let character - - for ( - let index = startingPosition; - lineLength < 0 && index < length; - ++index - ) { - character = buffer[index] - if (character === ':' && fieldLength < 0) { - fieldLength = index - position - } else if (character === '\r') { - discardTrailingNewline = true - lineLength = index - position - } else if (character === '\n') { - lineLength = index - position - } - } - - if (lineLength < 0) { - startingPosition = length - position - startingFieldLength = fieldLength - break - } else { - startingPosition = 0 - startingFieldLength = -1 - } - - parseEventStreamLine(buffer, position, fieldLength, lineLength) - - position += lineLength + 1 - } - - if (position === length) { - // If we consumed the entire buffer to read the event, reset the buffer - buffer = '' - } else if (position > 0) { - // If there are bytes left to process, set the buffer to the unprocessed - // portion of the buffer only - buffer = buffer.slice(position) - } - } - - function parseEventStreamLine ( - lineBuffer, - index, - fieldLength, - lineLength - ) { - if (lineLength === 0) { - // We reached the last line of this event - if (data.length > 0) { - onParse({ - type: 'event', - id: eventId, - event: eventName || undefined, - data: data.slice(0, -1) // remove trailing newline - }) - - data = '' - eventId = undefined - } - eventName = undefined - return - } - - const noValue = fieldLength < 0 - const field = lineBuffer.slice( - index, - index + (noValue ? lineLength : fieldLength) - ) - let step = 0 - - if (noValue) { - step = lineLength - } else if (lineBuffer[index + fieldLength + 1] === ' ') { - step = fieldLength + 2 - } else { - step = fieldLength + 1 - } - - const position = index + step - const valueLength = lineLength - step - const value = lineBuffer - .slice(position, position + valueLength) - .toString() - - if (field === 'data') { - data += value ? `${value}\n` : '\n' - } else if (field === 'event') { - eventName = value - } else if (field === 'id' && !value.includes('\u0000')) { - eventId = value - } else if (field === 'retry') { - const retry = parseInt(value, 10) - if (!Number.isNaN(retry)) { - onParse({ type: 'reconnect-interval', value: retry }) - } - } - } - } - - function hasBom (buffer) { - return BOM.every( - (charCode, index) => buffer.charCodeAt(index) === charCode - ) - } - - // @see https://github.com/sindresorhus/p-timeout - function pTimeout ( - promise, - options - ) { - const { - milliseconds, - fallback, - message, - customTimers = { setTimeout, clearTimeout } - } = options - - let timer - - const cancelablePromise = new Promise((resolve, reject) => { - if (typeof milliseconds !== 'number' || Math.sign(milliseconds) !== 1) { - throw new TypeError( - `Expected \`milliseconds\` to be a positive number, got \`${milliseconds}\`` - ) - } - - if (milliseconds === Number.POSITIVE_INFINITY) { - resolve(promise) - return - } - - if (options.signal) { - const { signal } = options - if (signal.aborted) { - reject(getAbortedReason(signal)) - } - - signal.addEventListener('abort', () => { - reject(getAbortedReason(signal)) - }) - } - - timer = customTimers.setTimeout.call( - undefined, - () => { - if (fallback) { - try { - resolve(fallback()) - } catch (error) { - reject(error) - } - - return - } - - const errorMessage = - typeof message === 'string' - ? message - : `Promise timed out after ${milliseconds} milliseconds` - const timeoutError = - message instanceof Error ? message : new Error(errorMessage) - - if (typeof promise.cancel === 'function') { - promise.cancel() - } - - reject(timeoutError) - }, - milliseconds - ) - ;(async () => { - try { - resolve(await promise) - } catch (error) { - reject(error) - } finally { - customTimers.clearTimeout.call(undefined, timer) - } - })() - }) - - cancelablePromise.clear = () => { - customTimers.clearTimeout.call(undefined, timer) - timer = undefined - } - - return cancelablePromise - } - /** - TODO: Remove below function and just 'reject(signal.reason)' when targeting Node 18. - */ - function getAbortedReason (signal) { - const reason = - signal.reason === undefined - ? getDOMException('This operation was aborted.') - : signal.reason - - return reason instanceof Error ? reason : getDOMException(reason) - } - /** - TODO: Remove AbortError and just throw DOMException when targeting Node 18. - */ - function getDOMException (errorMessage) { - return globalThis.DOMException === undefined - ? new Error(errorMessage) - : new DOMException(errorMessage) - } -} diff --git a/utils/chat.js b/utils/chat.js index 10bcd09..ba8d398 100644 --- a/utils/chat.js +++ b/utils/chat.js @@ -1,4 +1,3 @@ - export async function getChatHistoryGroup (e, num) { // if (e.adapter === 'shamrock') { // return await e.group.getChatHistory(0, num, false) diff --git a/utils/config.js b/utils/config.js index fdaf0f3..9a7b712 100644 --- a/utils/config.js +++ b/utils/config.js @@ -187,7 +187,7 @@ const defaultConfig = { claudeSystemPrompt: '', // claude api 设定 translateSource: 'openai', enableMd: false, // 第三方md,非QQBot。需要适配器实现segment.markdown和segment.button方可使用,否则不建议开启,会造成各种错误 - enableToolbox: false, // 默认关闭工具箱节省占用和加速启动 + enableToolbox: true, // 默认关闭工具箱节省占用和加速启动 version: 'v2.8.1' } const _path = process.cwd() diff --git a/utils/jwt.js b/utils/jwt.js index 1af66f3..b1a1d77 100644 --- a/utils/jwt.js +++ b/utils/jwt.js @@ -1,7 +1,7 @@ export function decrypt (jwtToken) { const [encodedHeader, encodedPayload, signature] = jwtToken.split('.') - const decodedHeader = Buffer.from(encodedHeader, 'base64').toString('utf-8') + // const decodedHeader = Buffer.from(encodedHeader, 'base64').toString('utf-8') const decodedPayload = Buffer.from(encodedPayload, 'base64').toString('utf-8') return decodedPayload diff --git a/utils/openai-auth.js b/utils/openai-auth.js deleted file mode 100644 index a10709d..0000000 --- a/utils/openai-auth.js +++ /dev/null @@ -1,281 +0,0 @@ -import { Config } from '../utils/config.js' -import random from 'random' -import common from '../../../lib/common/common.js' - -let hasRecaptchaPlugin = !!Config['2captchaToken'] - -export async function getOpenAIAuth (opt) { - let { - email, - password, - browser, - page, - timeoutMs = Config.chromeTimeoutMS, - isGoogleLogin = false, - captchaToken = Config['2captchaToken'], - executablePath = Config.chromePath - } = opt - const origBrowser = browser - const origPage = page - - try { - const userAgent = await browser.userAgent() - if (!page) { - page = (await browser.pages())[0] || (await browser.newPage()) - page.setDefaultTimeout(timeoutMs) - } - await page.goto('https://chat.openai.com/auth/login', { - waitUntil: 'networkidle2' - }) - logger.mark('chatgpt checkForChatGPTAtCapacity') - - await checkForChatGPTAtCapacity(page) - - // NOTE: this is where you may encounter a CAPTCHA - if (hasRecaptchaPlugin) { - logger.mark('RecaptchaPlugin key exists, try to solve recaptchas') - await page.solveRecaptchas() - } - - logger.mark('chatgpt checkForChatGPTAtCapacity again') - await checkForChatGPTAtCapacity(page) - - // once we get to this point, the Cloudflare cookies should be available - - // login as well (optional) - if (email && password) { - let retry = 3 - while (retry > 0) { - try { - await waitForConditionOrAtCapacity(page, () => - page.waitForSelector('#__next .btn-primary', { timeout: timeoutMs / 3 }) - ) - } catch (e) { - await checkForChatGPTAtCapacity(page) - } - retry-- - } - await waitForConditionOrAtCapacity(page, () => - page.waitForSelector('#__next .btn-primary', { timeout: timeoutMs / 3 }) - ) - await common.sleep(500) - - // click login button and wait for navigation to finish - do { - await Promise.all([ - page.waitForNavigation({ - waitUntil: 'networkidle2', - timeout: timeoutMs - }), - page.click('#__next .btn-primary') - ]) - await common.sleep(1000) - } while (page.url().endsWith('/auth/login')) - logger.mark('进入登录页面') - await checkForChatGPTAtCapacity(page) - - let submitP - - if (isGoogleLogin) { - await page.click('button[data-provider="google"]') - await page.waitForSelector('input[type="email"]') - await page.type('input[type="email"]', email, { delay: 10 }) - await Promise.all([ - page.waitForNavigation(), - await page.keyboard.press('Enter') - ]) - await page.waitForSelector('input[type="password"]', { visible: true }) - await page.type('input[type="password"]', password, { delay: 10 }) - submitP = () => page.keyboard.press('Enter') - } else { - await page.waitForSelector('#username') - await page.type('#username', email, { delay: 20 }) - await common.sleep(100) - - if (hasRecaptchaPlugin) { - // console.log('solveRecaptchas()') - const res = await page.solveRecaptchas() - // console.log('solveRecaptchas result', res) - } - - await page.click('button[type="submit"]') - await page.waitForSelector('#password', { timeout: timeoutMs }) - await page.type('#password', password, { delay: 10 }) - submitP = () => page.click('button[type="submit"]') - } - - await Promise.all([ - waitForConditionOrAtCapacity(page, () => - page.waitForNavigation({ - waitUntil: 'networkidle2', - timeout: timeoutMs - }) - ), - submitP() - ]) - } else { - await common.sleep(2000) - await checkForChatGPTAtCapacity(page) - } - - const pageCookies = await page.cookies() - await redis.set('CHATGPT:RAW_COOKIES', JSON.stringify(pageCookies)) - const cookies = pageCookies.reduce( - (map, cookie) => ({ ...map, [cookie.name]: cookie }), - {} - ) - - const authInfo = { - userAgent, - clearanceToken: cookies.cf_clearance?.value, - sessionToken: cookies['__Secure-next-auth.session-token']?.value, - cookies - } - logger.info('chatgpt登录成功') - - return authInfo - } catch (err) { - throw err - } finally { - await page.screenshot({ - type: 'png', - path: './error.png' - }) - if (origBrowser) { - if (page && page !== origPage) { - await page.close() - } - } else if (browser) { - await browser.close() - } - - page = null - browser = null - } -} - -async function checkForChatGPTAtCapacity (page, opts = {}) { - const { - timeoutMs = Config.chromeTimeoutMS, // 2 minutes - pollingIntervalMs = 3000, - retries = 10 - } = opts - - // console.log('checkForChatGPTAtCapacity', page.url()) - let isAtCapacity = false - let numTries = 0 - - do { - try { - await solveSimpleCaptchas(page) - - const res = await page.$x("//div[contains(., 'ChatGPT is at capacity')]") - isAtCapacity = !!res?.length - - if (isAtCapacity) { - if (++numTries >= retries) { - break - } - - // try refreshing the page if chatgpt is at capacity - await page.reload({ - waitUntil: 'networkidle2', - timeout: timeoutMs - }) - - await common.sleep(pollingIntervalMs) - } - } catch (err) { - // ignore errors likely due to navigation - ++numTries - break - } - } while (isAtCapacity) - - if (isAtCapacity) { - const error = new Error('ChatGPT is at capacity') - error.statusCode = 503 - throw error - } -} - -async function waitForConditionOrAtCapacity ( - page, - condition, - opts = {} -) { - const { pollingIntervalMs = 500 } = opts - - return new Promise((resolve, reject) => { - let resolved = false - - async function waitForCapacityText () { - if (resolved) { - return - } - - try { - await checkForChatGPTAtCapacity(page) - - if (!resolved) { - setTimeout(waitForCapacityText, pollingIntervalMs) - } - } catch (err) { - if (!resolved) { - resolved = true - return reject(err) - } - } - } - - condition() - .then(() => { - if (!resolved) { - resolved = true - resolve() - } - }) - .catch((err) => { - if (!resolved) { - resolved = true - reject(err) - } - }) - - setTimeout(waitForCapacityText, pollingIntervalMs) - }) -} - -async function solveSimpleCaptchas (page) { - try { - const verifyYouAreHuman = await page.$('text=Verify you are human') - if (verifyYouAreHuman) { - logger.mark('encounter cloudflare simple captcha "Verify you are human"') - await common.sleep(2000) - await verifyYouAreHuman.click({ - delay: random.int(5, 25) - }) - await common.sleep(1000) - } - const verifyYouAreHumanCN = await page.$('text=确认您是真人') - if (verifyYouAreHumanCN) { - logger.mark('encounter cloudflare simple captcha "确认您是真人"') - await common.sleep(2000) - await verifyYouAreHumanCN.click({ - delay: random.int(5, 25) - }) - await common.sleep(1000) - } - - const cloudflareButton = await page.$('.hcaptcha-box') - if (cloudflareButton) { - await common.sleep(2000) - await cloudflareButton.click({ - delay: random.int(5, 25) - }) - await common.sleep(1000) - } - } catch (err) { - // ignore errors - } -} diff --git a/utils/poe/credential.js b/utils/poe/credential.js deleted file mode 100644 index b16a86d..0000000 --- a/utils/poe/credential.js +++ /dev/null @@ -1,48 +0,0 @@ -import fetch from 'node-fetch' -import { readFileSync, writeFile } from 'fs' - -const scrape = async (pbCookie, proxy) => { - let option = { headers: { cookie: `${pbCookie}` } } - if (proxy) { - option.agent = proxy - } - const _setting = await fetch( - 'https://poe.com/api/settings', - option - ) - if (_setting.status !== 200) throw new Error('Failed to fetch token') - const appSettings = await _setting.json() - console.log(appSettings) - const { tchannelData: { channel: channelName } } = appSettings - return { - channelName, - appSettings, - formKey: appSettings.formKey - } -} - -const getUpdatedSettings = async (channelName, pbCookie, proxy) => { - let option = { headers: { cookie: `${pbCookie}` } } - if (proxy) { - option.agent = proxy - } - const _setting = await fetch( - `https://poe.com/api/settings?channel=${channelName}`, - option - ) - if (_setting.status !== 200) throw new Error('Failed to fetch token') - const appSettings = await _setting.json() - const { tchannelData: { minSeq } } = appSettings - const credentials = JSON.parse(readFileSync('config.json', 'utf8')) - credentials.app_settings.tchannelData.minSeq = minSeq - writeFile('config.json', JSON.stringify(credentials, null, 4), function (err) { - if (err) { - console.log(err) - } - }) - return { - minSeq - } -} - -export { scrape, getUpdatedSettings } diff --git a/utils/poe/graphql/AddHumanMessageMutation.graphql b/utils/poe/graphql/AddHumanMessageMutation.graphql deleted file mode 100644 index 01e6bc8..0000000 --- a/utils/poe/graphql/AddHumanMessageMutation.graphql +++ /dev/null @@ -1,52 +0,0 @@ -mutation AddHumanMessageMutation( - $chatId: BigInt! - $bot: String! - $query: String! - $source: MessageSource - $withChatBreak: Boolean! = false -) { - messageCreateWithStatus( - chatId: $chatId - bot: $bot - query: $query - source: $source - withChatBreak: $withChatBreak - ) { - message { - id - __typename - messageId - text - linkifiedText - authorNickname - state - vote - voteReason - creationTime - suggestedReplies - chat { - id - shouldShowDisclaimer - } - } - messageLimit{ - canSend - numMessagesRemaining - resetTime - shouldShowReminder - } - chatBreak { - id - __typename - messageId - text - linkifiedText - authorNickname - state - vote - voteReason - creationTime - suggestedReplies - } - } -} diff --git a/utils/poe/graphql/AddMessageBreakMutation.graphql b/utils/poe/graphql/AddMessageBreakMutation.graphql deleted file mode 100644 index b28d990..0000000 --- a/utils/poe/graphql/AddMessageBreakMutation.graphql +++ /dev/null @@ -1,17 +0,0 @@ -mutation AddMessageBreakMutation($chatId: BigInt!) { - messageBreakCreate(chatId: $chatId) { - message { - id - __typename - messageId - text - linkifiedText - authorNickname - state - vote - voteReason - creationTime - suggestedReplies - } - } -} diff --git a/utils/poe/graphql/AutoSubscriptionMutation.graphql b/utils/poe/graphql/AutoSubscriptionMutation.graphql deleted file mode 100644 index 6cf7bf7..0000000 --- a/utils/poe/graphql/AutoSubscriptionMutation.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation AutoSubscriptionMutation($subscriptions: [AutoSubscriptionQuery!]!) { - autoSubscribe(subscriptions: $subscriptions) { - viewer { - id - } - } -} diff --git a/utils/poe/graphql/BioFragment.graphql b/utils/poe/graphql/BioFragment.graphql deleted file mode 100644 index c421803..0000000 --- a/utils/poe/graphql/BioFragment.graphql +++ /dev/null @@ -1,8 +0,0 @@ -fragment BioFragment on Viewer { - id - poeUser { - id - uid - bio - } -} diff --git a/utils/poe/graphql/ChatAddedSubscription.graphql b/utils/poe/graphql/ChatAddedSubscription.graphql deleted file mode 100644 index 664b107..0000000 --- a/utils/poe/graphql/ChatAddedSubscription.graphql +++ /dev/null @@ -1,5 +0,0 @@ -subscription ChatAddedSubscription { - chatAdded { - ...ChatFragment - } -} diff --git a/utils/poe/graphql/ChatFragment.graphql b/utils/poe/graphql/ChatFragment.graphql deleted file mode 100644 index 605645f..0000000 --- a/utils/poe/graphql/ChatFragment.graphql +++ /dev/null @@ -1,6 +0,0 @@ -fragment ChatFragment on Chat { - id - chatId - defaultBotNickname - shouldShowDisclaimer -} diff --git a/utils/poe/graphql/ChatPaginationQuery.graphql b/utils/poe/graphql/ChatPaginationQuery.graphql deleted file mode 100644 index f2452cd..0000000 --- a/utils/poe/graphql/ChatPaginationQuery.graphql +++ /dev/null @@ -1,26 +0,0 @@ -query ChatPaginationQuery($bot: String!, $before: String, $last: Int! = 10) { - chatOfBot(bot: $bot) { - id - __typename - messagesConnection(before: $before, last: $last) { - pageInfo { - hasPreviousPage - } - edges { - node { - id - __typename - messageId - text - linkifiedText - authorNickname - state - vote - voteReason - creationTime - suggestedReplies - } - } - } - } -} diff --git a/utils/poe/graphql/ChatViewQuery.graphql b/utils/poe/graphql/ChatViewQuery.graphql deleted file mode 100644 index c330107..0000000 --- a/utils/poe/graphql/ChatViewQuery.graphql +++ /dev/null @@ -1,8 +0,0 @@ -query ChatViewQuery($bot: String!) { - chatOfBot(bot: $bot) { - id - chatId - defaultBotNickname - shouldShowDisclaimer - } -} diff --git a/utils/poe/graphql/DeleteHumanMessagesMutation.graphql b/utils/poe/graphql/DeleteHumanMessagesMutation.graphql deleted file mode 100644 index 42692c6..0000000 --- a/utils/poe/graphql/DeleteHumanMessagesMutation.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation DeleteHumanMessagesMutation($messageIds: [BigInt!]!) { - messagesDelete(messageIds: $messageIds) { - viewer { - id - } - } -} diff --git a/utils/poe/graphql/HandleFragment.graphql b/utils/poe/graphql/HandleFragment.graphql deleted file mode 100644 index f53c484..0000000 --- a/utils/poe/graphql/HandleFragment.graphql +++ /dev/null @@ -1,8 +0,0 @@ -fragment HandleFragment on Viewer { - id - poeUser { - id - uid - handle - } -} diff --git a/utils/poe/graphql/LoginWithVerificationCodeMutation.graphql b/utils/poe/graphql/LoginWithVerificationCodeMutation.graphql deleted file mode 100644 index 723b1f4..0000000 --- a/utils/poe/graphql/LoginWithVerificationCodeMutation.graphql +++ /dev/null @@ -1,13 +0,0 @@ -mutation LoginWithVerificationCodeMutation( - $verificationCode: String! - $emailAddress: String - $phoneNumber: String -) { - loginWithVerificationCode( - verificationCode: $verificationCode - emailAddress: $emailAddress - phoneNumber: $phoneNumber - ) { - status - } -} diff --git a/utils/poe/graphql/MessageAddedSubscription.graphql b/utils/poe/graphql/MessageAddedSubscription.graphql deleted file mode 100644 index 0492baa..0000000 --- a/utils/poe/graphql/MessageAddedSubscription.graphql +++ /dev/null @@ -1,5 +0,0 @@ -subscription MessageAddedSubscription($chatId: BigInt!) { - messageAdded(chatId: $chatId) { - ...MessageFragment - } -} diff --git a/utils/poe/graphql/MessageDeletedSubscription.graphql b/utils/poe/graphql/MessageDeletedSubscription.graphql deleted file mode 100644 index 54c1c16..0000000 --- a/utils/poe/graphql/MessageDeletedSubscription.graphql +++ /dev/null @@ -1,6 +0,0 @@ -subscription MessageDeletedSubscription($chatId: BigInt!) { - messageDeleted(chatId: $chatId) { - id - messageId - } -} diff --git a/utils/poe/graphql/MessageFragment.graphql b/utils/poe/graphql/MessageFragment.graphql deleted file mode 100644 index cc86081..0000000 --- a/utils/poe/graphql/MessageFragment.graphql +++ /dev/null @@ -1,13 +0,0 @@ -fragment MessageFragment on Message { - id - __typename - messageId - text - linkifiedText - authorNickname - state - vote - voteReason - creationTime - suggestedReplies -} diff --git a/utils/poe/graphql/MessageRemoveVoteMutation.graphql b/utils/poe/graphql/MessageRemoveVoteMutation.graphql deleted file mode 100644 index d5e6e61..0000000 --- a/utils/poe/graphql/MessageRemoveVoteMutation.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation MessageRemoveVoteMutation($messageId: BigInt!) { - messageRemoveVote(messageId: $messageId) { - message { - ...MessageFragment - } - } -} diff --git a/utils/poe/graphql/MessageSetVoteMutation.graphql b/utils/poe/graphql/MessageSetVoteMutation.graphql deleted file mode 100644 index 76000df..0000000 --- a/utils/poe/graphql/MessageSetVoteMutation.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation MessageSetVoteMutation($messageId: BigInt!, $voteType: VoteType!, $reason: String) { - messageSetVote(messageId: $messageId, voteType: $voteType, reason: $reason) { - message { - ...MessageFragment - } - } -} diff --git a/utils/poe/graphql/SendVerificationCodeForLoginMutation.graphql b/utils/poe/graphql/SendVerificationCodeForLoginMutation.graphql deleted file mode 100644 index 45af479..0000000 --- a/utils/poe/graphql/SendVerificationCodeForLoginMutation.graphql +++ /dev/null @@ -1,12 +0,0 @@ -mutation SendVerificationCodeForLoginMutation( - $emailAddress: String - $phoneNumber: String -) { - sendVerificationCode( - verificationReason: login - emailAddress: $emailAddress - phoneNumber: $phoneNumber - ) { - status - } -} diff --git a/utils/poe/graphql/ShareMessagesMutation.graphql b/utils/poe/graphql/ShareMessagesMutation.graphql deleted file mode 100644 index 92e80db..0000000 --- a/utils/poe/graphql/ShareMessagesMutation.graphql +++ /dev/null @@ -1,9 +0,0 @@ -mutation ShareMessagesMutation( - $chatId: BigInt! - $messageIds: [BigInt!]! - $comment: String -) { - messagesShare(chatId: $chatId, messageIds: $messageIds, comment: $comment) { - shareCode - } -} diff --git a/utils/poe/graphql/SignupWithVerificationCodeMutation.graphql b/utils/poe/graphql/SignupWithVerificationCodeMutation.graphql deleted file mode 100644 index 06b2826..0000000 --- a/utils/poe/graphql/SignupWithVerificationCodeMutation.graphql +++ /dev/null @@ -1,13 +0,0 @@ -mutation SignupWithVerificationCodeMutation( - $verificationCode: String! - $emailAddress: String - $phoneNumber: String -) { - signupWithVerificationCode( - verificationCode: $verificationCode - emailAddress: $emailAddress - phoneNumber: $phoneNumber - ) { - status - } -} diff --git a/utils/poe/graphql/StaleChatUpdateMutation.graphql b/utils/poe/graphql/StaleChatUpdateMutation.graphql deleted file mode 100644 index de203d4..0000000 --- a/utils/poe/graphql/StaleChatUpdateMutation.graphql +++ /dev/null @@ -1,7 +0,0 @@ -mutation StaleChatUpdateMutation($chatId: BigInt!) { - staleChatUpdate(chatId: $chatId) { - message { - ...MessageFragment - } - } -} diff --git a/utils/poe/graphql/SummarizePlainPostQuery.graphql b/utils/poe/graphql/SummarizePlainPostQuery.graphql deleted file mode 100644 index afa2a84..0000000 --- a/utils/poe/graphql/SummarizePlainPostQuery.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query SummarizePlainPostQuery($comment: String!) { - summarizePlainPost(comment: $comment) -} diff --git a/utils/poe/graphql/SummarizeQuotePostQuery.graphql b/utils/poe/graphql/SummarizeQuotePostQuery.graphql deleted file mode 100644 index 5147c3c..0000000 --- a/utils/poe/graphql/SummarizeQuotePostQuery.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query SummarizeQuotePostQuery($comment: String, $quotedPostId: BigInt!) { - summarizeQuotePost(comment: $comment, quotedPostId: $quotedPostId) -} diff --git a/utils/poe/graphql/SummarizeSharePostQuery.graphql b/utils/poe/graphql/SummarizeSharePostQuery.graphql deleted file mode 100644 index cb4a623..0000000 --- a/utils/poe/graphql/SummarizeSharePostQuery.graphql +++ /dev/null @@ -1,3 +0,0 @@ -query SummarizeSharePostQuery($comment: String!, $chatId: BigInt!, $messageIds: [BigInt!]!) { - summarizeSharePost(comment: $comment, chatId: $chatId, messageIds: $messageIds) -} diff --git a/utils/poe/graphql/UserSnippetFragment.graphql b/utils/poe/graphql/UserSnippetFragment.graphql deleted file mode 100644 index 17fc842..0000000 --- a/utils/poe/graphql/UserSnippetFragment.graphql +++ /dev/null @@ -1,14 +0,0 @@ -fragment UserSnippetFragment on PoeUser { - id - uid - bio - handle - fullName - viewerIsFollowing - isPoeOnlyUser - profilePhotoURLTiny: profilePhotoUrl(size: tiny) - profilePhotoURLSmall: profilePhotoUrl(size: small) - profilePhotoURLMedium: profilePhotoUrl(size: medium) - profilePhotoURLLarge: profilePhotoUrl(size: large) - isFollowable -} diff --git a/utils/poe/graphql/ViewerInfoQuery.graphql b/utils/poe/graphql/ViewerInfoQuery.graphql deleted file mode 100644 index 1ecaf9e..0000000 --- a/utils/poe/graphql/ViewerInfoQuery.graphql +++ /dev/null @@ -1,21 +0,0 @@ -query ViewerInfoQuery { - viewer { - id - uid - ...ViewerStateFragment - ...BioFragment - ...HandleFragment - hasCompletedMultiplayerNux - poeUser { - id - ...UserSnippetFragment - } - messageLimit{ - canSend - numMessagesRemaining - resetTime - shouldShowReminder - } - } -} - diff --git a/utils/poe/graphql/ViewerStateFragment.graphql b/utils/poe/graphql/ViewerStateFragment.graphql deleted file mode 100644 index 3cd83e9..0000000 --- a/utils/poe/graphql/ViewerStateFragment.graphql +++ /dev/null @@ -1,30 +0,0 @@ -fragment ViewerStateFragment on Viewer { - id - __typename - iosMinSupportedVersion: integerGate(gateName: "poe_ios_min_supported_version") - iosMinEncouragedVersion: integerGate( - gateName: "poe_ios_min_encouraged_version" - ) - macosMinSupportedVersion: integerGate( - gateName: "poe_macos_min_supported_version" - ) - macosMinEncouragedVersion: integerGate( - gateName: "poe_macos_min_encouraged_version" - ) - showPoeDebugPanel: booleanGate(gateName: "poe_show_debug_panel") - enableCommunityFeed: booleanGate(gateName: "enable_poe_shares_feed") - linkifyText: booleanGate(gateName: "poe_linkify_response") - enableSuggestedReplies: booleanGate(gateName: "poe_suggested_replies") - removeInviteLimit: booleanGate(gateName: "poe_remove_invite_limit") - enableInAppPurchases: booleanGate(gateName: "poe_enable_in_app_purchases") - availableBots { - nickname - displayName - profilePicture - isDown - disclaimer - subtitle - poweredBy - } -} - diff --git a/utils/poe/graphql/ViewerStateUpdatedSubscription.graphql b/utils/poe/graphql/ViewerStateUpdatedSubscription.graphql deleted file mode 100644 index dd6d2d1..0000000 --- a/utils/poe/graphql/ViewerStateUpdatedSubscription.graphql +++ /dev/null @@ -1,5 +0,0 @@ -subscription ViewerStateUpdatedSubscription { - viewerStateUpdated { - ...ViewerStateFragment - } -} diff --git a/utils/poe/index.js b/utils/poe/index.js deleted file mode 100644 index 5a60356..0000000 --- a/utils/poe/index.js +++ /dev/null @@ -1,299 +0,0 @@ -import { readFileSync } from 'fs' -import { scrape } from './credential.js' -import fetch from 'node-fetch' -import crypto from 'crypto' -import { Config } from '../config.js' - -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') - } -} -// used when test as a single file -// const _path = process.cwd() -const _path = process.cwd() + '/plugins/chatgpt-plugin/utils/poe' -const gqlDir = `${_path}/graphql` -const queries = { - // chatViewQuery: readFileSync(gqlDir + '/ChatViewQuery.graphql', 'utf8'), - addMessageBreakMutation: readFileSync(gqlDir + '/AddMessageBreakMutation.graphql', 'utf8'), - chatPaginationQuery: readFileSync(gqlDir + '/ChatPaginationQuery.graphql', 'utf8'), - addHumanMessageMutation: readFileSync(gqlDir + '/AddHumanMessageMutation.graphql', 'utf8'), - loginMutation: readFileSync(gqlDir + '/LoginWithVerificationCodeMutation.graphql', 'utf8'), - signUpWithVerificationCodeMutation: readFileSync(gqlDir + '/SignupWithVerificationCodeMutation.graphql', 'utf8'), - sendVerificationCodeMutation: readFileSync(gqlDir + '/SendVerificationCodeForLoginMutation.graphql', 'utf8') -} -const optionMap = [ - { title: 'Claude (Powered by Anthropic)', value: 'a2' }, - { title: 'Sage (Powered by OpenAI - logical)', value: 'capybara' }, - { title: 'Dragonfly (Powered by OpenAI - simpler)', value: 'nutria' }, - { title: 'ChatGPT (Powered by OpenAI - current)', value: 'chinchilla' }, - { title: 'Claude+', value: 'a2_2' }, - { title: 'GPT-4', value: 'beaver' } -] -export class PoeClient { - constructor (props) { - this.config = props - } - - headers = { - 'Content-Type': 'application/json', - Referrer: 'https://poe.com/', - Origin: 'https://poe.com', - 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36' - } - - chatId = 0 - bot = '' - - reConnectWs = false - - async setCredentials () { - let result = await scrape(this.config.quora_cookie, this.config.proxy ? proxy(Config.proxy) : null) - console.log(result) - this.config.quora_formkey = result.appSettings.formkey - this.config.channel_name = result.channelName - this.config.app_settings = result.appSettings - - // set value - this.headers['poe-formkey'] = this.config.quora_formkey // unused - this.headers['poe-tchannel'] = this.config.channel_name - this.headers.Cookie = this.config.quora_cookie - console.log(this.headers) - } - - async subscribe () { - const query = { - queryName: 'subscriptionsMutation', - variables: { - subscriptions: [ - { - subscriptionName: 'messageAdded', - query: 'subscription subscriptions_messageAdded_Subscription(\n $chatId: BigInt!\n) {\n messageAdded(chatId: $chatId) {\n id\n messageId\n creationTime\n state\n ...ChatMessage_message\n ...chatHelpers_isBotMessage\n }\n}\n\nfragment ChatMessageDownvotedButton_message on Message {\n ...MessageFeedbackReasonModal_message\n ...MessageFeedbackOtherModal_message\n}\n\nfragment ChatMessageDropdownMenu_message on Message {\n id\n messageId\n vote\n text\n ...chatHelpers_isBotMessage\n}\n\nfragment ChatMessageFeedbackButtons_message on Message {\n id\n messageId\n vote\n voteReason\n ...ChatMessageDownvotedButton_message\n}\n\nfragment ChatMessageOverflowButton_message on Message {\n text\n ...ChatMessageDropdownMenu_message\n ...chatHelpers_isBotMessage\n}\n\nfragment ChatMessageSuggestedReplies_SuggestedReplyButton_message on Message {\n messageId\n}\n\nfragment ChatMessageSuggestedReplies_message on Message {\n suggestedReplies\n ...ChatMessageSuggestedReplies_SuggestedReplyButton_message\n}\n\nfragment ChatMessage_message on Message {\n id\n messageId\n text\n author\n linkifiedText\n state\n ...ChatMessageSuggestedReplies_message\n ...ChatMessageFeedbackButtons_message\n ...ChatMessageOverflowButton_message\n ...chatHelpers_isHumanMessage\n ...chatHelpers_isBotMessage\n ...chatHelpers_isChatBreak\n ...chatHelpers_useTimeoutLevel\n ...MarkdownLinkInner_message\n}\n\nfragment MarkdownLinkInner_message on Message {\n messageId\n}\n\nfragment MessageFeedbackOtherModal_message on Message {\n id\n messageId\n}\n\nfragment MessageFeedbackReasonModal_message on Message {\n id\n messageId\n}\n\nfragment chatHelpers_isBotMessage on Message {\n ...chatHelpers_isHumanMessage\n ...chatHelpers_isChatBreak\n}\n\nfragment chatHelpers_isChatBreak on Message {\n author\n}\n\nfragment chatHelpers_isHumanMessage on Message {\n author\n}\n\nfragment chatHelpers_useTimeoutLevel on Message {\n id\n state\n text\n messageId\n}\n' - }, - { - subscriptionName: 'viewerStateUpdated', - query: 'subscription subscriptions_viewerStateUpdated_Subscription {\n viewerStateUpdated {\n id\n ...ChatPageBotSwitcher_viewer\n }\n}\n\nfragment BotHeader_bot on Bot {\n displayName\n ...BotImage_bot\n}\n\nfragment BotImage_bot on Bot {\n profilePicture\n displayName\n}\n\nfragment BotLink_bot on Bot {\n displayName\n}\n\nfragment ChatPageBotSwitcher_viewer on Viewer {\n availableBots {\n id\n ...BotLink_bot\n ...BotHeader_bot\n }\n}\n' - } - ] - }, - query: 'mutation subscriptionsMutation(\n $subscriptions: [AutoSubscriptionQuery!]!\n) {\n autoSubscribe(subscriptions: $subscriptions) {\n viewer {\n id\n }\n }\n}\n' - } - - await this.makeRequest(query) - } - - async makeRequest (request) { - let payload = JSON.stringify(request) - let baseString = payload + this.headers['poe-formkey'] + 'WpuLMiXEKKE98j56k' - const md5 = crypto.createHash('md5').update(baseString).digest('hex') - let option = { - method: 'POST', - headers: Object.assign(this.headers, { - 'poe-tag-id': md5, - 'content-type': 'application/json' - }), - body: payload - } - if (this.config.proxy) { - option.agent = proxy(Config.proxy) - } - const response = await fetch('https://poe.com/api/gql_POST', option) - let text = await response.text() - try { - let result = JSON.parse(text) - console.log({ result }) - return result - } catch (e) { - console.error(text) - throw e - } - } - - async getBot (displayName) { - let r - let retry = 10 - while (retry >= 0) { - let url = `https://poe.com/_next/data/${this.nextData.buildId}/${displayName}.json` - let option = { - headers: this.headers - } - if (this.config.proxy) { - option.agent = proxy(Config.proxy) - } - let r = await fetch(url, option) - let res = await r.text() - try { - let chatData = (JSON.parse(res)).pageProps.payload.chatOfBotDisplayName - return chatData - } catch (e) { - r = res - retry-- - } - } - throw new Error(r) - } - - async getChatId () { - let option = { - headers: this.headers - } - if (this.config.proxy) { - option.agent = proxy(Config.proxy) - } - let r = await fetch('https://poe.com', option) - let text = await r.text() - const jsonRegex = /