kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
Merge pull request #142 from transitive-bullshit/feature/improve-robustness
commit
278ed47dc5
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
142
docs/modules.md
142
docs/modules.md
|
@ -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)
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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'}
|
||||
|
|
54
readme.md
54
readme.md
|
@ -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.
|
||||
|
|
|
@ -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')
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export * from './chatgpt-api'
|
||||
export * from './chatgpt-api-browser'
|
||||
export * from './chatgpt-conversation'
|
||||
export * from './types'
|
||||
export * from './utils'
|
||||
|
|
|
@ -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)
|
||||
})
|
||||
}
|
||||
|
|
47
src/utils.ts
47
src/utils.ts
|
@ -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
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue