Commit e5c0b05b authored by Travis Fischer's avatar Travis Fischer Committed by GitHub

Merge pull request #108 from transitive-bullshit/feature/robustness-automation-improvements

parents 2e654be5 6cf9396c
import dotenv from 'dotenv-safe' import dotenv from 'dotenv-safe'
import { oraPromise } from 'ora' import { oraPromise } from 'ora'
import { ChatGPTAPI } from '../src' import { ChatGPTAPI, getOpenAIAuth } from '../src'
import { getOpenAIAuthInfo } from './openai-auth-puppeteer'
dotenv.config() dotenv.config()
...@@ -17,7 +16,7 @@ async function main() { ...@@ -17,7 +16,7 @@ async function main() {
const email = process.env.EMAIL const email = process.env.EMAIL
const password = process.env.PASSWORD const password = process.env.PASSWORD
const authInfo = await getOpenAIAuthInfo({ const authInfo = await getOpenAIAuth({
email, email,
password password
}) })
......
import dotenv from 'dotenv-safe' import dotenv from 'dotenv-safe'
import { oraPromise } from 'ora' import { oraPromise } from 'ora'
import { ChatGPTAPI } from '../src' import { ChatGPTAPI, getOpenAIAuth } from '../src'
import { getOpenAIAuthInfo } from './openai-auth-puppeteer'
dotenv.config() dotenv.config()
...@@ -17,7 +16,7 @@ async function main() { ...@@ -17,7 +16,7 @@ async function main() {
const email = process.env.EMAIL const email = process.env.EMAIL
const password = process.env.PASSWORD const password = process.env.PASSWORD
const authInfo = await getOpenAIAuthInfo({ const authInfo = await getOpenAIAuth({
email, email,
password password
}) })
......
...@@ -10,7 +10,10 @@ ...@@ -10,7 +10,10 @@
### Accessors ### Accessors
- [clearanceToken](ChatGPTAPI.md#clearancetoken)
- [sessionToken](ChatGPTAPI.md#sessiontoken)
- [user](ChatGPTAPI.md#user) - [user](ChatGPTAPI.md#user)
- [userAgent](ChatGPTAPI.md#useragent)
### Methods ### Methods
...@@ -28,26 +31,63 @@ ...@@ -28,26 +31,63 @@
Creates a new client wrapper around the unofficial ChatGPT REST API. Creates a new client wrapper around the unofficial ChatGPT REST API.
Note that your IP address and `userAgent` must match the same values that you used
to obtain your `clearanceToken`.
#### Parameters #### Parameters
| Name | Type | Description | | Name | Type | Description |
| :------ | :------ | :------ | | :------ | :------ | :------ |
| `opts` | `Object` | - | | `opts` | `Object` | - |
| `opts.accessToken?` | `string` | - | | `opts.accessToken?` | `string` | **`Default Value`** `undefined` * |
| `opts.accessTokenTTL?` | `number` | **`Default Value`** 60000 (60 seconds) | | `opts.accessTokenTTL?` | `number` | **`Default Value`** 1 hour * |
| `opts.apiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/api'` * | | `opts.apiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/api'` * |
| `opts.backendApiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/backend-api'` * | | `opts.backendApiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/backend-api'` * |
| `opts.clearanceToken` | `string` | - | | `opts.clearanceToken` | `string` | = **Required** Cloudflare `cf_clearance` cookie value (see readme for instructions) |
| `opts.debug?` | `boolean` | **`Default Value`** `false` * |
| `opts.headers?` | `Record`<`string`, `string`\> | **`Default Value`** `undefined` * |
| `opts.markdown?` | `boolean` | **`Default Value`** `true` * | | `opts.markdown?` | `boolean` | **`Default Value`** `true` * |
| `opts.sessionToken` | `string` | = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions) | | `opts.sessionToken` | `string` | = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions) |
| `opts.userAgent?` | `string` | **`Default Value`** `'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'` * | | `opts.userAgent?` | `string` | **`Default Value`** `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'` * |
#### Defined in #### Defined in
[src/chatgpt-api.ts:39](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L39) [src/chatgpt-api.ts:45](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L45)
## Accessors ## Accessors
### clearanceToken
`get` **clearanceToken**(): `string`
Gets the current Cloudflare clearance token (`cf_clearance` cookie value).
#### Returns
`string`
#### Defined in
[src/chatgpt-api.ts:136](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L136)
___
### sessionToken
`get` **sessionToken**(): `string`
Gets the current session token.
#### Returns
`string`
#### Defined in
[src/chatgpt-api.ts:131](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L131)
___
### user ### user
`get` **user**(): [`User`](../modules.md#user) `get` **user**(): [`User`](../modules.md#user)
...@@ -60,7 +100,23 @@ Gets the currently signed-in user, if authenticated, `null` otherwise. ...@@ -60,7 +100,23 @@ Gets the currently signed-in user, if authenticated, `null` otherwise.
#### Defined in #### Defined in
[src/chatgpt-api.ts:98](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L98) [src/chatgpt-api.ts:126](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L126)
___
### userAgent
`get` **userAgent**(): `string`
Gets the current user agent.
#### Returns
`string`
#### Defined in
[src/chatgpt-api.ts:141](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L141)
## Methods ## Methods
...@@ -77,7 +133,7 @@ is still valid. ...@@ -77,7 +133,7 @@ is still valid.
#### Defined in #### Defined in
[src/chatgpt-api.ts:250](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L250) [src/chatgpt-api.ts:319](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L319)
___ ___
...@@ -104,7 +160,7 @@ The new conversation instance ...@@ -104,7 +160,7 @@ The new conversation instance
#### Defined in #### Defined in
[src/chatgpt-api.ts:344](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L344) [src/chatgpt-api.ts:425](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L425)
___ ___
...@@ -121,7 +177,7 @@ the token fails. ...@@ -121,7 +177,7 @@ the token fails.
#### Defined in #### Defined in
[src/chatgpt-api.ts:237](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L237) [src/chatgpt-api.ts:306](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L306)
___ ___
...@@ -147,7 +203,7 @@ A valid access token ...@@ -147,7 +203,7 @@ A valid access token
#### Defined in #### Defined in
[src/chatgpt-api.ts:264](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L264) [src/chatgpt-api.ts:333](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L333)
___ ___
...@@ -178,4 +234,4 @@ The response from ChatGPT ...@@ -178,4 +234,4 @@ The response from ChatGPT
#### Defined in #### Defined in
[src/chatgpt-api.ts:121](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-api.ts#L121) [src/chatgpt-api.ts:166](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-api.ts#L166)
...@@ -41,7 +41,7 @@ Creates a new conversation wrapper around the ChatGPT API. ...@@ -41,7 +41,7 @@ Creates a new conversation wrapper around the ChatGPT API.
#### Defined in #### Defined in
[src/chatgpt-conversation.ts:21](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-conversation.ts#L21) [src/chatgpt-conversation.ts:21](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-conversation.ts#L21)
## Properties ## Properties
...@@ -51,7 +51,7 @@ Creates a new conversation wrapper around the ChatGPT API. ...@@ -51,7 +51,7 @@ Creates a new conversation wrapper around the ChatGPT API.
#### Defined in #### Defined in
[src/chatgpt-conversation.ts:10](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-conversation.ts#L10) [src/chatgpt-conversation.ts:10](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-conversation.ts#L10)
___ ___
...@@ -61,7 +61,7 @@ ___ ...@@ -61,7 +61,7 @@ ___
#### Defined in #### Defined in
[src/chatgpt-conversation.ts:11](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-conversation.ts#L11) [src/chatgpt-conversation.ts:11](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-conversation.ts#L11)
___ ___
...@@ -71,7 +71,7 @@ ___ ...@@ -71,7 +71,7 @@ ___
#### Defined in #### Defined in
[src/chatgpt-conversation.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-conversation.ts#L12) [src/chatgpt-conversation.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-conversation.ts#L12)
## Methods ## Methods
...@@ -104,4 +104,4 @@ The response from ChatGPT ...@@ -104,4 +104,4 @@ The response from ChatGPT
#### Defined in #### Defined in
[src/chatgpt-conversation.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/chatgpt-conversation.ts#L48) [src/chatgpt-conversation.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/chatgpt-conversation.ts#L48)
...@@ -66,7 +66,7 @@ node_modules/.pnpm/typescript@4.9.3/node_modules/typescript/lib/lib.es2022.error ...@@ -66,7 +66,7 @@ node_modules/.pnpm/typescript@4.9.3/node_modules/typescript/lib/lib.es2022.error
#### Defined in #### Defined in
[src/types.ts:295](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L295) [src/types.ts:298](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L298)
___ ___
...@@ -76,7 +76,7 @@ ___ ...@@ -76,7 +76,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:294](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L294) [src/types.ts:297](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L297)
___ ___
...@@ -86,7 +86,7 @@ ___ ...@@ -86,7 +86,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:292](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L292) [src/types.ts:295](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L295)
___ ___
...@@ -96,4 +96,4 @@ ___ ...@@ -96,4 +96,4 @@ ___
#### Defined in #### Defined in
[src/types.ts:293](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L293) [src/types.ts:296](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L296)
...@@ -17,6 +17,7 @@ ...@@ -17,6 +17,7 @@
- [ConversationJSONBody](modules.md#conversationjsonbody) - [ConversationJSONBody](modules.md#conversationjsonbody)
- [ConversationResponseEvent](modules.md#conversationresponseevent) - [ConversationResponseEvent](modules.md#conversationresponseevent)
- [Message](modules.md#message) - [Message](modules.md#message)
- [MessageActionType](modules.md#messageactiontype)
- [MessageContent](modules.md#messagecontent) - [MessageContent](modules.md#messagecontent)
- [MessageFeedbackJSONBody](modules.md#messagefeedbackjsonbody) - [MessageFeedbackJSONBody](modules.md#messagefeedbackjsonbody)
- [MessageFeedbackRating](modules.md#messagefeedbackrating) - [MessageFeedbackRating](modules.md#messagefeedbackrating)
...@@ -27,6 +28,7 @@ ...@@ -27,6 +28,7 @@
- [ModelsResult](modules.md#modelsresult) - [ModelsResult](modules.md#modelsresult)
- [ModerationsJSONBody](modules.md#moderationsjsonbody) - [ModerationsJSONBody](modules.md#moderationsjsonbody)
- [ModerationsJSONResult](modules.md#moderationsjsonresult) - [ModerationsJSONResult](modules.md#moderationsjsonresult)
- [OpenAIAuth](modules.md#openaiauth)
- [Prompt](modules.md#prompt) - [Prompt](modules.md#prompt)
- [PromptContent](modules.md#promptcontent) - [PromptContent](modules.md#promptcontent)
- [Role](modules.md#role) - [Role](modules.md#role)
...@@ -37,6 +39,8 @@ ...@@ -37,6 +39,8 @@
### Functions ### Functions
- [getBrowser](modules.md#getbrowser)
- [getOpenAIAuth](modules.md#getopenaiauth)
- [markdownToText](modules.md#markdowntotext) - [markdownToText](modules.md#markdowntotext)
## Type Aliases ## Type Aliases
...@@ -47,7 +51,7 @@ ...@@ -47,7 +51,7 @@
#### Defined in #### Defined in
[src/types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L109) [src/types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L109)
___ ___
...@@ -57,7 +61,7 @@ ___ ...@@ -57,7 +61,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L1) [src/types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L1)
___ ___
...@@ -79,7 +83,7 @@ https://chat.openapi.com/backend-api/conversation ...@@ -79,7 +83,7 @@ https://chat.openapi.com/backend-api/conversation
#### Defined in #### Defined in
[src/types.ts:134](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L134) [src/types.ts:134](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L134)
___ ___
...@@ -97,7 +101,7 @@ ___ ...@@ -97,7 +101,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L251) [src/types.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L251)
___ ___
...@@ -122,7 +126,17 @@ ___ ...@@ -122,7 +126,17 @@ ___
#### Defined in #### Defined in
[src/types.ts:257](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L257) [src/types.ts:257](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L257)
___
### MessageActionType
Ƭ **MessageActionType**: ``"next"`` \| ``"variant"``
#### Defined in
[src/types.ts:276](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L276)
___ ___
...@@ -139,7 +153,7 @@ ___ ...@@ -139,7 +153,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L270) [src/types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L270)
___ ___
...@@ -161,7 +175,7 @@ https://chat.openapi.com/backend-api/conversation/message_feedback ...@@ -161,7 +175,7 @@ https://chat.openapi.com/backend-api/conversation/message_feedback
#### Defined in #### Defined in
[src/types.ts:193](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L193) [src/types.ts:193](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L193)
___ ___
...@@ -171,7 +185,7 @@ ___ ...@@ -171,7 +185,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:249](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L249) [src/types.ts:249](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L249)
___ ___
...@@ -191,7 +205,7 @@ ___ ...@@ -191,7 +205,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:222](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L222) [src/types.ts:222](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L222)
___ ___
...@@ -201,7 +215,7 @@ ___ ...@@ -201,7 +215,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:220](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L220) [src/types.ts:220](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L220)
___ ___
...@@ -211,7 +225,7 @@ ___ ...@@ -211,7 +225,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:275](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L275) [src/types.ts:275](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L275)
___ ___
...@@ -229,7 +243,7 @@ ___ ...@@ -229,7 +243,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:77](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L77) [src/types.ts:77](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L77)
___ ___
...@@ -247,7 +261,7 @@ https://chat.openapi.com/backend-api/models ...@@ -247,7 +261,7 @@ https://chat.openapi.com/backend-api/models
#### Defined in #### Defined in
[src/types.ts:70](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L70) [src/types.ts:70](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L70)
___ ___
...@@ -266,7 +280,7 @@ https://chat.openapi.com/backend-api/moderations ...@@ -266,7 +280,7 @@ https://chat.openapi.com/backend-api/moderations
#### Defined in #### Defined in
[src/types.ts:97](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L97) [src/types.ts:97](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L97)
___ ___
...@@ -286,7 +300,29 @@ https://chat.openapi.com/backend-api/moderations ...@@ -286,7 +300,29 @@ https://chat.openapi.com/backend-api/moderations
#### Defined in #### Defined in
[src/types.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L114) [src/types.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L114)
___
### OpenAIAuth
Ƭ **OpenAIAuth**: `Object`
Represents everything that's required to pass into `ChatGPTAPI` in order
to authenticate with the unofficial ChatGPT API.
#### Type declaration
| Name | Type |
| :------ | :------ |
| `clearanceToken` | `string` |
| `cookies?` | `Record`<`string`, `Protocol.Network.Cookie`\> |
| `sessionToken` | `string` |
| `userAgent` | `string` |
#### Defined in
[src/openai-auth.ts:17](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/openai-auth.ts#L17)
___ ___
...@@ -304,7 +340,7 @@ ___ ...@@ -304,7 +340,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:161](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L161) [src/types.ts:161](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L161)
___ ___
...@@ -321,7 +357,7 @@ ___ ...@@ -321,7 +357,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:178](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L178) [src/types.ts:178](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L178)
___ ___
...@@ -331,7 +367,7 @@ ___ ...@@ -331,7 +367,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L3) [src/types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L3)
___ ___
...@@ -341,7 +377,7 @@ ___ ...@@ -341,7 +377,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:286](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L286) [src/types.ts:289](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L289)
___ ___
...@@ -354,7 +390,9 @@ ___ ...@@ -354,7 +390,9 @@ ___
| Name | Type | | Name | Type |
| :------ | :------ | | :------ | :------ |
| `abortSignal?` | `AbortSignal` | | `abortSignal?` | `AbortSignal` |
| `action?` | [`MessageActionType`](modules.md#messageactiontype) |
| `conversationId?` | `string` | | `conversationId?` | `string` |
| `messageId?` | `string` |
| `onConversationResponse?` | (`response`: [`ConversationResponseEvent`](modules.md#conversationresponseevent)) => `void` | | `onConversationResponse?` | (`response`: [`ConversationResponseEvent`](modules.md#conversationresponseevent)) => `void` |
| `onProgress?` | (`partialResponse`: `string`) => `void` | | `onProgress?` | (`partialResponse`: `string`) => `void` |
| `parentMessageId?` | `string` | | `parentMessageId?` | `string` |
...@@ -362,7 +400,7 @@ ___ ...@@ -362,7 +400,7 @@ ___
#### Defined in #### Defined in
[src/types.ts:277](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L277) [src/types.ts:278](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L278)
___ ___
...@@ -383,7 +421,7 @@ https://chat.openapi.com/api/auth/session ...@@ -383,7 +421,7 @@ https://chat.openapi.com/api/auth/session
#### Defined in #### Defined in
[src/types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L8) [src/types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L8)
___ ___
...@@ -405,10 +443,72 @@ ___ ...@@ -405,10 +443,72 @@ ___
#### Defined in #### Defined in
[src/types.ts:30](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/types.ts#L30) [src/types.ts:30](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/types.ts#L30)
## Functions ## Functions
### getBrowser
**getBrowser**(`launchOptions?`): `Promise`<`Browser`\>
Launches a non-puppeteer instance of Chrome. Note that in my testing, I wasn't
able to use the built-in `puppeteer` version of Chromium because Cloudflare
recognizes it and blocks access.
#### Parameters
| Name | Type |
| :------ | :------ |
| `launchOptions?` | `PuppeteerLaunchOptions` |
#### Returns
`Promise`<`Browser`\>
#### Defined in
[src/openai-auth.ts:127](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/openai-auth.ts#L127)
___
### getOpenAIAuth
**getOpenAIAuth**(`__namedParameters`): `Promise`<[`OpenAIAuth`](modules.md#openaiauth)\>
Bypasses OpenAI's use of Cloudflare to get the cookies required to use
ChatGPT. Uses Puppeteer with a stealth plugin under the hood.
If you pass `email` and `password`, then it will log into the account and
include a `sessionToken` in the response.
If you don't pass `email` and `password`, then it will just return a valid
`clearanceToken`.
This can be useful because `clearanceToken` expires after ~2 hours, whereas
`sessionToken` generally lasts much longer. We recommend renewing your
`clearanceToken` every hour or so and creating a new instance of `ChatGPTAPI`
with your updated credentials.
#### Parameters
| Name | Type |
| :------ | :------ |
| `__namedParameters` | `Object` |
| `__namedParameters.browser?` | `Browser` |
| `__namedParameters.email?` | `string` |
| `__namedParameters.password?` | `string` |
| `__namedParameters.timeoutMs?` | `number` |
#### Returns
`Promise`<[`OpenAIAuth`](modules.md#openaiauth)\>
#### Defined in
[src/openai-auth.ts:39](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/openai-auth.ts#L39)
___
### markdownToText ### markdownToText
**markdownToText**(`markdown?`): `string` **markdownToText**(`markdown?`): `string`
...@@ -425,4 +525,4 @@ ___ ...@@ -425,4 +525,4 @@ ___
#### Defined in #### Defined in
[src/utils.ts:4](https://github.com/transitive-bullshit/chatgpt-api/blob/8e1cde4/src/utils.ts#L4) [src/utils.ts:4](https://github.com/transitive-bullshit/chatgpt-api/blob/a48c177/src/utils.ts#L4)
This diff is collapsed.
...@@ -20,14 +20,13 @@ ...@@ -20,14 +20,13 @@
"build" "build"
], ],
"engines": { "engines": {
"node": ">=16.8" "node": ">=18"
}, },
"scripts": { "scripts": {
"build": "tsup", "build": "tsup",
"dev": "tsup --watch", "dev": "tsup --watch",
"clean": "del build", "clean": "del build",
"prebuild": "run-s clean", "prebuild": "run-s clean",
"postbuild": "[ -n CI ] && sed -i '' 's/await import(\"undici\")/null/' build/browser/index.js || echo 'skipping postbuild on CI'",
"predev": "run-s clean", "predev": "run-s clean",
"pretest": "run-s build", "pretest": "run-s build",
"docs": "typedoc", "docs": "typedoc",
...@@ -42,7 +41,10 @@ ...@@ -42,7 +41,10 @@
"p-timeout": "^6.0.0", "p-timeout": "^6.0.0",
"remark": "^14.0.2", "remark": "^14.0.2",
"strip-markdown": "^5.0.0", "strip-markdown": "^5.0.0",
"uuid": "^9.0.0" "delay": "^5.0.0",
"uuid": "^9.0.0",
"puppeteer-extra": "^3.3.4",
"puppeteer-extra-plugin-stealth": "^2.11.1"
}, },
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.0.0", "@trivago/prettier-plugin-sort-imports": "^4.0.0",
...@@ -50,7 +52,6 @@ ...@@ -50,7 +52,6 @@
"@types/uuid": "^9.0.0", "@types/uuid": "^9.0.0",
"ava": "^5.1.0", "ava": "^5.1.0",
"del-cli": "^5.0.0", "del-cli": "^5.0.0",
"delay": "^5.0.0",
"dotenv-safe": "^8.2.0", "dotenv-safe": "^8.2.0",
"husky": "^8.0.2", "husky": "^8.0.2",
"lint-staged": "^13.0.3", "lint-staged": "^13.0.3",
...@@ -58,16 +59,14 @@ ...@@ -58,16 +59,14 @@
"ora": "^6.1.2", "ora": "^6.1.2",
"prettier": "^2.8.0", "prettier": "^2.8.0",
"puppeteer": "^19.4.0", "puppeteer": "^19.4.0",
"puppeteer-extra": "^3.3.4",
"puppeteer-extra-plugin-stealth": "^2.11.1",
"tsup": "^6.5.0", "tsup": "^6.5.0",
"tsx": "^3.12.1", "tsx": "^3.12.1",
"typedoc": "^0.23.21", "typedoc": "^0.23.21",
"typedoc-plugin-markdown": "^3.13.6", "typedoc-plugin-markdown": "^3.13.6",
"typescript": "^4.9.3" "typescript": "^4.9.3"
}, },
"optionalDependencies": { "peerDependencies": {
"undici": "^5.13.0" "puppeteer": "*"
}, },
"lint-staged": { "lint-staged": {
"*.{ts,tsx}": [ "*.{ts,tsx}": [
......
This diff is collapsed.
This diff is collapsed.
...@@ -16,27 +16,35 @@ export class ChatGPTAPI { ...@@ -16,27 +16,35 @@ export class ChatGPTAPI {
protected _sessionToken: string protected _sessionToken: string
protected _clearanceToken: string protected _clearanceToken: string
protected _markdown: boolean protected _markdown: boolean
protected _debug: boolean
protected _apiBaseUrl: string protected _apiBaseUrl: string
protected _backendApiBaseUrl: string protected _backendApiBaseUrl: string
protected _userAgent: string protected _userAgent: string
protected _headers: Record<string, string> protected _headers: Record<string, string>
protected _user: types.User | null = null
// Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh // Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh
protected _accessTokenCache: ExpiryMap<string, string> protected _accessTokenCache: ExpiryMap<string, string>
protected _user: types.User | null = null
/** /**
* Creates a new client wrapper around the unofficial ChatGPT REST API. * Creates a new client wrapper around the unofficial ChatGPT REST API.
* *
* Note that your IP address and `userAgent` must match the same values that you used
* to obtain your `clearanceToken`.
*
* @param opts.sessionToken = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions) * @param opts.sessionToken = **Required** OpenAI session token which can be found in a valid session's cookies (see readme for instructions)
* @param opts.clearanceToken = **Required** Cloudflare `cf_clearance` cookie value (see readme for instructions)
* @param apiBaseUrl - Optional override; the base URL for ChatGPT webapp's API (`/api`) * @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 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 * @param userAgent - Optional override; the `user-agent` header to use with ChatGPT requests
* @param accessTokenTTL - Optional override; how long in milliseconds access tokens should last before being forcefully refreshed * @param accessTokenTTL - Optional override; how long in milliseconds access tokens should last before being forcefully refreshed
* @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
*/ */
constructor(opts: { constructor(opts: {
sessionToken: string sessionToken: string
clearanceToken: string clearanceToken: string
/** @defaultValue `true` **/ /** @defaultValue `true` **/
...@@ -48,15 +56,20 @@ export class ChatGPTAPI { ...@@ -48,15 +56,20 @@ export class ChatGPTAPI {
/** @defaultValue `'https://chat.openai.com/backend-api'` **/ /** @defaultValue `'https://chat.openai.com/backend-api'` **/
backendApiBaseUrl?: string backendApiBaseUrl?: string
/** @defaultValue `'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36'` **/ /** @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'` **/
userAgent?: string userAgent?: string
/** @defaultValue 1 hour */ /** @defaultValue 1 hour **/
accessTokenTTL?: number accessTokenTTL?: number
/** @defaultValue `undefined` **/
accessToken?: string accessToken?: string
/** @defaultValue `undefined` **/
headers?: Record<string, string> headers?: Record<string, string>
/** @defaultValue `false` **/
debug?: boolean
}) { }) {
const { const {
sessionToken, sessionToken,
...@@ -67,12 +80,14 @@ export class ChatGPTAPI { ...@@ -67,12 +80,14 @@ export class ChatGPTAPI {
userAgent = USER_AGENT, userAgent = USER_AGENT,
accessTokenTTL = 60 * 60000, // 1 hour accessTokenTTL = 60 * 60000, // 1 hour
accessToken, accessToken,
headers headers,
debug = false
} = opts } = opts
this._sessionToken = sessionToken this._sessionToken = sessionToken
this._clearanceToken = clearanceToken this._clearanceToken = clearanceToken
this._markdown = !!markdown this._markdown = !!markdown
this._debug = !!debug
this._apiBaseUrl = apiBaseUrl this._apiBaseUrl = apiBaseUrl
this._backendApiBaseUrl = backendApiBaseUrl this._backendApiBaseUrl = backendApiBaseUrl
this._userAgent = userAgent this._userAgent = userAgent
...@@ -112,6 +127,21 @@ export class ChatGPTAPI { ...@@ -112,6 +127,21 @@ export class ChatGPTAPI {
return this._user return this._user
} }
/** 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
}
/** /**
* Sends a message to ChatGPT, waits for the response to resolve, and returns * Sends a message to ChatGPT, waits for the response to resolve, and returns
* the response. * the response.
...@@ -124,6 +154,8 @@ export class ChatGPTAPI { ...@@ -124,6 +154,8 @@ export class ChatGPTAPI {
* @param message - The prompt message to send * @param message - The prompt message to send
* @param opts.conversationId - Optional ID of a conversation to continue * @param opts.conversationId - Optional ID of a conversation to continue
* @param opts.parentMessageId - Optional ID of the previous message in the conversation * @param opts.parentMessageId - Optional ID of the previous message in the conversation
* @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`)
* @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout) * @param opts.timeoutMs - Optional timeout in milliseconds (defaults to no timeout)
* @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated * @param opts.onProgress - Optional callback which will be invoked every time the partial response is updated
* @param opts.onConversationResponse - Optional callback which will be invoked every time the partial response is updated with the full conversation response * @param opts.onConversationResponse - Optional callback which will be invoked every time the partial response is updated with the full conversation response
...@@ -138,6 +170,8 @@ export class ChatGPTAPI { ...@@ -138,6 +170,8 @@ export class ChatGPTAPI {
const { const {
conversationId, conversationId,
parentMessageId = uuidv4(), parentMessageId = uuidv4(),
messageId = uuidv4(),
action = 'next',
timeoutMs, timeoutMs,
onProgress, onProgress,
onConversationResponse onConversationResponse
...@@ -154,10 +188,10 @@ export class ChatGPTAPI { ...@@ -154,10 +188,10 @@ export class ChatGPTAPI {
const accessToken = await this.refreshAccessToken() const accessToken = await this.refreshAccessToken()
const body: types.ConversationJSONBody = { const body: types.ConversationJSONBody = {
action: 'next', action,
messages: [ messages: [
{ {
id: uuidv4(), id: messageId,
role: 'user', role: 'user',
content: { content: {
content_type: 'text', content_type: 'text',
...@@ -173,19 +207,25 @@ export class ChatGPTAPI { ...@@ -173,19 +207,25 @@ export class ChatGPTAPI {
body.conversation_id = conversationId body.conversation_id = conversationId
} }
const url = `${this._backendApiBaseUrl}/conversation`
let response = '' let response = ''
const responseP = new Promise<string>((resolve, reject) => { const responseP = new Promise<string>((resolve, reject) => {
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 })
}
fetchSSE(url, { fetchSSE(url, {
method: 'POST', method: 'POST',
headers: { headers,
...this._headers,
Authorization: `Bearer ${accessToken}`,
Accept: 'text/event-stream',
'Content-Type': 'application/json',
Cookie: `cf_clearance=${this._clearanceToken}`
},
body: JSON.stringify(body), body: JSON.stringify(body),
signal: abortSignal, signal: abortSignal,
onMessage: (data: string) => { onMessage: (data: string) => {
...@@ -222,7 +262,23 @@ export class ChatGPTAPI { ...@@ -222,7 +262,23 @@ export class ChatGPTAPI {
reject(err) reject(err)
} }
} }
}).catch(reject) }).catch((err) => {
const errMessageL = err.toString().toLowerCase()
if (
response &&
(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.
return resolve(response)
} else {
return reject(err)
}
})
}) })
if (timeoutMs) { if (timeoutMs) {
...@@ -282,14 +338,18 @@ export class ChatGPTAPI { ...@@ -282,14 +338,18 @@ export class ChatGPTAPI {
let response: Response let response: Response
try { try {
const url = `${this._apiBaseUrl}/auth/session`
const headers = { const headers = {
...this._headers, ...this._headers,
cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`, cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`,
accept: '*/*' accept: '*/*'
} }
console.log(`${this._apiBaseUrl}/auth/session`, headers)
const res = await fetch(`${this._apiBaseUrl}/auth/session`, { if (this._debug) {
console.log('GET', url, headers)
}
const res = await fetch(url, {
headers headers
}).then((r) => { }).then((r) => {
response = r response = r
...@@ -339,6 +399,10 @@ export class ChatGPTAPI { ...@@ -339,6 +399,10 @@ export class ChatGPTAPI {
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken) this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
return accessToken return accessToken
} catch (err: any) { } catch (err: any) {
if (this._debug) {
console.error(err)
}
const error = new types.ChatGPTError( const error = new types.ChatGPTError(
`ChatGPT failed to refresh auth token. ${err.toString()}` `ChatGPT failed to refresh auth token. ${err.toString()}`
) )
......
/// <reference lib="dom" /> /// <reference lib="dom" />
let _undici: any
// Use `undici` for node.js 16 and 17
// Use `fetch` for node.js >= 18 // Use `fetch` for node.js >= 18
// Use `fetch` for all other environments, including browsers // Use `fetch` for all other environments, including browsers
// NOTE: The top-level await is removed in a `postbuild` npm script for the const fetch = globalThis.fetch
// browser build
const fetch =
globalThis.fetch ??
async function undiciFetchWrapper(
...args: Parameters<typeof globalThis.fetch>
): Promise<Response> {
if (!_undici) {
_undici = await import('undici')
}
if (typeof _undici?.fetch !== 'function') {
throw new Error(
'Invalid undici installation; please make sure undici is installed correctly in your node_modules. Note that this package requires Node.js >= 16.8'
)
}
return _undici.fetch(...args) if (typeof fetch !== 'function') {
} throw new Error(
'Invalid environment: global fetch not defined; `chatgpt` requires Node.js >= 18 at the moment due to Cloudflare protections'
)
}
export { fetch } export { fetch }
...@@ -2,3 +2,4 @@ export * from './chatgpt-api' ...@@ -2,3 +2,4 @@ export * from './chatgpt-api'
export * from './chatgpt-conversation' export * from './chatgpt-conversation'
export * from './types' export * from './types'
export * from './utils' export * from './utils'
export * from './openai-auth'
...@@ -10,7 +10,11 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth' ...@@ -10,7 +10,11 @@ import StealthPlugin from 'puppeteer-extra-plugin-stealth'
puppeteer.use(StealthPlugin()) puppeteer.use(StealthPlugin())
export type OpenAIAuthInfo = { /**
* Represents everything that's required to pass into `ChatGPTAPI` in order
* to authenticate with the unofficial ChatGPT API.
*/
export type OpenAIAuth = {
userAgent: string userAgent: string
clearanceToken: string clearanceToken: string
sessionToken: string sessionToken: string
...@@ -20,18 +24,29 @@ export type OpenAIAuthInfo = { ...@@ -20,18 +24,29 @@ export type OpenAIAuthInfo = {
/** /**
* Bypasses OpenAI's use of Cloudflare to get the cookies required to use * Bypasses OpenAI's use of Cloudflare to get the cookies required to use
* ChatGPT. Uses Puppeteer with a stealth plugin under the hood. * ChatGPT. Uses Puppeteer with a stealth plugin under the hood.
*
* If you pass `email` and `password`, then it will log into the account and
* include a `sessionToken` in the response.
*
* If you don't pass `email` and `password`, then it will just return a valid
* `clearanceToken`.
*
* This can be useful because `clearanceToken` expires after ~2 hours, whereas
* `sessionToken` generally lasts much longer. We recommend renewing your
* `clearanceToken` every hour or so and creating a new instance of `ChatGPTAPI`
* with your updated credentials.
*/ */
export async function getOpenAIAuthInfo({ export async function getOpenAIAuth({
email, email,
password, password,
timeout = 2 * 60 * 1000, timeoutMs = 2 * 60 * 1000,
browser browser
}: { }: {
email: string email?: string
password: string password?: string
timeout?: number timeoutMs?: number
browser?: Browser browser?: Browser
}): Promise<OpenAIAuthInfo> { }): Promise<OpenAIAuth> {
let page: Page let page: Page
let origBrowser = browser let origBrowser = browser
...@@ -42,12 +57,18 @@ export async function getOpenAIAuthInfo({ ...@@ -42,12 +57,18 @@ export async function getOpenAIAuthInfo({
const userAgent = await browser.userAgent() const userAgent = await browser.userAgent()
page = (await browser.pages())[0] || (await browser.newPage()) page = (await browser.pages())[0] || (await browser.newPage())
page.setDefaultTimeout(timeout) page.setDefaultTimeout(timeoutMs)
await page.goto('https://chat.openai.com/auth/login') await page.goto('https://chat.openai.com/auth/login')
await page.waitForSelector('#__next .btn-primary', { timeout })
// NOTE: this is where you may encounter a CAPTCHA
await page.waitForSelector('#__next .btn-primary', { timeout: timeoutMs })
// once we get to this point, the Cloudflare cookies are available
await delay(1000) await delay(1000)
// login as well (optional)
if (email && password) { if (email && password) {
await Promise.all([ await Promise.all([
page.click('#__next .btn-primary'), page.click('#__next .btn-primary'),
...@@ -73,7 +94,7 @@ export async function getOpenAIAuthInfo({ ...@@ -73,7 +94,7 @@ export async function getOpenAIAuthInfo({
{} {}
) )
const authInfo: OpenAIAuthInfo = { const authInfo: OpenAIAuth = {
userAgent, userAgent,
clearanceToken: cookies['cf_clearance']?.value, clearanceToken: cookies['cf_clearance']?.value,
sessionToken: cookies['__Secure-next-auth.session-token']?.value, sessionToken: cookies['__Secure-next-auth.session-token']?.value,
...@@ -83,7 +104,7 @@ export async function getOpenAIAuthInfo({ ...@@ -83,7 +104,7 @@ export async function getOpenAIAuthInfo({
return authInfo return authInfo
} catch (err) { } catch (err) {
console.error(err) console.error(err)
throw null throw err
} finally { } finally {
if (origBrowser) { if (origBrowser) {
if (page) { if (page) {
...@@ -98,6 +119,11 @@ export async function getOpenAIAuthInfo({ ...@@ -98,6 +119,11 @@ export async function getOpenAIAuthInfo({
} }
} }
/**
* Launches a non-puppeteer instance of Chrome. Note that in my testing, I wasn't
* able to use the built-in `puppeteer` version of Chromium because Cloudflare
* recognizes it and blocks access.
*/
export async function getBrowser(launchOptions?: PuppeteerLaunchOptions) { export async function getBrowser(launchOptions?: PuppeteerLaunchOptions) {
const macChromePath = const macChromePath =
'/Applications/Google Chrome.app/Contents/MacOS/Google Chrome' '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
......
...@@ -273,10 +273,13 @@ export type MessageContent = { ...@@ -273,10 +273,13 @@ export type MessageContent = {
} }
export type MessageMetadata = any export type MessageMetadata = any
export type MessageActionType = 'next' | 'variant'
export type SendMessageOptions = { export type SendMessageOptions = {
conversationId?: string conversationId?: string
parentMessageId?: string parentMessageId?: string
messageId?: string
action?: MessageActionType
timeoutMs?: number timeoutMs?: number
onProgress?: (partialResponse: string) => void onProgress?: (partialResponse: string) => void
onConversationResponse?: (response: ConversationResponseEvent) => void onConversationResponse?: (response: ConversationResponseEvent) => void
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment