Skip to content

Commit

Permalink
patch: fix(upgrade) bing client (#505)
Browse files Browse the repository at this point in the history
  • Loading branch information
josStorer committed Sep 6, 2023
1 parent 97c04c2 commit e71ee74
Show file tree
Hide file tree
Showing 9 changed files with 287 additions and 122 deletions.
1 change: 1 addition & 0 deletions build.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ async function copyFiles(entryPoints, targetDir) {
async function finishOutput(outputDirSuffix) {
const commonFiles = [
{ src: 'src/logo.png', dst: 'logo.png' },
{ src: 'src/rules.json', dst: 'rules.json' },

{ src: 'build/shared.js', dst: 'shared.js' },
{ src: 'build/content-script.css', dst: 'content-script.css' }, // shared
Expand Down
42 changes: 41 additions & 1 deletion src/background/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ async function executeApi(session, port, config) {
const accessToken = await getChatGptAccessToken()
await generateAnswersWithChatgptWebApi(port, session.question, session, accessToken)
}
} else if (bingWebModelKeys.some((n) => session.modelName.includes(n))) {
} else if (bingWebModelKeys.includes(session.modelName)) {
const accessToken = await getBingAccessToken()
if (session.modelName.includes('bingFreeSydney'))
await generateAnswersWithBingWebApi(port, session.question, session, accessToken, true)
Expand Down Expand Up @@ -192,9 +192,49 @@ Browser.runtime.onMessage.addListener(async (message, sender) => {
}
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,
},
null,
]
} catch (error) {
return [null, error]
}
}
}
})

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'],
)

registerPortListener(async (session, port, config) => await executeApi(session, port, config))
registerCommands()
refreshMenu()
183 changes: 114 additions & 69 deletions src/components/ConversationCard/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,25 @@ import InputBox from '../InputBox'
import ConversationItem from '../ConversationItem'
import { createElementAtPosition, isFirefox, isMobile, isSafari } from '../../utils'
import {
LinkExternalIcon,
ArchiveIcon,
DesktopDownloadIcon,
LinkExternalIcon,
MoveToBottomIcon,
} from '@primer/octicons-react'
import { WindowDesktop, XLg, Pin } from 'react-bootstrap-icons'
import { Pin, WindowDesktop, XLg } from 'react-bootstrap-icons'
import FileSaver from 'file-saver'
import { render } from 'preact'
import FloatingToolbar from '../FloatingToolbar'
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
import { ModelMode, Models } from '../../config/index.mjs'
import { bingWebModelKeys, getUserConfig, ModelMode, Models } from '../../config/index.mjs'
import { useTranslation } from 'react-i18next'
import DeleteButton from '../DeleteButton'
import { useConfig } from '../../hooks/use-config.mjs'
import { createSession } from '../../services/local-session.mjs'
import { v4 as uuidv4 } from 'uuid'
import { initSession } from '../../services/init-session.mjs'
import { findLastIndex } from 'lodash-es'
import { generateAnswersWithBingWebApi } from '../../services/apis/bing-web.mjs'

const logo = Browser.runtime.getURL('logo.png')

