diff --git a/README.md b/README.md index 52920f2..bcc26b2 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Deep ChatGPT integrations in your browser, completely for free. [![license][license-image]][license-url] [![release][release-image]][release-url] -[![size](https://img.shields.io/badge/minified%20size-360%20kB-blue)][release-url] +[![size](https://img.shields.io/badge/minified%20size-370%20kB-blue)][release-url] [![verfiy][verify-image]][verify-url] English   |   [Indonesia](README_IN.md)   |   [简体中文](README_ZH.md) diff --git a/README_IN.md b/README_IN.md index 6e00863..e6f1dbd 100644 --- a/README_IN.md +++ b/README_IN.md @@ -10,7 +10,7 @@ Integrasi Deep ChatGPT di browser Anda, sepenuhnya gratis. [![license][license-image]][license-url] [![release][release-image]][release-url] -[![size](https://img.shields.io/badge/minified%20size-360%20kB-blue)][release-url] +[![size](https://img.shields.io/badge/minified%20size-370%20kB-blue)][release-url] [![verfiy][verify-image]][verify-url] [Inggris](README.md)   |   Indonesia   |   [简体中文](README_ZH.md) diff --git a/README_ZH.md b/README_ZH.md index 63230df..318054c 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -10,7 +10,7 @@ [![license][license-image]][license-url] [![release][release-image]][release-url] -[![size](https://img.shields.io/badge/minified%20size-360%20kB-blue)][release-url] +[![size](https://img.shields.io/badge/minified%20size-370%20kB-blue)][release-url] [![verfiy][verify-image]][verify-url] [English](README.md)   |   [Indonesia](README_IN.md)   |   简体中文 diff --git a/src/background/index.mjs b/src/background/index.mjs index 56edf07..5879714 100644 --- a/src/background/index.mjs +++ b/src/background/index.mjs @@ -15,6 +15,7 @@ import { generateAnswersWithWaylaidwandererApi } from '../services/apis/waylaidw import { generateAnswersWithPoeWebApi } from '../services/apis/poe-web.mjs' import { azureOpenAiApiModelKeys, + bardWebModelKeys, bingWebModelKeys, chatgptApiModelKeys, chatgptWebModelKeys, @@ -30,12 +31,14 @@ import { import '../_locales/i18n' import { openUrl } from '../utils/open-url' import { + getBardCookies, getBingAccessToken, getChatGptAccessToken, registerPortListener, } from '../services/wrappers.mjs' import { refreshMenu } from './menus.mjs' import { registerCommands } from './commands.mjs' +import { generateAnswersWithBardWebApi } from '../services/apis/bard-web.mjs' function setPortProxy(port, proxyTabId) { port.proxy = Browser.tabs.connect(proxyTabId) @@ -117,6 +120,9 @@ async function executeApi(session, port, config) { session, Models[session.modelName].value, ) + } else if (bardWebModelKeys.includes(session.modelName)) { + const cookies = await getBardCookies() + await generateAnswersWithBardWebApi(port, session.question, session, cookies) } } diff --git a/src/config/index.mjs b/src/config/index.mjs index 75befc7..b3015e0 100644 --- a/src/config/index.mjs +++ b/src/config/index.mjs @@ -29,6 +29,7 @@ export const chatgptWebModelKeys = [ 'chatgptPlus4Mobile', ] export const bingWebModelKeys = ['bingFree4', 'bingFreeSydney'] +export const bardWebModelKeys = ['bardWebFree'] export const gptApiModelKeys = ['gptApiDavinci'] export const chatgptApiModelKeys = ['chatgptApi35', 'chatgptApi4_8k', 'chatgptApi4_32k'] export const customApiModelKeys = ['customModel'] @@ -62,6 +63,7 @@ export const Models = { chatgptApi35: { value: 'gpt-3.5-turbo', desc: 'ChatGPT (GPT-3.5-turbo)' }, bingFree4: { value: '', desc: 'Bing (Web, GPT-4)' }, bingFreeSydney: { value: '', desc: 'Bing (Web, GPT-4, Sydney)' }, + bardWebFree: { value: '', desc: 'Bard (Web)' }, poeAiWebSage: { value: 'sage', desc: 'Poe AI (Web, Sage)' }, poeAiWebGPT4: { value: 'gpt-4', desc: 'Poe AI (Web, GPT-4)' }, poeAiWebClaudePlus: { value: 'claude+', desc: 'Poe AI (Web, Claude+)' }, diff --git a/src/manifest.json b/src/manifest.json index 998249d..2e84f67 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -12,7 +12,8 @@ "host_permissions": [ "https://*.openai.com/", "https://*.bing.com/", - "https://*.poe.com/" + "https://*.poe.com/", + "https://*.google.com/" ], "permissions": [ "commands", diff --git a/src/manifest.v2.json b/src/manifest.v2.json index b238c60..8ff1dba 100644 --- a/src/manifest.v2.json +++ b/src/manifest.v2.json @@ -18,7 +18,8 @@ "tabs", "https://*.openai.com/", "https://*.bing.com/", - "https://*.poe.com/" + "https://*.poe.com/", + "https://*.google.com/" ], "background": { "scripts": [ diff --git a/src/services/apis/bard-web.mjs b/src/services/apis/bard-web.mjs new file mode 100644 index 0000000..1ca79d7 --- /dev/null +++ b/src/services/apis/bard-web.mjs @@ -0,0 +1,26 @@ +import { pushRecord } from './shared.mjs' +import Bard from '../clients/bard' + +/** + * @param {Runtime.Port} port + * @param {string} question + * @param {Session} session + * @param {string} cookies + */ +export async function generateAnswersWithBardWebApi(port, question, session, cookies) { + // const { controller, messageListener } = setAbortController(port) + const bot = new Bard(cookies) + + // eslint-disable-next-line + try { + const { answer, conversationObj } = await bot.ask(question, session.bard_conversationObj || {}) + session.bard_conversationObj = conversationObj + pushRecord(session, question, answer) + console.debug('conversation history', { content: session.conversationRecords }) + // port.onMessage.removeListener(messageListener) + port.postMessage({ answer: answer, done: true, session: session }) + } catch (err) { + // port.onMessage.removeListener(messageListener) + throw err + } +} diff --git a/src/services/clients/bard/index.mjs b/src/services/clients/bard/index.mjs new file mode 100644 index 0000000..307d476 --- /dev/null +++ b/src/services/clients/bard/index.mjs @@ -0,0 +1,137 @@ +// https://github.com/PawanOsman/GoogleBard + +export default class Bard { + cookies = '' + + constructor(cookies) { + this.cookies = cookies + } + + ParseResponse(text) { + let resData = { + r: '', + c: '', + rc: '', + responses: [], + } + try { + let parseData = (data) => { + if (typeof data === 'string') { + if (data?.startsWith('c_')) { + resData.c = data + return + } + if (data?.startsWith('r_')) { + resData.r = data + return + } + if (data?.startsWith('rc_')) { + resData.rc = data + return + } + resData.responses.push(data) + } + if (Array.isArray(data)) { + data.forEach((item) => { + parseData(item) + }) + } + } + try { + const lines = text.split('\n') + for (let i in lines) { + const line = lines[i] + if (line.includes('wrb.fr')) { + let data = JSON.parse(line) + let responsesData = JSON.parse(data[0][2]) + responsesData.forEach((response) => { + parseData(response) + }) + } + } + } catch (e) { + throw new Error( + `Error parsing response: make sure you are using the correct cookie, copy the value of "__Secure-1PSID" cookie and set it like this: \n\nnew Bard("__Secure-1PSID=")\n\nAlso using a US proxy is recommended.\n\nIf this error persists, please open an issue on github.\nhttps://github.com/PawanOsman/GoogleBard`, + ) + } + } catch (err) { + throw new Error( + `Error parsing response: make sure you are using the correct cookie, copy the value of "__Secure-1PSID" cookie and set it like this: \n\nnew Bard("__Secure-1PSID=")\n\nAlso using a US proxy is recommended.\n\nIf this error persists, please open an issue on github.\nhttps://github.com/PawanOsman/GoogleBard`, + ) + } + return resData + } + + async GetRequestParams() { + try { + const response = await fetch('https://bard.google.com', { + headers: { + Cookie: this.cookies, + }, + }) + const text = await response.text() + const cfb2h = text.match(/"cfb2h":\s*"([^"]+)"/)?.[1] + const SNlM0e = text.match(/"SNlM0e":\s*"([^"]+)"/)?.[1] + const context = { googleData: { cfb2h, SNlM0e } } + const at = context.googleData.SNlM0e + const bl = context.googleData.cfb2h + return { at, bl } + } catch (e) { + throw new Error( + `Error parsing response: make sure you are using the correct cookie, copy the value of "__Secure-1PSID" cookie and set it like this: \n\nnew Bard("__Secure-1PSID=")\n\nAlso using a US proxy is recommended.\n\nIf this error persists, please open an issue on github.\nhttps://github.com/PawanOsman/GoogleBard`, + ) + } + } + + async ask(prompt, conversationObj) { + return await this.send(prompt, conversationObj) + } + + async send(prompt, conversationObj) { + let conversation = { + id: conversationObj.id || '', + c: conversationObj.c || '', + r: conversationObj.r || '', + rc: conversationObj.rc || '', + lastActive: Date.now(), + } + // eslint-disable-next-line + try { + let { at, bl } = await this.GetRequestParams() + const response = await fetch( + 'https://bard.google.com/_/BardChatUi/data/assistant.lamda.BardFrontendService/StreamGenerate?' + + new URLSearchParams({ + bl: bl, + rt: 'c', + _reqid: 0, + }), + { + method: 'POST', + body: new URLSearchParams({ + at: at, + 'f.req': JSON.stringify([ + null, + `[[${JSON.stringify(prompt)}],null,${JSON.stringify([ + conversation.c, + conversation.r, + conversation.rc, + ])}]`, + ]), + }), + headers: { + Cookie: this.cookies, + }, + }, + ) + const data = await response.text() + let parsedResponse = this.ParseResponse(data) + conversation.c = parsedResponse.c + conversation.r = parsedResponse.r + conversation.rc = parsedResponse.rc + const conversationObj = { c: conversation.c, r: conversation.r, rc: conversation.rc } + return { answer: parsedResponse.responses[0], conversationObj: conversationObj } + } catch (e) { + throw e + } + } +} diff --git a/src/services/init-session.mjs b/src/services/init-session.mjs index 2366196..0c10961 100644 --- a/src/services/init-session.mjs +++ b/src/services/init-session.mjs @@ -24,6 +24,7 @@ import { v4 as uuidv4 } from 'uuid' * @property {string|null} bingWeb_parentMessageId * @property {Object|null} bingWeb_jailbreakConversationCache * @property {number|null} poe_chatId + * @property {object|null} bard_conversationObj */ /** * @param {string|null} question @@ -74,5 +75,8 @@ export function initSession({ // poe poe_chatId: null, + + // bard + bard_conversationObj: null, } } diff --git a/src/services/wrappers.mjs b/src/services/wrappers.mjs index c8aaec0..f8e5950 100644 --- a/src/services/wrappers.mjs +++ b/src/services/wrappers.mjs @@ -34,6 +34,12 @@ export async function getBingAccessToken() { return (await Browser.cookies.get({ url: 'https://bing.com/', name: '_U' }))?.value } +export async function getBardCookies() { + const token = (await Browser.cookies.get({ url: 'https://google.com/', name: '__Secure-1PSID' })) + ?.value + return '__Secure-1PSID=' + token +} + export function registerPortListener(executor) { Browser.runtime.onConnect.addListener((port) => { console.debug('connected')