diff --git a/package-lock.json b/package-lock.json index 22ac4c0..7366c0a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "gpt-3-encoder": "^1.1.4", "graphql": "^16.6.0", "i18next": "^22.4.15", + "js-sha3": "^0.9.3", "jsonwebtoken": "8.5.1", "katex": "^0.16.6", "lodash-es": "^4.17.21", @@ -27,6 +28,7 @@ "preact": "^10.13.2", "process": "^0.11.10", "prop-types": "^15.8.1", + "random-int": "^3.0.0", "react": "npm:@preact/compat@^17.1.2", "react-bootstrap-icons": "^1.10.3", "react-dom": "npm:@preact/compat@^17.1.2", @@ -6308,6 +6310,11 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/js-sha3": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.9.3.tgz", + "integrity": "sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9136,6 +9143,17 @@ } ] }, + "node_modules/random-int": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/random-int/-/random-int-3.0.0.tgz", + "integrity": "sha512-QvewnOwigesW2WFyTHiQzR6XUUcSQO/BqmfgRz5N5GpGrKQnTf7ebMz8UtuwaET8IfO1n0wLx8/fHsI8E0Jpow==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", diff --git a/package.json b/package.json index 9956b53..e6f9f19 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "gpt-3-encoder": "^1.1.4", "graphql": "^16.6.0", "i18next": "^22.4.15", + "js-sha3": "^0.9.3", "jsonwebtoken": "8.5.1", "katex": "^0.16.6", "lodash-es": "^4.17.21", @@ -40,6 +41,7 @@ "preact": "^10.13.2", "process": "^0.11.10", "prop-types": "^15.8.1", + "random-int": "^3.0.0", "react": "npm:@preact/compat@^17.1.2", "react-bootstrap-icons": "^1.10.3", "react-dom": "npm:@preact/compat@^17.1.2", diff --git a/src/services/apis/chatgpt-web.mjs b/src/services/apis/chatgpt-web.mjs index 1938be0..961fc3c 100644 --- a/src/services/apis/chatgpt-web.mjs +++ b/src/services/apis/chatgpt-web.mjs @@ -7,6 +7,8 @@ import { pushRecord, setAbortController } from './shared.mjs' import Browser from 'webextension-polyfill' import { v4 as uuidv4 } from 'uuid' import { t } from 'i18next' +import { sha3_512 } from 'js-sha3' +import randomInt from 'random-int' async function request(token, method, path, data) { const apiUrl = (await getUserConfig()).customChatGptWebApiUrl @@ -49,12 +51,12 @@ export async function getModels(token) { if (response.models) return response.models.map((m) => m.slug) } -export async function getRequirementsToken(accessToken) { +export async function getRequirements(accessToken) { const response = JSON.parse( (await request(accessToken, 'POST', '/sentinel/chat-requirements')).responseText, ) - if (response.token) { - return response.token + if (response) { + return response } } @@ -91,6 +93,38 @@ export async function getArkoseToken(config) { return arkoseToken } +// https://github.com/tctien342/chatgpt-proxy/blob/9147a4345b34eece20681f257fd475a8a2c81171/src/openai.ts#L103 +function generateProofToken(seed, diff, userAgent) { + const cores = [8, 12, 16, 24] + const screens = [3000, 4000, 6000] + + const core = cores[randomInt(0, cores.length)] + const screen = screens[randomInt(0, screens.length)] + + const parseTime = new Date().toString() + + const config = [core + screen, parseTime, 4294705152, 0, userAgent] + + const diffLen = diff.length / 2 + + for (let i = 0; i < 100000; i++) { + config[3] = i + const jsonData = JSON.stringify(config) + // eslint-disable-next-line no-undef + const base = Buffer.from(jsonData).toString('base64') + const hashValue = sha3_512.create().update(seed + base) + + if (hashValue.hex().substring(0, diffLen) <= diff) { + const result = 'gAAAAAB' + base + return result + } + } + + // eslint-disable-next-line no-undef + const fallbackBase = Buffer.from(`"${seed}"`).toString('base64') + return 'gAAAAABwQ8Lk5FbGpA2NcR9dShT6gYjU7VxZ4D' + fallbackBase +} + export async function isNeedWebsocket(accessToken) { return (await request(accessToken, 'GET', '/accounts/check/v4-2023-04-27')).responseText.includes( 'shared_websocket', @@ -167,9 +201,9 @@ export async function generateAnswersWithChatgptWebApi(port, question, session, const config = await getUserConfig() let arkoseError - const [models, requirementsToken, arkoseToken, useWebsocket] = await Promise.all([ + const [models, requirements, arkoseToken, useWebsocket] = await Promise.all([ getModels(accessToken).catch(() => undefined), - getRequirementsToken(accessToken).catch(() => undefined), + getRequirements(accessToken).catch(() => undefined), getArkoseToken(config).catch((e) => { arkoseError = e }), @@ -180,9 +214,17 @@ export async function generateAnswersWithChatgptWebApi(port, question, session, const usedModel = models && models.includes(selectedModel) ? selectedModel : Models[chatgptWebModelKeys[0]].value console.debug('usedModel', usedModel) - const needArkoseToken = !usedModel.includes(Models[chatgptWebModelKeys[0]].value) + const needArkoseToken = requirements && requirements.arkose?.required if (arkoseError && needArkoseToken) throw arkoseError + let proofToken + if (requirements?.proofofwork?.required) { + proofToken = generateProofToken( + requirements.proofofwork.seed, + requirements.proofofwork.difficulty, + ) + } + let cookie let oaiDeviceId if (Browser.cookies && Browser.cookies.getAll) { @@ -213,8 +255,9 @@ export async function generateAnswersWithChatgptWebApi(port, question, session, 'Content-Type': 'application/json', Authorization: `Bearer ${accessToken}`, ...(cookie && { Cookie: cookie }), - 'Openai-Sentinel-Arkose-Token': arkoseToken || '', - 'Openai-Sentinel-Chat-Requirements-Token': requirementsToken || '', + ...(needArkoseToken && { 'Openai-Sentinel-Arkose-Token': arkoseToken }), + ...(requirements && { 'Openai-Sentinel-Chat-Requirements-Token': requirements.token }), + ...(proofToken && { 'Openai-Sentinel-Proof-Token': proofToken }), 'Oai-Device-Id': oaiDeviceId, 'Oai-Language': 'en-US', },