Merge pull request #142 from transitive-bullshit/feature/improve-robustness

pull/146/head
Travis Fischer 2022-12-15 17:17:53 -06:00 zatwierdzone przez GitHub
commit 278ed47dc5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
15 zmienionych plików z 1105 dodań i 142 usunięć

Wyświetl plik

@ -1,7 +1,7 @@
import dotenv from 'dotenv-safe'
import { oraPromise } from 'ora'
import { ChatGPTAPI, getOpenAIAuth } from '../src'
import { ChatGPTAPIBrowser } from '../src'
dotenv.config()
@ -16,13 +16,9 @@ async function main() {
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 api = new ChatGPTAPIBrowser({ email, password, debug: true })
const res = await api.init()
console.log('init result', res)
const prompt =
'Write a python version of bubble sort. Do not include example usage.'
@ -31,6 +27,7 @@ async function main() {
text: prompt
})
await api.close()
return response
}

Wyświetl plik

@ -22,6 +22,7 @@
- [getIsAuthenticated](ChatGPTAPI.md#getisauthenticated)
- [refreshAccessToken](ChatGPTAPI.md#refreshaccesstoken)
- [sendMessage](ChatGPTAPI.md#sendmessage)
- [sendModeration](ChatGPTAPI.md#sendmoderation)
## Constructors
@ -52,7 +53,7 @@ to obtain your `clearanceToken`.
#### Defined in
[src/chatgpt-api.ts:45](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L45)
[src/chatgpt-api.ts:45](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L45)
## Accessors
@ -68,7 +69,7 @@ Gets the current Cloudflare clearance token (`cf_clearance` cookie value).
#### Defined in
[src/chatgpt-api.ts:136](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L136)
[src/chatgpt-api.ts:137](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L137)
___
@ -84,7 +85,7 @@ Gets the current session token.
#### Defined in
[src/chatgpt-api.ts:131](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L131)
[src/chatgpt-api.ts:132](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L132)
___
@ -100,7 +101,7 @@ Gets the currently signed-in user, if authenticated, `null` otherwise.
#### Defined in
[src/chatgpt-api.ts:126](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L126)
[src/chatgpt-api.ts:127](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L127)
___
@ -116,7 +117,7 @@ Gets the current user agent.
#### Defined in
[src/chatgpt-api.ts:141](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L141)
[src/chatgpt-api.ts:142](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L142)
## Methods
@ -133,7 +134,7 @@ is still valid.
#### Defined in
[src/chatgpt-api.ts:319](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L319)
[src/chatgpt-api.ts:359](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L359)
___
@ -160,7 +161,7 @@ The new conversation instance
#### Defined in
[src/chatgpt-api.ts:425](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L425)
[src/chatgpt-api.ts:465](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L465)
___
@ -177,7 +178,7 @@ the token fails.
#### Defined in
[src/chatgpt-api.ts:306](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L306)
[src/chatgpt-api.ts:346](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L346)
___
@ -203,7 +204,7 @@ A valid access token
#### Defined in
[src/chatgpt-api.ts:333](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L333)
[src/chatgpt-api.ts:373](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L373)
___
@ -234,4 +235,24 @@ The response from ChatGPT
#### Defined in
[src/chatgpt-api.ts:166](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-api.ts#L166)
[src/chatgpt-api.ts:167](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L167)
___
### sendModeration
**sendModeration**(`input`): `Promise`<[`ModerationsJSONResult`](../modules.md#moderationsjsonresult)\>
#### Parameters
| Name | Type |
| :------ | :------ |
| `input` | `string` |
#### Returns
`Promise`<[`ModerationsJSONResult`](../modules.md#moderationsjsonresult)\>
#### Defined in
[src/chatgpt-api.ts:303](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api.ts#L303)

Wyświetl plik

@ -0,0 +1,221 @@
[chatgpt](../readme.md) / [Exports](../modules.md) / ChatGPTAPIBrowser
# Class: ChatGPTAPIBrowser
## Table of contents
### Constructors
- [constructor](ChatGPTAPIBrowser.md#constructor)
### Methods
- [\_onRequest](ChatGPTAPIBrowser.md#_onrequest)
- [\_onResponse](ChatGPTAPIBrowser.md#_onresponse)
- [close](ChatGPTAPIBrowser.md#close)
- [getIsAuthenticated](ChatGPTAPIBrowser.md#getisauthenticated)
- [getLastMessage](ChatGPTAPIBrowser.md#getlastmessage)
- [getMessages](ChatGPTAPIBrowser.md#getmessages)
- [getPrompts](ChatGPTAPIBrowser.md#getprompts)
- [handle403Error](ChatGPTAPIBrowser.md#handle403error)
- [init](ChatGPTAPIBrowser.md#init)
- [resetThread](ChatGPTAPIBrowser.md#resetthread)
- [sendMessage](ChatGPTAPIBrowser.md#sendmessage)
## Constructors
### constructor
**new ChatGPTAPIBrowser**(`opts`)
Creates a new client wrapper for automating the ChatGPT webapp.
#### Parameters
| Name | Type | Description |
| :------ | :------ | :------ |
| `opts` | `Object` | - |
| `opts.captchaToken?` | `string` | - |
| `opts.debug?` | `boolean` | **`Default Value`** `false` * |
| `opts.email` | `string` | - |
| `opts.isGoogleLogin?` | `boolean` | - |
| `opts.markdown?` | `boolean` | **`Default Value`** `true` * |
| `opts.password` | `string` | - |
#### Defined in
[src/chatgpt-api-browser.ts:24](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L24)
## Methods
### \_onRequest
**_onRequest**(`request`): `void`
#### Parameters
| Name | Type |
| :------ | :------ |
| `request` | `HTTPRequest` |
#### Returns
`void`
#### Defined in
[src/chatgpt-api-browser.ts:125](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L125)
___
### \_onResponse
**_onResponse**(`response`): `Promise`<`void`\>
#### Parameters
| Name | Type |
| :------ | :------ |
| `response` | `HTTPResponse` |
#### Returns
`Promise`<`void`\>
#### Defined in
[src/chatgpt-api-browser.ts:162](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L162)
___
### close
**close**(): `Promise`<`void`\>
#### Returns
`Promise`<`void`\>
#### Defined in
[src/chatgpt-api-browser.ts:352](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L352)
___
### getIsAuthenticated
**getIsAuthenticated**(): `Promise`<`boolean`\>
#### Returns
`Promise`<`boolean`\>
#### Defined in
[src/chatgpt-api-browser.ts:221](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L221)
___
### getLastMessage
**getLastMessage**(): `Promise`<`string`\>
#### Returns
`Promise`<`string`\>
#### Defined in
[src/chatgpt-api-browser.ts:231](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L231)
___
### getMessages
**getMessages**(): `Promise`<`string`[]\>
#### Returns
`Promise`<`string`[]\>
#### Defined in
[src/chatgpt-api-browser.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L251)
___
### getPrompts
**getPrompts**(): `Promise`<`string`[]\>
#### Returns
`Promise`<`string`[]\>
#### Defined in
[src/chatgpt-api-browser.ts:241](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L241)
___
### handle403Error
**handle403Error**(): `Promise`<`void`\>
#### Returns
`Promise`<`void`\>
#### Defined in
[src/chatgpt-api-browser.ts:204](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L204)
___
### init
**init**(): `Promise`<`boolean`\>
#### Returns
`Promise`<`boolean`\>
#### Defined in
[src/chatgpt-api-browser.ts:55](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L55)
___
### resetThread
**resetThread**(): `Promise`<`void`\>
#### Returns
`Promise`<`void`\>
#### Defined in
[src/chatgpt-api-browser.ts:345](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L345)
___
### sendMessage
**sendMessage**(`message`, `opts?`): `Promise`<`string`\>
#### Parameters
| Name | Type |
| :------ | :------ |
| `message` | `string` |
| `opts` | `Object` |
| `opts.timeoutMs?` | `number` |
#### Returns
`Promise`<`string`\>
#### Defined in
[src/chatgpt-api-browser.ts:291](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-api-browser.ts#L291)

Wyświetl plik

@ -41,7 +41,7 @@ Creates a new conversation wrapper around the ChatGPT API.
#### Defined in
[src/chatgpt-conversation.ts:21](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L21)
[src/chatgpt-conversation.ts:21](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-conversation.ts#L21)
## Properties
@ -51,7 +51,7 @@ Creates a new conversation wrapper around the ChatGPT API.
#### Defined in
[src/chatgpt-conversation.ts:10](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L10)
[src/chatgpt-conversation.ts:10](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-conversation.ts#L10)
___
@ -61,7 +61,7 @@ ___
#### Defined in
[src/chatgpt-conversation.ts:11](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L11)
[src/chatgpt-conversation.ts:11](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-conversation.ts#L11)
___
@ -71,7 +71,7 @@ ___
#### Defined in
[src/chatgpt-conversation.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L12)
[src/chatgpt-conversation.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-conversation.ts#L12)
## Methods
@ -104,4 +104,4 @@ The response from ChatGPT
#### Defined in
[src/chatgpt-conversation.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/chatgpt-conversation.ts#L48)
[src/chatgpt-conversation.ts:48](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/chatgpt-conversation.ts#L48)

Wyświetl plik

@ -66,7 +66,7 @@ node_modules/.pnpm/typescript@4.9.3/node_modules/typescript/lib/lib.es2022.error
#### Defined in
[src/types.ts:298](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L298)
[src/types.ts:298](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L298)
___
@ -76,7 +76,7 @@ ___
#### Defined in
[src/types.ts:297](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L297)
[src/types.ts:297](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L297)
___
@ -86,7 +86,7 @@ ___
#### Defined in
[src/types.ts:295](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L295)
[src/types.ts:295](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L295)
___
@ -96,4 +96,4 @@ ___
#### Defined in
[src/types.ts:296](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L296)
[src/types.ts:296](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L296)

Wyświetl plik

@ -7,6 +7,7 @@
### Classes
- [ChatGPTAPI](classes/ChatGPTAPI.md)
- [ChatGPTAPIBrowser](classes/ChatGPTAPIBrowser.md)
- [ChatGPTConversation](classes/ChatGPTConversation.md)
- [ChatGPTError](classes/ChatGPTError.md)
@ -39,9 +40,13 @@
### Functions
- [defaultChromeExecutablePath](modules.md#defaultchromeexecutablepath)
- [getBrowser](modules.md#getbrowser)
- [getOpenAIAuth](modules.md#getopenaiauth)
- [isRelevantRequest](modules.md#isrelevantrequest)
- [markdownToText](modules.md#markdowntotext)
- [maximizePage](modules.md#maximizepage)
- [minimizePage](modules.md#minimizepage)
## Type Aliases
@ -51,7 +56,7 @@
#### Defined in
[src/types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L109)
[src/types.ts:109](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L109)
___
@ -61,7 +66,7 @@ ___
#### Defined in
[src/types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L1)
[src/types.ts:1](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L1)
___
@ -83,7 +88,7 @@ https://chat.openapi.com/backend-api/conversation
#### Defined in
[src/types.ts:134](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L134)
[src/types.ts:134](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L134)
___
@ -101,7 +106,7 @@ ___
#### Defined in
[src/types.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L251)
[src/types.ts:251](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L251)
___
@ -126,7 +131,7 @@ ___
#### Defined in
[src/types.ts:257](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L257)
[src/types.ts:257](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L257)
___
@ -136,7 +141,7 @@ ___
#### Defined in
[src/types.ts:276](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L276)
[src/types.ts:276](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L276)
___
@ -153,7 +158,7 @@ ___
#### Defined in
[src/types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L270)
[src/types.ts:270](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L270)
___
@ -175,7 +180,7 @@ https://chat.openapi.com/backend-api/conversation/message_feedback
#### Defined in
[src/types.ts:193](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L193)
[src/types.ts:193](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L193)
___
@ -185,7 +190,7 @@ ___
#### Defined in
[src/types.ts:249](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L249)
[src/types.ts:249](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L249)
___
@ -205,7 +210,7 @@ ___
#### Defined in
[src/types.ts:222](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L222)
[src/types.ts:222](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L222)
___
@ -215,7 +220,7 @@ ___
#### Defined in
[src/types.ts:220](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L220)
[src/types.ts:220](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L220)
___
@ -225,7 +230,7 @@ ___
#### Defined in
[src/types.ts:275](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L275)
[src/types.ts:275](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L275)
___
@ -243,7 +248,7 @@ ___
#### Defined in
[src/types.ts:77](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L77)
[src/types.ts:77](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L77)
___
@ -261,7 +266,7 @@ https://chat.openapi.com/backend-api/models
#### Defined in
[src/types.ts:70](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L70)
[src/types.ts:70](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L70)
___
@ -280,7 +285,7 @@ https://chat.openapi.com/backend-api/moderations
#### Defined in
[src/types.ts:97](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L97)
[src/types.ts:97](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L97)
___
@ -300,7 +305,7 @@ https://chat.openapi.com/backend-api/moderations
#### Defined in
[src/types.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L114)
[src/types.ts:114](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L114)
___
@ -322,7 +327,7 @@ to authenticate with the unofficial ChatGPT API.
#### Defined in
[src/openai-auth.ts:17](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/openai-auth.ts#L17)
[src/openai-auth.ts:20](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/openai-auth.ts#L20)
___
@ -340,7 +345,7 @@ ___
#### Defined in
[src/types.ts:161](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L161)
[src/types.ts:161](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L161)
___
@ -357,7 +362,7 @@ ___
#### Defined in
[src/types.ts:178](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L178)
[src/types.ts:178](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L178)
___
@ -367,7 +372,7 @@ ___
#### Defined in
[src/types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L3)
[src/types.ts:3](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L3)
___
@ -377,7 +382,7 @@ ___
#### Defined in
[src/types.ts:289](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L289)
[src/types.ts:289](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L289)
___
@ -400,7 +405,7 @@ ___
#### Defined in
[src/types.ts:278](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L278)
[src/types.ts:278](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L278)
___
@ -421,7 +426,7 @@ https://chat.openapi.com/api/auth/session
#### Defined in
[src/types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L8)
[src/types.ts:8](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L8)
___
@ -443,13 +448,29 @@ ___
#### Defined in
[src/types.ts:30](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/types.ts#L30)
[src/types.ts:30](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/types.ts#L30)
## Functions
### defaultChromeExecutablePath
**defaultChromeExecutablePath**(): `string`
Gets the default path to chrome's executable for the current platform.
#### Returns
`string`
#### Defined in
[src/openai-auth.ts:216](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/openai-auth.ts#L216)
___
### getBrowser
**getBrowser**(`launchOptions?`): `Promise`<`Browser`\>
**getBrowser**(`opts?`): `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
@ -459,7 +480,7 @@ recognizes it and blocks access.
| Name | Type |
| :------ | :------ |
| `launchOptions?` | `PuppeteerLaunchOptions` |
| `opts` | `PuppeteerLaunchOptions` & { `captchaToken?`: `string` } |
#### Returns
@ -467,7 +488,7 @@ recognizes it and blocks access.
#### Defined in
[src/openai-auth.ts:127](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/openai-auth.ts#L127)
[src/openai-auth.ts:182](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/openai-auth.ts#L182)
___
@ -495,7 +516,10 @@ with your updated credentials.
| :------ | :------ |
| `__namedParameters` | `Object` |
| `__namedParameters.browser?` | `Browser` |
| `__namedParameters.captchaToken?` | `string` |
| `__namedParameters.email?` | `string` |
| `__namedParameters.isGoogleLogin?` | `boolean` |
| `__namedParameters.page?` | `Page` |
| `__namedParameters.password?` | `string` |
| `__namedParameters.timeoutMs?` | `number` |
@ -505,7 +529,27 @@ with your updated credentials.
#### Defined in
[src/openai-auth.ts:39](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/openai-auth.ts#L39)
[src/openai-auth.ts:42](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/openai-auth.ts#L42)
___
### isRelevantRequest
**isRelevantRequest**(`url`): `boolean`
#### Parameters
| Name | Type |
| :------ | :------ |
| `url` | `string` |
#### Returns
`boolean`
#### Defined in
[src/utils.ts:32](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/utils.ts#L32)
___
@ -525,4 +569,44 @@ ___
#### Defined in
[src/utils.ts:4](https://github.com/transitive-bullshit/chatgpt-api/blob/c257286/src/utils.ts#L4)
[src/utils.ts:5](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/utils.ts#L5)
___
### maximizePage
**maximizePage**(`page`): `Promise`<`void`\>
#### Parameters
| Name | Type |
| :------ | :------ |
| `page` | `Page` |
#### Returns
`Promise`<`void`\>
#### Defined in
[src/utils.ts:22](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/utils.ts#L22)
___
### minimizePage
**minimizePage**(`page`): `Promise`<`void`\>
#### Parameters
| Name | Type |
| :------ | :------ |
| `page` | `Page` |
#### Returns
`Promise`<`void`\>
#### Defined in
[src/utils.ts:12](https://github.com/transitive-bullshit/chatgpt-api/blob/16d1699/src/utils.ts#L12)

Wyświetl plik

@ -1,11 +1,33 @@
chatgpt / [Exports](modules.md)
# Update December 12, 2022 <!-- omit in toc -->
# Update December 15, 2022 <!-- omit in toc -->
Yesterday, OpenAI added additional Cloudflare protections that make it more difficult to access the unofficial API.
On December 11th, OpenAI added 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. 🔥
Even with this in place, however, it's not uncommon to run into 429 / 403 errors at the moment using the `getOpenAIAuth` + `ChatGPTAPI` approach.
To circumvent these issues, we've also added a full browser-based solution, which uses Puppeteer to log into the webapp and fully automate the web UI.
The full browser version is working consistently and can be used via:
```ts
import { ChatGPTAPIBrowser } from 'chatgpt'
const api = new ChatGPTAPIBrowser({
email: process.env.OPENAI_EMAIL,
password: process.env.OPENAI_PASSWORD
})
await api.init()
const response = await api.sendMessage('Hello World!')
```
Note that this solution is not lightweight, but it does work a lot more consistently than the REST API-based versions. I'm currently using this solution to power 10 OpenAI accounts concurrently across 10 minimized Chrome windows for my [Twitter bot](https://github.com/transitive-bullshit/chatgpt-twitter-bot). 😂
If you get a "ChatGPT is at capacity" error when logging in, note that this is also happening quite frequently on the official webapp. Their servers are overloaded, and we're all trying our best to offer access to this amazing technology.
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. 💪
@ -13,7 +35,8 @@ We're working hard to improve this process (especially CAPTCHA automation). Keep
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
[Travis](https://twitter.com/transitive_bs)
---
@ -33,7 +56,7 @@ Travis
- [Docs](#docs)
- [Demos](#demos)
- [Authentication](#authentication)
- [Restrictions](#restrictions)
- [Restrictions](#restrictions)
- [Projects](#projects)
- [Compatibility](#compatibility)
- [Credits](#credits)
@ -78,6 +101,25 @@ async function example() {
}
```
Or, if you want to try the full browser-based solution:
```ts
import { ChatGPTAPIBrowser } from 'chatgpt'
async function example() {
// use puppeteer to bypass cloudflare (headful because of captchas)
const api = new ChatGPTAPIBrowser({
email: process.env.OPENAI_EMAIL,
password: process.env.OPENAI_PASSWORD
})
await api.init()
const response = await api.sendMessage('Hello World!')
console.log(response)
}
```
ChatGPT responses are formatted as markdown by default. If you want to work with plaintext instead, you can use:
```ts
@ -191,9 +233,11 @@ Pass `sessionToken`, `clearanceToken`, and `userAgent` to the `ChatGPTAPI` const
> **Note**
> This package will switch to using the official API once it's released, which will make this process much simpler.
#### Restrictions
### Restrictions
**Please read these carefully**
These restrictions are for the `getOpenAIAuth` + `ChatGPTAPI` solution, which uses the unofficial API. The browser-based solution, `ChatGPTAPIBrowser`, doesn't have many of these restrictions, though you'll still have to manually bypass CAPTCHAs by hand.
**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.
@ -211,10 +255,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))
- [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)
@ -236,7 +282,6 @@ All of these awesome projects are built using the `chatgpt` package. 🤯
- [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)
@ -249,6 +294,8 @@ All of these awesome projects are built using the `chatgpt` package. 🤯
- [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.

Wyświetl plik

@ -38,8 +38,10 @@
"delay": "^5.0.0",
"eventsource-parser": "^0.0.5",
"expiry-map": "^2.0.0",
"html-to-md": "^0.8.3",
"p-timeout": "^6.0.0",
"puppeteer-extra": "^3.3.4",
"puppeteer-extra-plugin-recaptcha": "^3.6.6",
"puppeteer-extra-plugin-stealth": "^2.11.1",
"remark": "^14.0.2",
"strip-markdown": "^5.0.0",

Wyświetl plik

@ -10,6 +10,7 @@ specifiers:
dotenv-safe: ^8.2.0
eventsource-parser: ^0.0.5
expiry-map: ^2.0.0
html-to-md: ^0.8.3
husky: ^8.0.2
lint-staged: ^13.0.3
npm-run-all: ^4.1.5
@ -18,6 +19,7 @@ specifiers:
prettier: ^2.8.0
puppeteer: ^19.4.0
puppeteer-extra: ^3.3.4
puppeteer-extra-plugin-recaptcha: ^3.6.6
puppeteer-extra-plugin-stealth: ^2.11.1
remark: ^14.0.2
strip-markdown: ^5.0.0
@ -32,8 +34,10 @@ dependencies:
delay: 5.0.0
eventsource-parser: 0.0.5
expiry-map: 2.0.0
html-to-md: 0.8.3
p-timeout: 6.0.0
puppeteer-extra: 3.3.4_puppeteer@19.4.0
puppeteer-extra-plugin-recaptcha: 3.6.6_puppeteer-extra@3.3.4
puppeteer-extra-plugin-stealth: 2.11.1_puppeteer-extra@3.3.4
remark: 14.0.2
strip-markdown: 5.0.0
@ -1789,6 +1793,10 @@ packages:
lru-cache: 6.0.0
dev: true
/html-to-md/0.8.3:
resolution: {integrity: sha512-Va+bB1YOdD6vMRDue9/l7YxbERgwOgsos4erUDRfRN6YE0B2Wbbw8uAj6xZJk9A9vrjVy7mG/WLlhDw6RXfgsA==}
dev: false
/https-proxy-agent/5.0.1:
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
engines: {node: '>= 6'}
@ -3065,6 +3073,26 @@ packages:
- supports-color
- utf-8-validate
/puppeteer-extra-plugin-recaptcha/3.6.6_puppeteer-extra@3.3.4:
resolution: {integrity: sha512-SVbmL+igGX8m0Qg9dn85trWDghbfUCTG/QUHYscYx5XgMZVVb0/v0a6MqbPdHoKmBx5BS2kLd6rorMlncMcXdw==}
engines: {node: '>=9.11.2'}
peerDependencies:
playwright-extra: '*'
puppeteer-extra: '*'
peerDependenciesMeta:
playwright-extra:
optional: true
puppeteer-extra:
optional: true
dependencies:
debug: 4.3.4
merge-deep: 3.0.3
puppeteer-extra: 3.3.4_puppeteer@19.4.0
puppeteer-extra-plugin: 3.2.2_puppeteer-extra@3.3.4
transitivePeerDependencies:
- supports-color
dev: false
/puppeteer-extra-plugin-stealth/2.11.1_puppeteer-extra@3.3.4:
resolution: {integrity: sha512-n0wdC0Ilc9tk5L6FWLyd0P2gT8b2fp+2NuB+KB0oTSw3wXaZ0D6WNakjJsayJ4waGzIJFCUHkmK9zgx5NKMoFw==}
engines: {node: '>=8'}

Wyświetl plik

@ -1,9 +1,31 @@
# Update December 12, 2022 <!-- omit in toc -->
# Update December 15, 2022 <!-- omit in toc -->
Yesterday, OpenAI added additional Cloudflare protections that make it more difficult to access the unofficial API.
On December 11th, OpenAI added 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. 🔥
Even with this in place, however, it's not uncommon to run into 429 / 403 errors at the moment using the `getOpenAIAuth` + `ChatGPTAPI` approach.
To circumvent these issues, we've also added a full browser-based solution, which uses Puppeteer to log into the webapp and fully automate the web UI.
The full browser version is working consistently and can be used via:
```ts
import { ChatGPTAPIBrowser } from 'chatgpt'
const api = new ChatGPTAPIBrowser({
email: process.env.OPENAI_EMAIL,
password: process.env.OPENAI_PASSWORD
})
await api.init()
const response = await api.sendMessage('Hello World!')
```
Note that this solution is not lightweight, but it does work a lot more consistently than the REST API-based versions. I'm currently using this solution to power 10 OpenAI accounts concurrently across 10 minimized Chrome windows for my [Twitter bot](https://github.com/transitive-bullshit/chatgpt-twitter-bot). 😂
If you get a "ChatGPT is at capacity" error when logging in, note that this is also happening quite frequently on the official webapp. Their servers are overloaded, and we're all trying our best to offer access to this amazing technology.
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. 💪
@ -11,7 +33,8 @@ We're working hard to improve this process (especially CAPTCHA automation). Keep
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
[Travis](https://twitter.com/transitive_bs)
---
@ -76,6 +99,25 @@ async function example() {
}
```
Or, if you want to try the full browser-based solution:
```ts
import { ChatGPTAPIBrowser } from 'chatgpt'
async function example() {
// use puppeteer to bypass cloudflare (headful because of captchas)
const api = new ChatGPTAPIBrowser({
email: process.env.OPENAI_EMAIL,
password: process.env.OPENAI_PASSWORD
})
await api.init()
const response = await api.sendMessage('Hello World!')
console.log(response)
}
```
ChatGPT responses are formatted as markdown by default. If you want to work with plaintext instead, you can use:
```ts
@ -137,7 +179,7 @@ async function example() {
### Docs
See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on methods and parameters.
See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on methods and parameters. Here are the [docs](./docs/classes/ChatGPTAPIBrowser.md) for the browser-based version.
### Demos
@ -191,6 +233,10 @@ Pass `sessionToken`, `clearanceToken`, and `userAgent` to the `ChatGPTAPI` const
### Restrictions
These restrictions are for the `getOpenAIAuth` + `ChatGPTAPI` solution, which uses the unofficial API. The browser-based solution, `ChatGPTAPIBrowser`, doesn't have many of these restrictions, though you'll still have to manually bypass CAPTCHAs by hand.
Note: currently `ChatGPTAPIBrowser` doesn't support continuing arbitrary conversations based on `conversationId`. You can only continue conversations in the current tab or start new conversations using the `resetThread()` function.
**Please read carefully**
- You must use `node >= 18` at the moment. I'm using `v19.2.0` in my testing.

Wyświetl plik

@ -0,0 +1,362 @@
import delay from 'delay'
import html2md from 'html-to-md'
import pTimeout from 'p-timeout'
import type { Browser, HTTPRequest, HTTPResponse, Page } from 'puppeteer'
import { getBrowser, getOpenAIAuth } from './openai-auth'
import { isRelevantRequest, maximizePage, minimizePage } from './utils'
export class ChatGPTAPIBrowser {
protected _markdown: boolean
protected _debug: boolean
protected _isGoogleLogin: boolean
protected _captchaToken: string
protected _email: string
protected _password: string
protected _browser: Browser
protected _page: Page
/**
* Creates a new client wrapper for automating the ChatGPT webapp.
*/
constructor(opts: {
email: string
password: string
/** @defaultValue `true` **/
markdown?: boolean
/** @defaultValue `false` **/
debug?: boolean
isGoogleLogin?: boolean
captchaToken?: string
}) {
const {
email,
password,
markdown = true,
debug = false,
isGoogleLogin = false,
captchaToken
} = opts
this._email = email
this._password = password
this._markdown = !!markdown
this._debug = !!debug
this._isGoogleLogin = !!isGoogleLogin
this._captchaToken = captchaToken
}
async init() {
if (this._browser) {
await this._browser.close()
this._page = null
this._browser = null
}
try {
this._browser = await getBrowser({ captchaToken: this._captchaToken })
this._page =
(await this._browser.pages())[0] || (await this._browser.newPage())
// bypass cloudflare and login
await getOpenAIAuth({
email: this._email,
password: this._password,
browser: this._browser,
page: this._page,
isGoogleLogin: this._isGoogleLogin
})
} catch (err) {
if (this._browser) {
await this._browser.close()
}
this._browser = null
this._page = null
throw err
}
const chatUrl = 'https://chat.openai.com/chat'
const url = this._page.url().replace(/\/$/, '')
if (url !== chatUrl) {
await this._page.goto(chatUrl, {
waitUntil: 'networkidle0'
})
}
// dismiss welcome modal
do {
const modalSelector = '[data-headlessui-state="open"]'
if (!(await this._page.$(modalSelector))) {
break
}
try {
await this._page.click(`${modalSelector} button:last-child`)
} catch (err) {
// "next" button not found in welcome modal
break
}
await delay(500)
} while (true)
if (!this.getIsAuthenticated()) {
return false
}
await minimizePage(this._page)
this._page.on('request', this._onRequest.bind(this))
this._page.on('response', this._onResponse.bind(this))
return true
}
_onRequest = (request: HTTPRequest) => {
const url = request.url()
if (!isRelevantRequest(url)) {
return
}
const method = request.method()
let body: any
if (method === 'POST') {
body = request.postData()
try {
body = JSON.parse(body)
} catch (_) {}
// if (url.endsWith('/conversation') && typeof body === 'object') {
// const conversationBody: types.ConversationJSONBody = body
// const conversationId = conversationBody.conversation_id
// const parentMessageId = conversationBody.parent_message_id
// const messageId = conversationBody.messages?.[0]?.id
// const prompt = conversationBody.messages?.[0]?.content?.parts?.[0]
// // TODO: store this info for the current sendMessage request
// }
}
if (this._debug) {
console.log('\nrequest', {
url,
method,
headers: request.headers(),
body
})
}
}
_onResponse = async (response: HTTPResponse) => {
const request = response.request()
const url = response.url()
if (!isRelevantRequest(url)) {
return
}
const status = response.status()
let body: any
try {
body = await response.json()
} catch (_) {}
if (this._debug) {
console.log('\nresponse', {
url,
ok: response.ok(),
status,
statusText: response.statusText(),
headers: response.headers(),
body,
request: {
method: request.method(),
headers: request.headers(),
body: request.postData()
}
})
}
if (url.endsWith('/conversation')) {
if (status === 403) {
await this.handle403Error()
}
} else if (url.endsWith('api/auth/session')) {
if (status === 403) {
await this.handle403Error()
}
}
}
async handle403Error() {
console.log(`ChatGPT "${this._email}" session expired; refreshing...`)
try {
await maximizePage(this._page)
await this._page.reload({
waitUntil: 'networkidle0',
timeout: 2 * 60 * 1000 // 2 minutes
})
await minimizePage(this._page)
} catch (err) {
console.error(
`ChatGPT "${this._email}" error refreshing session`,
err.toString()
)
}
}
async getIsAuthenticated() {
try {
const inputBox = await this._getInputBox()
return !!inputBox
} catch (err) {
// can happen when navigating during login
return false
}
}
async getLastMessage(): Promise<string | null> {
const messages = await this.getMessages()
if (messages) {
return messages[messages.length - 1]
} else {
return null
}
}
async getPrompts(): Promise<string[]> {
// Get all prompts
const messages = await this._page.$$(
'.text-base:has(.whitespace-pre-wrap):not(:has(button:nth-child(2))) .whitespace-pre-wrap'
)
// Prompts are always plaintext
return Promise.all(messages.map((a) => a.evaluate((el) => el.textContent)))
}
async getMessages(): Promise<string[]> {
// Get all complete messages
// (in-progress messages that are being streamed back don't contain action buttons)
const messages = await this._page.$$(
'.text-base:has(.whitespace-pre-wrap):has(button:nth-child(2)) .whitespace-pre-wrap'
)
if (this._markdown) {
const htmlMessages = await Promise.all(
messages.map((a) => a.evaluate((el) => el.innerHTML))
)
const markdownMessages = htmlMessages.map((messageHtml) => {
// parse markdown from message HTML
messageHtml = messageHtml.replace('Copy code</button>', '</button>')
return html2md(messageHtml, {
ignoreTags: [
'button',
'svg',
'style',
'form',
'noscript',
'script',
'meta',
'head'
],
skipTags: ['button', 'svg']
})
})
return markdownMessages
} else {
// plaintext
const plaintextMessages = await Promise.all(
messages.map((a) => a.evaluate((el) => el.textContent))
)
return plaintextMessages
}
}
async sendMessage(
message: string,
opts: {
timeoutMs?: number
} = {}
): Promise<string> {
const { timeoutMs } = opts
const inputBox = await this._getInputBox()
if (!inputBox) throw new Error('not signed in')
const lastMessage = await this.getLastMessage()
await inputBox.focus()
const paragraphs = message.split('\n')
for (let i = 0; i < paragraphs.length; i++) {
await inputBox.type(paragraphs[i], { delay: 0 })
if (i < paragraphs.length - 1) {
await this._page.keyboard.down('Shift')
await inputBox.press('Enter')
await this._page.keyboard.up('Shift')
} else {
await inputBox.press('Enter')
}
}
const responseP = new Promise<string>(async (resolve, reject) => {
try {
do {
await delay(1000)
// TODO: this logic needs some work because we can have repeat messages...
const newLastMessage = await this.getLastMessage()
if (
newLastMessage &&
lastMessage?.toLowerCase() !== newLastMessage?.toLowerCase()
) {
return resolve(newLastMessage)
}
} while (true)
} catch (err) {
return reject(err)
}
})
if (timeoutMs) {
return pTimeout(responseP, {
milliseconds: timeoutMs
})
} else {
return responseP
}
}
async resetThread() {
const resetButton = await this._page.$('nav > a:nth-child(1)')
if (!resetButton) throw new Error('not signed in')
await resetButton.click()
}
async close() {
await this._browser.close()
this._page = null
this._browser = null
}
protected async _getInputBox() {
// [data-id="root"]
return this._page.$('textarea')
}
}

Wyświetl plik

@ -95,6 +95,7 @@ export class ChatGPTAPI {
'user-agent': this._userAgent,
'x-openai-assistant-app-id': '',
'accept-language': 'en-US,en;q=0.9',
'accept-encoding': 'gzip, deflate, br',
origin: 'https://chat.openai.com',
referer: 'https://chat.openai.com/chat',
'sec-ch-ua':
@ -299,6 +300,45 @@ export class ChatGPTAPI {
}
}
async sendModeration(input: string) {
const accessToken = await this.refreshAccessToken()
const url = `${this._backendApiBaseUrl}/moderations`
const headers = {
...this._headers,
Authorization: `Bearer ${accessToken}`,
Accept: '*/*',
'Content-Type': 'application/json',
Cookie: `cf_clearance=${this._clearanceToken}`
}
const body: types.ModerationsJSONBody = {
input,
model: 'text-moderation-playground'
}
if (this._debug) {
console.log('POST', url, headers, body)
}
const res = await fetch(url, {
method: 'POST',
headers,
body: JSON.stringify(body)
}).then((r) => {
if (!r.ok) {
const error = new types.ChatGPTError(`${r.status} ${r.statusText}`)
error.response = r
error.statusCode = r.status
error.statusText = r.statusText
throw error
}
return r.json() as any as types.ModerationsJSONResult
})
return res
}
/**
* @returns `true` if the client has a valid acces token or `false` if refreshing
* the token fails.

Wyświetl plik

@ -1,4 +1,5 @@
export * from './chatgpt-api'
export * from './chatgpt-api-browser'
export * from './chatgpt-conversation'
export * from './types'
export * from './utils'

Wyświetl plik

@ -2,20 +2,17 @@ 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 type { Browser, Page, Protocol, PuppeteerLaunchOptions } from 'puppeteer'
import puppeteer from 'puppeteer-extra'
import RecaptchaPlugin from 'puppeteer-extra-plugin-recaptcha'
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
import * as types from './types'
puppeteer.use(StealthPlugin())
let hasRecaptchaPlugin = false
/**
* Represents everything that's required to pass into `ChatGPTAPI` in order
* to authenticate with the unofficial ChatGPT API.
@ -46,47 +43,65 @@ export async function getOpenAIAuth({
email,
password,
browser,
page,
timeoutMs = 2 * 60 * 1000,
isGoogleLogin = false
isGoogleLogin = false,
captchaToken = process.env.CAPTCHA_TOKEN
}: {
email?: string
password?: string
browser?: Browser
page?: Page
timeoutMs?: number
isGoogleLogin?: boolean
captchaToken?: string
}): Promise<OpenAIAuth> {
let page: Page
let origBrowser = browser
const origBrowser = browser
const origPage = page
try {
if (!browser) {
browser = await getBrowser()
browser = await getBrowser({ captchaToken })
}
const userAgent = await browser.userAgent()
page = (await browser.pages())[0] || (await browser.newPage())
page.setDefaultTimeout(timeoutMs)
if (!page) {
page = (await browser.pages())[0] || (await browser.newPage())
page.setDefaultTimeout(timeoutMs)
}
await page.goto('https://chat.openai.com/auth/login')
await page.goto('https://chat.openai.com/auth/login', {
waitUntil: 'networkidle0'
})
// NOTE: this is where you may encounter a CAPTCHA
if (hasRecaptchaPlugin) {
await page.solveRecaptchas()
}
await checkForChatGPTAtCapacity(page)
await page.waitForSelector('#__next .btn-primary', { timeout: timeoutMs })
// once we get to this point, the Cloudflare cookies are available
await delay(1000)
// once we get to this point, the Cloudflare cookies should be available
// login as well (optional)
if (email && password) {
await waitForConditionOrAtCapacity(page, () =>
page.waitForSelector('#__next .btn-primary', { timeout: timeoutMs })
)
await delay(500)
// click login button and wait for navigation to finish
await Promise.all([
page.click('#__next .btn-primary'),
page.waitForNavigation({
waitUntil: 'networkidle0'
})
}),
page.click('#__next .btn-primary')
])
let submitP: Promise<void>
await checkForChatGPTAtCapacity(page)
let submitP: () => Promise<void>
if (isGoogleLogin) {
await page.click('button[data-provider="google"]')
@ -98,61 +113,35 @@ export async function getOpenAIAuth({
])
await page.waitForSelector('input[type="password"]', { visible: true })
await page.type('input[type="password"]', password, { delay: 10 })
submitP = page.keyboard.press('Enter')
submitP = () => page.keyboard.press('Enter')
} else {
await page.waitForSelector('#username')
await page.type('#username', email, { delay: 10 })
await page.type('#username', email, { delay: 20 })
await delay(100)
if (hasRecaptchaPlugin) {
// console.log('solveRecaptchas()')
const res = await page.solveRecaptchas()
// console.log('solveRecaptchas result', res)
}
await page.click('button[type="submit"]')
await page.waitForSelector('#password')
await page.type('#password', password, { delay: 10 })
submitP = page.click('button[type="submit"]')
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)
})
waitForConditionOrAtCapacity(page, () =>
page.waitForNavigation({
waitUntil: 'networkidle0'
})
),
submitP()
])
} else {
await delay(2000)
await checkForChatGPTAtCapacity(page)
}
const pageCookies = await page.cookies()
@ -170,11 +159,10 @@ export async function getOpenAIAuth({
return authInfo
} catch (err) {
console.error(err)
throw err
} finally {
if (origBrowser) {
if (page) {
if (page && page !== origPage) {
await page.close()
}
} else if (browser) {
@ -191,7 +179,28 @@ export async function getOpenAIAuth({
* 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(
opts: PuppeteerLaunchOptions & {
captchaToken?: string
} = {}
) {
const { captchaToken = process.env.CAPTCHA_TOKEN, ...launchOptions } = opts
if (captchaToken && !hasRecaptchaPlugin) {
hasRecaptchaPlugin = true
// console.log('use captcha', captchaToken)
puppeteer.use(
RecaptchaPlugin({
provider: {
id: '2captcha',
token: captchaToken
},
visualFeedback: true // colorize reCAPTCHAs (violet = detected, green = solved)
})
)
}
return puppeteer.launch({
headless: false,
args: ['--no-sandbox', '--exclude-switches', 'enable-automation'],
@ -212,31 +221,89 @@ export const defaultChromeExecutablePath = (): string => {
case 'darwin':
return '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
default:
default: {
/**
* Since two (2) separate chrome releases exists on linux
* we first do a check to ensure we're executing the right one.
* Since two (2) separate chrome releases exist 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<Node>[]
// console.log('checkForChatGPTAtCapacity', page.url())
let res: any[]
try {
res = await page.$x("//div[contains(., 'ChatGPT is at capacity')]")
// console.log('capacity1', els)
// if (els?.length) {
// res = await Promise.all(
// els.map((a) => a.evaluate((el) => el.textContent))
// )
// console.log('capacity2', res)
// }
} catch (err) {
// ignore errors likely due to navigation
}
if (res?.length) {
const error = new types.ChatGPTError(`ChatGPT is at capacity: ${res}`)
const error = new types.ChatGPTError('ChatGPT is at capacity')
error.statusCode = 503
throw error
}
}
async function waitForConditionOrAtCapacity(
page: Page,
condition: () => Promise<any>,
opts: {
pollingIntervalMs?: number
} = {}
) {
const { pollingIntervalMs = 500 } = opts
return new Promise<void>((resolve, reject) => {
let resolved = false
async function waitForCapacityText() {
if (resolved) {
return
}
try {
await checkForChatGPTAtCapacity(page)
if (!resolved) {
setTimeout(waitForCapacityText, pollingIntervalMs)
}
} catch (err) {
if (!resolved) {
resolved = true
return reject(err)
}
}
}
condition()
.then(() => {
if (!resolved) {
resolved = true
resolve()
}
})
.catch((err) => {
if (!resolved) {
resolved = true
reject(err)
}
})
setTimeout(waitForCapacityText, pollingIntervalMs)
})
}

Wyświetl plik

@ -1,3 +1,4 @@
import type { Page } from 'puppeteer'
import { remark } from 'remark'
import stripMarkdown from 'strip-markdown'
@ -7,3 +8,49 @@ export function markdownToText(markdown?: string): string {
.processSync(markdown ?? '')
.toString()
}
export async function minimizePage(page: Page) {
const session = await page.target().createCDPSession()
const goods = await session.send('Browser.getWindowForTarget')
const { windowId } = goods
await session.send('Browser.setWindowBounds', {
windowId,
bounds: { windowState: 'minimized' }
})
}
export async function maximizePage(page: Page) {
const session = await page.target().createCDPSession()
const goods = await session.send('Browser.getWindowForTarget')
const { windowId } = goods
await session.send('Browser.setWindowBounds', {
windowId,
bounds: { windowState: 'normal' }
})
}
export function isRelevantRequest(url: string): boolean {
let pathname
try {
const parsedUrl = new URL(url)
pathname = parsedUrl.pathname
url = parsedUrl.toString()
} catch (_) {
return false
}
if (!url.startsWith('https://chat.openai.com')) {
return false
}
if (pathname.startsWith('/_next')) {
return false
}
if (pathname.endsWith('backend-api/moderations')) {
return false
}
return true
}