2022-12-05 05:13:36 +00:00
|
|
|
import ExpiryMap from 'expiry-map'
|
2022-12-10 22:19:35 +00:00
|
|
|
import pTimeout from 'p-timeout'
|
2022-12-05 05:13:36 +00:00
|
|
|
import { v4 as uuidv4 } from 'uuid'
|
|
|
|
|
|
|
|
import * as types from './types'
|
2022-12-17 04:48:42 +00:00
|
|
|
import { AChatGPTAPI } from './abstract-chatgpt-api'
|
2022-12-05 23:09:31 +00:00
|
|
|
import { fetch } from './fetch'
|
|
|
|
import { fetchSSE } from './fetch-sse'
|
2022-12-05 05:13:36 +00:00
|
|
|
import { markdownToText } from './utils'
|
|
|
|
|
|
|
|
const KEY_ACCESS_TOKEN = 'accessToken'
|
|
|
|
const USER_AGENT =
|
2022-12-12 00:42:44 +00:00
|
|
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
export class ChatGPTAPI extends AChatGPTAPI {
|
2022-12-05 05:13:36 +00:00
|
|
|
protected _sessionToken: string
|
2022-12-11 23:18:51 +00:00
|
|
|
protected _clearanceToken: string
|
2022-12-02 23:43:59 +00:00
|
|
|
protected _markdown: boolean
|
2022-12-12 16:37:44 +00:00
|
|
|
protected _debug: boolean
|
2022-12-05 05:13:36 +00:00
|
|
|
protected _apiBaseUrl: string
|
|
|
|
protected _backendApiBaseUrl: string
|
|
|
|
protected _userAgent: string
|
2022-12-11 11:43:39 +00:00
|
|
|
protected _headers: Record<string, string>
|
2022-12-12 16:37:44 +00:00
|
|
|
protected _user: types.User | null = null
|
2022-12-05 05:34:15 +00:00
|
|
|
|
2022-12-07 04:07:14 +00:00
|
|
|
// Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh
|
|
|
|
protected _accessTokenCache: ExpiryMap<string, string>
|
2022-12-02 23:43:59 +00:00
|
|
|
|
|
|
|
/**
|
2022-12-05 05:13:36 +00:00
|
|
|
* Creates a new client wrapper around the unofficial ChatGPT REST API.
|
|
|
|
*
|
2022-12-12 17:23:03 +00:00
|
|
|
* Note that your IP address and `userAgent` must match the same values that you used
|
|
|
|
* to obtain your `clearanceToken`.
|
|
|
|
*
|
2022-12-05 05:13:36 +00:00
|
|
|
* @param opts.sessionToken = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions)
|
2022-12-12 16:37:44 +00:00
|
|
|
* @param opts.clearanceToken = **Required** Cloudflare `cf_clearance` cookie value (see readme for instructions)
|
2022-12-05 05:13:36 +00:00
|
|
|
* @param apiBaseUrl - Optional override; the base URL for ChatGPT webapp's API (`/api`)
|
|
|
|
* @param backendApiBaseUrl - Optional override; the base URL for the ChatGPT backend API (`/backend-api`)
|
|
|
|
* @param userAgent - Optional override; the `user-agent` header to use with ChatGPT requests
|
2022-12-07 04:07:14 +00:00
|
|
|
* @param accessTokenTTL - Optional override; how long in milliseconds access tokens should last before being forcefully refreshed
|
2022-12-12 16:37:44 +00:00
|
|
|
* @param accessToken - Optional default access token if you already have a valid one generated
|
|
|
|
* @param heaaders - Optional additional HTTP headers to be added to each `fetch` request
|
|
|
|
* @param debug - Optional enables logging debugging into to stdout
|
2022-12-02 23:43:59 +00:00
|
|
|
*/
|
2022-12-05 05:13:36 +00:00
|
|
|
constructor(opts: {
|
|
|
|
sessionToken: string
|
2022-12-12 16:37:44 +00:00
|
|
|
|
2022-12-11 23:18:51 +00:00
|
|
|
clearanceToken: string
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
/** @defaultValue `true` **/
|
|
|
|
markdown?: boolean
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
/** @defaultValue `'https://chat.openai.com/api'` **/
|
|
|
|
apiBaseUrl?: string
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
/** @defaultValue `'https://chat.openai.com/backend-api'` **/
|
|
|
|
backendApiBaseUrl?: string
|
|
|
|
|
2022-12-12 16:37:44 +00:00
|
|
|
/** @defaultValue `Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36'` **/
|
2022-12-05 05:13:36 +00:00
|
|
|
userAgent?: string
|
2022-12-07 04:07:14 +00:00
|
|
|
|
2022-12-12 16:37:44 +00:00
|
|
|
/** @defaultValue 1 hour **/
|
2022-12-07 04:07:14 +00:00
|
|
|
accessTokenTTL?: number
|
2022-12-11 16:46:03 +00:00
|
|
|
|
2022-12-12 16:37:44 +00:00
|
|
|
/** @defaultValue `undefined` **/
|
2022-12-11 16:46:03 +00:00
|
|
|
accessToken?: string
|
2022-12-12 01:40:06 +00:00
|
|
|
|
2022-12-12 16:37:44 +00:00
|
|
|
/** @defaultValue `undefined` **/
|
2022-12-12 01:40:06 +00:00
|
|
|
headers?: Record<string, string>
|
2022-12-12 16:37:44 +00:00
|
|
|
|
|
|
|
/** @defaultValue `false` **/
|
|
|
|
debug?: boolean
|
2022-12-05 05:13:36 +00:00
|
|
|
}) {
|
2022-12-17 04:48:42 +00:00
|
|
|
super()
|
|
|
|
|
2022-12-02 23:43:59 +00:00
|
|
|
const {
|
2022-12-05 05:13:36 +00:00
|
|
|
sessionToken,
|
2022-12-11 23:18:51 +00:00
|
|
|
clearanceToken,
|
2022-12-05 05:13:36 +00:00
|
|
|
markdown = true,
|
|
|
|
apiBaseUrl = 'https://chat.openai.com/api',
|
|
|
|
backendApiBaseUrl = 'https://chat.openai.com/backend-api',
|
2022-12-07 04:07:14 +00:00
|
|
|
userAgent = USER_AGENT,
|
2022-12-12 00:42:44 +00:00
|
|
|
accessTokenTTL = 60 * 60000, // 1 hour
|
2022-12-12 01:40:06 +00:00
|
|
|
accessToken,
|
2022-12-12 16:37:44 +00:00
|
|
|
headers,
|
|
|
|
debug = false
|
2022-12-02 23:43:59 +00:00
|
|
|
} = opts
|
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
this._sessionToken = sessionToken
|
2022-12-11 23:18:51 +00:00
|
|
|
this._clearanceToken = clearanceToken
|
2022-12-02 23:43:59 +00:00
|
|
|
this._markdown = !!markdown
|
2022-12-12 16:37:44 +00:00
|
|
|
this._debug = !!debug
|
2022-12-05 05:13:36 +00:00
|
|
|
this._apiBaseUrl = apiBaseUrl
|
|
|
|
this._backendApiBaseUrl = backendApiBaseUrl
|
|
|
|
this._userAgent = userAgent
|
2022-12-11 11:43:39 +00:00
|
|
|
this._headers = {
|
2022-12-12 01:40:06 +00:00
|
|
|
'user-agent': this._userAgent,
|
2022-12-11 11:43:39 +00:00
|
|
|
'x-openai-assistant-app-id': '',
|
|
|
|
'accept-language': 'en-US,en;q=0.9',
|
2022-12-15 04:41:43 +00:00
|
|
|
'accept-encoding': 'gzip, deflate, br',
|
2022-12-11 11:43:39 +00:00
|
|
|
origin: 'https://chat.openai.com',
|
2022-12-12 00:42:44 +00:00
|
|
|
referer: 'https://chat.openai.com/chat',
|
|
|
|
'sec-ch-ua':
|
|
|
|
'"Not?A_Brand";v="8", "Chromium";v="108", "Google Chrome";v="108"',
|
|
|
|
'sec-ch-ua-platform': '"macOS"',
|
|
|
|
'sec-fetch-dest': 'empty',
|
|
|
|
'sec-fetch-mode': 'cors',
|
2022-12-12 01:40:06 +00:00
|
|
|
'sec-fetch-site': 'same-origin',
|
|
|
|
...headers
|
2022-12-11 11:43:39 +00:00
|
|
|
}
|
2022-12-03 00:04:53 +00:00
|
|
|
|
2022-12-07 04:07:14 +00:00
|
|
|
this._accessTokenCache = new ExpiryMap<string, string>(accessTokenTTL)
|
2022-12-11 23:18:51 +00:00
|
|
|
if (accessToken) {
|
|
|
|
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
|
|
|
|
}
|
2022-12-07 04:07:14 +00:00
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
if (!this._sessionToken) {
|
2022-12-17 04:48:42 +00:00
|
|
|
const error = new types.ChatGPTError('ChatGPT invalid session token')
|
|
|
|
error.statusCode = 401
|
|
|
|
throw error
|
2022-12-03 00:04:53 +00:00
|
|
|
}
|
2022-12-12 11:28:53 +00:00
|
|
|
|
|
|
|
if (!this._clearanceToken) {
|
2022-12-17 04:48:42 +00:00
|
|
|
const error = new types.ChatGPTError('ChatGPT invalid clearance token')
|
|
|
|
error.statusCode = 401
|
|
|
|
throw error
|
2022-12-12 11:28:53 +00:00
|
|
|
}
|
2022-12-02 23:43:59 +00:00
|
|
|
}
|
|
|
|
|
2022-12-11 11:43:39 +00:00
|
|
|
/**
|
|
|
|
* Gets the currently signed-in user, if authenticated, `null` otherwise.
|
|
|
|
*/
|
|
|
|
get user() {
|
|
|
|
return this._user
|
|
|
|
}
|
|
|
|
|
2022-12-12 17:23:03 +00:00
|
|
|
/** Gets the current session token. */
|
|
|
|
get sessionToken() {
|
|
|
|
return this._sessionToken
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the current Cloudflare clearance token (`cf_clearance` cookie value). */
|
|
|
|
get clearanceToken() {
|
|
|
|
return this._clearanceToken
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Gets the current user agent. */
|
|
|
|
get userAgent() {
|
|
|
|
return this._userAgent
|
|
|
|
}
|
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
/**
|
|
|
|
* Refreshes the client's access token which will succeed only if the session
|
|
|
|
* is valid.
|
|
|
|
*/
|
|
|
|
override async initSession() {
|
|
|
|
await this.refreshSession()
|
|
|
|
}
|
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
/**
|
|
|
|
* Sends a message to ChatGPT, waits for the response to resolve, and returns
|
|
|
|
* the response.
|
|
|
|
*
|
2022-12-07 04:07:14 +00:00
|
|
|
* If you want to receive a stream of partial responses, use `opts.onProgress`.
|
|
|
|
* If you want to receive the full response, including message and conversation IDs,
|
|
|
|
* you can use `opts.onConversationResponse` or use the `ChatGPTAPI.getConversation`
|
|
|
|
* helper.
|
|
|
|
*
|
2022-12-06 22:13:11 +00:00
|
|
|
* @param message - The prompt message to send
|
|
|
|
* @param opts.conversationId - Optional ID of a conversation to continue
|
|
|
|
* @param opts.parentMessageId - Optional ID of the previous message in the conversation
|
2022-12-12 16:37:44 +00:00
|
|
|
* @param opts.messageId - Optional ID of the message to send (defaults to a random UUID)
|
|
|
|
* @param opts.action - Optional ChatGPT `action` (either `next` or `variant`)
|
2022-12-07 04:07:14 +00:00
|
|
|
* @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout)
|
2022-12-07 00:19:30 +00:00
|
|
|
* @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
|
|
|
|
* @param opts.abortSignal - Optional callback used to abort the underlying `fetch` call using an [AbortController](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
|
|
|
|
*
|
2022-12-06 22:13:11 +00:00
|
|
|
* @returns The response from ChatGPT
|
2022-12-05 05:13:36 +00:00
|
|
|
*/
|
2022-12-17 04:48:42 +00:00
|
|
|
override async sendMessage(
|
2022-12-05 05:13:36 +00:00
|
|
|
message: string,
|
2022-12-07 00:19:30 +00:00
|
|
|
opts: types.SendMessageOptions = {}
|
2022-12-17 04:48:42 +00:00
|
|
|
): Promise<types.ChatResponse> {
|
2022-12-06 07:38:32 +00:00
|
|
|
const {
|
|
|
|
conversationId,
|
|
|
|
parentMessageId = uuidv4(),
|
2022-12-12 16:37:44 +00:00
|
|
|
messageId = uuidv4(),
|
|
|
|
action = 'next',
|
2022-12-07 04:07:14 +00:00
|
|
|
timeoutMs,
|
2022-12-17 04:48:42 +00:00
|
|
|
onProgress
|
2022-12-06 07:38:32 +00:00
|
|
|
} = opts
|
2022-12-05 05:13:36 +00:00
|
|
|
|
2022-12-07 04:07:14 +00:00
|
|
|
let { abortSignal } = opts
|
|
|
|
|
|
|
|
let abortController: AbortController = null
|
|
|
|
if (timeoutMs && !abortSignal) {
|
|
|
|
abortController = new AbortController()
|
|
|
|
abortSignal = abortController.signal
|
|
|
|
}
|
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
const accessToken = await this.refreshSession()
|
2022-12-05 05:13:36 +00:00
|
|
|
|
|
|
|
const body: types.ConversationJSONBody = {
|
2022-12-12 16:37:44 +00:00
|
|
|
action,
|
2022-12-05 05:13:36 +00:00
|
|
|
messages: [
|
|
|
|
{
|
2022-12-12 16:37:44 +00:00
|
|
|
id: messageId,
|
2022-12-05 05:13:36 +00:00
|
|
|
role: 'user',
|
|
|
|
content: {
|
|
|
|
content_type: 'text',
|
|
|
|
parts: [message]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
],
|
|
|
|
model: 'text-davinci-002-render',
|
2022-12-06 07:38:32 +00:00
|
|
|
parent_message_id: parentMessageId
|
|
|
|
}
|
|
|
|
|
|
|
|
if (conversationId) {
|
|
|
|
body.conversation_id = conversationId
|
2022-12-05 05:13:36 +00:00
|
|
|
}
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
const result: types.ChatResponse = {
|
|
|
|
conversationId,
|
|
|
|
messageId,
|
|
|
|
response: ''
|
|
|
|
}
|
2022-12-05 05:13:36 +00:00
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
const responseP = new Promise<types.ChatResponse>((resolve, reject) => {
|
2022-12-12 16:37:44 +00:00
|
|
|
const url = `${this._backendApiBaseUrl}/conversation`
|
|
|
|
const headers = {
|
|
|
|
...this._headers,
|
|
|
|
Authorization: `Bearer ${accessToken}`,
|
|
|
|
Accept: 'text/event-stream',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
Cookie: `cf_clearance=${this._clearanceToken}`
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._debug) {
|
|
|
|
console.log('POST', url, { body, headers })
|
|
|
|
}
|
|
|
|
|
2022-12-05 23:09:31 +00:00
|
|
|
fetchSSE(url, {
|
2022-12-05 05:13:36 +00:00
|
|
|
method: 'POST',
|
2022-12-12 16:37:44 +00:00
|
|
|
headers,
|
2022-12-05 05:13:36 +00:00
|
|
|
body: JSON.stringify(body),
|
2022-12-06 22:13:11 +00:00
|
|
|
signal: abortSignal,
|
2022-12-05 05:13:36 +00:00
|
|
|
onMessage: (data: string) => {
|
|
|
|
if (data === '[DONE]') {
|
2022-12-17 04:48:42 +00:00
|
|
|
return resolve(result)
|
2022-12-05 05:13:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
2022-12-17 04:48:42 +00:00
|
|
|
const convoResponseEvent: types.ConversationResponseEvent =
|
|
|
|
JSON.parse(data)
|
|
|
|
if (convoResponseEvent.conversation_id) {
|
|
|
|
result.conversationId = convoResponseEvent.conversation_id
|
2022-12-06 07:38:32 +00:00
|
|
|
}
|
2022-12-07 00:19:30 +00:00
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
if (convoResponseEvent.message?.id) {
|
|
|
|
result.messageId = convoResponseEvent.message.id
|
|
|
|
}
|
|
|
|
|
|
|
|
const message = convoResponseEvent.message
|
|
|
|
// console.log('event', JSON.stringify(convoResponseEvent, null, 2))
|
2022-12-05 05:13:36 +00:00
|
|
|
|
|
|
|
if (message) {
|
|
|
|
let text = message?.content?.parts?.[0]
|
|
|
|
|
|
|
|
if (text) {
|
|
|
|
if (!this._markdown) {
|
|
|
|
text = markdownToText(text)
|
|
|
|
}
|
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
result.response = text
|
2022-12-05 05:13:36 +00:00
|
|
|
|
|
|
|
if (onProgress) {
|
2022-12-17 04:48:42 +00:00
|
|
|
onProgress(result)
|
2022-12-05 05:13:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (err) {
|
|
|
|
console.warn('fetchSSE onMessage unexpected error', err)
|
|
|
|
reject(err)
|
|
|
|
}
|
|
|
|
}
|
2022-12-12 17:23:03 +00:00
|
|
|
}).catch((err) => {
|
|
|
|
const errMessageL = err.toString().toLowerCase()
|
|
|
|
|
|
|
|
if (
|
2022-12-17 04:48:42 +00:00
|
|
|
result.response &&
|
2022-12-12 17:23:03 +00:00
|
|
|
(errMessageL === 'error: typeerror: terminated' ||
|
|
|
|
errMessageL === 'typeerror: terminated')
|
|
|
|
) {
|
|
|
|
// OpenAI sometimes forcefully terminates the socket from their end before
|
|
|
|
// the HTTP request has resolved cleanly. In my testing, these cases tend to
|
|
|
|
// happen when OpenAI has already send the last `response`, so we can ignore
|
|
|
|
// the `fetch` error in this case.
|
2022-12-17 04:48:42 +00:00
|
|
|
return resolve(result)
|
2022-12-12 17:23:03 +00:00
|
|
|
} else {
|
|
|
|
return reject(err)
|
|
|
|
}
|
|
|
|
})
|
2022-12-05 05:13:36 +00:00
|
|
|
})
|
2022-12-07 04:07:14 +00:00
|
|
|
|
|
|
|
if (timeoutMs) {
|
|
|
|
if (abortController) {
|
|
|
|
// This will be called when a timeout occurs in order for us to forcibly
|
|
|
|
// ensure that the underlying HTTP request is aborted.
|
|
|
|
;(responseP as any).cancel = () => {
|
|
|
|
abortController.abort()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pTimeout(responseP, {
|
|
|
|
milliseconds: timeoutMs,
|
|
|
|
message: 'ChatGPT timed out waiting for response'
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
return responseP
|
|
|
|
}
|
2022-12-02 23:43:59 +00:00
|
|
|
}
|
|
|
|
|
2022-12-15 04:41:43 +00:00
|
|
|
async sendModeration(input: string) {
|
2022-12-17 04:48:42 +00:00
|
|
|
const accessToken = await this.refreshSession()
|
2022-12-15 04:41:43 +00:00
|
|
|
const url = `${this._backendApiBaseUrl}/moderations`
|
|
|
|
const headers = {
|
|
|
|
...this._headers,
|
|
|
|
Authorization: `Bearer ${accessToken}`,
|
|
|
|
Accept: '*/*',
|
|
|
|
'Content-Type': 'application/json',
|
|
|
|
Cookie: `cf_clearance=${this._clearanceToken}`
|
|
|
|
}
|
|
|
|
|
|
|
|
const body: types.ModerationsJSONBody = {
|
|
|
|
input,
|
|
|
|
model: 'text-moderation-playground'
|
|
|
|
}
|
|
|
|
|
|
|
|
if (this._debug) {
|
|
|
|
console.log('POST', url, headers, body)
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = await fetch(url, {
|
|
|
|
method: 'POST',
|
|
|
|
headers,
|
|
|
|
body: JSON.stringify(body)
|
|
|
|
}).then((r) => {
|
|
|
|
if (!r.ok) {
|
|
|
|
const error = new types.ChatGPTError(`${r.status} ${r.statusText}`)
|
|
|
|
error.response = r
|
|
|
|
error.statusCode = r.status
|
|
|
|
error.statusText = r.statusText
|
|
|
|
throw error
|
|
|
|
}
|
|
|
|
|
|
|
|
return r.json() as any as types.ModerationsJSONResult
|
|
|
|
})
|
|
|
|
|
|
|
|
return res
|
|
|
|
}
|
|
|
|
|
2022-12-07 04:07:14 +00:00
|
|
|
/**
|
|
|
|
* @returns `true` if the client has a valid acces token or `false` if refreshing
|
|
|
|
* the token fails.
|
|
|
|
*/
|
2022-12-17 04:48:42 +00:00
|
|
|
override async getIsAuthenticated() {
|
2022-12-07 04:07:14 +00:00
|
|
|
try {
|
2022-12-17 04:48:42 +00:00
|
|
|
void (await this.refreshSession())
|
2022-12-07 04:07:14 +00:00
|
|
|
return true
|
|
|
|
} catch (err) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Attempts to refresh the current access token using the ChatGPT
|
|
|
|
* `sessionToken` cookie.
|
|
|
|
*
|
|
|
|
* Access tokens will be cached for up to `accessTokenTTL` milliseconds to
|
|
|
|
* prevent refreshing access tokens too frequently.
|
|
|
|
*
|
|
|
|
* @returns A valid access token
|
|
|
|
* @throws An error if refreshing the access token fails.
|
|
|
|
*/
|
2022-12-17 04:48:42 +00:00
|
|
|
override async refreshSession(): Promise<string> {
|
2022-12-05 05:13:36 +00:00
|
|
|
const cachedAccessToken = this._accessTokenCache.get(KEY_ACCESS_TOKEN)
|
|
|
|
if (cachedAccessToken) {
|
|
|
|
return cachedAccessToken
|
2022-12-02 23:43:59 +00:00
|
|
|
}
|
|
|
|
|
2022-12-10 22:19:35 +00:00
|
|
|
let response: Response
|
2022-12-05 05:13:36 +00:00
|
|
|
try {
|
2022-12-12 16:37:44 +00:00
|
|
|
const url = `${this._apiBaseUrl}/auth/session`
|
2022-12-12 00:42:44 +00:00
|
|
|
const headers = {
|
|
|
|
...this._headers,
|
|
|
|
cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`,
|
|
|
|
accept: '*/*'
|
|
|
|
}
|
|
|
|
|
2022-12-12 16:37:44 +00:00
|
|
|
if (this._debug) {
|
|
|
|
console.log('GET', url, headers)
|
|
|
|
}
|
|
|
|
|
|
|
|
const res = await fetch(url, {
|
2022-12-12 00:42:44 +00:00
|
|
|
headers
|
2022-12-07 00:19:30 +00:00
|
|
|
}).then((r) => {
|
2022-12-10 22:19:35 +00:00
|
|
|
response = r
|
|
|
|
|
2022-12-07 00:19:30 +00:00
|
|
|
if (!r.ok) {
|
2022-12-10 22:19:35 +00:00
|
|
|
const error = new types.ChatGPTError(`${r.status} ${r.statusText}`)
|
|
|
|
error.response = r
|
|
|
|
error.statusCode = r.status
|
|
|
|
error.statusText = r.statusText
|
|
|
|
throw error
|
2022-12-07 00:19:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return r.json() as any as types.SessionResult
|
|
|
|
})
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
const accessToken = res?.accessToken
|
2022-12-02 23:43:59 +00:00
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
if (!accessToken) {
|
2022-12-10 22:19:35 +00:00
|
|
|
const error = new types.ChatGPTError('Unauthorized')
|
|
|
|
error.response = response
|
|
|
|
error.statusCode = response?.status
|
|
|
|
error.statusText = response?.statusText
|
|
|
|
throw error
|
2022-12-02 23:43:59 +00:00
|
|
|
}
|
|
|
|
|
2022-12-10 22:19:35 +00:00
|
|
|
const appError = res?.error
|
|
|
|
if (appError) {
|
|
|
|
if (appError === 'RefreshAccessTokenError') {
|
|
|
|
const error = new types.ChatGPTError('session token may have expired')
|
|
|
|
error.response = response
|
|
|
|
error.statusCode = response?.status
|
|
|
|
error.statusText = response?.statusText
|
|
|
|
throw error
|
2022-12-05 23:09:31 +00:00
|
|
|
} else {
|
2022-12-10 22:19:35 +00:00
|
|
|
const error = new types.ChatGPTError(appError)
|
|
|
|
error.response = response
|
|
|
|
error.statusCode = response?.status
|
|
|
|
error.statusText = response?.statusText
|
|
|
|
throw error
|
2022-12-05 23:09:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-11 11:43:39 +00:00
|
|
|
if (res.user) {
|
|
|
|
this._user = res.user
|
|
|
|
}
|
|
|
|
|
2022-12-05 05:13:36 +00:00
|
|
|
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
|
|
|
|
return accessToken
|
|
|
|
} catch (err: any) {
|
2022-12-12 16:37:44 +00:00
|
|
|
if (this._debug) {
|
|
|
|
console.error(err)
|
|
|
|
}
|
|
|
|
|
2022-12-10 22:19:35 +00:00
|
|
|
const error = new types.ChatGPTError(
|
|
|
|
`ChatGPT failed to refresh auth token. ${err.toString()}`
|
|
|
|
)
|
|
|
|
error.response = response
|
|
|
|
error.statusCode = response?.status
|
|
|
|
error.statusText = response?.statusText
|
|
|
|
error.originalError = err
|
|
|
|
throw error
|
2022-12-05 05:13:36 +00:00
|
|
|
}
|
2022-12-05 05:14:23 +00:00
|
|
|
}
|
2022-12-06 07:38:32 +00:00
|
|
|
|
2022-12-17 04:48:42 +00:00
|
|
|
override async closeSession(): Promise<void> {
|
|
|
|
this._accessTokenCache.delete(KEY_ACCESS_TOKEN)
|
2022-12-06 07:38:32 +00:00
|
|
|
}
|
2022-12-02 23:43:59 +00:00
|
|
|
}
|