Merge pull request #78 from max298/main

Update chatgpt-api to use proper api
This commit is contained in:
bertybuttface 2023-02-03 15:05:18 +00:00 committed by GitHub
commit 1f0b7733e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 91 additions and 3449 deletions

View File

@ -1,13 +1,11 @@
# ChatGPT Settings (required)
OPENAI_EMAIL=
OPENAI_PASSWORD=
# What type of Login it is, possibility's are google, openai, microsoft
OPENAI_LOGIN_TYPE=google
# Set the next line to true if you are using a ChatGPT pro account.
OPENAI_PRO=false
# Set the API Key from OpenAI
OPENAI_API_KEY=
# Set the ChatGPT conversation context to 'thread', 'room' or 'both'.
CHATGPT_CONTEXT=thread
# (Optional) Explicitly set the ChatGPT model to be used by the API.
#CHATGPT_MODEL=text-chat-davinci-002-20221122
# Matrix Static Settings (required, see notes)
# Defaults to "https://matrix.org"
@ -36,4 +34,4 @@ MATRIX_ENCRYPTION=true
# If you turn threads off you will have problems if you don't set CHATGPT_CONTEXT=room
MATRIX_THREADS=true
MATRIX_PREFIX_DM=false
MATRIX_RICH_TEXT=true
MATRIX_RICH_TEXT=true

View File

