Skip to content

Commit

Permalink
Merge pull request transitive-bullshit#4 from bertybuttface/chatgpt-a…
Browse files Browse the repository at this point in the history
…pi-upgrade

ChatGPT API Upgrade (New Authentication)
  • Loading branch information
jakecoppinger authored Dec 24, 2022
2 parents 22a896c + 518519c commit 186048d
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 27 deletions.
77 changes: 70 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,71 @@
FROM node:18
RUN git clone https://github.com/jakecoppinger/matrix-chatgpt-bot.git
WORKDIR matrix-chatgpt-bot
COPY env .env
#RUN yarn install
RUN yarn install --har --production=true
FROM node:18 as builder

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies
COPY package*.json ./

# We don't need the standalone Chromium here.
ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true

RUN yarn install --frozen-lockfile && yarn cache clean

COPY . .

RUN yarn build
RUN yarn start


FROM node:18-slim

RUN mkdir -p /home/pptruser/Downloads
# Create app directory
WORKDIR /home/pptruser

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
ENV CHROME_PATH=/usr/bin/chromium
ENV DEBIAN_FRONTEND=noninteractive

RUN apt update -qq \
&& apt install -qq -y --no-install-recommends \
curl \
git \
gnupg \
libgconf-2-4 \
libxss1 \
libxtst6 \
python \
g++ \
build-essential \
chromium \
chromium-sandbox \
dumb-init \
fonts-ipafont-gothic fonts-wqy-zenhei fonts-thai-tlwg fonts-kacst \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /src/*.deb

# To run Headful mode, you will need to have a display, which is not present in a server.
# To avoid this, we will use Xvfb, and create a fake display, so the chrome will think there is a display and run properly.
# So we just need to install Xvfb and Puppeteer related dependencies.
RUN apt-get update && apt-get install -yq gconf-service libasound2 libatk1.0-0 libc6 libcairo2 libcups2 libdbus-1-3 libexpat1 libfontconfig1 libgcc1 libgconf-2-4 libgdk-pixbuf2.0-0 libglib2.0-0 libgtk-3-0 libnspr4 libpango-1.0-0 libpangocairo-1.0-0 libstdc++6 libx11-6 libx11-xcb1 libxcb1 libxcomposite1 libxcursor1 libxdamage1 libxext6 libxfixes3 libxi6 libxrandr2 libxrender1 libxss1 libxtst6 ca-certificates fonts-liberation libappindicator1 libnss3 lsb-release xdg-utils wget x11vnc x11-xkb-utils xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic x11-apps xvfb

# Install app dependencies
COPY package*.json ./

COPY .env .env

RUN yarn install --frozen-lockfile --production && yarn cache clean

COPY --from=builder /usr/src/app/dist ./dist

RUN npm install puppeteer@19.4.1 \
# Add user so we don't need --no-sandbox.
# same layer as npm install to keep re-chowned files from using up several hundred MBs more space
&& groupadd -r pptruser && useradd -rm -g pptruser -G audio,video pptruser \
&& chown -R pptruser:pptruser /home/pptruser
USER pptruser

# We run a fake display and run our script.
# Start script on Xvfb
CMD xvfb-run --server-args="-screen 0 1024x768x24" yarn start
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ Talk to ChatGPT via your favourite Matrix client!

This is an unofficial Matrix bot that uses https://github.com/transitive-bullshit/chatgpt-api to access the unofficial ChatGPT API.

It is worth reading the [authentication instructions](https://www.npmjs.com/package/chatgpt#usage) for the unofficial ChatGPT API.

Your user-agent and IP address must match from the real browser window you're logged in with to the one you're using for ChatGPTAPI. This means that you currently can't log in with your laptop and then run the bot on a server or proxy somewhere.

Cloudflare will still sometimes ask you to complete a CAPTCHA, so you may need to keep an eye on it and manually resolve the CAPTCHA.

You should not be using this ChatGPT account while the bot is using it, because that browser window may refresh one of your tokens and invalidate the bot's session.

If your OpenAI account uses Google Auth, you shouldn't encounter any of the more complicated Recaptchas — and can avoid using paid third-party CAPTCHA solving providers. To use Google auth, make sure your OpenAI account is using Google and then set IS_GOOGLE_LOGIN to true.

# Usage
- Create an unencrypted room
- Add the bot
Expand All @@ -27,9 +37,9 @@ This is an unofficial Matrix bot that uses https://github.com/transitive-bullshi
MATRIX_HOMESERVER_URL=
MATRIX_ACCESS_TOKEN=
# The value of the __Secure-next-auth.session-token cookie. See instructions on
# https://www.npmjs.com/package/chatgpt
CHATGPT_SESSION_TOKEN=
OPENAI_EMAIL=
OPENAI_PASSWORD=
IS_GOOGLE_LOGIN=true
# With the @ and :DOMAIN, ie @SOMETHING:DOMAIN
MATRIX_BOT_USERNAME=
Expand All @@ -45,5 +55,14 @@ MATRIX_BOT_PASSWORD=
- `yarn build`
- `yarn start`

## Running with Docker

```
docker build . -t matrix-chatgpt-bot
docker run --cap-add=SYS_ADMIN -it matrix-chatgpt-bot
```

Note: Without -it flags in the command above you won't be able to stop the container using Ctrl-C

# License
GNU AGPLv3. See LICENSE
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
"typecheck": "npx tsc"
},
"dependencies": {
"chatgpt": "^2.0.5",
"chatgpt": "^3.3.1",
"dotenv": "^14.2.0",
"matrix-bot-sdk": "^0.6.2",
"puppeteer": "^19.4.1",
"typescript": "^4.5.2"
},
"devDependencies": {
Expand Down
18 changes: 15 additions & 3 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ export const homeserverUrl = process.env.MATRIX_HOMESERVER_URL as string;
/** The full username: eg @bot:server.com */
export const matrixBotUsername = process.env.MATRIX_BOT_USERNAME as string;
export const matrixBotPassword = process.env.MATRIX_BOT_PASSWORD as string;
export const chatGPTApiKey = process.env.CHATGPT_SESSION_TOKEN as string;