Expand All @@ -48,6 +49,7 @@ function ConversationCard(props) {
const windowSize = useClampWindowSize([750, 1500], [250, 1100])
const bodyRef = useRef(null)
const [completeDraggable, setCompleteDraggable] = useState(false)
const useForegroundFetch = bingWebModelKeys.includes(session.modelName)

/**
* @type {[ConversationItemData[], (conversationItemData: ConversationItemData[]) => void]}
Expand Down Expand Up @@ -96,12 +98,12 @@ function ConversationCard(props) {
}
}, [conversationItemData])

useEffect(() => {
useEffect(async () => {
// when the page is responsive, session may accumulate redundant data and needs to be cleared after remounting and before making a new request
if (props.question) {
const newSession = initSession({ question: props.question })
setSession(newSession)
port.postMessage({ session: newSession })
await postMessage({ session: newSession })
}
}, [props.question]) // usually only triggered once

Expand All @@ -125,6 +127,100 @@ function ConversationCard(props) {
})
}

const portMessageListener = (msg) => {
if (msg.answer) {
updateAnswer(msg.answer, false, 'answer')
}
if (msg.session) {
if (msg.done) msg.session = { ...msg.session, isRetry: false }
setSession(msg.session)
}
if (msg.done) {
updateAnswer('', true, 'answer', true)
setIsReady(true)
}
if (msg.error) {
switch (msg.error) {
case 'UNAUTHORIZED':
updateAnswer(
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
}<br>${t('And refresh this page or type you question again')}` +
`<br><br>${t(
'Consider creating an api key at https://platform.openai.com/account/api-keys',
)}`,
false,
'error',
)
break
case 'CLOUDFLARE':
updateAnswer(
`${t('OpenAI Security Check Required')}<br>${
isSafari()
? t('Please open https://chat.openai.com/api/auth/session')
: t('Please open https://chat.openai.com')
}<br>${t('And refresh this page or type you question again')}` +
`<br><br>${t(
'Consider creating an api key at https://platform.openai.com/account/api-keys',
)}`,
false,
'error',
)
break
default:
if (conversationItemData[conversationItemData.length - 1].content.includes('gpt-loading'))
updateAnswer(msg.error, false, 'error')
else
setConversationItemData([
...conversationItemData,
new ConversationItemData('error', msg.error),
])
break
}
setIsReady(true)
}
}

const foregroundMessageListeners = useRef([])

/**
* @param {Session|undefined} session
* @param {boolean|undefined} stop
*/
const postMessage = async ({ session, stop }) => {
if (useForegroundFetch) {
foregroundMessageListeners.current.forEach((listener) => listener({ session, stop }))
if (session) {
const fakePort = {
postMessage: (msg) => {
portMessageListener(msg)
},
onMessage: {
addListener: (listener) => {
foregroundMessageListeners.current.push(listener)
},
removeListener: (listener) => {
foregroundMessageListeners.current.splice(
foregroundMessageListeners.current.indexOf(listener),
1,
)
},
},
onDisconnect: {
addListener: () => {},
removeListener: () => {},
},
}
const bingToken = (await getUserConfig()).bingAccessToken
if (session.modelName.includes('bingFreeSydney'))
await generateAnswersWithBingWebApi(fakePort, session.question, session, bingToken, true)
else await generateAnswersWithBingWebApi(fakePort, session.question, session, bingToken)
}
} else {
port.postMessage({ session, stop })
}
}

useEffect(() => {
const portListener = () => {
setPort(Browser.runtime.connect())
Expand All @@ -146,68 +242,17 @@ function ConversationCard(props) {
}
}, [port])
useEffect(() => {
const listener = (msg) => {
if (msg.answer) {
updateAnswer(msg.answer, false, 'answer')
}
if (msg.session) {
if (msg.done) msg.session = { ...msg.session, isRetry: false }
setSession(msg.session)
if (useForegroundFetch) {
return () => {}
} else {
port.onMessage.addListener(portMessageListener)
return () => {
port.onMessage.removeListener(portMessageListener)
}
if (msg.done) {
updateAnswer('', true, 'answer', true)
setIsReady(true)
}
if (msg.error) {
switch (msg.error) {
case 'UNAUTHORIZED':
updateAnswer(
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
}<br>${t('And refresh this page or type you question again')}` +
`<br><br>${t(
'Consider creating an api key at https://platform.openai.com/account/api-keys',
)}`,
false,
'error',
)
break
case 'CLOUDFLARE':
updateAnswer(
`${t('OpenAI Security Check Required')}<br>${
isSafari()
? t('Please open https://chat.openai.com/api/auth/session')
: t('Please open https://chat.openai.com')
}<br>${t('And refresh this page or type you question again')}` +
`<br><br>${t(
'Consider creating an api key at https://platform.openai.com/account/api-keys',
)}`,
false,
'error',
)
break
default:
if (
conversationItemData[conversationItemData.length - 1].content.includes('gpt-loading')
)
updateAnswer(msg.error, false, 'error')
else
setConversationItemData([
...conversationItemData,
new ConversationItemData('error', msg.error),
])
break
}
setIsReady(true)
}
}
port.onMessage.addListener(listener)
return () => {
port.onMessage.removeListener(listener)
}
}, [conversationItemData])

const getRetryFn = (session) => () => {
const getRetryFn = (session) => async () => {
updateAnswer(`<p class="gpt-loading">${t('Waiting for response...')}</p>`, false, 'answer')
setIsReady(false)

Expand All @@ -223,8 +268,8 @@ function ConversationCard(props) {
const newSession = { ...session, isRetry: true }
setSession(newSession)
try {
port.postMessage({ stop: true })
port.postMessage({ session: newSession })
await postMessage({ stop: true })
await postMessage({ session: newSession })
} catch (e) {
updateAnswer(e, false, 'error')
}
Expand Down Expand Up @@ -348,8 +393,8 @@ function ConversationCard(props) {
<DeleteButton
size={16}
text={t('Clear Conversation')}
onConfirm={() => {
port.postMessage({ stop: true })
onConfirm={async () => {
await postMessage({ stop: true })
Browser.runtime.sendMessage({
type: 'DELETE_CONVERSATION',
data: {
Expand Down Expand Up @@ -449,7 +494,7 @@ function ConversationCard(props) {
enabled={isReady}
port={port}
reverseResizeDir={props.pageMode}
onSubmit={(question) => {
onSubmit={async (question) => {
const newQuestion = new ConversationItemData('question', question)
const newAnswer = new ConversationItemData(
'answer',
Expand All @@ -461,7 +506,7 @@ function ConversationCard(props) {
const newSession = { ...session, question, isRetry: false }
setSession(newSession)
try {
port.postMessage({ session: newSession })
await postMessage({ session: newSession })
} catch (e) {
updateAnswer(e, false, 'error')
}
Expand Down
1 change: 1 addition & 0 deletions src/config/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ export const defaultConfig = {
],
accessToken: '',
tokenSavedOn: 0,
bingAccessToken: '',
chatgptJumpBackTabId: 0,
chatgptTabId: 0,

Expand Down
24 changes: 19 additions & 5 deletions src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,23 @@
"128": "logo.png"
},
"host_permissions": [
"https://*.openai.com/",
"https://*.bing.com/",
"https://*.poe.com/",
"https://*.google.com/"
"https://*.openai.com/*",
"https://*.bing.com/*",
"wss://*.bing.com/*",
"https://*.poe.com/*",
"https://*.google.com/*",
"https://claude.ai/*",
"<all_urls>"
],
"permissions": [
"commands",
"cookies",
"storage",
"contextMenus",
"unlimitedStorage",
"tabs"
"tabs",
"webRequest",
"declarativeNetRequestWithHostAccess"
],
"optional_permissions": [
"background"
Expand All @@ -32,6 +37,15 @@
"action": {
"default_popup": "popup.html"
},
"declarative_net_request": {
"rule_resources": [
{
"id": "ruleset",
"enabled": true,
"path": "rules.json"
}
]
},
"options_ui": {
"page": "popup.html",
"open_in_tab": true
Expand Down
Loading

0 comments on commit e71ee74

Please sign in to comment.