Update ChatGPT API wrapper to latest, uses Puppeteer

This commit is contained in:
bertybuttface 2022-12-22 14:57:34 +00:00
parent 22a896c6f8
commit 0175a09e33
6 changed files with 118 additions and 27 deletions

View File

@ -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

View File

@ -7,6 +7,17 @@ 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
@ -27,9 +38,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=

View File

@ -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": {

View File

@ -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");
@ -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);
}

View File

@ -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";
@ -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;
@ -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)
@ -45,4 +48,4 @@ Please add me to an unencrypted chat.`);
}
}
}
}
}

View File

@ -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());
@ -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);