Skip to content

Commit e71ee74

Browse files
committed
patch: fix(upgrade) bing client (#505)
1 parent 97c04c2 commit e71ee74

File tree

9 files changed

+287
-122
lines changed

9 files changed

+287
-122
lines changed

build.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@ async function copyFiles(entryPoints, targetDir) {
249249
async function finishOutput(outputDirSuffix) {
250250
const commonFiles = [
251251
{ src: 'src/logo.png', dst: 'logo.png' },
252+
{ src: 'src/rules.json', dst: 'rules.json' },
252253

253254
{ src: 'build/shared.js', dst: 'shared.js' },
254255
{ src: 'build/content-script.css', dst: 'content-script.css' }, // shared

src/background/index.mjs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ async function executeApi(session, port, config) {
8383
const accessToken = await getChatGptAccessToken()
8484
await generateAnswersWithChatgptWebApi(port, session.question, session, accessToken)
8585
}
86-
} else if (bingWebModelKeys.some((n) => session.modelName.includes(n))) {
86+
} else if (bingWebModelKeys.includes(session.modelName)) {
8787
const accessToken = await getBingAccessToken()
8888
if (session.modelName.includes('bingFreeSydney'))
8989
await generateAnswersWithBingWebApi(port, session.question, session, accessToken, true)
@@ -192,9 +192,49 @@ Browser.runtime.onMessage.addListener(async (message, sender) => {
192192
}
193193
break
194194
}
195+
case 'FETCH': {
196+
if (message.data.input.includes('bing.com')) {
197+
const accessToken = await getBingAccessToken()
198+
await setUserConfig({ bingAccessToken: accessToken })
199+
}
200+
201+
try {
202+
const response = await fetch(message.data.input, message.data.init)
203+
const text = await response.text()
204+
return [
205+
{
206+
body: text,
207+
status: response.status,
208+
statusText: response.statusText,
209+
},
210+
null,
211+
]
212+
} catch (error) {
213+
return [null, error]
214+
}
215+
}
195216
}
196217
})
197218

219+
Browser.webRequest.onBeforeSendHeaders.addListener(
220+
(details) => {
221+
const headers = details.requestHeaders
222+
for (let i = 0; i < headers.length; i++) {
223+
if (headers[i].name === 'Origin') {
224+
headers[i].value = 'https://www.bing.com'
225+
} else if (headers[i].name === 'Referer') {
226+
headers[i].value = 'https://www.bing.com/search?q=Bing+AI&showconv=1&FORM=hpcodx'
227+
}
228+
}
229+
return { requestHeaders: headers }
230+
},
231+
{
232+
urls: ['wss://sydney.bing.com/*', 'https://www.bing.com/*'],
233+
types: ['xmlhttprequest', 'websocket'],
234+
},
235+
['requestHeaders'],
236+
)
237+
198238
registerPortListener(async (session, port, config) => await executeApi(session, port, config))
199239
registerCommands()
200240
refreshMenu()

src/components/ConversationCard/index.jsx

Lines changed: 114 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,25 @@ import InputBox from '../InputBox'
55
import ConversationItem from '../ConversationItem'
66
import { createElementAtPosition, isFirefox, isMobile, isSafari } from '../../utils'
77
import {
8-
LinkExternalIcon,
98
ArchiveIcon,
109
DesktopDownloadIcon,
10+
LinkExternalIcon,
1111
MoveToBottomIcon,
1212
} from '@primer/octicons-react'
13-
import { WindowDesktop, XLg, Pin } from 'react-bootstrap-icons'
13+
import { Pin, WindowDesktop, XLg } from 'react-bootstrap-icons'
1414
import FileSaver from 'file-saver'
1515
import { render } from 'preact'
1616
import FloatingToolbar from '../FloatingToolbar'
1717
import { useClampWindowSize } from '../../hooks/use-clamp-window-size'
18-
import { ModelMode, Models } from '../../config/index.mjs'
18+
import { bingWebModelKeys, getUserConfig, ModelMode, Models } from '../../config/index.mjs'
1919
import { useTranslation } from 'react-i18next'
2020
import DeleteButton from '../DeleteButton'
2121
import { useConfig } from '../../hooks/use-config.mjs'
2222
import { createSession } from '../../services/local-session.mjs'
2323
import { v4 as uuidv4 } from 'uuid'
2424
import { initSession } from '../../services/init-session.mjs'
2525
import { findLastIndex } from 'lodash-es'
26+
import { generateAnswersWithBingWebApi } from '../../services/apis/bing-web.mjs'
2627

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

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

5254
/**
5355
* @type {[ConversationItemData[], (conversationItemData: ConversationItemData[]) => void]}
@@ -96,12 +98,12 @@ function ConversationCard(props) {
9698
}
9799
}, [conversationItemData])
98100

99-
useEffect(() => {
101+
useEffect(async () => {
100102
// when the page is responsive, session may accumulate redundant data and needs to be cleared after remounting and before making a new request
101103
if (props.question) {
102104
const newSession = initSession({ question: props.question })
103105
setSession(newSession)
104-
port.postMessage({ session: newSession })
106+
await postMessage({ session: newSession })
105107
}
106108
}, [props.question]) // usually only triggered once
107109

@@ -125,6 +127,100 @@ function ConversationCard(props) {
125127
})
126128
}
127129

130+
const portMessageListener = (msg) => {
131+
if (msg.answer) {
132+
updateAnswer(msg.answer, false, 'answer')
133+
}
134+
if (msg.session) {
135+
if (msg.done) msg.session = { ...msg.session, isRetry: false }
136+
setSession(msg.session)
137+
}
138+
if (msg.done) {
139+
updateAnswer('', true, 'answer', true)
140+
setIsReady(true)
141+
}
142+
if (msg.error) {
143+
switch (msg.error) {
144+
case 'UNAUTHORIZED':
145+
updateAnswer(
146+
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
147+
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
148+
}<br>${t('And refresh this page or type you question again')}` +
149+
`<br><br>${t(
150+
'Consider creating an api key at https://platform.openai.com/account/api-keys',
151+
)}`,
152+
false,
153+
'error',
154+
)
155+
break
156+
case 'CLOUDFLARE':
157+
updateAnswer(
158+
`${t('OpenAI Security Check Required')}<br>${
159+
isSafari()
160+
? t('Please open https://chat.openai.com/api/auth/session')
161+
: t('Please open https://chat.openai.com')
162+
}<br>${t('And refresh this page or type you question again')}` +
163+
`<br><br>${t(
164+
'Consider creating an api key at https://platform.openai.com/account/api-keys',
165+
)}`,
166+
false,
167+
'error',
168+
)
169+
break
170+
default:
171+
if (conversationItemData[conversationItemData.length - 1].content.includes('gpt-loading'))
172+
updateAnswer(msg.error, false, 'error')
173+
else
174+
setConversationItemData([
175+
...conversationItemData,
176+
new ConversationItemData('error', msg.error),
177+
])
178+
break
179+
}
180+
setIsReady(true)
181+
}
182+
}
183+
184+
const foregroundMessageListeners = useRef([])
185+
186+
/**
187+
* @param {Session|undefined} session
188+
* @param {boolean|undefined} stop
189+
*/
190+
const postMessage = async ({ session, stop }) => {
191+
if (useForegroundFetch) {
192+
foregroundMessageListeners.current.forEach((listener) => listener({ session, stop }))
193+
if (session) {
194+
const fakePort = {
195+
postMessage: (msg) => {
196+
portMessageListener(msg)
197+
},
198+
onMessage: {
199+
addListener: (listener) => {
200+
foregroundMessageListeners.current.push(listener)
201+
},
202+
removeListener: (listener) => {
203+
foregroundMessageListeners.current.splice(
204+
foregroundMessageListeners.current.indexOf(listener),
205+
1,
206+
)
207+
},
208+
},
209+
onDisconnect: {
210+
addListener: () => {},
211+
removeListener: () => {},
212+
},
213+
}
214+
const bingToken = (await getUserConfig()).bingAccessToken
215+
if (session.modelName.includes('bingFreeSydney'))
216+
await generateAnswersWithBingWebApi(fakePort, session.question, session, bingToken, true)
217+
else await generateAnswersWithBingWebApi(fakePort, session.question, session, bingToken)
218+
}
219+
} else {
220+
port.postMessage({ session, stop })
221+
}
222+
}
223+
128224
useEffect(() => {
129225
const portListener = () => {
130226
setPort(Browser.runtime.connect())
@@ -146,68 +242,17 @@ function ConversationCard(props) {
146242
}
147243
}, [port])
148244
useEffect(() => {
149-
const listener = (msg) => {
150-
if (msg.answer) {
151-
updateAnswer(msg.answer, false, 'answer')
152-
}
153-
if (msg.session) {
154-
if (msg.done) msg.session = { ...msg.session, isRetry: false }
155-
setSession(msg.session)
245+
if (useForegroundFetch) {
246+
return () => {}
247+
} else {
248+
port.onMessage.addListener(portMessageListener)
249+
return () => {
250+
port.onMessage.removeListener(portMessageListener)
156251
}
157-
if (msg.done) {
158-
updateAnswer('', true, 'answer', true)
159-
setIsReady(true)
160-
}
161-
if (msg.error) {
162-
switch (msg.error) {
163-
case 'UNAUTHORIZED':
164-
updateAnswer(
165-
`${t('UNAUTHORIZED')}<br>${t('Please login at https://chat.openai.com first')}${
166-
isSafari() ? `<br>${t('Then open https://chat.openai.com/api/auth/session')}` : ''
167-
}<br>${t('And refresh this page or type you question again')}` +
168-
`<br><br>${t(
169-
'Consider creating an api key at https://platform.openai.com/account/api-keys',
170-
)}`,
171-
false,
172-
'error',
173-
)
174-
break
175-
case 'CLOUDFLARE':
176-
updateAnswer(
177-
`${t('OpenAI Security Check Required')}<br>${
178-
isSafari()
179-
? t('Please open https://chat.openai.com/api/auth/session')
180-
: t('Please open https://chat.openai.com')
181-
}<br>${t('And refresh this page or type you question again')}` +
182-
`<br><br>${t(
183-
'Consider creating an api key at https://platform.openai.com/account/api-keys',
184-
)}`,
185-
false,
186-
'error',
187-
)
188-
break
189-
default:
190-
if (
191-
conversationItemData[conversationItemData.length - 1].content.includes('gpt-loading')
192-
)
193-
updateAnswer(msg.error, false, 'error')
194-
else
195-
setConversationItemData([
196-
...conversationItemData,
197-
new ConversationItemData('error', msg.error),
198-
])
199-
break
200-
}
201-
setIsReady(true)
202-
}
203-
}
204-
port.onMessage.addListener(listener)
205-
return () => {
206-
port.onMessage.removeListener(listener)
207252
}
208253
}, [conversationItemData])
209254

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

@@ -223,8 +268,8 @@ function ConversationCard(props) {
223268
const newSession = { ...session, isRetry: true }
224269
setSession(newSession)
225270
try {
226-
port.postMessage({ stop: true })
227-
port.postMessage({ session: newSession })
271+
await postMessage({ stop: true })
272+
await postMessage({ session: newSession })
228273
} catch (e) {
229274
updateAnswer(e, false, 'error')
230275
}
@@ -348,8 +393,8 @@ function ConversationCard(props) {
348393
<DeleteButton
349394
size={16}
350395
text={t('Clear Conversation')}
351-
onConfirm={() => {
352-
port.postMessage({ stop: true })
396+
onConfirm={async () => {
397+
await postMessage({ stop: true })
353398
Browser.runtime.sendMessage({
354399
type: 'DELETE_CONVERSATION',
355400
data: {
@@ -449,7 +494,7 @@ function ConversationCard(props) {
449494
enabled={isReady}
450495
port={port}
451496
reverseResizeDir={props.pageMode}
452-
onSubmit={(question) => {
497+
onSubmit={async (question) => {
453498
const newQuestion = new ConversationItemData('question', question)
454499
const newAnswer = new ConversationItemData(
455500
'answer',
@@ -461,7 +506,7 @@ function ConversationCard(props) {
461506
const newSession = { ...session, question, isRetry: false }
462507
setSession(newSession)
463508
try {
464-
port.postMessage({ session: newSession })
509+
await postMessage({ session: newSession })
465510
} catch (e) {
466511
updateAnswer(e, false, 'error')
467512
}

src/config/index.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ export const defaultConfig = {
207207
],
208208
accessToken: '',
209209
tokenSavedOn: 0,
210+
bingAccessToken: '',
210211
chatgptJumpBackTabId: 0,
211212
chatgptTabId: 0,
212213

src/manifest.json

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,23 @@
1010
"128": "logo.png"
1111
},
1212
"host_permissions": [
13-
"https://*.openai.com/",
14-
"https://*.bing.com/",
15-
"https://*.poe.com/",
16-
"https://*.google.com/"
13+
"https://*.openai.com/*",
14+
"https://*.bing.com/*",
15+
"wss://*.bing.com/*",
16+
"https://*.poe.com/*",
17+
"https://*.google.com/*",
18+
"https://claude.ai/*",
19+
"<all_urls>"
1720
],
1821
"permissions": [
1922
"commands",
2023
"cookies",
2124
"storage",
2225
"contextMenus",
2326
"unlimitedStorage",
24-
"tabs"
27+
"tabs",
28+
"webRequest",
29+
"declarativeNetRequestWithHostAccess"
2530
],
2631
"optional_permissions": [
2732
"background"
@@ -32,6 +37,15 @@
3237
"action": {
3338
"default_popup": "popup.html"
3439
},
40+
"declarative_net_request": {
41+
"rule_resources": [
42+
{
43+
"id": "ruleset",
44+
"enabled": true,
45+
"path": "rules.json"
46+
}
47+
]
48+
},
3549
"options_ui": {
3650
"page": "popup.html",
3751
"open_in_tab": true

0 commit comments

Comments
 (0)