kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
Merge branch 'main' into patch-2
commit
c746d70aef
|
@ -9,5 +9,5 @@
|
|||
# ChatGPT
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# see the readme for how to find this
|
||||
SESSION_TOKEN=
|
||||
OPENAI_EMAIL=
|
||||
OPENAI_PASSWORD=
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
name: Bug Report
|
||||
description: Create a bug report
|
||||
labels: ['template: bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: 'If you follow all of the instructions carefully, and your account / IP hasn't been permanently flagged by Cloudflare / OpenAI, then you shouldn't ever get a 403 at this point.'
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: 'My Twitter bot has been running for the past 2 days without a single 403, and others have been able to get it working on Discord. Although it can take a bit of effort to get working, once you have it working, you're set.'
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Verify latest release
|
||||
description: '`chatgpt@latest` is the latest version of `chatgpt`. Some issues may already be fixed in the latest version, so please verify that your issue reproduces before opening a new issue.'
|
||||
options:
|
||||
- label: I verified that the issue exists in the latest `chatgpt` release
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Verify webapp is working
|
||||
description: 'Verify that the [ChatGPT webapp](https://chat.openai.com/) is working for you locally using the same browser and account.'
|
||||
options:
|
||||
- label: I verify that the ChatGPT webapp is working properly for this account.
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Verify [restrictions](https://github.com/transitive-bullshit/chatgpt-api#restrictions)
|
||||
description: 'Verify that you\'ve double-checked all of the restrictions listed in the readme.'
|
||||
options:
|
||||
- label: I verify that I've double-checked all of the restrictions.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Provide environment information
|
||||
description: Please enter `node --version`, `Chrome` version, OS, OS version.
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
attributes:
|
||||
label: Link to the code that reproduces this issue
|
||||
description: A link to a GitHub repository you own with a minimal reproduction. Minimal reproductions should be forked from this repo and should include only changes that contribute to the issue.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: To Reproduce
|
||||
description: Steps to reproduce the behavior, please provide a clear description of how to reproduce the issue, based on the linked minimal reproduction. Screenshots can be provided in the issue body below. If using code blocks, make sure that [syntax highlighting is correct](https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-and-highlighting-code-blocks#syntax-highlighting) and double check that the rendered preview is not broken. **Is `getOpenAIAuth` returning successfully? If so, please print out its values.** If not, please describe what happens in the browser when trying to auth.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the Bug
|
||||
description: A clear and concise description of what the bug is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A clear and concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Before posting the issue go through the steps you've written down to make sure the steps provided are detailed and clear.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: Contributors should be able to follow the steps provided in order to reproduce the bug.
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: These steps are used to improve the docs to ensure the same issue does not happen again. Thanks in advance!
|
|
@ -0,0 +1,5 @@
|
|||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Ask a question
|
||||
url: https://github.com/vercel/next.js/discussions
|
||||
about: Ask questions and discuss with other community members
|
|
@ -1,7 +1,7 @@
|
|||
import dotenv from 'dotenv-safe'
|
||||
import { oraPromise } from 'ora'
|
||||
|
||||
import { ChatGPTAPI } from '.'
|
||||
import { ChatGPTAPI, getOpenAIAuth } from '../src'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
|
@ -13,7 +13,15 @@ dotenv.config()
|
|||
* ```
|
||||
*/
|
||||
async function main() {
|
||||
const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN })
|
||||
const email = process.env.OPENAI_EMAIL
|
||||
const password = process.env.OPENAI_PASSWORD
|
||||
|
||||
const authInfo = await getOpenAIAuth({
|
||||
email,
|
||||
password
|
||||
})
|
||||
|
||||
const api = new ChatGPTAPI({ ...authInfo })
|
||||
await api.ensureAuth()
|
||||
|
||||
const conversation = api.getConversation()
|
|
@ -1,7 +1,7 @@
|
|||
import dotenv from 'dotenv-safe'
|
||||
import { oraPromise } from 'ora'
|
||||
|
||||
import { ChatGPTAPI } from '.'
|
||||
import { ChatGPTAPI, getOpenAIAuth } from '../src'
|
||||
|
||||
dotenv.config()
|
||||
|
||||
|
@ -13,7 +13,15 @@ dotenv.config()
|
|||
* ```
|
||||
*/
|
||||
async function main() {
|
||||
const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN })
|
||||
const email = process.env.OPENAI_EMAIL
|
||||
const password = process.env.OPENAI_PASSWORD
|
||||
|
||||
const authInfo = await getOpenAIAuth({
|
||||
email,
|
||||
password
|
||||
})
|
||||
|
||||
const api = new ChatGPTAPI({ ...authInfo })
|
||||
await api.ensureAuth()
|
||||
|
||||
const prompt =
|
|
@ -8,6 +8,13 @@
|
|||
|
||||
- [constructor](ChatGPTAPI.md#constructor)
|
||||
|
||||
### Accessors
|
||||
|
||||
- [clearanceToken](ChatGPTAPI.md#clearancetoken)
|
||||
- [sessionToken](ChatGPTAPI.md#sessiontoken)
|
||||
- [user](ChatGPTAPI.md#user)
|
||||
- [userAgent](ChatGPTAPI.md#useragent)
|
||||
|
||||
### Methods
|
||||
|
||||
- [ensureAuth](ChatGPTAPI.md#ensureauth)
|
||||
|
@ -24,21 +31,92 @@
|
|||
|
||||
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
|
||||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `opts` | `Object` | - |
|
||||
| `opts.accessTokenTTL?` | `number` | **`Default Value`** 60000 (60 seconds) |
|
||||
| `opts.accessToken?` | `string` | **`Default Value`** `undefined` * |
|
||||
| `opts.accessTokenTTL?` | `number` | **`Default Value`** 1 hour * |
|
||||
| `opts.apiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/api'` * |
|
||||
| `opts.backendApiBaseUrl?` | `string` | **`Default Value`** `'https://chat.openai.com/backend-api'` * |
|
||||
| `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.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
|
||||
|
||||
[chatgpt-api.ts:35](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-api.ts#L35)
|
||||
[src/chatgpt-api.ts:45](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L45)
|
||||
|
||||
## 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/c257286/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/c257286/src/chatgpt-api.ts#L131)
|
||||
|
||||
___
|
||||
|
||||
### user
|
||||
|
||||
• `get` **user**(): [`User`](../modules.md#user)
|
||||
|
||||
Gets the currently signed-in user, if authenticated, `null` otherwise.
|
||||
|
||||
#### Returns
|
||||
|
||||
[`User`](../modules.md#user)
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/chatgpt-api.ts:126](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/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/c257286/src/chatgpt-api.ts#L141)
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -55,7 +133,7 @@ is still valid.
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-api.ts:221](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-api.ts#L221)
|
||||
[src/chatgpt-api.ts:319](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L319)
|
||||
|
||||
___
|
||||
|
||||
|
@ -82,7 +160,7 @@ The new conversation instance
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-api.ts:285](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-api.ts#L285)
|
||||
[src/chatgpt-api.ts:425](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L425)
|
||||
|
||||
___
|
||||
|
||||
|
@ -99,7 +177,7 @@ the token fails.
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-api.ts:208](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-api.ts#L208)
|
||||
[src/chatgpt-api.ts:306](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L306)
|
||||
|
||||
___
|
||||
|
||||
|
@ -125,7 +203,7 @@ A valid access token
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-api.ts:235](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-api.ts#L235)
|
||||
[src/chatgpt-api.ts:333](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L333)
|
||||
|
||||
___
|
||||
|
||||
|
@ -156,4 +234,4 @@ The response from ChatGPT
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-api.ts:94](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-api.ts#L94)
|
||||
[src/chatgpt-api.ts:166](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L166)
|
||||
|
|
|
@ -41,7 +41,7 @@ Creates a new conversation wrapper around the ChatGPT API.
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-conversation.ts:21](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-conversation.ts#L21)
|
||||
[src/chatgpt-conversation.ts:21](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L21)
|
||||
|
||||
## Properties
|
||||
|
||||
|
@ -51,7 +51,7 @@ Creates a new conversation wrapper around the ChatGPT API.
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-conversation.ts:10](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-conversation.ts#L10)
|
||||
[src/chatgpt-conversation.ts:10](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L10)
|
||||
|
||||
___
|
||||
|
||||
|
@ -61,7 +61,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-conversation.ts:11](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-conversation.ts#L11)
|
||||
[src/chatgpt-conversation.ts:11](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L11)
|
||||
|
||||
___
|
||||
|
||||
|
@ -71,7 +71,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-conversation.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-conversation.ts#L12)
|
||||
[src/chatgpt-conversation.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L12)
|
||||
|
||||
## Methods
|
||||
|
||||
|
@ -104,4 +104,4 @@ The response from ChatGPT
|
|||
|
||||
#### Defined in
|
||||
|
||||
[chatgpt-conversation.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/chatgpt-conversation.ts#L48)
|
||||
[src/chatgpt-conversation.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L48)
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
[chatgpt](../readme.md) / [Exports](../modules.md) / ChatGPTError
|
||||
|
||||
# Class: ChatGPTError
|
||||
|
||||
## Hierarchy
|
||||
|
||||
- `Error`
|
||||
|
||||
↳ **`ChatGPTError`**
|
||||
|
||||
## Table of contents
|
||||
|
||||
### Constructors
|
||||
|
||||
- [constructor](ChatGPTError.md#constructor)
|
||||
|
||||
### Properties
|
||||
|
||||
- [originalError](ChatGPTError.md#originalerror)
|
||||
- [response](ChatGPTError.md#response)
|
||||
- [statusCode](ChatGPTError.md#statuscode)
|
||||
- [statusText](ChatGPTError.md#statustext)
|
||||
|
||||
## Constructors
|
||||
|
||||
### constructor
|
||||
|
||||
• **new ChatGPTError**(`message?`)
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `message?` | `string` |
|
||||
|
||||
#### Inherited from
|
||||
|
||||
Error.constructor
|
||||
|
||||
#### Defined in
|
||||
|
||||
node_modules/.pnpm/typescript@4.9.3/node_modules/typescript/lib/lib.es5.d.ts:1059
|
||||
|
||||
• **new ChatGPTError**(`message?`, `options?`)
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `message?` | `string` |
|
||||
| `options?` | `ErrorOptions` |
|
||||
|
||||
#### Inherited from
|
||||
|
||||
Error.constructor
|
||||
|
||||
#### Defined in
|
||||
|
||||
node_modules/.pnpm/typescript@4.9.3/node_modules/typescript/lib/lib.es2022.error.d.ts:30
|
||||
|
||||
## Properties
|
||||
|
||||
### originalError
|
||||
|
||||
• `Optional` **originalError**: `Error`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/types.ts:298](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L298)
|
||||
|
||||
___
|
||||
|
||||
### response
|
||||
|
||||
• `Optional` **response**: `Response`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/types.ts:297](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L297)
|
||||
|
||||
___
|
||||
|
||||
### statusCode
|
||||
|
||||
• `Optional` **statusCode**: `number`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/types.ts:295](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L295)
|
||||
|
||||
___
|
||||
|
||||
### statusText
|
||||
|
||||
• `Optional` **statusText**: `string`
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/types.ts:296](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L296)
|
153
docs/modules.md
153
docs/modules.md
|
@ -8,6 +8,7 @@
|
|||
|
||||
- [ChatGPTAPI](classes/ChatGPTAPI.md)
|
||||
- [ChatGPTConversation](classes/ChatGPTConversation.md)
|
||||
- [ChatGPTError](classes/ChatGPTError.md)
|
||||
|
||||
### Type Aliases
|
||||
|
||||
|
@ -16,6 +17,7 @@
|
|||
- [ConversationJSONBody](modules.md#conversationjsonbody)
|
||||
- [ConversationResponseEvent](modules.md#conversationresponseevent)
|
||||
- [Message](modules.md#message)
|
||||
- [MessageActionType](modules.md#messageactiontype)
|
||||
- [MessageContent](modules.md#messagecontent)
|
||||
- [MessageFeedbackJSONBody](modules.md#messagefeedbackjsonbody)
|
||||
- [MessageFeedbackRating](modules.md#messagefeedbackrating)
|
||||
|
@ -26,6 +28,7 @@
|
|||
- [ModelsResult](modules.md#modelsresult)
|
||||
- [ModerationsJSONBody](modules.md#moderationsjsonbody)
|
||||
- [ModerationsJSONResult](modules.md#moderationsjsonresult)
|
||||
- [OpenAIAuth](modules.md#openaiauth)
|
||||
- [Prompt](modules.md#prompt)
|
||||
- [PromptContent](modules.md#promptcontent)
|
||||
- [Role](modules.md#role)
|
||||
|
@ -36,6 +39,8 @@
|
|||
|
||||
### Functions
|
||||
|
||||
- [getBrowser](modules.md#getbrowser)
|
||||
- [getOpenAIAuth](modules.md#getopenaiauth)
|
||||
- [markdownToText](modules.md#markdowntotext)
|
||||
|
||||
## Type Aliases
|
||||
|
@ -46,7 +51,7 @@
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L109)
|
||||
[src/types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L109)
|
||||
|
||||
___
|
||||
|
||||
|
@ -56,7 +61,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L1)
|
||||
[src/types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L1)
|
||||
|
||||
___
|
||||
|
||||
|
@ -78,7 +83,7 @@ https://chat.openapi.com/backend-api/conversation
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:134](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L134)
|
||||
[src/types.ts:134](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L134)
|
||||
|
||||
___
|
||||
|
||||
|
@ -96,7 +101,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L251)
|
||||
[src/types.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L251)
|
||||
|
||||
___
|
||||
|
||||
|
@ -121,7 +126,17 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:257](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L257)
|
||||
[src/types.ts:257](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L257)
|
||||
|
||||
___
|
||||
|
||||
### MessageActionType
|
||||
|
||||
Ƭ **MessageActionType**: ``"next"`` \| ``"variant"``
|
||||
|
||||
#### Defined in
|
||||
|
||||
[src/types.ts:276](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L276)
|
||||
|
||||
___
|
||||
|
||||
|
@ -138,7 +153,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L270)
|
||||
[src/types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L270)
|
||||
|
||||
___
|
||||
|
||||
|
@ -160,7 +175,7 @@ https://chat.openapi.com/backend-api/conversation/message_feedback
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:193](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L193)
|
||||
[src/types.ts:193](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L193)
|
||||
|
||||
___
|
||||
|
||||
|
@ -170,7 +185,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:249](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L249)
|
||||
[src/types.ts:249](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L249)
|
||||
|
||||
___
|
||||
|
||||
|
@ -190,7 +205,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:222](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L222)
|
||||
[src/types.ts:222](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L222)
|
||||
|
||||
___
|
||||
|
||||
|
@ -200,7 +215,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:220](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L220)
|
||||
[src/types.ts:220](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L220)
|
||||
|
||||
___
|
||||
|
||||
|
@ -210,7 +225,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:275](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L275)
|
||||
[src/types.ts:275](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L275)
|
||||
|
||||
___
|
||||
|
||||
|
@ -228,7 +243,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:77](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L77)
|
||||
[src/types.ts:77](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L77)
|
||||
|
||||
___
|
||||
|
||||
|
@ -246,7 +261,7 @@ https://chat.openapi.com/backend-api/models
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:70](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L70)
|
||||
[src/types.ts:70](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L70)
|
||||
|
||||
___
|
||||
|
||||
|
@ -265,7 +280,7 @@ https://chat.openapi.com/backend-api/moderations
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:97](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L97)
|
||||
[src/types.ts:97](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L97)
|
||||
|
||||
___
|
||||
|
||||
|
@ -285,7 +300,29 @@ https://chat.openapi.com/backend-api/moderations
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L114)
|
||||
[src/types.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/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/c257286/src/openai-auth.ts#L17)
|
||||
|
||||
___
|
||||
|
||||
|
@ -303,7 +340,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:161](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L161)
|
||||
[src/types.ts:161](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L161)
|
||||
|
||||
___
|
||||
|
||||
|
@ -320,7 +357,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:178](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L178)
|
||||
[src/types.ts:178](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L178)
|
||||
|
||||
___
|
||||
|
||||
|
@ -330,7 +367,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L3)
|
||||
[src/types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L3)
|
||||
|
||||
___
|
||||
|
||||
|
@ -340,7 +377,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:286](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L286)
|
||||
[src/types.ts:289](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L289)
|
||||
|
||||
___
|
||||
|
||||
|
@ -353,7 +390,9 @@ ___
|
|||
| Name | Type |
|
||||
| :------ | :------ |
|
||||
| `abortSignal?` | `AbortSignal` |
|
||||
| `action?` | [`MessageActionType`](modules.md#messageactiontype) |
|
||||
| `conversationId?` | `string` |
|
||||
| `messageId?` | `string` |
|
||||
| `onConversationResponse?` | (`response`: [`ConversationResponseEvent`](modules.md#conversationresponseevent)) => `void` |
|
||||
| `onProgress?` | (`partialResponse`: `string`) => `void` |
|
||||
| `parentMessageId?` | `string` |
|
||||
|
@ -361,7 +400,7 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:277](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L277)
|
||||
[src/types.ts:278](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L278)
|
||||
|
||||
___
|
||||
|
||||
|
@ -382,7 +421,7 @@ https://chat.openapi.com/api/auth/session
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L8)
|
||||
[src/types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L8)
|
||||
|
||||
___
|
||||
|
||||
|
@ -394,9 +433,9 @@ ___
|
|||
|
||||
| Name | Type | Description |
|
||||
| :------ | :------ | :------ |
|
||||
| `email` | `string` | Email of the user |
|
||||
| `features` | `string`[] \| [] | Features the user is in |
|
||||
| `groups` | `string`[] \| [] | Groups the user is in |
|
||||
| `email?` | `string` | Email of the user |
|
||||
| `features` | `string`[] | Features the user is in |
|
||||
| `groups` | `string`[] | Groups the user is in |
|
||||
| `id` | `string` | ID of the user |
|
||||
| `image` | `string` | Image of the user |
|
||||
| `name` | `string` | Name of the user |
|
||||
|
@ -404,10 +443,72 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[types.ts:30](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/types.ts#L30)
|
||||
[src/types.ts:30](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L30)
|
||||
|
||||
## 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/c257286/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/c257286/src/openai-auth.ts#L39)
|
||||
|
||||
___
|
||||
|
||||
### markdownToText
|
||||
|
||||
▸ **markdownToText**(`markdown?`): `string`
|
||||
|
@ -424,4 +525,4 @@ ___
|
|||
|
||||
#### Defined in
|
||||
|
||||
[utils.ts:4](https://github.com/transitive-bullshit/chatgpt-api/blob/20c376e/src/utils.ts#L4)
|
||||
[src/utils.ts:4](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/utils.ts#L4)
|
||||
|
|
172
docs/readme.md
172
docs/readme.md
|
@ -1,5 +1,22 @@
|
|||
chatgpt / [Exports](modules.md)
|
||||
|
||||
# Update December 12, 2022 <!-- omit in toc -->
|
||||
|
||||
Yesterday, OpenAI added additional Cloudflare protections that make it more difficult to access the unofficial API.
|
||||
|
||||
This package has been updated to use Puppeteer to automatically log in to ChatGPT and extract the necessary auth credentials. 🔥
|
||||
|
||||
To use the updated version, **make sure you're using the latest version of this package and Node.js >= 18**. Then update your code following the examples below, paying special attention to the sections on [Authentication](#authentication) and [Restrictions](#restrictions).
|
||||
|
||||
We're working hard to improve this process (especially CAPTCHA automation). Keep in mind that this package will be updated to use the official API as soon as it's released, so things should get much easier over time. 💪
|
||||
|
||||
Lastly, please consider starring this repo and <a href="https://twitter.com/transitive_bs">following me on twitter <img src="https://storage.googleapis.com/saasify-assets/twitter-logo.svg" alt="twitter" height="24px" align="center"></a> to help support the project.
|
||||
|
||||
Thanks && cheers,
|
||||
Travis
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<img alt="Example usage" src="/media/demo.gif">
|
||||
</p>
|
||||
|
@ -15,7 +32,8 @@ chatgpt / [Exports](modules.md)
|
|||
- [Usage](#usage)
|
||||
- [Docs](#docs)
|
||||
- [Demos](#demos)
|
||||
- [Session Tokens](#session-tokens)
|
||||
- [Authentication](#authentication)
|
||||
- [Restrictions](#restrictions)
|
||||
- [Projects](#projects)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Credits](#credits)
|
||||
|
@ -30,26 +48,29 @@ You can use it to start building projects powered by ChatGPT like chatbots, webs
|
|||
## Install
|
||||
|
||||
```bash
|
||||
npm install chatgpt
|
||||
npm install chatgpt puppeteer
|
||||
```
|
||||
|
||||
`puppeteer` is an optional peer dependency used to automate bypassing the Cloudflare protections via `getOpenAIAuth`. The main API wrapper uses `fetch` directly.
|
||||
|
||||
## Usage
|
||||
|
||||
```ts
|
||||
import { ChatGPTAPI } from 'chatgpt'
|
||||
import { ChatGPTAPI, getOpenAIAuth } from 'chatgpt'
|
||||
|
||||
async function example() {
|
||||
// sessionToken is required; see below for details
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
// use puppeteer to bypass cloudflare (headful because of captchas)
|
||||
const openAIAuth = await getOpenAIAuth({
|
||||
email: process.env.OPENAI_EMAIL,
|
||||
password: process.env.OPENAI_PASSWORD
|
||||
})
|
||||
|
||||
// ensure the API is properly authenticated
|
||||
const api = new ChatGPTAPI({ ...openAIAuth })
|
||||
await api.ensureAuth()
|
||||
|
||||
// send a message and wait for the response
|
||||
const response = await api.sendMessage(
|
||||
'Write a python version of bubble sort. Do not include example usage.'
|
||||
'Write a python version of bubble sort.'
|
||||
)
|
||||
|
||||
// response is a markdown-formatted string
|
||||
|
@ -57,37 +78,32 @@ async function example() {
|
|||
}
|
||||
```
|
||||
|
||||
The default ChatGPT responses are formatted as markdown. If you want to work with plaintext only, you can use:
|
||||
ChatGPT responses are formatted as markdown by default. If you want to work with plaintext instead, you can use:
|
||||
|
||||
```ts
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN,
|
||||
markdown: false
|
||||
})
|
||||
const api = new ChatGPTAPI({ ...openAIAuth, markdown: false })
|
||||
```
|
||||
|
||||
If you want to automatically track the conversation, you can use `ChatGPTAPI.getConversation()`:
|
||||
|
||||
```ts
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
})
|
||||
const api = new ChatGPTAPI({ ...openAIAuth, markdown: false })
|
||||
|
||||
const conversation = api.getConversation()
|
||||
|
||||
// send a message and wait for the response
|
||||
const response0 = await conversation.sendMessage('What is OpenAI?')
|
||||
|
||||
// send a follow-up prompt to the previous message and wait for the response
|
||||
// send a follow-up
|
||||
const response1 = await conversation.sendMessage('Can you expand on that?')
|
||||
|
||||
// send another follow-up to the same conversation
|
||||
// send another follow-up
|
||||
const response2 = await conversation.sendMessage('Oh cool; thank you')
|
||||
```
|
||||
|
||||
Sometimes, ChatGPT will hang for an extended period of time before sending it's response. This may be due to rate limiting or it may be due to OpenAI's servers being overloaded.
|
||||
Sometimes, ChatGPT will hang for an extended period of time before beginning to respond. This may be due to rate limiting or it may be due to OpenAI's servers being overloaded.
|
||||
|
||||
To mitigate this issues, you can add a timeout like this:
|
||||
To mitigate these issues, you can add a timeout like this:
|
||||
|
||||
```ts
|
||||
// timeout after 2 minutes (which will also abort the underlying HTTP request)
|
||||
|
@ -96,17 +112,22 @@ const response = await api.sendMessage('this is a timeout test', {
|
|||
})
|
||||
```
|
||||
|
||||
You can stream responses using the `onProgress` or `onConversationResponse` callbacks. See the [docs](./docs/classes/ChatGPTAPI.md) for more details.
|
||||
|
||||
<details>
|
||||
<summary>Usage in CommonJS (Dynamic import)</summary>
|
||||
|
||||
```js
|
||||
async function example() {
|
||||
// To use ESM in CommonJS, you can use a dynamic import
|
||||
const { ChatGPTAPI } = await import('chatgpt')
|
||||
const { ChatGPTAPI, getOpenAIAuth } = await import('chatgpt')
|
||||
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
const openAIAuth = await getOpenAIAuth({
|
||||
email: process.env.OPENAI_EMAIL,
|
||||
password: process.env.OPENAI_PASSWORD
|
||||
})
|
||||
|
||||
const api = new ChatGPTAPI({ ...openAIAuth })
|
||||
await api.ensureAuth()
|
||||
|
||||
const response = await api.sendMessage('Hello World!')
|
||||
|
@ -122,42 +143,64 @@ See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on met
|
|||
|
||||
### Demos
|
||||
|
||||
A [basic demo](./src/demo.ts) is included for testing purposes:
|
||||
To run the included demos:
|
||||
|
||||
1. clone repo
|
||||
2. install node deps
|
||||
3. set `OPENAI_EMAIL` and `OPENAI_PASSWORD` in .env
|
||||
|
||||
A [basic demo](./demos/demo.ts) is included for testing purposes:
|
||||
|
||||
```bash
|
||||
# 1. clone repo
|
||||
# 2. install node deps
|
||||
# 3. set `SESSION_TOKEN` in .env
|
||||
# 4. run:
|
||||
npx tsx src/demo.ts
|
||||
npx tsx demos/demo.ts
|
||||
```
|
||||
|
||||
A [conversation demo](./src/demo-conversation.ts) is also included:
|
||||
A [conversation demo](./demos/demo-conversation.ts) is also included:
|
||||
|
||||
```bash
|
||||
# 1. clone repo
|
||||
# 2. install node deps
|
||||
# 3. set `SESSION_TOKEN` in .env
|
||||
# 4. run:
|
||||
npx tsx src/demo-conversation.ts
|
||||
npx tsx demos/demo-conversation.ts
|
||||
```
|
||||
|
||||
### Session Tokens
|
||||
### Authentication
|
||||
|
||||
**This package requires a valid session token from ChatGPT to access it's unofficial REST API.**
|
||||
On December 11, 2022, OpenAI added some additional Cloudflare protections which make it more difficult to access the unofficial API.
|
||||
|
||||
To get a session token:
|
||||
You'll need a valid OpenAI "session token" and Cloudflare "clearance token" in order to use the API.
|
||||
|
||||
We've provided an automated, Puppeteer-based solution `getOpenAIAuth` to fetch these for you, but you may still run into cases where you have to manually pass the CAPTCHA. We're working on a solution to automate this further.
|
||||
|
||||
You can also get these tokens manually, but keep in mind that the `clearanceToken` only lasts for max 2 hours.
|
||||
|
||||
<details>
|
||||
<summary>Getting tokens manually</summary>
|
||||
|
||||
To get session token manually:
|
||||
|
||||
1. Go to https://chat.openai.com/chat and log in or sign up.
|
||||
2. Open dev tools.
|
||||
3. Open `Application` > `Cookies`.
|
||||

|
||||
4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment.
|
||||
4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment. This will be your `sessionToken`.
|
||||
5. Copy the value for `cf_clearance` and save it to your environment. This will be your `clearanceToken`.
|
||||
6. Copy the value of the `user-agent` header from any request in your `Network` tab. This will be your `userAgent`.
|
||||
|
||||
If you want to run the built-in demo, store this value as `SESSION_TOKEN` in a local `.env` file.
|
||||
Pass `sessionToken`, `clearanceToken`, and `userAgent` to the `ChatGPTAPI` constructor.
|
||||
|
||||
</details>
|
||||
|
||||
> **Note**
|
||||
> This package will switch to using the official API once it's released.
|
||||
> This package will switch to using the official API once it's released, which will make this process much simpler.
|
||||
|
||||
#### Restrictions
|
||||
|
||||
**Please read these carefully**
|
||||
|
||||
- You must use `node >= 18` at the moment. I'm using `v19.2.0` in my testing.
|
||||
- Cloudflare `cf_clearance` **tokens expire after 2 hours**, so right now we recommend that you refresh your `cf_clearance` token every hour or so.
|
||||
- Your `user-agent` and `IP address` **must match** from the real browser window you're logged in with to the one you're using for `ChatGPTAPI`.
|
||||
- This means that you currently can't log in with your laptop and then run the bot on a server or proxy somewhere.
|
||||
- Cloudflare will still sometimes ask you to complete a CAPTCHA, so you may need to keep an eye on it and manually resolve the CAPTCHA. Automated CAPTCHA bypass is coming soon.
|
||||
- You should not be using this account while the bot is using it, because that browser window may refresh one of your tokens and invalidate the bot's session.
|
||||
|
||||
> **Note**
|
||||
> Prior to v1.0.0, this package used a headless browser via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2).
|
||||
|
@ -169,17 +212,43 @@ All of these awesome projects are built using the `chatgpt` package. 🤯
|
|||
- [Twitter Bot](https://github.com/transitive-bullshit/chatgpt-twitter-bot) powered by ChatGPT ✨
|
||||
- Mention [@ChatGPTBot](https://twitter.com/ChatGPTBot) on Twitter with your prompt to try it out
|
||||
- [Chrome Extension](https://github.com/gragland/chatgpt-everywhere) ([demo](https://twitter.com/gabe_ragland/status/1599466486422470656))
|
||||
- [VSCode Extension #1](https://github.com/mpociot/chatgpt-vscode) ([demo](https://twitter.com/marcelpociot/status/1599180144551526400))
|
||||
- [VSCode Extension #1](https://github.com/mpociot/chatgpt-vscode) ([demo](https://twitter.com/marcelpociot/status/1599180144551526400), [updated version](https://github.com/timkmecl/chatgpt-vscode), [marketplace](https://marketplace.visualstudio.com/items?itemName=timkmecl.chatgpt))
|
||||
- [VSCode Extension #2](https://github.com/barnesoir/chatgpt-vscode-plugin) ([marketplace](https://marketplace.visualstudio.com/items?itemName=JayBarnes.chatgpt-vscode-plugin))
|
||||
- [VSCode Extension #3](https://github.com/gencay/vscode-chatgpt) ([marketplace](https://marketplace.visualstudio.com/items?itemName=gencay.vscode-chatgpt))
|
||||
- [Raycast Extension](https://github.com/abielzulio/chatgpt-raycast) ([demo](https://twitter.com/abielzulio/status/1600176002042191875))
|
||||
- [Raycast Extension #1](https://github.com/abielzulio/chatgpt-raycast) ([demo](https://twitter.com/abielzulio/status/1600176002042191875))
|
||||
- [Raycast Extension #2](https://github.com/domnantas/raycast-chatgpt)
|
||||
- [Telegram Bot #1](https://github.com/realies/chatgpt-telegram-bot)
|
||||
- [Telegram Bot #2](https://github.com/dawangraoming/chatgpt-telegram-bot)
|
||||
- [Deno Telegram Bot](https://github.com/Ciyou/chatbot-telegram)
|
||||
- [Go Telegram Bot](https://github.com/m1guelpf/chatgpt-telegram)
|
||||
- [GitHub ProBot](https://github.com/oceanlvr/ChatGPTBot)
|
||||
- [Discord Bot](https://github.com/onury5506/Discord-ChatGPT-Bot)
|
||||
- [WeChat Bot](https://github.com/AutumnWhj/ChatGPT-wechat-bot)
|
||||
- [Discord Bot #1](https://github.com/onury5506/Discord-ChatGPT-Bot)
|
||||
- [Discord Bot #2](https://github.com/Nageld/ChatGPT-Bot)
|
||||
- [Discord Bot #3](https://github.com/leinstay/gptbot)
|
||||
- [Discord Bot #4 (selfbot)](https://github.com/0x7030676e31/cumsocket)
|
||||
- [WeChat Bot #1](https://github.com/AutumnWhj/ChatGPT-wechat-bot)
|
||||
- [WeChat Bot #2](https://github.com/fuergaosi233/wechat-chatgpt)
|
||||
- [WeChat Bot #3](https://github.com/wangrongding/wechat-bot)
|
||||
- [WeChat Bot #4](https://github.com/darknightlab/wechat-bot)
|
||||
- [WeChat Bot #5](https://github.com/sunshanpeng/wechaty-chatgpt)
|
||||
- [QQ Bot (plugin for Yunzai-bot)](https://github.com/ikechan8370/chatgpt-plugin)
|
||||
- [QQ Bot (plugin for KiviBot)](https://github.com/KiviBotLab/kivibot-plugin-chatgpt)
|
||||
- [QQ Bot (oicq)](https://github.com/easydu2002/chat_gpt_oicq)
|
||||
- [QQ Bot (oicq + RabbitMQ)](https://github.com/linsyking/ChatGPT-QQBot)
|
||||
- [QQ Bot (go-cqhttp)](https://github.com/PairZhu/ChatGPT-QQRobot)
|
||||
- [Lovelines.xyz](https://lovelines.xyz)
|
||||
- [EXM smart contracts](https://github.com/decentldotland/molecule)
|
||||
- [Flutter ChatGPT API](https://github.com/coskuncay/flutter_chatgpt_api)
|
||||
- [Carik Bot](https://github.com/luridarmawan/Carik)
|
||||
- [Github Action for reviewing PRs](https://github.com/kxxt/chatgpt-action/)
|
||||
- [WhatsApp Bot #1](https://github.com/pascalroget/whatsgpt) (multi-user support)
|
||||
- [WhatsApp Bot #2](https://github.com/amosayomide05/chatgpt-whatsapp-bot)
|
||||
- [WhatsApp Bot #3](https://github.com/navopw/whatsapp-chatgpt)
|
||||
- [Matrix Bot](https://github.com/jakecoppinger/matrix-chatgpt-bot)
|
||||
- [Rental Cover Letter Generator](https://sharehouse.app/ai)
|
||||
- [Assistant CLI](https://github.com/diciaup/assistant-cli)
|
||||
- [Teams Bot](https://github.com/formulahendry/chatgpt-teams-bot)
|
||||
- [Askai](https://github.com/yudax42/askai)
|
||||
|
||||
If you create a cool integration, feel free to open a PR and add it to the list.
|
||||
|
||||
|
@ -187,19 +256,16 @@ If you create a cool integration, feel free to open a PR and add it to the list.
|
|||
|
||||
This package is ESM-only. It supports:
|
||||
|
||||
- Node.js >= 16.8
|
||||
- If you need Node.js 14 support, use [`v1.4.0`](https://github.com/transitive-bullshit/chatgpt-api/releases/tag/v1.4.0)
|
||||
- Edge runtimes like CF workers and Vercel edge functions
|
||||
- Modern browsers
|
||||
- Mainly chrome extensions where your code is protected to a degree
|
||||
- **We do not recommend using `chatgpt` from client-side browser code** because it would expose your private session token
|
||||
- Node.js >= 18
|
||||
- Node.js 17, 16, and 14 were supported in earlier versions, but OpenAI's Cloudflare update caused a bug with `undici` on v17 and v16 that needs investigation. So for now, use `node >= 18`
|
||||
- We recommend against using `chatgpt` from client-side browser code because it would expose your private session token
|
||||
- If you want to build a website using `chatgpt`, we recommend using it only from your backend API
|
||||
|
||||
## Credits
|
||||
|
||||
- Huge thanks to [@RomanHotsiy](https://github.com/RomanHotsiy), [@ElijahPepe](https://github.com/ElijahPepe), and all the other contributors 💪
|
||||
- Huge thanks to [@wong2](https://github.com/wong2), [@simon300000](https://github.com/simon300000), [@RomanHotsiy](https://github.com/RomanHotsiy), [@ElijahPepe](https://github.com/ElijahPepe), and all the other contributors 💪
|
||||
- The original browser version was inspired by this [Go module](https://github.com/danielgross/whatsapp-gpt) by [Daniel Gross](https://github.com/danielgross)
|
||||
- The original REST version was inspired by [chat-gpt-google-extension](https://github.com/wong2/chat-gpt-google-extension) by [@wong2](https://github.com/wong2)
|
||||
- [OpenAI](https://openai.com) for creating [ChatGPT](https://openai.com/blog/chatgpt/) 🔥
|
||||
|
||||
## License
|
||||
|
||||
|
|
17
package.json
17
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "chatgpt",
|
||||
"version": "2.0.5",
|
||||
"version": "2.3.2",
|
||||
"description": "Node.js client for the unofficial ChatGPT API.",
|
||||
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||
"repository": "transitive-bullshit/chatgpt-api",
|
||||
|
@ -10,7 +10,6 @@
|
|||
"types": "./build/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"browser": "./build/browser/index.js",
|
||||
"import": "./build/index.js",
|
||||
"types": "./build/index.d.ts",
|
||||
"default": "./build/index.js"
|
||||
|
@ -20,21 +19,19 @@
|
|||
"build"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=16.8"
|
||||
"node": ">=18"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsup",
|
||||
"dev": "tsup --watch",
|
||||
"clean": "del build",
|
||||
"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",
|
||||
"pretest": "run-s build",
|
||||
"docs": "typedoc",
|
||||
"prepare": "husky install",
|
||||
"pre-commit": "lint-staged",
|
||||
"test": "run-p test:*",
|
||||
"test:unit": "ava",
|
||||
"test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -43,7 +40,10 @@
|
|||
"p-timeout": "^6.0.0",
|
||||
"remark": "^14.0.2",
|
||||
"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": {
|
||||
"@trivago/prettier-plugin-sort-imports": "^4.0.0",
|
||||
|
@ -57,14 +57,15 @@
|
|||
"npm-run-all": "^4.1.5",
|
||||
"ora": "^6.1.2",
|
||||
"prettier": "^2.8.0",
|
||||
"puppeteer": "^19.4.0",
|
||||
"tsup": "^6.5.0",
|
||||
"tsx": "^3.12.1",
|
||||
"typedoc": "^0.23.21",
|
||||
"typedoc-plugin-markdown": "^3.13.6",
|
||||
"typescript": "^4.9.3"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"undici": "^5.13.0"
|
||||
"peerDependencies": {
|
||||
"puppeteer": "*"
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{ts,tsx}": [
|
||||
|
|
539
pnpm-lock.yaml
539
pnpm-lock.yaml
Plik diff jest za duży
Load Diff
143
readme.md
143
readme.md
|
@ -1,3 +1,20 @@
|
|||
# Update December 12, 2022 <!-- omit in toc -->
|
||||
|
||||
Yesterday, OpenAI added additional Cloudflare protections that make it more difficult to access the unofficial API.
|
||||
|
||||
This package has been updated to use Puppeteer to automatically log in to ChatGPT and extract the necessary auth credentials. 🔥
|
||||
|
||||
To use the updated version, **make sure you're using the latest version of this package and Node.js >= 18**. Then update your code following the examples below, paying special attention to the sections on [Authentication](#authentication) and [Restrictions](#restrictions).
|
||||
|
||||
We're working hard to improve this process (especially CAPTCHA automation). Keep in mind that this package will be updated to use the official API as soon as it's released, so things should get much easier over time. 💪
|
||||
|
||||
Lastly, please consider starring this repo and <a href="https://twitter.com/transitive_bs">following me on twitter <img src="https://storage.googleapis.com/saasify-assets/twitter-logo.svg" alt="twitter" height="24px" align="center"></a> to help support the project.
|
||||
|
||||
Thanks && cheers,
|
||||
Travis
|
||||
|
||||
---
|
||||
|
||||
<p align="center">
|
||||
<img alt="Example usage" src="/media/demo.gif">
|
||||
</p>
|
||||
|
@ -13,7 +30,8 @@
|
|||
- [Usage](#usage)
|
||||
- [Docs](#docs)
|
||||
- [Demos](#demos)
|
||||
- [Session Tokens](#session-tokens)
|
||||
- [Authentication](#authentication)
|
||||
- [Restrictions](#restrictions)
|
||||
- [Projects](#projects)
|
||||
- [Compatibility](#compatibility)
|
||||
- [Credits](#credits)
|
||||
|
@ -28,24 +46,24 @@ You can use it to start building projects powered by ChatGPT like chatbots, webs
|
|||
## Install
|
||||
|
||||
```bash
|
||||
npm install chatgpt
|
||||
npm install chatgpt puppeteer
|
||||
```
|
||||
|
||||
`puppeteer` is an optional peer dependency used to automate bypassing the Cloudflare protections via `getOpenAIAuth`. The main API wrapper uses `fetch` directly.
|
||||
|
||||
## Usage
|
||||
|
||||
> **Note**
|
||||
> Per the official OpenAI Discord on December 7th, 2022: The ChatGPT servers are currently experiencing "exceptionally high demand," so some requests may respond with [HTTP 503 errors](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/503).
|
||||
|
||||
```ts
|
||||
import { ChatGPTAPI } from 'chatgpt'
|
||||
import { ChatGPTAPI, getOpenAIAuth } from 'chatgpt'
|
||||
|
||||
async function example() {
|
||||
// sessionToken is required; see below for details
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
// use puppeteer to bypass cloudflare (headful because of captchas)
|
||||
const openAIAuth = await getOpenAIAuth({
|
||||
email: process.env.OPENAI_EMAIL,
|
||||
password: process.env.OPENAI_PASSWORD
|
||||
})
|
||||
|
||||
// ensure the API is properly authenticated
|
||||
const api = new ChatGPTAPI({ ...openAIAuth })
|
||||
await api.ensureAuth()
|
||||
|
||||
// send a message and wait for the response
|
||||
|
@ -61,28 +79,23 @@ async function example() {
|
|||
ChatGPT responses are formatted as markdown by default. If you want to work with plaintext instead, you can use:
|
||||
|
||||
```ts
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN,
|
||||
markdown: false
|
||||
})
|
||||
const api = new ChatGPTAPI({ ...openAIAuth, markdown: false })
|
||||
```
|
||||
|
||||
If you want to automatically track the conversation, you can use `ChatGPTAPI.getConversation()`:
|
||||
|
||||
```ts
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
})
|
||||
const api = new ChatGPTAPI({ ...openAIAuth, markdown: false })
|
||||
|
||||
const conversation = api.getConversation()
|
||||
|
||||
// send a message and wait for the response
|
||||
const response0 = await conversation.sendMessage('What is OpenAI?')
|
||||
|
||||
// send a follow-up prompt to the previous message and wait for the response
|
||||
// send a follow-up
|
||||
const response1 = await conversation.sendMessage('Can you expand on that?')
|
||||
|
||||
// send another follow-up to the same conversation
|
||||
// send another follow-up
|
||||
const response2 = await conversation.sendMessage('Oh cool; thank you')
|
||||
```
|
||||
|
||||
|
@ -105,11 +118,14 @@ You can stream responses using the `onProgress` or `onConversationResponse` call
|
|||
```js
|
||||
async function example() {
|
||||
// To use ESM in CommonJS, you can use a dynamic import
|
||||
const { ChatGPTAPI } = await import('chatgpt')
|
||||
const { ChatGPTAPI, getOpenAIAuth } = await import('chatgpt')
|
||||
|
||||
const api = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
const openAIAuth = await getOpenAIAuth({
|
||||
email: process.env.OPENAI_EMAIL,
|
||||
password: process.env.OPENAI_PASSWORD
|
||||
})
|
||||
|
||||
const api = new ChatGPTAPI({ ...openAIAuth })
|
||||
await api.ensureAuth()
|
||||
|
||||
const response = await api.sendMessage('Hello World!')
|
||||
|
@ -125,42 +141,64 @@ See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on met
|
|||
|
||||
### Demos
|
||||
|
||||
A [basic demo](./src/demo.ts) is included for testing purposes:
|
||||
To run the included demos:
|
||||
|
||||
1. clone repo
|
||||
2. install node deps
|
||||
3. set `OPENAI_EMAIL` and `OPENAI_PASSWORD` in .env
|
||||
|
||||
A [basic demo](./demos/demo.ts) is included for testing purposes:
|
||||
|
||||
```bash
|
||||
# 1. clone repo
|
||||
# 2. install node deps
|
||||
# 3. set `SESSION_TOKEN` in .env
|
||||
# 4. run:
|
||||
npx tsx src/demo.ts
|
||||
npx tsx demos/demo.ts
|
||||
```
|
||||
|
||||
A [conversation demo](./src/demo-conversation.ts) is also included:
|
||||
A [conversation demo](./demos/demo-conversation.ts) is also included:
|
||||
|
||||
```bash
|
||||
# 1. clone repo
|
||||
# 2. install node deps
|
||||
# 3. set `SESSION_TOKEN` in .env
|
||||
# 4. run:
|
||||
npx tsx src/demo-conversation.ts
|
||||
npx tsx demos/demo-conversation.ts
|
||||
```
|
||||
|
||||
### Session Tokens
|
||||
### Authentication
|
||||
|
||||
**This package requires a valid session token from ChatGPT to access it's unofficial REST API.**
|
||||
On December 11, 2022, OpenAI added some additional Cloudflare protections which make it more difficult to access the unofficial API.
|
||||
|
||||
To get a session token:
|
||||
You'll need a valid OpenAI "session token" and Cloudflare "clearance token" in order to use the API.
|
||||
|
||||
We've provided an automated, Puppeteer-based solution `getOpenAIAuth` to fetch these for you, but you may still run into cases where you have to manually pass the CAPTCHA. We're working on a solution to automate this further.
|
||||
|
||||
You can also get these tokens manually, but keep in mind that the `clearanceToken` only lasts for max 2 hours.
|
||||
|
||||
<details>
|
||||
<summary>Getting tokens manually</summary>
|
||||
|
||||
To get session token manually:
|
||||
|
||||
1. Go to https://chat.openai.com/chat and log in or sign up.
|
||||
2. Open dev tools.
|
||||
3. Open `Application` > `Cookies`.
|
||||

|
||||
4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment.
|
||||
4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment. This will be your `sessionToken`.
|
||||
5. Copy the value for `cf_clearance` and save it to your environment. This will be your `clearanceToken`.
|
||||
6. Copy the value of the `user-agent` header from any request in your `Network` tab. This will be your `userAgent`.
|
||||
|
||||
If you want to run the built-in demo, store this value as `SESSION_TOKEN` in a local `.env` file.
|
||||
Pass `sessionToken`, `clearanceToken`, and `userAgent` to the `ChatGPTAPI` constructor.
|
||||
|
||||
</details>
|
||||
|
||||
> **Note**
|
||||
> This package will switch to using the official API once it's released.
|
||||
> This package will switch to using the official API once it's released, which will make this process much simpler.
|
||||
|
||||
### Restrictions
|
||||
|
||||
**Please read carefully**
|
||||
|
||||
- You must use `node >= 18` at the moment. I'm using `v19.2.0` in my testing.
|
||||
- Cloudflare `cf_clearance` **tokens expire after 2 hours**, so right now we recommend that you refresh your `cf_clearance` token every hour or so.
|
||||
- Your `user-agent` and `IP address` **must match** from the real browser window you're logged in with to the one you're using for `ChatGPTAPI`.
|
||||
- This means that you currently can't log in with your laptop and then run the bot on a server or proxy somewhere.
|
||||
- Cloudflare will still sometimes ask you to complete a CAPTCHA, so you may need to keep an eye on it and manually resolve the CAPTCHA. Automated CAPTCHA bypass is coming soon.
|
||||
- You should not be using this account while the bot is using it, because that browser window may refresh one of your tokens and invalidate the bot's session.
|
||||
|
||||
> **Note**
|
||||
> Prior to v1.0.0, this package used a headless browser via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2).
|
||||
|
@ -171,11 +209,12 @@ All of these awesome projects are built using the `chatgpt` package. 🤯
|
|||
|
||||
- [Twitter Bot](https://github.com/transitive-bullshit/chatgpt-twitter-bot) powered by ChatGPT ✨
|
||||
- Mention [@ChatGPTBot](https://twitter.com/ChatGPTBot) on Twitter with your prompt to try it out
|
||||
- [Lovelines.xyz](https://lovelines.xyz?ref=chatgpt-api)
|
||||
- [Chrome Extension](https://github.com/gragland/chatgpt-everywhere) ([demo](https://twitter.com/gabe_ragland/status/1599466486422470656))
|
||||
- [Lovelines.xyz](https://lovelines.xyz)
|
||||
- [VSCode Extension #1](https://github.com/mpociot/chatgpt-vscode) ([demo](https://twitter.com/marcelpociot/status/1599180144551526400), [updated version](https://github.com/timkmecl/chatgpt-vscode), [marketplace](https://marketplace.visualstudio.com/items?itemName=timkmecl.chatgpt))
|
||||
- [VSCode Extension #2](https://github.com/barnesoir/chatgpt-vscode-plugin) ([marketplace](https://marketplace.visualstudio.com/items?itemName=JayBarnes.chatgpt-vscode-plugin))
|
||||
- [VSCode Extension #3](https://github.com/gencay/vscode-chatgpt) ([marketplace](https://marketplace.visualstudio.com/items?itemName=gencay.vscode-chatgpt))
|
||||
- [VSCode Extension #4](https://github.com/dogukanakkaya/chatgpt-code-vscode-extension) ([marketplace](https://marketplace.visualstudio.com/items?itemName=dogukanakkaya.chatgpt-code))
|
||||
- [Raycast Extension #1](https://github.com/abielzulio/chatgpt-raycast) ([demo](https://twitter.com/abielzulio/status/1600176002042191875))
|
||||
- [Raycast Extension #2](https://github.com/domnantas/raycast-chatgpt)
|
||||
- [Telegram Bot #1](https://github.com/realies/chatgpt-telegram-bot)
|
||||
|
@ -186,6 +225,7 @@ All of these awesome projects are built using the `chatgpt` package. 🤯
|
|||
- [Discord Bot #1](https://github.com/onury5506/Discord-ChatGPT-Bot)
|
||||
- [Discord Bot #2](https://github.com/Nageld/ChatGPT-Bot)
|
||||
- [Discord Bot #3](https://github.com/leinstay/gptbot)
|
||||
- [Discord Bot #4 (selfbot)](https://github.com/0x7030676e31/cumsocket)
|
||||
- [WeChat Bot #1](https://github.com/AutumnWhj/ChatGPT-wechat-bot)
|
||||
- [WeChat Bot #2](https://github.com/fuergaosi233/wechat-chatgpt)
|
||||
- [WeChat Bot #3](https://github.com/wangrongding/wechat-bot)
|
||||
|
@ -195,12 +235,21 @@ All of these awesome projects are built using the `chatgpt` package. 🤯
|
|||
- [QQ Bot (plugin for KiviBot)](https://github.com/KiviBotLab/kivibot-plugin-chatgpt)
|
||||
- [QQ Bot (oicq)](https://github.com/easydu2002/chat_gpt_oicq)
|
||||
- [QQ Bot (oicq + RabbitMQ)](https://github.com/linsyking/ChatGPT-QQBot)
|
||||
- [QQ Bot (go-cqhttp)](https://github.com/PairZhu/ChatGPT-QQRobot)
|
||||
- [EXM smart contracts](https://github.com/decentldotland/molecule)
|
||||
- [Flutter ChatGPT API](https://github.com/coskuncay/flutter_chatgpt_api)
|
||||
- [Carik Bot](https://github.com/luridarmawan/Carik)
|
||||
- [Github Action for reviewing PRs](https://github.com/kxxt/chatgpt-action/)
|
||||
- [WhatsApp Bot](https://github.com/amosayomide05/chatgpt-whatsapp-bot)
|
||||
- [WhatsApp Bot #1](https://github.com/pascalroget/whatsgpt) (multi-user support)
|
||||
- [WhatsApp Bot #2](https://github.com/amosayomide05/chatgpt-whatsapp-bot)
|
||||
- [WhatsApp Bot #3](https://github.com/navopw/whatsapp-chatgpt)
|
||||
- [Matrix Bot](https://github.com/jakecoppinger/matrix-chatgpt-bot)
|
||||
- [Rental Cover Letter Generator](https://sharehouse.app/ai)
|
||||
- [Assistant CLI](https://github.com/diciaup/assistant-cli)
|
||||
- [Teams Bot](https://github.com/formulahendry/chatgpt-teams-bot)
|
||||
- [Askai](https://github.com/yudax42/askai)
|
||||
- [TalkGPT](https://github.com/ShadovvBeast/TalkGPT)
|
||||
- [iOS Shortcut](https://github.com/leecobaby/shortcuts/blob/master/other/ChatGPT_EN.md)
|
||||
|
||||
If you create a cool integration, feel free to open a PR and add it to the list.
|
||||
|
||||
|
@ -208,19 +257,15 @@ If you create a cool integration, feel free to open a PR and add it to the list.
|
|||
|
||||
This package is ESM-only. It supports:
|
||||
|
||||
- Node.js >= 16.8
|
||||
- If you need Node.js 14 support, use [`v1.4.0`](https://github.com/transitive-bullshit/chatgpt-api/releases/tag/v1.4.0)
|
||||
- Edge runtimes like CF workers and Vercel edge functions
|
||||
- Modern browsers
|
||||
- Mainly meant for chrome extensions where your code is protected to a degree
|
||||
- Node.js >= 18
|
||||
- Node.js 17, 16, and 14 were supported in earlier versions, but OpenAI's Cloudflare update caused a bug with `undici` on v17 and v16 that needs investigation. So for now, use `node >= 18`
|
||||
- We recommend against using `chatgpt` from client-side browser code because it would expose your private session token
|
||||
- If you want to build a website using `chatgpt`, we recommend using it only from your backend API
|
||||
|
||||
## Credits
|
||||
|
||||
- Huge thanks to [@simon300000](https://github.com/simon300000), [@RomanHotsiy](https://github.com/RomanHotsiy), [@ElijahPepe](https://github.com/ElijahPepe), and all the other contributors 💪
|
||||
- Huge thanks to [@wong2](https://github.com/wong2), [@simon300000](https://github.com/simon300000), [@RomanHotsiy](https://github.com/RomanHotsiy), [@ElijahPepe](https://github.com/ElijahPepe), and all the other contributors 💪
|
||||
- The original browser version was inspired by this [Go module](https://github.com/danielgross/whatsapp-gpt) by [Daniel Gross](https://github.com/danielgross)
|
||||
- The original REST version was inspired by [chat-gpt-google-extension](https://github.com/wong2/chat-gpt-google-extension) by [@wong2](https://github.com/wong2)
|
||||
- [OpenAI](https://openai.com) for creating [ChatGPT](https://openai.com/blog/chatgpt/) 🔥
|
||||
|
||||
## License
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import test from 'ava'
|
||||
import dotenv from 'dotenv-safe'
|
||||
|
||||
import * as types from './types'
|
||||
import { ChatGPTAPI } from './chatgpt-api'
|
||||
|
||||
dotenv.config()
|
||||
|
@ -10,16 +11,20 @@ const isCI = !!process.env.CI
|
|||
test('ChatGPTAPI invalid session token', async (t) => {
|
||||
t.timeout(30 * 1000) // 30 seconds
|
||||
|
||||
t.throws(() => new ChatGPTAPI({ sessionToken: null }), {
|
||||
t.throws(() => new ChatGPTAPI({ sessionToken: null, clearanceToken: null }), {
|
||||
message: 'ChatGPT invalid session token'
|
||||
})
|
||||
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
const chatgpt = new ChatGPTAPI({ sessionToken: 'invalid' })
|
||||
const chatgpt = new ChatGPTAPI({
|
||||
sessionToken: 'invalid',
|
||||
clearanceToken: 'invalid'
|
||||
})
|
||||
await chatgpt.ensureAuth()
|
||||
},
|
||||
{
|
||||
instanceOf: types.ChatGPTError,
|
||||
message: 'ChatGPT failed to refresh auth token. Error: Unauthorized'
|
||||
}
|
||||
)
|
||||
|
@ -31,13 +36,18 @@ test('ChatGPTAPI valid session token', async (t) => {
|
|||
}
|
||||
|
||||
t.notThrows(
|
||||
() => new ChatGPTAPI({ sessionToken: 'fake valid session token' })
|
||||
() =>
|
||||
new ChatGPTAPI({
|
||||
sessionToken: 'fake valid session token',
|
||||
clearanceToken: 'invalid'
|
||||
})
|
||||
)
|
||||
|
||||
await t.notThrowsAsync(
|
||||
(async () => {
|
||||
const chatgpt = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
sessionToken: process.env.SESSION_TOKEN,
|
||||
clearanceToken: process.env.CLEARANCE_TOKEN
|
||||
})
|
||||
|
||||
// Don't make any real API calls using our session token if we're running on CI
|
||||
|
@ -60,10 +70,14 @@ if (!isCI) {
|
|||
|
||||
await t.throwsAsync(
|
||||
async () => {
|
||||
const chatgpt = new ChatGPTAPI({ sessionToken: expiredSessionToken })
|
||||
const chatgpt = new ChatGPTAPI({
|
||||
sessionToken: expiredSessionToken,
|
||||
clearanceToken: 'invalid'
|
||||
})
|
||||
await chatgpt.ensureAuth()
|
||||
},
|
||||
{
|
||||
instanceOf: types.ChatGPTError,
|
||||
message:
|
||||
'ChatGPT failed to refresh auth token. Error: session token may have expired'
|
||||
}
|
||||
|
@ -78,7 +92,8 @@ if (!isCI) {
|
|||
await t.throwsAsync(
|
||||
async () => {
|
||||
const chatgpt = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
sessionToken: process.env.SESSION_TOKEN,
|
||||
clearanceToken: process.env.CLEARANCE_TOKEN
|
||||
})
|
||||
|
||||
await chatgpt.sendMessage('test', {
|
||||
|
@ -97,7 +112,8 @@ if (!isCI) {
|
|||
await t.throwsAsync(
|
||||
async () => {
|
||||
const chatgpt = new ChatGPTAPI({
|
||||
sessionToken: process.env.SESSION_TOKEN
|
||||
sessionToken: process.env.SESSION_TOKEN,
|
||||
clearanceToken: process.env.CLEARANCE_TOKEN
|
||||
})
|
||||
|
||||
const abortController = new AbortController()
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import ExpiryMap from 'expiry-map'
|
||||
import pTimeout, { TimeoutError } from 'p-timeout'
|
||||
import pTimeout from 'p-timeout'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
|
||||
import * as types from './types'
|
||||
|
@ -10,31 +10,43 @@ import { markdownToText } from './utils'
|
|||
|
||||
const KEY_ACCESS_TOKEN = 'accessToken'
|
||||
const USER_AGENT =
|
||||
'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'
|
||||
'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'
|
||||
|
||||
export class ChatGPTAPI {
|
||||
protected _sessionToken: string
|
||||
protected _clearanceToken: string
|
||||
protected _markdown: boolean
|
||||
protected _debug: boolean
|
||||
protected _apiBaseUrl: string
|
||||
protected _backendApiBaseUrl: string
|
||||
protected _userAgent: string
|
||||
protected _headers: Record<string, string>
|
||||
protected _user: types.User | null = null
|
||||
|
||||
// Stores access tokens for `accessTokenTTL` milliseconds before needing to refresh
|
||||
// (defaults to 60 seconds)
|
||||
protected _accessTokenCache: ExpiryMap<string, string>
|
||||
|
||||
/**
|
||||
* 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.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 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 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: {
|
||||
sessionToken: string
|
||||
|
||||
clearanceToken: string
|
||||
|
||||
/** @defaultValue `true` **/
|
||||
markdown?: boolean
|
||||
|
||||
|
@ -44,32 +56,90 @@ export class ChatGPTAPI {
|
|||
/** @defaultValue `'https://chat.openai.com/backend-api'` **/
|
||||
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
|
||||
|
||||
/** @defaultValue 60000 (60 seconds) */
|
||||
/** @defaultValue 1 hour **/
|
||||
accessTokenTTL?: number
|
||||
|
||||
/** @defaultValue `undefined` **/
|
||||
accessToken?: string
|
||||
|
||||
/** @defaultValue `undefined` **/
|
||||
headers?: Record<string, string>
|
||||
|
||||
/** @defaultValue `false` **/
|
||||
debug?: boolean
|
||||
}) {
|
||||
const {
|
||||
sessionToken,
|
||||
clearanceToken,
|
||||
markdown = true,
|
||||
apiBaseUrl = 'https://chat.openai.com/api',
|
||||
backendApiBaseUrl = 'https://chat.openai.com/backend-api',
|
||||
userAgent = USER_AGENT,
|
||||
accessTokenTTL = 60000 // 60 seconds
|
||||
accessTokenTTL = 60 * 60000, // 1 hour
|
||||
accessToken,
|
||||
headers,
|
||||
debug = false
|
||||
} = opts
|
||||
|
||||
this._sessionToken = sessionToken
|
||||
this._clearanceToken = clearanceToken
|
||||
this._markdown = !!markdown
|
||||
this._debug = !!debug
|
||||
this._apiBaseUrl = apiBaseUrl
|
||||
this._backendApiBaseUrl = backendApiBaseUrl
|
||||
this._userAgent = userAgent
|
||||
this._headers = {
|
||||
'user-agent': this._userAgent,
|
||||
'x-openai-assistant-app-id': '',
|
||||
'accept-language': 'en-US,en;q=0.9',
|
||||
origin: 'https://chat.openai.com',
|
||||
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',
|
||||
'sec-fetch-site': 'same-origin',
|
||||
...headers
|
||||
}
|
||||
|
||||
this._accessTokenCache = new ExpiryMap<string, string>(accessTokenTTL)
|
||||
if (accessToken) {
|
||||
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
|
||||
}
|
||||
|
||||
if (!this._sessionToken) {
|
||||
throw new Error('ChatGPT invalid session token')
|
||||
throw new types.ChatGPTError('ChatGPT invalid session token')
|
||||
}
|
||||
|
||||
if (!this._clearanceToken) {
|
||||
throw new types.ChatGPTError('ChatGPT invalid clearance token')
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the currently signed-in user, if authenticated, `null` otherwise.
|
||||
*/
|
||||
get 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
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,6 +154,8 @@ export class ChatGPTAPI {
|
|||
* @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
|
||||
* @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.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
|
||||
|
@ -98,6 +170,8 @@ export class ChatGPTAPI {
|
|||
const {
|
||||
conversationId,
|
||||
parentMessageId = uuidv4(),
|
||||
messageId = uuidv4(),
|
||||
action = 'next',
|
||||
timeoutMs,
|
||||
onProgress,
|
||||
onConversationResponse
|
||||
|
@ -114,10 +188,10 @@ export class ChatGPTAPI {
|
|||
const accessToken = await this.refreshAccessToken()
|
||||
|
||||
const body: types.ConversationJSONBody = {
|
||||
action: 'next',
|
||||
action,
|
||||
messages: [
|
||||
{
|
||||
id: uuidv4(),
|
||||
id: messageId,
|
||||
role: 'user',
|
||||
content: {
|
||||
content_type: 'text',
|
||||
|
@ -133,17 +207,25 @@ export class ChatGPTAPI {
|
|||
body.conversation_id = conversationId
|
||||
}
|
||||
|
||||
const url = `${this._backendApiBaseUrl}/conversation`
|
||||
let response = ''
|
||||
|
||||
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, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': this._userAgent
|
||||
},
|
||||
headers,
|
||||
body: JSON.stringify(body),
|
||||
signal: abortSignal,
|
||||
onMessage: (data: string) => {
|
||||
|
@ -180,7 +262,23 @@ export class ChatGPTAPI {
|
|||
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) {
|
||||
|
@ -238,15 +336,30 @@ export class ChatGPTAPI {
|
|||
return cachedAccessToken
|
||||
}
|
||||
|
||||
let response: Response
|
||||
try {
|
||||
const res = await fetch('https://chat.openai.com/api/auth/session', {
|
||||
headers: {
|
||||
cookie: `__Secure-next-auth.session-token=${this._sessionToken}`,
|
||||
'user-agent': this._userAgent
|
||||
const url = `${this._apiBaseUrl}/auth/session`
|
||||
const headers = {
|
||||
...this._headers,
|
||||
cookie: `cf_clearance=${this._clearanceToken}; __Secure-next-auth.session-token=${this._sessionToken}`,
|
||||
accept: '*/*'
|
||||
}
|
||||
|
||||
if (this._debug) {
|
||||
console.log('GET', url, headers)
|
||||
}
|
||||
|
||||
const res = await fetch(url, {
|
||||
headers
|
||||
}).then((r) => {
|
||||
response = r
|
||||
|
||||
if (!r.ok) {
|
||||
throw new Error(`${r.status} ${r.statusText}`)
|
||||
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.SessionResult
|
||||
|
@ -255,22 +368,49 @@ export class ChatGPTAPI {
|
|||
const accessToken = res?.accessToken
|
||||
|
||||
if (!accessToken) {
|
||||
throw new Error('Unauthorized')
|
||||
const error = new types.ChatGPTError('Unauthorized')
|
||||
error.response = response
|
||||
error.statusCode = response?.status
|
||||
error.statusText = response?.statusText
|
||||
throw error
|
||||
}
|
||||
|
||||
const error = res?.error
|
||||
if (error) {
|
||||
if (error === 'RefreshAccessTokenError') {
|
||||
throw new Error('session token may have expired')
|
||||
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
|
||||
} else {
|
||||
throw new Error(error)
|
||||
const error = new types.ChatGPTError(appError)
|
||||
error.response = response
|
||||
error.statusCode = response?.status
|
||||
error.statusText = response?.statusText
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
if (res.user) {
|
||||
this._user = res.user
|
||||
}
|
||||
|
||||
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
|
||||
return accessToken
|
||||
} catch (err: any) {
|
||||
throw new Error(`ChatGPT failed to refresh auth token. ${err.toString()}`)
|
||||
if (this._debug) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { createParser } from 'eventsource-parser'
|
||||
|
||||
import * as types from './types'
|
||||
import { fetch } from './fetch'
|
||||
import { streamAsyncIterable } from './stream-async-iterable'
|
||||
|
||||
|
@ -10,7 +11,12 @@ export async function fetchSSE(
|
|||
const { onMessage, ...fetchOptions } = options
|
||||
const res = await fetch(url, fetchOptions)
|
||||
if (!res.ok) {
|
||||
throw new Error(`ChatGPTAPI error ${res.status || res.statusText}`)
|
||||
const msg = `ChatGPTAPI error ${res.status || res.statusText}`
|
||||
const error = new types.ChatGPTError(msg)
|
||||
error.statusCode = res.status
|
||||
error.statusText = res.statusText
|
||||
error.response = res
|
||||
throw error
|
||||
}
|
||||
|
||||
const parser = createParser((event) => {
|
||||
|
@ -25,7 +31,7 @@ export async function fetchSSE(
|
|||
const body: NodeJS.ReadableStream = res.body as any
|
||||
|
||||
if (!body.on || !body.read) {
|
||||
throw new Error('unsupported "fetch" implementation')
|
||||
throw new types.ChatGPTError('unsupported "fetch" implementation')
|
||||
}
|
||||
|
||||
body.on('readable', () => {
|
||||
|
|
21
src/fetch.ts
21
src/fetch.ts
|
@ -1,28 +1,13 @@
|
|||
/// <reference lib="dom" />
|
||||
|
||||
let _undici: any
|
||||
|
||||
// Use `undici` for node.js 16 and 17
|
||||
// Use `fetch` for node.js >= 18
|
||||
// Use `fetch` for all other environments, including browsers
|
||||
// NOTE: The top-level await is removed in a `postbuild` npm script for the
|
||||
// browser build
|
||||
const fetch =
|
||||
globalThis.fetch ??
|
||||
async function undiciFetchWrapper(
|
||||
...args: Parameters<typeof globalThis.fetch>
|
||||
): Promise<Response> {
|
||||
if (!_undici) {
|
||||
_undici = await import('undici')
|
||||
}
|
||||
const fetch = globalThis.fetch
|
||||
|
||||
if (typeof _undici?.fetch !== 'function') {
|
||||
if (typeof 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'
|
||||
'Invalid environment: global fetch not defined; `chatgpt` requires Node.js >= 18 at the moment due to Cloudflare protections'
|
||||
)
|
||||
}
|
||||
|
||||
return _undici.fetch(...args)
|
||||
}
|
||||
|
||||
export { fetch }
|
||||
|
|
|
@ -2,3 +2,4 @@ export * from './chatgpt-api'
|
|||
export * from './chatgpt-conversation'
|
||||
export * from './types'
|
||||
export * from './utils'
|
||||
export * from './openai-auth'
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
import * as fs from 'node:fs'
|
||||
import * as os from 'node:os'
|
||||
|
||||
import delay from 'delay'
|
||||
import {
|
||||
type Browser,
|
||||
type ElementHandle,
|
||||
type Page,
|
||||
type Protocol,
|
||||
type PuppeteerLaunchOptions
|
||||
} from 'puppeteer'
|
||||
import puppeteer from 'puppeteer-extra'
|
||||
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
||||
|
||||
import * as types from './types'
|
||||
|
||||
puppeteer.use(StealthPlugin())
|
||||
|
||||
/**
|
||||
* Represents everything that's required to pass into `ChatGPTAPI` in order
|
||||
* to authenticate with the unofficial ChatGPT API.
|
||||
*/
|
||||
export type OpenAIAuth = {
|
||||
userAgent: string
|
||||
clearanceToken: string
|
||||
sessionToken: string
|
||||
cookies?: Record<string, Protocol.Network.Cookie>
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export async function getOpenAIAuth({
|
||||
email,
|
||||
password,
|
||||
browser,
|
||||
timeoutMs = 2 * 60 * 1000,
|
||||
isGoogleLogin = false
|
||||
}: {
|
||||
email?: string
|
||||
password?: string
|
||||
browser?: Browser
|
||||
timeoutMs?: number
|
||||
isGoogleLogin?: boolean
|
||||
}): Promise<OpenAIAuth> {
|
||||
let page: Page
|
||||
let origBrowser = browser
|
||||
|
||||
try {
|
||||
if (!browser) {
|
||||
browser = await getBrowser()
|
||||
}
|
||||
|
||||
const userAgent = await browser.userAgent()
|
||||
page = (await browser.pages())[0] || (await browser.newPage())
|
||||
page.setDefaultTimeout(timeoutMs)
|
||||
|
||||
await page.goto('https://chat.openai.com/auth/login')
|
||||
|
||||
await checkForChatGPTAtCapacity(page)
|
||||
|
||||
// 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)
|
||||
|
||||
// login as well (optional)
|
||||
if (email && password) {
|
||||
await Promise.all([
|
||||
page.click('#__next .btn-primary'),
|
||||
page.waitForNavigation({
|
||||
waitUntil: 'networkidle0'
|
||||
})
|
||||
])
|
||||
|
||||
let submitP: Promise<void>
|
||||
|
||||
if (isGoogleLogin) {
|
||||
await page.click('button[data-provider="google"]')
|
||||
await page.waitForSelector('input[type="email"]')
|
||||
await page.type('input[type="email"]', email, { delay: 10 })
|
||||
await Promise.all([
|
||||
page.waitForNavigation(),
|
||||
await page.keyboard.press('Enter')
|
||||
])
|
||||
await page.waitForSelector('input[type="password"]', { visible: true })
|
||||
await page.type('input[type="password"]', password, { delay: 10 })
|
||||
submitP = page.keyboard.press('Enter')
|
||||
} else {
|
||||
await page.waitForSelector('#username')
|
||||
await page.type('#username', email, { delay: 10 })
|
||||
await page.click('button[type="submit"]')
|
||||
await page.waitForSelector('#password')
|
||||
await page.type('#password', password, { delay: 10 })
|
||||
submitP = page.click('button[type="submit"]')
|
||||
}
|
||||
|
||||
await Promise.all([
|
||||
submitP,
|
||||
|
||||
new Promise<void>((resolve, reject) => {
|
||||
let resolved = false
|
||||
|
||||
async function waitForCapacityText() {
|
||||
if (resolved) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await checkForChatGPTAtCapacity(page)
|
||||
|
||||
if (!resolved) {
|
||||
setTimeout(waitForCapacityText, 500)
|
||||
}
|
||||
} catch (err) {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
return reject(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
page
|
||||
.waitForNavigation({
|
||||
waitUntil: 'networkidle0'
|
||||
})
|
||||
.then(() => {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
if (!resolved) {
|
||||
resolved = true
|
||||
reject(err)
|
||||
}
|
||||
})
|
||||
|
||||
setTimeout(waitForCapacityText, 500)
|
||||
})
|
||||
])
|
||||
}
|
||||
|
||||
const pageCookies = await page.cookies()
|
||||
const cookies = pageCookies.reduce(
|
||||
(map, cookie) => ({ ...map, [cookie.name]: cookie }),
|
||||
{}
|
||||
)
|
||||
|
||||
const authInfo: OpenAIAuth = {
|
||||
userAgent,
|
||||
clearanceToken: cookies['cf_clearance']?.value,
|
||||
sessionToken: cookies['__Secure-next-auth.session-token']?.value,
|
||||
cookies
|
||||
}
|
||||
|
||||
return authInfo
|
||||
} catch (err) {
|
||||
console.error(err)
|
||||
throw err
|
||||
} finally {
|
||||
if (origBrowser) {
|
||||
if (page) {
|
||||
await page.close()
|
||||
}
|
||||
} else if (browser) {
|
||||
await browser.close()
|
||||
}
|
||||
|
||||
page = null
|
||||
browser = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
return puppeteer.launch({
|
||||
headless: false,
|
||||
args: ['--no-sandbox', '--exclude-switches', 'enable-automation'],
|
||||
ignoreHTTPSErrors: true,
|
||||
executablePath: defaultChromeExecutablePath(),
|
||||
...launchOptions
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default path to chrome's executable for the current platform.
|
||||
*/
|
||||
export const defaultChromeExecutablePath = (): string => {
|
||||
switch (os.platform()) {
|
||||
case 'win32':
|
||||
return 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe'
|
||||
|
||||
case 'darwin':
|
||||
return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
|
||||
|
||||
default:
|
||||
/**
|
||||
* Since two (2) separate chrome releases exists on linux
|
||||
* we first do a check to ensure we're executing the right one.
|
||||
*/
|
||||
const chromeExists = fs.existsSync('/usr/bin/google-chrome')
|
||||
|
||||
return chromeExists
|
||||
? '/usr/bin/google-chrome'
|
||||
: '/usr/bin/google-chrome-stable'
|
||||
}
|
||||
}
|
||||
|
||||
async function checkForChatGPTAtCapacity(page: Page) {
|
||||
let res: ElementHandle<Element> | null
|
||||
|
||||
try {
|
||||
res = await page.$('[role="alert"]')
|
||||
console.log('capacity text', res)
|
||||
} catch (err) {
|
||||
// ignore errors likely due to navigation
|
||||
console.warn(err.toString())
|
||||
}
|
||||
|
||||
if (res) {
|
||||
const error = new types.ChatGPTError('ChatGPT is at capacity')
|
||||
error.statusCode = 503
|
||||
throw error
|
||||
}
|
||||
}
|
16
src/types.ts
16
src/types.ts
|
@ -41,7 +41,7 @@ export type User = {
|
|||
/**
|
||||
* Email of the user
|
||||
*/
|
||||
email: string
|
||||
email?: string
|
||||
|
||||
/**
|
||||
* Image of the user
|
||||
|
@ -56,12 +56,12 @@ export type User = {
|
|||
/**
|
||||
* Groups the user is in
|
||||
*/
|
||||
groups: string[] | []
|
||||
groups: string[]
|
||||
|
||||
/**
|
||||
* Features the user is in
|
||||
*/
|
||||
features: string[] | []
|
||||
features: string[]
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -273,10 +273,13 @@ export type MessageContent = {
|
|||
}
|
||||
|
||||
export type MessageMetadata = any
|
||||
export type MessageActionType = 'next' | 'variant'
|
||||
|
||||
export type SendMessageOptions = {
|
||||
conversationId?: string
|
||||
parentMessageId?: string
|
||||
messageId?: string
|
||||
action?: MessageActionType
|
||||
timeoutMs?: number
|
||||
onProgress?: (partialResponse: string) => void
|
||||
onConversationResponse?: (response: ConversationResponseEvent) => void
|
||||
|
@ -287,3 +290,10 @@ export type SendConversationMessageOptions = Omit<
|
|||
SendMessageOptions,
|
||||
'conversationId' | 'parentMessageId'
|
||||
>
|
||||
|
||||
export class ChatGPTError extends Error {
|
||||
statusCode?: number
|
||||
statusText?: string
|
||||
response?: Response
|
||||
originalError?: Error
|
||||
}
|
||||
|
|
|
@ -11,20 +11,6 @@ export default defineConfig([
|
|||
sourcemap: true,
|
||||
minify: false,
|
||||
shims: true,
|
||||
dts: true,
|
||||
external: ['undici']
|
||||
},
|
||||
{
|
||||
entry: ['src/index.ts'],
|
||||
outDir: 'build/browser',
|
||||
target: 'chrome89',
|
||||
platform: 'browser',
|
||||
format: ['esm'],
|
||||
splitting: false,
|
||||
sourcemap: true,
|
||||
minify: false,
|
||||
shims: true,
|
||||
dts: true,
|
||||
external: ['undici']
|
||||
dts: true
|
||||
}
|
||||
])
|
||||
|
|
Ładowanie…
Reference in New Issue