Merge pull request #166 from waylaidwanderer/feat/add-proxy-support

pull/168/head
Travis Fischer 2022-12-18 17:25:23 -06:00 zatwierdzone przez GitHub
commit 29ace8f990
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 4AEE18F83AFDEB23
5 zmienionych plików z 13904 dodań i 4 usunięć

1
.gitignore vendored
Wyświetl plik

@ -1,6 +1,7 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
*.swp *.swp
.idea
# dependencies # dependencies
/node_modules /node_modules

13840
package-lock.json wygenerowano 100644

Plik diff jest za duży Load Diff

Wyświetl plik

@ -47,6 +47,7 @@ Thanks && cheers,
- [Demos](#demos) - [Demos](#demos)
- [Authentication](#authentication) - [Authentication](#authentication)
- [CAPTCHAs](#captchas) - [CAPTCHAs](#captchas)
- [Using Proxies](#using-proxies)
- [Restrictions](#restrictions) - [Restrictions](#restrictions)
- [Projects](#projects) - [Projects](#projects)
- [Compatibility](#compatibility) - [Compatibility](#compatibility)
@ -264,6 +265,22 @@ const api = new ChatGPTAPIBrowser({
}) })
``` ```
### Using Proxies
The browser implementation supports setting a proxy server. This is useful if you're running into rate limiting issues or if you want to use a proxy to hide your IP address.
To use a proxy, pass the `proxyServer` option to the `ChatGPTAPIBrowser` constructor, or simply set the `PROXY_SERVER` env var. For more information on the format, see [here](https://www.chromium.org/developers/design-documents/network-settings).
```ts
const api = new ChatGPTAPIBrowser({
email: process.env.OPENAI_EMAIL,
password: process.env.OPENAI_PASSWORD,
proxyServer: '<ip>:<port>'
})
```
You can also set the `PROXY_VALIDATE_IP` env var to your proxy's IP address. This will be used to validate that the proxy is working correctly, and will throw an error if it's not.
### Restrictions ### Restrictions
These restrictions are for the `getOpenAIAuth` + `ChatGPTAPI` solution, which uses the unofficial API. The browser-based solution, `ChatGPTAPIBrowser`, generally doesn't have any of these restrictions. These restrictions are for the `getOpenAIAuth` + `ChatGPTAPI` solution, which uses the unofficial API. The browser-based solution, `ChatGPTAPIBrowser`, generally doesn't have any of these restrictions.

Wyświetl plik

@ -29,6 +29,7 @@ export class ChatGPTAPIBrowser extends AChatGPTAPI {
protected _executablePath: string protected _executablePath: string
protected _browser: Browser protected _browser: Browser
protected _page: Page protected _page: Page
protected _proxyServer: string
/** /**
* Creates a new client for automating the ChatGPT webapp. * Creates a new client for automating the ChatGPT webapp.
@ -54,6 +55,9 @@ export class ChatGPTAPIBrowser extends AChatGPTAPI {
/** @defaultValue `undefined` **/ /** @defaultValue `undefined` **/
executablePath?: string executablePath?: string
/** @defaultValue `undefined` **/
proxyServer?: string
}) { }) {
super() super()
@ -65,7 +69,8 @@ export class ChatGPTAPIBrowser extends AChatGPTAPI {
isGoogleLogin = false, isGoogleLogin = false,
minimize = true, minimize = true,
captchaToken, captchaToken,
executablePath executablePath,
proxyServer
} = opts } = opts
this._email = email this._email = email
@ -77,6 +82,7 @@ export class ChatGPTAPIBrowser extends AChatGPTAPI {
this._minimize = !!minimize this._minimize = !!minimize
this._captchaToken = captchaToken this._captchaToken = captchaToken
this._executablePath = executablePath this._executablePath = executablePath
this._proxyServer = proxyServer
if (!this._email) { if (!this._email) {
const error = new types.ChatGPTError('ChatGPT invalid email') const error = new types.ChatGPTError('ChatGPT invalid email')
@ -99,7 +105,8 @@ export class ChatGPTAPIBrowser extends AChatGPTAPI {
try { try {
this._browser = await getBrowser({ this._browser = await getBrowser({
captchaToken: this._captchaToken, captchaToken: this._captchaToken,
executablePath: this._executablePath executablePath: this._executablePath,
proxyServer: this._proxyServer
}) })
this._page = this._page =
(await this._browser.pages())[0] || (await this._browser.newPage()) (await this._browser.pages())[0] || (await this._browser.newPage())

Wyświetl plik

@ -55,7 +55,8 @@ export async function getOpenAIAuth({
isGoogleLogin = false, isGoogleLogin = false,
captchaToken = process.env.CAPTCHA_TOKEN, captchaToken = process.env.CAPTCHA_TOKEN,
nopechaKey = process.env.NOPECHA_KEY, nopechaKey = process.env.NOPECHA_KEY,
executablePath executablePath,
proxyServer = process.env.PROXY_SERVER
}: { }: {
email?: string email?: string
password?: string password?: string
@ -66,13 +67,19 @@ export async function getOpenAIAuth({
captchaToken?: string captchaToken?: string
nopechaKey?: string nopechaKey?: string
executablePath?: string executablePath?: string
proxyServer?: string
}): Promise<OpenAIAuth> { }): Promise<OpenAIAuth> {
const origBrowser = browser const origBrowser = browser
const origPage = page const origPage = page
try { try {
if (!browser) { if (!browser) {
browser = await getBrowser({ captchaToken, nopechaKey, executablePath }) browser = await getBrowser({
captchaToken,
nopechaKey,
executablePath,
proxyServer
})
} }
const userAgent = await browser.userAgent() const userAgent = await browser.userAgent()
@ -216,12 +223,14 @@ export async function getBrowser(
opts: PuppeteerLaunchOptions & { opts: PuppeteerLaunchOptions & {
captchaToken?: string captchaToken?: string
nopechaKey?: string nopechaKey?: string
proxyServer?: string
} = {} } = {}
) { ) {
const { const {
captchaToken = process.env.CAPTCHA_TOKEN, captchaToken = process.env.CAPTCHA_TOKEN,
nopechaKey = process.env.NOPECHA_KEY, nopechaKey = process.env.NOPECHA_KEY,
executablePath = defaultChromeExecutablePath(), executablePath = defaultChromeExecutablePath(),
proxyServer = process.env.PROXY_SERVER,
...launchOptions ...launchOptions
} = opts } = opts
@ -274,6 +283,10 @@ export async function getBrowser(
hasNopechaExtension = true hasNopechaExtension = true
} }
if (proxyServer) {
puppeteerArgs.push(`--proxy-server=${proxyServer}`)
}
const browser = await puppeteer.launch({ const browser = await puppeteer.launch({
headless: false, headless: false,
// https://peter.sh/experiments/chromium-command-line-switches/ // https://peter.sh/experiments/chromium-command-line-switches/
@ -288,6 +301,28 @@ export async function getBrowser(
...launchOptions ...launchOptions
}) })
if (process.env.PROXY_VALIDATE_IP) {
const page = (await browser.pages())[0] || (await browser.newPage())
// send a fetch request to https://ifconfig.co using page.evaluate() and verify the IP matches
let ip
try {
;({ ip } = await page.evaluate(() => {
return fetch('https://ifconfig.co', {
headers: {
Accept: 'application/json'
}
}).then((res) => res.json())
}))
} catch (err) {
throw new Error(`Proxy IP validation failed: ${err.message}`)
}
if (ip !== process.env.PROXY_VALIDATE_IP) {
throw new Error(
`Proxy IP mismatch: ${ip} !== ${process.env.PROXY_VALIDATE_IP}`
)
}
}
// TOdO: this is a really hackity hack way of setting the API key... // TOdO: this is a really hackity hack way of setting the API key...
if (hasNopechaExtension) { if (hasNopechaExtension) {
const page = (await browser.pages())[0] || (await browser.newPage()) const page = (await browser.pages())[0] || (await browser.newPage())