patch: fix(upgrade) bing client (#505)

This commit is contained in:
josc146
2023-09-06 23:52:28 +08:00
parent 97c04c2464
commit e71ee74c6d
9 changed files with 288 additions and 123 deletions
+114 -69
View File
@@ -5,17 +5,17 @@ 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'
@@ -23,6 +23,7 @@ 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')
@@ -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]}
@@ -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
@@ -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())
@@ -146,68 +242,17 @@ function ConversationCard(props) {
}
}, [port])
useEffect(() => {
const listener = (msg) => {
if (msg.answer) {
updateAnswer(msg.answer, false, 'answer')
if (useForegroundFetch) {
return () => {}
} else {
port.onMessage.addListener(portMessageListener)
return () => {
port.onMessage.removeListener(portMessageListener)
}
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)
}
}
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)
@@ -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')
}
@@ -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: {
@@ -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',
@@ -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')
}