Merge remote-tracking branch 'origin/main'

# Conflicts:
#	pnpm-lock.yaml
pull/19/head
GoneTone 2022-12-06 08:18:09 +08:00
commit 3de6f5ab3f
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: B9068CA583835DB2
13 zmienionych plików z 975 dodań i 166 usunięć

Wyświetl plik

@ -48,4 +48,6 @@ jobs:
run: pnpm install --frozen-lockfile
- name: Run test
env:
SESSION_TOKEN: 'fake-session-token-for-CI'
run: pnpm run test

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 144 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 122 KiB

Wyświetl plik

@ -56,7 +56,7 @@ Hide
Require npx
#Set Shell bash
Set FontSize 17
Set FontSize 22
Set Width 1200
Set Height 600
Set Padding 24
@ -67,7 +67,7 @@ Type " "
Sleep 980ms
Backspace 1
Show
Type "npx tsx src/example.ts"
Type "npx tsx src/demo.ts"
Sleep 500ms
Enter
Show

Wyświetl plik

@ -1,6 +1,6 @@
{
"name": "chatgpt",
"version": "1.1.3",
"version": "1.2.0",
"description": "Node.js client for the unofficial ChatGPT API.",
"author": "Travis Fischer <travis@transitivebullsh.it>",
"repository": "transitive-bullshit/chatgpt-api",
@ -32,6 +32,7 @@
"prepare": "husky install",
"pre-commit": "lint-staged",
"test": "run-p test:*",
"test:unit": "ava",
"test:prettier": "prettier '**/*.{js,jsx,ts,tsx}' --check"
},
"dependencies": {
@ -47,6 +48,7 @@
"@types/node": "^18.11.9",
"@types/node-fetch": "^2.6.2",
"@types/uuid": "^9.0.0",
"ava": "^5.1.0",
"del-cli": "^5.0.0",
"dotenv-safe": "^8.2.0",
"husky": "^8.0.2",
@ -65,6 +67,14 @@
"prettier --write"
]
},
"ava": {
"extensions": {
"ts": "module"
},
"nodeArguments": [
"--loader=tsx"
]
},
"keywords": [
"openai",
"chatgpt",

Plik diff jest za duży Load Diff

Wyświetl plik

@ -9,10 +9,10 @@
[![NPM](https://img.shields.io/npm/v/chatgpt.svg)](https://www.npmjs.com/package/chatgpt) [![Build Status](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml/badge.svg)](https://github.com/transitive-bullshit/chatgpt-api/actions/workflows/test.yml) [![MIT License](https://img.shields.io/badge/license-MIT-blue)](https://github.com/transitive-bullshit/chatgpt-api/blob/main/license) [![Prettier Code Formatting](https://img.shields.io/badge/code_style-prettier-brightgreen.svg)](https://prettier.io)
- [Intro](#intro)
- [How it works](#how-it-works)
- [Install](#install)
- [Usage](#usage)
- [Docs](#docs)
- [How it works](#how-it-works)
- [Examples](#examples)
- [Credit](#credit)
- [License](#license)
@ -23,34 +23,10 @@ This package is a Node.js wrapper around [ChatGPT](https://openai.com/blog/chatg
You can use it to start building projects powered by ChatGPT like chatbots, websites, etc...
## How it works
This package requires a valid session token from ChatGPT to access it's unofficial REST API.
To get a session token:
1. Go to https://chat.openai.com/chat and log in or sign up.
2. Open dev tools.
3. Open `Application` > `Cookies`.
![ChatGPT cookies](./media/session-token.png)
4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment.
If you want to run the built-in demo, store this value as `SESSION_TOKEN` in a local `.env` file.
> **Note**
> This package will switch to using the official API once it's released.
> **Note**
> Prior to v1.0.0, this package used a headless browser via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2).
## Install
```bash
npm install --save chatgpt
# or
yarn add chatgpt
# or
pnpm add chatgpt
npm install chatgpt
```
## Usage
@ -59,9 +35,10 @@ pnpm add chatgpt
import { ChatGPTAPI } from 'chatgpt'
async function example() {
// sessionToken is required; see below for details
const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN })
// ensure the API is properly authenticated (optional)
// ensure the API is properly authenticated
await api.ensureAuth()
// send a message and wait for the response
@ -83,31 +60,53 @@ const api = new ChatGPTAPI({
})
```
A full [example](./src/example.ts) is included for testing purposes:
A full [demo](./src/demo.ts) is included for testing purposes:
```bash
# 1. clone repo
# 2. install node deps
# 3. set `SESSION_TOKEN` in .env
# 4. run:
npx tsx src/example.ts
npx tsx src/demo.ts
```
## Docs
See the [auto-generated docs](./docs/classes/ChatGPTAPI.md) for more info on methods and parameters.
## How it works
**This package requires a valid session token from ChatGPT to access it's unofficial REST API.**
To get a session token:
1. Go to https://chat.openai.com/chat and log in or sign up.
2. Open dev tools.
3. Open `Application` > `Cookies`.
![ChatGPT cookies](./media/session-token.png)
4. Copy the value for `__Secure-next-auth.session-token` and save it to your environment.
If you want to run the built-in demo, store this value as `SESSION_TOKEN` in a local `.env` file.
> **Note**
> This package will switch to using the official API once it's released.
> **Note**
> Prior to v1.0.0, this package used a headless browser via [Playwright](https://playwright.dev/) to automate the web UI. Here are the [docs for the initial browser version](https://github.com/transitive-bullshit/chatgpt-api/tree/v0.4.2).
## Examples
All of these awesome projects use the `chatgpt` package. 🤯
All of these awesome projects are built using the `chatgpt` package. 🤯
- [Twitter Bot](https://github.com/transitive-bullshit/chatgpt-twitter-bot) powered by ChatGPT ✨
- Mention [@ChatGPTBot](https://twitter.com/ChatGPTBot) on Twitter with your prompt to try it out
- [Chrome Extension](https://github.com/gragland/chatgpt-everywhere) ([demo](https://twitter.com/gabe_ragland/status/1599466486422470656))
- [VSCode Extension](https://github.com/mpociot/chatgpt-vscode) ([demo](https://twitter.com/marcelpociot/status/1599180144551526400))
- [Go Telegram Bot](https://github.com/m1guelpf/chatgpt-telegram)
- [Github ProBot](https://github.com/oceanlvr/ChatGPTBot)
- [GitHub ProBot](https://github.com/oceanlvr/ChatGPTBot)
- [Discord Bot](https://github.com/onury5506/Discord-ChatGPT-Bot)
- [Lovelines.xyz](https://lovelines.xyz)
- [EXM smart contracts](https://github.com/decentldotland/molecule)
If you create a cool integration, feel free to open a PR and add it to the list.

Wyświetl plik

@ -0,0 +1,67 @@
import test from 'ava'
import dotenv from 'dotenv-safe'
import { ChatGPTAPI } from './chatgpt-api'
dotenv.config()
const isCI = !!process.env.CI
test('ChatGPTAPI invalid session token', async (t) => {
t.throws(() => new ChatGPTAPI({ sessionToken: null }), {
message: 'ChatGPT invalid session token'
})
await t.throwsAsync(
async () => {
const chatgpt = new ChatGPTAPI({ sessionToken: 'invalid' })
await chatgpt.ensureAuth()
},
{
message: 'ChatGPT failed to refresh auth token. Error: Unauthorized'
}
)
})
test('ChatGPTAPI valid session token', async (t) => {
if (!isCI) {
t.timeout(2 * 60 * 1000) // 2 minutes
}
t.notThrows(
() => new ChatGPTAPI({ sessionToken: 'fake valid session token' })
)
await t.notThrowsAsync(
(async () => {
const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN })
// Don't make any real API calls using our session token if we're running on CI
if (!isCI) {
await api.ensureAuth()
const response = await api.sendMessage('test')
console.log('chatgpt response', response)
t.truthy(response)
t.is(typeof response, 'string')
}
})()
)
})
if (!isCI) {
test('ChatGPTAPI expired session token', async (t) => {
const expiredSessionToken = process.env.TEST_EXPIRED_SESSION_TOKEN
await t.throwsAsync(
async () => {
const chatgpt = new ChatGPTAPI({ sessionToken: expiredSessionToken })
await chatgpt.ensureAuth()
},
{
message:
'ChatGPT failed to refresh auth token. Error: session token has expired'
}
)
})
}

Wyświetl plik

@ -1,9 +1,9 @@
import { createParser } from 'eventsource-parser'
import ExpiryMap from 'expiry-map'
import fetch from 'node-fetch'
import { v4 as uuidv4 } from 'uuid'
import * as types from './types'
import { fetch } from './fetch'
import { fetchSSE } from './fetch-sse'
import { markdownToText } from './utils'
const KEY_ACCESS_TOKEN = 'accessToken'
@ -119,7 +119,7 @@ export class ChatGPTAPI {
let response = ''
return new Promise((resolve, reject) => {
this._fetchSSE(url, {
fetchSSE(url, {
method: 'POST',
headers: {
Authorization: `Bearer ${accessToken}`,
@ -179,34 +179,22 @@ export class ChatGPTAPI {
const accessToken = res?.accessToken
if (!accessToken) {
console.warn('no auth token', res)
throw new Error('Unauthorized')
}
const error = res?.error
if (error) {
if (error === 'RefreshAccessTokenError') {
throw new Error('session token has expired')
} else {
throw new Error(error)
}
}
this._accessTokenCache.set(KEY_ACCESS_TOKEN, accessToken)
return accessToken
} catch (err: any) {
throw new Error(`ChatGPT failed to refresh auth token: ${err.toString()}`)
throw new Error(`ChatGPT failed to refresh auth token. ${err.toString()}`)
}
}
protected async _fetchSSE(
url: string,
options: Parameters<typeof fetch>[1] & { onMessage: (data: string) => void }
) {
const { onMessage, ...fetchOptions } = options
const resp = await fetch(url, fetchOptions)
const parser = createParser((event) => {
if (event.type === 'event') {
onMessage(event.data)
}
})
resp.body.on('readable', () => {
let chunk: string | Buffer
while (null !== (chunk = resp.body.read())) {
parser.feed(chunk.toString())
}
})
}
}

Wyświetl plik

@ -1,12 +1,16 @@
import dotenv from 'dotenv-safe'
import { oraPromise } from 'ora'
import { ChatGPTAPI } from './chatgpt-api'
import { ChatGPTAPI } from '.'
dotenv.config()
/**
* Example CLI for testing functionality.
*
* ```
* npx tsx src/demo.ts
* ```
*/
async function main() {
const api = new ChatGPTAPI({ sessionToken: process.env.SESSION_TOKEN })

31
src/fetch-sse.ts 100644
Wyświetl plik

@ -0,0 +1,31 @@
import { createParser } from 'eventsource-parser'
import { fetch } from './fetch'
// import { streamAsyncIterable } from './stream-async-iterable'
export async function fetchSSE(
url: string,
options: Parameters<typeof fetch>[1] & { onMessage: (data: string) => void }
) {
const { onMessage, ...fetchOptions } = options
const resp = await fetch(url, fetchOptions)
const parser = createParser((event) => {
if (event.type === 'event') {
onMessage(event.data)
}
})
resp.body.on('readable', () => {
let chunk: string | Buffer
while (null !== (chunk = resp.body.read())) {
parser.feed(chunk.toString())
}
})
// TODO: add support for web-compatible `fetch`
// for await (const chunk of streamAsyncIterable(resp.body)) {
// const str = new TextDecoder().decode(chunk)
// parser.feed(str)
// }
}

3
src/fetch.ts 100644
Wyświetl plik

@ -0,0 +1,3 @@
import fetch from 'node-fetch'
export { fetch }

Wyświetl plik

@ -0,0 +1,16 @@
import { type ReadableStream } from 'stream/web'
export async function* streamAsyncIterable(stream: ReadableStream) {
const reader = stream.getReader()
try {
while (true) {
const { done, value } = await reader.read()
if (done) {
return
}
yield value
}
} finally {
reader.releaseLock()
}
}

Wyświetl plik

@ -7,7 +7,7 @@ export type Role = 'user' | 'assistant'
*/
export type SessionResult = {
/**
* Object of the current user
* Authenticated user
*/
user: User
@ -20,6 +20,11 @@ export type SessionResult = {
* The access token
*/
accessToken: string
/**
* If there was an error associated with this request
*/
error?: string | null
}
export type User = {