kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
Merge branch 'feature/improve-robustness' into feature/improve-robustness
commit
b8a3fe0d2a
|
@ -16,7 +16,7 @@ async function main() {
|
||||||
const email = process.env.OPENAI_EMAIL
|
const email = process.env.OPENAI_EMAIL
|
||||||
const password = process.env.OPENAI_PASSWORD
|
const password = process.env.OPENAI_PASSWORD
|
||||||
|
|
||||||
const api = new ChatGPTAPIBrowser({ email, password })
|
const api = new ChatGPTAPIBrowser({ email, password, debug: true })
|
||||||
const res = await api.init()
|
const res = await api.init()
|
||||||
console.log('init result', res)
|
console.log('init result', res)
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import delay from 'delay'
|
import delay from 'delay'
|
||||||
import html2md from 'html-to-md'
|
import html2md from 'html-to-md'
|
||||||
import { type Browser, type HTTPResponse, type Page } from 'puppeteer'
|
import type { Browser, HTTPRequest, HTTPResponse, Page } from 'puppeteer'
|
||||||
|
|
||||||
import * as types from './types'
|
import * as types from './types'
|
||||||
import { getBrowser, getOpenAIAuth } from './openai-auth'
|
import { getBrowser, getOpenAIAuth } from './openai-auth'
|
||||||
|
import { isRelevantRequest, minimizePage } from './utils'
|
||||||
|
|
||||||
export class ChatGPTAPIBrowser {
|
export class ChatGPTAPIBrowser {
|
||||||
protected _markdown: boolean
|
protected _markdown: boolean
|
||||||
|
@ -102,25 +103,93 @@ export class ChatGPTAPIBrowser {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// this._page.on('response', this._onResponse.bind(this))
|
// await minimizePage(this._page)
|
||||||
|
|
||||||
|
this._page.on('request', this._onRequest.bind(this))
|
||||||
|
this._page.on('response', this._onResponse.bind(this))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// _onResponse = (response: HTTPResponse) => {
|
_onRequest = (request: HTTPRequest) => {
|
||||||
// const request = response.request()
|
if (!this._debug) return
|
||||||
|
|
||||||
// console.log('response', {
|
const url = request.url()
|
||||||
// url: response.url(),
|
if (!isRelevantRequest(url)) {
|
||||||
// ok: response.ok(),
|
return
|
||||||
// status: response.status(),
|
}
|
||||||
// statusText: response.statusText(),
|
|
||||||
// headers: response.headers(),
|
const method = request.method()
|
||||||
// request: {
|
let body: any
|
||||||
// method: request.method(),
|
|
||||||
// headers: request.headers()
|
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
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nrequest', {
|
||||||
|
url,
|
||||||
|
method,
|
||||||
|
headers: request.headers(),
|
||||||
|
body
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
_onResponse = async (response: HTTPResponse) => {
|
||||||
|
if (!this._debug) return
|
||||||
|
|
||||||
|
const request = response.request()
|
||||||
|
|
||||||
|
const url = response.url()
|
||||||
|
if (!isRelevantRequest(url)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let body: any
|
||||||
|
try {
|
||||||
|
body = await response.json()
|
||||||
|
} catch (_) {}
|
||||||
|
|
||||||
|
if (url.endsWith('/conversation')) {
|
||||||
|
// const parser = createParser((event) => {
|
||||||
|
// if (event.type === 'event') {
|
||||||
|
// onMessage(event.data)
|
||||||
|
// }
|
||||||
|
// })
|
||||||
|
// await response.buffer()
|
||||||
|
// for await (const chunk of streamAsyncIterable(response.body)) {
|
||||||
|
// const str = new TextDecoder().decode(chunk)
|
||||||
|
// parser.feed(str)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\nresponse', {
|
||||||
|
url,
|
||||||
|
ok: response.ok(),
|
||||||
|
status: response.status(),
|
||||||
|
statusText: response.statusText(),
|
||||||
|
headers: response.headers(),
|
||||||
|
body,
|
||||||
|
request: {
|
||||||
|
method: request.method(),
|
||||||
|
headers: request.headers(),
|
||||||
|
body: request.postData()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async getIsAuthenticated() {
|
async getIsAuthenticated() {
|
||||||
try {
|
try {
|
||||||
|
@ -198,7 +267,7 @@ export class ChatGPTAPIBrowser {
|
||||||
|
|
||||||
const lastMessage = await this.getLastMessage()
|
const lastMessage = await this.getLastMessage()
|
||||||
|
|
||||||
await inputBox.click()
|
await inputBox.focus()
|
||||||
const paragraphs = message.split('\n')
|
const paragraphs = message.split('\n')
|
||||||
for (let i = 0; i < paragraphs.length; i++) {
|
for (let i = 0; i < paragraphs.length; i++) {
|
||||||
await inputBox.type(paragraphs[i], { delay: 0 })
|
await inputBox.type(paragraphs[i], { delay: 0 })
|
||||||
|
@ -220,6 +289,7 @@ export class ChatGPTAPIBrowser {
|
||||||
newLastMessage &&
|
newLastMessage &&
|
||||||
lastMessage?.toLowerCase() !== newLastMessage?.toLowerCase()
|
lastMessage?.toLowerCase() !== newLastMessage?.toLowerCase()
|
||||||
) {
|
) {
|
||||||
|
await delay(5000)
|
||||||
return newLastMessage
|
return newLastMessage
|
||||||
}
|
}
|
||||||
} while (true)
|
} while (true)
|
||||||
|
|
|
@ -2,12 +2,12 @@ import * as fs from 'node:fs'
|
||||||
import * as os from 'node:os'
|
import * as os from 'node:os'
|
||||||
|
|
||||||
import delay from 'delay'
|
import delay from 'delay'
|
||||||
import {
|
import type {
|
||||||
type Browser,
|
Browser,
|
||||||
type ElementHandle,
|
ElementHandle,
|
||||||
type Page,
|
Page,
|
||||||
type Protocol,
|
Protocol,
|
||||||
type PuppeteerLaunchOptions
|
PuppeteerLaunchOptions
|
||||||
} from 'puppeteer'
|
} from 'puppeteer'
|
||||||
import puppeteer from 'puppeteer-extra'
|
import puppeteer from 'puppeteer-extra'
|
||||||
import RecaptchaPlugin from 'puppeteer-extra-plugin-recaptcha'
|
import RecaptchaPlugin from 'puppeteer-extra-plugin-recaptcha'
|
||||||
|
|
47
src/utils.ts
47
src/utils.ts
|
@ -1,3 +1,4 @@
|
||||||
|
import type { Page } from 'puppeteer'
|
||||||
import { remark } from 'remark'
|
import { remark } from 'remark'
|
||||||
import stripMarkdown from 'strip-markdown'
|
import stripMarkdown from 'strip-markdown'
|
||||||
|
|
||||||
|
@ -7,3 +8,49 @@ export function markdownToText(markdown?: string): string {
|
||||||
.processSync(markdown ?? '')
|
.processSync(markdown ?? '')
|
||||||
.toString()
|
.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