@ -1,22 +1,7 @@
FROM satantime/puppeteer-node:19-slim
FROM node:19-slim
WORKDIR /usr/src/app
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 \
chromium \
dumb-init \
# To run Headful mode we just need to install Xvfb and Puppeteer related dependencies.
xfonts-100dpi xfonts-75dpi xfonts-scalable xfonts-cyrillic \
xvfb xauth\
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /src/*.deb
COPY package*.json ./
RUN yarn install --frozen-lockfile --production && yarn cache clean
@ -27,5 +12,4 @@ RUN yarn build
VOLUME /storage
ENV DATA_PATH="/storage"
# We run a fake display and run our script using Xvfb
CMD xvfb-run --server-args="-screen 0 1024x768x16" yarn start
CMD yarn start

View File

@ -35,12 +35,8 @@ You must adjust all required settings in the `.env` file according to your needs
Per default, whoever knows the name of your bot can add it to their rooms and start chatting. Access can be restricted by setting `MATRIX_BLACKLIST` or `MATRIX_WHISTLIST` in your `.env` file. When using a self-hosted setup, you could wildcard all your user by adding `MATRIX_WHITELIST=:anotherhomeserver.example` and change it to your homeserver address.
### OpenAI / ChatGPT
- You need to have an account at [openai.com. ](https://openai.com/) Its recommended to use an google account without 2FA for set up the account to avoid unsolvable captchas. Then, set `OPENAI_LOGIN_TYPE` to `google` in your `.env` file
You must read the [authentication instructions](https://www.npmjs.com/package/chatgpt#usage) for chatgpt-api if you get stuck.
Using the same account at [chat.openai.com](https://chat.openai.com) may refresh tokens invalidating the bot's session.
**If the Google account uses 2FA it will fail** but there is [a workaround](https://github.com/transitive-bullshit/chatgpt-api/issues/169#issuecomment-1362206780)
- You need to have an account at [openai.com. ](https://openai.com/). Create a [API Key](https://platform.openai.com/account/api-keys). Then, set `OPENAI_API_KEY` in your `.env` file
- You might want to change to chat-model by setting the `CHATGPT_MODEL` in your `.env` file. The model currently defaults to `text-chat-davinci-002-20221122`. Check the [node-chatgpt-api](https://github.com/waylaidwanderer/node-chatgpt-api) repository for keeping track of the models.
## Setup
@ -52,7 +48,7 @@ You no longer need `MATRIX_BOT_PASSWORD` set but you can leave it if you want.
# Run
There are multiple ways to run this bot. The easiest way is to run it within docker. You need to be logged into your OpenAi account from the same network your bot machine is. When you are logged in, you can safely close the window, just make sure you do not log out because this would make your session token invalid.
There are multiple ways to run this bot. The easiest way is to run it within docker.
## with Docker
@ -114,10 +110,6 @@ You only need to do this if you want to contribute code to this package.
# FAQ
## What do I do if a login fails?
- Its strongly suggested to use google as the prefererred login method to avoid being not abled to solve the required captchas
- Make sure that 2FA is deactivated (E.g. use the above mentioned workaroud or create a fresh google account)
## How do I handle "[Error: decryption failed because the room key is missing]"
Encryption works great with this package but can sometimes be a bit sensitive. Following steps can help to solve the "encryption" error
@ -134,11 +126,6 @@ Encryption works great with this package but can sometimes be a bit sensitive. F
4) Log into your bot account (e.g. via Element) and log out of all sessions
5) Verify the correctness of your `env` file and then run the bot setup again (e.g. via `docker-compose up` if you use docker-compose).
- If you experience this error after adding the bot to a new room but it was working before, you can try to use the command `/discardsession` in the element app to drop the session and trigger a new one. In this error case, the error only appears on a specific device, so using another client should work eitherway.
## What to do if I get an error saying I'm not logged in?
- If the bot can't log into your OpenAI account, your session token might not been valid anymore (e.g. you logged out of your OpenAI account through your browser). Re-login to your OpenAI account using a machine in the same network as the machine your bot is using. Then restart the bot.
## I just want to chat with the bot and don't want to deal with encryption problems
- Set `MATRIX_ENCRYPTION=false` in your env-file and restart the bot. If it previously was running with encryption switched on, you need to create a new room with the bot as encryption can't be switched off once it was activated.

3435
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,11 +15,10 @@
"typecheck": "npx tsc"
},
"dependencies": {
"chatgpt": "^3.5.1",
"chatgpt": "^4.1.1",
"dotenv": "^16.0.3",
"markdown-it": "^13.0.1",
"matrix-bot-sdk": "^0.6.3",
"puppeteer": "^19.4.1",
"typescript": "^4.9.4",
"znv": "^0.3.2",
"zod": "^3.20.2"

View File

@ -23,12 +23,10 @@ export const {
MATRIX_DEFAULT_PREFIX,
MATRIX_DEFAULT_PREFIX_REPLY,
/** ChatGPT Settings */
OPENAI_EMAIL,
OPENAI_PASSWORD,
OPENAI_LOGIN_TYPE,
OPENAI_PRO,
OPENAI_API_KEY,
CHATGPT_CONTEXT,
CHATGPT_TIMEOUT
CHATGPT_TIMEOUT,
CHATGPT_MODEL
} = parseEnv(process.env, {
DATA_PATH: { schema: z.string().default("./storage"), description: "Set to /storage/ if using docker, ./storage if running without" },
/** Matrix Bot Settings */
@ -49,10 +47,8 @@ export const {
MATRIX_DEFAULT_PREFIX: { schema: z.string().default(""), description: "Set to a string if you want the bot to respond only when messages start with this prefix. Trailing space matters. Empty for no prefix." },
MATRIX_DEFAULT_PREFIX_REPLY: { schema: z.boolean().default(false), description: "Set to false if you want the bot to answer to all messages in a thread/conversation" },
/** ChatGPT Settings */
OPENAI_EMAIL: { schema: z.string().min(3), description: "Set full username of OpenAI's account" },
OPENAI_PASSWORD: { schema: z.string().min(1), description: "Set password of OpenAI's account" },
OPENAI_LOGIN_TYPE: { schema: z.enum(["google", "openai", "microsoft"]).default("google"), description: "Set authentication provider to 'google', 'openai' or 'microsoft'" },
OPENAI_PRO: { schema: z.boolean().default(false), description: "Set to true if you have a paid ChatGPT subscription." },
OPENAI_API_KEY: { schema: z.string().default(""), description: "Set to the API key from https://platform.openai.com/account/api-keys"},
CHATGPT_TIMEOUT: { schema: z.number().default(2 * 60 * 1000), description: "Set number of milliseconds to wait for ChatGPT responses" },
CHATGPT_CONTEXT: { schema: z.enum(["thread", "room", "both"]).default("thread"), description: "Set the ChatGPT conversation context to 'thread', 'room' or 'both'" }
CHATGPT_CONTEXT: { schema: z.enum(["thread", "room", "both"]).default("thread"), description: "Set the ChatGPT conversation context to 'thread', 'room' or 'both'" },
CHATGPT_MODEL: { schema: z.string().default("text-chat-davinci-002-20221122"), description: "The model for the ChatGPT-API to use" }
});

View File

@ -1,4 +1,4 @@
import { ChatGPTAPIBrowser } from "chatgpt";
import { ChatGPTAPI } from "chatgpt";
import { LogService, MatrixClient, UserID } from "matrix-bot-sdk";
import { CHATGPT_CONTEXT, CHATGPT_TIMEOUT, MATRIX_DEFAULT_PREFIX_REPLY, MATRIX_DEFAULT_PREFIX, MATRIX_BLACKLIST, MATRIX_WHITELIST, MATRIX_RICH_TEXT, MATRIX_PREFIX_DM, MATRIX_THREADS } from "./env.js";
import { RelatesTo, MessageEvent, StoredConversation, StoredConversationConfig } from "./interfaces.js";
@ -11,7 +11,7 @@ export default class CommandHandler {
private userId: string;
private localpart: string;
constructor(private client: MatrixClient, private chatGPT: ChatGPTAPIBrowser) { }
constructor(private client: MatrixClient, private chatGPT: ChatGPTAPI) { }
public async start() {
await this.prepareProfile(); // Populate the variables above (async)
@ -124,11 +124,11 @@ export default class CommandHandler {
const result = await sendChatGPTMessage(this.chatGPT, await bodyWithoutPrefix, storedConversation);
await Promise.all([
this.client.setTyping(roomId, false, 500),
sendReply(this.client, roomId, this.getRootEventId(event), `${result.response}`, MATRIX_THREADS, MATRIX_RICH_TEXT)
sendReply(this.client, roomId, this.getRootEventId(event), `${result.text}`, MATRIX_THREADS, MATRIX_RICH_TEXT)
]);
const storedConfig = ((storedConversation !== undefined && storedConversation.config !== undefined) ? storedConversation.config : {})
const configString: string = JSON.stringify({conversationId: result.conversationId, messageId: result.messageId, config: storedConfig})
const configString: string = JSON.stringify({conversationId: result.conversationId, messageId: result.id, config: storedConfig})
await this.client.storageProvider.storeValue('gpt-' + storageKey, configString);
if ((storageKey === roomId) && (CHATGPT_CONTEXT === "both")) await this.client.storageProvider.storeValue('gpt-' + event.event_id, configString);
} catch (err) {

View File

@ -7,10 +7,10 @@ import {
} from "matrix-bot-sdk";
import * as path from "path";
import { DATA_PATH, OPENAI_EMAIL, OPENAI_PASSWORD, OPENAI_LOGIN_TYPE, OPENAI_PRO, MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, MATRIX_AUTOJOIN, MATRIX_BOT_PASSWORD, MATRIX_BOT_USERNAME, MATRIX_ENCRYPTION, MATRIX_THREADS, CHATGPT_CONTEXT } from './env.js'
import { DATA_PATH, OPENAI_API_KEY, MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, MATRIX_AUTOJOIN, MATRIX_BOT_PASSWORD, MATRIX_BOT_USERNAME, MATRIX_ENCRYPTION, MATRIX_THREADS, CHATGPT_CONTEXT, CHATGPT_MODEL } from './env.js'
import { parseMatrixUsernamePretty } from './utils.js';
import CommandHandler from "./handlers.js"
import { ChatGPTAPIBrowser } from 'chatgpt'
import { ChatGPTAPI } from 'chatgpt'
LogService.setLogger(new RichConsoleLogger());
@ -42,18 +42,13 @@ async function main() {
const client: MatrixClient = new MatrixClient(MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, storage, cryptoStore);
// use puppeteer to bypass cloudflare (headful because of captchas)
const chatGPT: ChatGPTAPIBrowser = new ChatGPTAPIBrowser({
email: OPENAI_EMAIL,
password: OPENAI_PASSWORD,
isGoogleLogin: (OPENAI_LOGIN_TYPE == "google"),
isMicrosoftLogin: (OPENAI_LOGIN_TYPE == "microsoft"),
isProAccount: OPENAI_PRO
const chatGPT: ChatGPTAPI = new ChatGPTAPI({
apiKey: OPENAI_API_KEY,
completionParams: {
model: CHATGPT_MODEL
}
})
chatGPT.initSession().then(() => {
LogService.info('ChatGPT session initialized');
});
// // call `api.refreshSession()` every hour to refresh the session
// setInterval(() => {
// chatGPT.refreshSession().then(() => {
@ -91,7 +86,7 @@ async function main() {
const commands = new CommandHandler(client, chatGPT);
await commands.start();
LogService.info("index", "Starting bot...");
LogService.info("index", `Starting bot using ChatGPT model ${CHATGPT_MODEL}`);
await client.start()
LogService.info("index", "Bot started!");
}

View File

@ -1,4 +1,4 @@
import { ChatGPTAPIBrowser, ChatResponse } from "chatgpt";
import { ChatGPTAPI, ChatMessage } from "chatgpt";
import Markdown from 'markdown-it';
import { MatrixClient } from "matrix-bot-sdk";
import { MessageEvent, StoredConversation } from "./interfaces.js";
@ -76,8 +76,8 @@ export async function sendReply(client: MatrixClient, roomId: string, rootEventI
await client.sendEvent(roomId, "m.room.message", finalContent);
}
export async function sendChatGPTMessage(chatGPT: ChatGPTAPIBrowser, question: string, storedConversation: StoredConversation) {
let result: ChatResponse
export async function sendChatGPTMessage(chatGPT: ChatGPTAPI, question: string, storedConversation: StoredConversation) {
let result: ChatMessage
if (storedConversation !== undefined) {
result = await chatGPT.sendMessage(question, {
timeoutMs: CHATGPT_TIMEOUT,