/** ChatGPT specific stuff */
export const openAiEmail = process.env.OPENAI_EMAIL as string;
export const openAiPassword = process.env.OPENAI_PASSWORD as string;
export const isGoogleLogin = Boolean(process.env.IS_GOOGLE_LOGIN) as boolean;

if(accessToken === undefined) {
console.error("MATRIX_ACCESS_TOKEN env variable is undefined");
Expand All @@ -30,7 +34,15 @@ if(matrixBotPassword === undefined) {
console.error("MATRIX_BOT_PASSWORD env variable is undefined");
process.exit(1);
}
if(chatGPTApiKey === undefined) {
console.error("MATRIX_BOT_PASSWORD env variable is undefined");
if(openAiEmail === undefined) {
console.error("OPENAI_EMAIL env variable is undefined");
process.exit(1);
}
if(openAiPassword === undefined) {
console.error("OPENAI_PASSWORD env variable is undefined");
process.exit(1);
}
if(isGoogleLogin === undefined) {
console.error("IS_GOOGLE_LOGIN env variable is undefined");
process.exit(1);
}
15 changes: 9 additions & 6 deletions src/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChatGPTAPI } from "chatgpt";
import { ChatGPTAPIBrowser } from "chatgpt";
import { MatrixClient } from "matrix-bot-sdk";
import { matrixBotUsername } from "./config.js";
import { isEventAMessage } from "./utils.js";
Expand All @@ -8,7 +8,7 @@ import { isEventAMessage } from "./utils.js";
* @param client
* @returns Room event handler, which itself returnings nothing
*/
export async function handleRoomEvent(client: MatrixClient, chatGPT: ChatGPTAPI): Promise<(roomId: string, event: any) => Promise<void>> {
export async function handleRoomEvent(client: MatrixClient, chatGPT: ChatGPTAPIBrowser): Promise<(roomId: string, event: any) => Promise<void>> {
return async (roomId: string, event: any) => {
if (event.sender === matrixBotUsername) {
return;
Expand All @@ -31,10 +31,13 @@ Please add me to an unencrypted chat.`);
await client.sendReadReceipt(roomId, event.event_id);
await client.setTyping(roomId, true, 10000)
try {
const response = await chatGPT.sendMessage(
question)
// timeout after 2 minutes (which will also abort the underlying HTTP request)
const result = await chatGPT.sendMessage(question, {
timeoutMs: 2 * 60 * 1000
})
await client.setTyping(roomId, false, 500)
await client.sendText(roomId, `${response}`);

await client.sendText(roomId, `${result.response}`);
await client.sendReadReceipt(roomId, event.event_id);
} catch (e) {
await client.setTyping(roomId, false, 500)
Expand All @@ -45,4 +48,4 @@ Please add me to an unencrypted chat.`);
}
}
}
}
}
15 changes: 8 additions & 7 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ import {
RichConsoleLogger,
// RustSdkCryptoStorageProvider,
} from "matrix-bot-sdk";
import { chatGPTApiKey, homeserverUrl, matrixBotPassword, matrixBotUsername } from './config.js'
import { openAiEmail, openAiPassword, isGoogleLogin, homeserverUrl, matrixBotPassword, matrixBotUsername } from './config.js'
import { parseMatrixUsernamePretty } from './utils.js';
import { handleRoomEvent } from './handlers.js';
import { ChatGPTAPI } from 'chatgpt'
import { ChatGPTAPIBrowser } from 'chatgpt'

LogService.setLogger(new RichConsoleLogger());

Expand All @@ -29,12 +29,13 @@ async function main() {
const authedClient = await (new MatrixAuth(homeserverUrl)).passwordLogin(botUsernameWithoutDomain, matrixBotPassword);
const client = new MatrixClient(authedClient.homeserverUrl, authedClient.accessToken, storage);

const chatGPT = new ChatGPTAPI({
sessionToken: chatGPTApiKey
// use puppeteer to bypass cloudflare (headful because of captchas)
const chatGPT = new ChatGPTAPIBrowser({
email: openAiEmail,
password: openAiPassword,
isGoogleLogin: isGoogleLogin
})

// ensure the API is properly authenticated
await chatGPT.ensureAuth()
await chatGPT.initSession()

// Automatically join rooms the bot is invited to
AutojoinRoomsMixin.setupOnClient(client);
Expand Down

0 comments on commit 186048d

Please sign in to comment.