Skip to content

Commit

Permalink
feat: support for safari
Browse files Browse the repository at this point in the history
  • Loading branch information
josStorer committed Jan 17, 2023
1 parent cd240aa commit 01f8b64
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 18 deletions.
10 changes: 4 additions & 6 deletions src/background/fetch-sse.mjs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import { createParser } from 'eventsource-parser'
import { isEmpty } from 'lodash-es'
import { streamAsyncIterable } from './stream-async-iterable.mjs'

export async function fetchSSE(resource, options) {
const { onMessage, onStart, onEnd, ...fetchOptions } = options
const { onMessage, onStart, onEnd, onError, ...fetchOptions } = options
const resp = await fetch(resource, fetchOptions)
if (!resp.ok) {
const error = await resp.json().catch(() => ({}))
throw new Error(!isEmpty(error) ? JSON.stringify(error) : `${resp.status} ${resp.statusText}`)
await onError(resp)
}
const parser = createParser((event) => {
if (event.type === 'event') {
Expand All @@ -21,8 +19,8 @@ export async function fetchSSE(resource, options) {

if (!hasStarted) {
hasStarted = true
onStart(str)
await onStart(str)
}
}
onEnd()
await onEnd()
}
39 changes: 30 additions & 9 deletions src/background/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ import { v4 as uuidv4 } from 'uuid'
import Browser from 'webextension-polyfill'
import { sendMessageFeedback, setConversationProperty } from './chatgpt.mjs'
import { fetchSSE } from './fetch-sse.mjs'
import { isSafari } from '../content-script/utils.mjs'
import { getUserConfig } from '../config.js'
import { isEmpty } from 'lodash-es'

const KEY_ACCESS_TOKEN = 'accessToken'
const cache = new ExpiryMap(10 * 1000)
Expand All @@ -14,14 +17,25 @@ async function getAccessToken() {
if (cache.get(KEY_ACCESS_TOKEN)) {
return cache.get(KEY_ACCESS_TOKEN)
}
const resp = await fetch('https://chat.openai.com/api/auth/session')
.then((r) => r.json())
.catch(() => ({}))
if (!resp.accessToken) {
throw new Error('UNAUTHORIZED')
if (isSafari()) {
const userConfig = await getUserConfig()
if (userConfig.accessToken) {
cache.set(KEY_ACCESS_TOKEN, userConfig.accessToken)
} else {
throw new Error('UNAUTHORIZED')
}
} else {
const resp = await fetch('https://chat.openai.com/api/auth/session')
if (resp.status === 403) {
throw new Error('CLOUDFLARE')
}
const data = await resp.json().catch(() => ({}))
if (!data.accessToken) {
throw new Error('UNAUTHORIZED')
}
cache.set(KEY_ACCESS_TOKEN, data.accessToken)
}
cache.set(KEY_ACCESS_TOKEN, resp.accessToken)
return resp.accessToken
return cache.get(KEY_ACCESS_TOKEN)
}

/**
Expand Down Expand Up @@ -87,10 +101,17 @@ async function generateAnswers(port, question, session) {
port.postMessage({ answer: text, done: false, session: session })
}
},
onStart() {
async onStart() {
// sendModerations(accessToken, question, session.conversationId, session.messageId)
},
onEnd() {},
async onEnd() {},
async onError(resp) {
if (resp.status === 403) {
throw new Error('CLOUDFLARE')
}
const error = await resp.json().catch(() => ({}))
throw new Error(!isEmpty(error) ? JSON.stringify(error) : `${resp.status} ${resp.statusText}`)
},
})
}

Expand Down
14 changes: 14 additions & 0 deletions src/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export const defaultConfig = {
inputQuery: '',
appendQuery: '',
prependQuery: '',
accessToken: '',
tokenSavedOn: 0,
}

/**
Expand All @@ -46,3 +48,15 @@ export async function getUserConfig() {
export async function setUserConfig(value) {
await Browser.storage.local.set(value)
}

export async function setAccessToken(accessToken) {
await setUserConfig({ accessToken, tokenSavedOn: Date.now() })
}

const TOKEN_DURATION = 30 * 24 * 3600 * 1000
export async function clearOldAccessToken() {
const duration = Date.now() - (await getUserConfig()).tokenSavedOn
if (duration > TOKEN_DURATION) {
await setAccessToken('')
}
}
14 changes: 13 additions & 1 deletion src/content-script/ChatGPTQuery.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import Browser from 'webextension-polyfill'
import ChatGPTFeedback from './ChatGPTFeedback'
import { ChevronDownIcon, CopyIcon, XCircleIcon } from '@primer/octicons-react'
import { motion } from 'framer-motion'
import { isSafari } from './utils.mjs'

let session = {
question: null,
Expand Down Expand Up @@ -170,7 +171,18 @@ function ChatGPTQuery(props) {
switch (msg.error) {
case 'UNAUTHORIZED':
UpdateAnswer(
'UNAUTHORIZED<br>Please login at https://chat.openai.com first',
`UNAUTHORIZED<br>Please login at https://chat.openai.com first${
isSafari() ? '<br>Then open https://chat.openai.com/api/auth/session' : ''
}<br>And refresh this page or type you question again`,
false,
'error',
)
break
case 'CLOUDFLARE':
UpdateAnswer(
`OpenAI Security Check Required<br>Please open ${
isSafari() ? 'https://chat.openai.com/api/auth/session' : 'https://chat.openai.com'
}`,
false,
'error',
)
Expand Down
25 changes: 23 additions & 2 deletions src/content-script/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import './styles.scss'
import { render } from 'preact'
import ChatGPTCard from './ChatGPTCard'
import { config as siteConfig } from './search-engine-configs.mjs'
import { getPossibleElementByQuerySelector } from './utils.mjs'
import { getUserConfig } from '../config'
import { getPossibleElementByQuerySelector, isSafari } from './utils.mjs'
import { clearOldAccessToken, getUserConfig, setAccessToken } from '../config'

/**
* @param {SiteConfig} siteConfig
Expand Down Expand Up @@ -33,7 +33,28 @@ function getSearchInputValue(inputQuery) {
}
}

async function prepareForSafari() {
await clearOldAccessToken()

if (location.hostname !== 'chat.openai.com' || location.pathname !== '/api/auth/session') return

const response = document.querySelector('pre').textContent

let data
try {
data = JSON.parse(response)
} catch (error) {
console.error('json error', error)
return
}
if (data.accessToken) {
await setAccessToken(data.accessToken)
}
}

async function run() {
if (isSafari()) await prepareForSafari()

const userConfig = await getUserConfig()
let siteRegex
if (userConfig.userSiteRegexOnly) siteRegex = userConfig.siteRegex
Expand Down
4 changes: 4 additions & 0 deletions src/content-script/utils.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,7 @@ export function endsWithQuestionMark(question) {
question.endsWith('⸮') // Arabic
)
}

export function isSafari() {
return navigator.vendor === 'Apple Computer, Inc.'
}

0 comments on commit 01f8b64

Please sign in to comment.