Files
chatGPTBox/src/background/index.mjs
T
josc146 6c645b87b7 chore
2024-08-06 14:05:13 +08:00

337 lines
11 KiB
JavaScript

import Browser from 'webextension-polyfill'
import {
deleteConversation,
generateAnswersWithChatgptWebApi,
sendMessageFeedback,
} from '../services/apis/chatgpt-web'
import { generateAnswersWithBingWebApi } from '../services/apis/bing-web.mjs'
import {
generateAnswersWithChatgptApi,
generateAnswersWithGptCompletionApi,
} from '../services/apis/openai-api'
import { generateAnswersWithCustomApi } from '../services/apis/custom-api.mjs'
import { generateAnswersWithOllamaApi } from '../services/apis/ollama-api.mjs'
import { generateAnswersWithAzureOpenaiApi } from '../services/apis/azure-openai-api.mjs'
import { generateAnswersWithClaudeApi } from '../services/apis/claude-api.mjs'
import { generateAnswersWithChatGLMApi } from '../services/apis/chatglm-api.mjs'
import { generateAnswersWithWaylaidwandererApi } from '../services/apis/waylaidwanderer-api.mjs'
import {
azureOpenAiApiModelKeys,
claudeApiModelKeys,
chatglmApiModelKeys,
bardWebModelKeys,
bingWebModelKeys,
chatgptApiModelKeys,
chatgptWebModelKeys,
claudeWebModelKeys,
moonshotWebModelKeys,
customApiModelKeys,
ollamaApiModelKeys,
defaultConfig,
getUserConfig,
githubThirdPartyApiModelKeys,
gptApiModelKeys,
poeWebModelKeys,
setUserConfig,
moonshotApiModelKeys,
} from '../config/index.mjs'
import '../_locales/i18n'
import { openUrl } from '../utils/open-url'
import {
getBardCookies,
getBingAccessToken,
getChatGptAccessToken,
getClaudeSessionKey,
registerPortListener,
} from '../services/wrappers.mjs'
import { refreshMenu } from './menus.mjs'
import { registerCommands } from './commands.mjs'
import { generateAnswersWithBardWebApi } from '../services/apis/bard-web.mjs'
import { generateAnswersWithClaudeWebApi } from '../services/apis/claude-web.mjs'
import { generateAnswersWithMoonshotCompletionApi } from '../services/apis/moonshot-api.mjs'
import { generateAnswersWithMoonshotWebApi } from '../services/apis/moonshot-web.mjs'
function setPortProxy(port, proxyTabId) {
port.proxy = Browser.tabs.connect(proxyTabId)
const proxyOnMessage = (msg) => {
port.postMessage(msg)
}
const portOnMessage = (msg) => {
port.proxy.postMessage(msg)
}
const proxyOnDisconnect = () => {
port.proxy = Browser.tabs.connect(proxyTabId)
}
const portOnDisconnect = () => {
port.proxy.onMessage.removeListener(proxyOnMessage)
port.onMessage.removeListener(portOnMessage)
port.proxy.onDisconnect.removeListener(proxyOnDisconnect)
port.onDisconnect.removeListener(portOnDisconnect)
}
port.proxy.onMessage.addListener(proxyOnMessage)
port.onMessage.addListener(portOnMessage)
port.proxy.onDisconnect.addListener(proxyOnDisconnect)
port.onDisconnect.addListener(portOnDisconnect)
}
async function executeApi(session, port, config) {
console.debug('modelName', session.modelName)
if (chatgptWebModelKeys.includes(session.modelName)) {
let tabId
if (
config.chatgptTabId &&
config.customChatGptWebApiUrl === defaultConfig.customChatGptWebApiUrl
) {
const tab = await Browser.tabs.get(config.chatgptTabId).catch(() => {})
if (tab) tabId = tab.id
}
if (tabId) {
if (!port.proxy) {
setPortProxy(port, tabId)
port.proxy.postMessage({ session })
}
} else {
const accessToken = await getChatGptAccessToken()
await generateAnswersWithChatgptWebApi(port, session.question, session, accessToken)
}
} else if (
// `.some` for multi mode models. e.g. bingFree4-balanced
bingWebModelKeys.some((n) => session.modelName.includes(n))
) {
const accessToken = await getBingAccessToken()
if (session.modelName.includes('bingFreeSydney'))
await generateAnswersWithBingWebApi(port, session.question, session, accessToken, true)
else await generateAnswersWithBingWebApi(port, session.question, session, accessToken)
} else if (gptApiModelKeys.includes(session.modelName)) {
await generateAnswersWithGptCompletionApi(
port,
session.question,
session,
config.apiKey,
session.modelName,
)
} else if (chatgptApiModelKeys.includes(session.modelName)) {
await generateAnswersWithChatgptApi(
port,
session.question,
session,
config.apiKey,
session.modelName,
)
} else if (customApiModelKeys.includes(session.modelName)) {
await generateAnswersWithCustomApi(
port,
session.question,
session,
config.customApiKey,
config.customModelName,
)
} else if (ollamaApiModelKeys.includes(session.modelName)) {
await generateAnswersWithOllamaApi(port, session.question, session)
} else if (azureOpenAiApiModelKeys.includes(session.modelName)) {
await generateAnswersWithAzureOpenaiApi(port, session.question, session)
} else if (claudeApiModelKeys.includes(session.modelName)) {
await generateAnswersWithClaudeApi(port, session.question, session)
} else if (chatglmApiModelKeys.includes(session.modelName)) {
await generateAnswersWithChatGLMApi(port, session.question, session, session.modelName)
} else if (githubThirdPartyApiModelKeys.includes(session.modelName)) {
await generateAnswersWithWaylaidwandererApi(port, session.question, session)
} else if (poeWebModelKeys.includes(session.modelName)) {
throw new Error('Due to the new verification, Poe Web API is currently not supported.')
// if (session.modelName === 'poeAiWebCustom')
// await generateAnswersWithPoeWebApi(port, session.question, session, config.poeCustomBotName)
// else
// await generateAnswersWithPoeWebApi(
// port,
// session.question,
// session,
// Models[session.modelName].value,
// )
} else if (bardWebModelKeys.includes(session.modelName)) {
const cookies = await getBardCookies()
await generateAnswersWithBardWebApi(port, session.question, session, cookies)
} else if (claudeWebModelKeys.includes(session.modelName)) {
const sessionKey = await getClaudeSessionKey()
await generateAnswersWithClaudeWebApi(
port,
session.question,
session,
sessionKey,
session.modelName,
)
} else if (moonshotApiModelKeys.includes(session.modelName)) {
await generateAnswersWithMoonshotCompletionApi(
port,
session.question,
session,
config.moonshotApiKey,
session.modelName,
)
} else if (moonshotWebModelKeys.includes(session.modelName)) {
await generateAnswersWithMoonshotWebApi(
port,
session.question,
session,
config,
session.modelName,
)
}
}
Browser.runtime.onMessage.addListener(async (message, sender) => {
switch (message.type) {
case 'FEEDBACK': {
const token = await getChatGptAccessToken()
await sendMessageFeedback(token, message.data)
break
}
case 'DELETE_CONVERSATION': {
const token = await getChatGptAccessToken()
await deleteConversation(token, message.data.conversationId)
break
}
case 'NEW_URL': {
await Browser.tabs.create({
url: message.data.url,
pinned: message.data.pinned,
})
if (message.data.jumpBack) {
await setUserConfig({
notificationJumpBackTabId: sender.tab.id,
})
}
break
}
case 'SET_CHATGPT_TAB': {
await setUserConfig({
chatgptTabId: sender.tab.id,
})
break
}
case 'ACTIVATE_URL':
await Browser.tabs.update(message.data.tabId, { active: true })
break
case 'OPEN_URL':
openUrl(message.data.url)
break
case 'OPEN_CHAT_WINDOW': {
const config = await getUserConfig()
const url = Browser.runtime.getURL('IndependentPanel.html')
const tabs = await Browser.tabs.query({ url: url, windowType: 'popup' })
if (!config.alwaysCreateNewConversationWindow && tabs.length > 0)
await Browser.windows.update(tabs[0].windowId, { focused: true })
else
await Browser.windows.create({
url: url,
type: 'popup',
width: 500,
height: 650,
})
break
}
case 'REFRESH_MENU':
refreshMenu()
break
case 'PIN_TAB': {
let tabId
if (message.data.tabId) tabId = message.data.tabId
else tabId = sender.tab.id
await Browser.tabs.update(tabId, { pinned: true })
if (message.data.saveAsChatgptConfig) {
await setUserConfig({ chatgptTabId: tabId })
}
break
}
case 'FETCH': {
if (message.data.input.includes('bing.com')) {
const accessToken = await getBingAccessToken()
await setUserConfig({ bingAccessToken: accessToken })
}
try {
const response = await fetch(message.data.input, message.data.init)
const text = await response.text()
return [
{
body: text,
status: response.status,
statusText: response.statusText,
headers: Object.fromEntries(response.headers),
},
null,
]
} catch (error) {
return [null, error]
}
}
case 'GET_COOKIE': {
return (await Browser.cookies.get({ url: message.data.url, name: message.data.name }))?.value
}
}
})
try {
Browser.webRequest.onBeforeRequest.addListener(
(details) => {
if (
details.url.includes('/public_key') &&
!details.url.includes(defaultConfig.chatgptArkoseReqParams)
) {
let formData = new URLSearchParams()
for (const k in details.requestBody.formData) {
formData.append(k, details.requestBody.formData[k])
}
setUserConfig({
chatgptArkoseReqUrl: details.url,
chatgptArkoseReqForm:
formData.toString() ||
new TextDecoder('utf-8').decode(new Uint8Array(details.requestBody.raw[0].bytes)),
}).then(() => {
console.log('Arkose req url and form saved')
})
}
},
{
urls: ['https://*.openai.com/*', 'https://*.chatgpt.com/*'],
types: ['xmlhttprequest'],
},
['requestBody'],
)
Browser.webRequest.onBeforeSendHeaders.addListener(
(details) => {
const headers = details.requestHeaders
for (let i = 0; i < headers.length; i++) {
if (headers[i].name === 'Origin') {
headers[i].value = 'https://www.bing.com'
} else if (headers[i].name === 'Referer') {
headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
}
}
return { requestHeaders: headers }
},
{
urls: ['wss://sydney.bing.com/*', 'https://www.bing.com/*'],
types: ['xmlhttprequest', 'websocket'],
},
['requestHeaders'],
)
Browser.tabs.onUpdated.addListener(async (tabId, info, tab) => {
if (!tab.url) return
// eslint-disable-next-line no-undef
await chrome.sidePanel.setOptions({
tabId,
path: 'IndependentPanel.html',
enabled: true,
})
})
} catch (error) {
console.log(error)
}
registerPortListener(async (session, port, config) => await executeApi(session, port, config))
registerCommands()
refreshMenu()