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) # ChatGPT Settings (required)
OPENAI_EMAIL= # Set the API Key from OpenAI
OPENAI_PASSWORD= OPENAI_API_KEY=
# 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 ChatGPT conversation context to 'thread', 'room' or 'both'. # Set the ChatGPT conversation context to 'thread', 'room' or 'both'.
CHATGPT_CONTEXT=thread 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) # Matrix Static Settings (required, see notes)
# Defaults to "https://matrix.org" # 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 # If you turn threads off you will have problems if you don't set CHATGPT_CONTEXT=room
MATRIX_THREADS=true MATRIX_THREADS=true
MATRIX_PREFIX_DM=false 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 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 ./ COPY package*.json ./
RUN yarn install --frozen-lockfile --production && yarn cache clean RUN yarn install --frozen-lockfile --production && yarn cache clean
@ -27,5 +12,4 @@ RUN yarn build
VOLUME /storage VOLUME /storage
ENV DATA_PATH="/storage" ENV DATA_PATH="/storage"
# We run a fake display and run our script using Xvfb CMD yarn start
CMD xvfb-run --server-args="-screen 0 1024x768x16" 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. 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 ### 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 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.
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)
## Setup ## Setup
@ -52,7 +48,7 @@ You no longer need `MATRIX_BOT_PASSWORD` set but you can leave it if you want.
# Run # 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 ## with Docker
@ -114,10 +110,6 @@ You only need to do this if you want to contribute code to this package.
# FAQ # 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]" ## 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 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 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). 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 ## 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. - 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" "typecheck": "npx tsc"
}, },
"dependencies": { "dependencies": {
"chatgpt": "^3.5.1", "chatgpt": "^4.1.1",
"dotenv": "^16.0.3", "dotenv": "^16.0.3",
"markdown-it": "^13.0.1", "markdown-it": "^13.0.1",
"matrix-bot-sdk": "^0.6.3", "matrix-bot-sdk": "^0.6.3",
"puppeteer": "^19.4.1",
"typescript": "^4.9.4", "typescript": "^4.9.4",
"znv": "^0.3.2", "znv": "^0.3.2",
"zod": "^3.20.2" "zod": "^3.20.2"

View File

@ -23,12 +23,10 @@ export const {
MATRIX_DEFAULT_PREFIX, MATRIX_DEFAULT_PREFIX,
MATRIX_DEFAULT_PREFIX_REPLY, MATRIX_DEFAULT_PREFIX_REPLY,
/** ChatGPT Settings */ /** ChatGPT Settings */
OPENAI_EMAIL, OPENAI_API_KEY,
OPENAI_PASSWORD,
OPENAI_LOGIN_TYPE,
OPENAI_PRO,
CHATGPT_CONTEXT, CHATGPT_CONTEXT,
CHATGPT_TIMEOUT CHATGPT_TIMEOUT,
CHATGPT_MODEL
} = parseEnv(process.env, { } = parseEnv(process.env, {
DATA_PATH: { schema: z.string().default("./storage"), description: "Set to /storage/ if using docker, ./storage if running without" }, DATA_PATH: { schema: z.string().default("./storage"), description: "Set to /storage/ if using docker, ./storage if running without" },
/** Matrix Bot Settings */ /** 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: { 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" }, 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 */ /** ChatGPT Settings */
OPENAI_EMAIL: { schema: z.string().min(3), description: "Set full username of OpenAI's account" }, OPENAI_API_KEY: { schema: z.string().default(""), description: "Set to the API key from https://platform.openai.com/account/api-keys"},
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." },
CHATGPT_TIMEOUT: { schema: z.number().default(2 * 60 * 1000), description: "Set number of milliseconds to wait for ChatGPT responses" }, 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 { 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 { 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"; import { RelatesTo, MessageEvent, StoredConversation, StoredConversationConfig } from "./interfaces.js";
@ -11,7 +11,7 @@ export default class CommandHandler {
private userId: string; private userId: string;
private localpart: string; private localpart: string;
constructor(private client: MatrixClient, private chatGPT: ChatGPTAPIBrowser) { } constructor(private client: MatrixClient, private chatGPT: ChatGPTAPI) { }
public async start() { public async start() {
await this.prepareProfile(); // Populate the variables above (async) 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); const result = await sendChatGPTMessage(this.chatGPT, await bodyWithoutPrefix, storedConversation);
await Promise.all([ await Promise.all([
this.client.setTyping(roomId, false, 500), 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 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); await this.client.storageProvider.storeValue('gpt-' + storageKey, configString);
if ((storageKey === roomId) && (CHATGPT_CONTEXT === "both")) await this.client.storageProvider.storeValue('gpt-' + event.event_id, configString); if ((storageKey === roomId) && (CHATGPT_CONTEXT === "both")) await this.client.storageProvider.storeValue('gpt-' + event.event_id, configString);
} catch (err) { } catch (err) {

View File

@ -7,10 +7,10 @@ import {
} from "matrix-bot-sdk"; } from "matrix-bot-sdk";
import * as path from "path"; 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 { parseMatrixUsernamePretty } from './utils.js';
import CommandHandler from "./handlers.js" import CommandHandler from "./handlers.js"
import { ChatGPTAPIBrowser } from 'chatgpt' import { ChatGPTAPI } from 'chatgpt'
LogService.setLogger(new RichConsoleLogger()); LogService.setLogger(new RichConsoleLogger());
@ -42,18 +42,13 @@ async function main() {
const client: MatrixClient = new MatrixClient(MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, storage, cryptoStore); const client: MatrixClient = new MatrixClient(MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, storage, cryptoStore);
// use puppeteer to bypass cloudflare (headful because of captchas) // use puppeteer to bypass cloudflare (headful because of captchas)
const chatGPT: ChatGPTAPIBrowser = new ChatGPTAPIBrowser({ const chatGPT: ChatGPTAPI = new ChatGPTAPI({
email: OPENAI_EMAIL, apiKey: OPENAI_API_KEY,
password: OPENAI_PASSWORD, completionParams: {
isGoogleLogin: (OPENAI_LOGIN_TYPE == "google"), model: CHATGPT_MODEL
isMicrosoftLogin: (OPENAI_LOGIN_TYPE == "microsoft"), }
isProAccount: OPENAI_PRO
}) })
chatGPT.initSession().then(() => {
LogService.info('ChatGPT session initialized');
});
// // call `api.refreshSession()` every hour to refresh the session // // call `api.refreshSession()` every hour to refresh the session
// setInterval(() => { // setInterval(() => {
// chatGPT.refreshSession().then(() => { // chatGPT.refreshSession().then(() => {
@ -91,7 +86,7 @@ async function main() {
const commands = new CommandHandler(client, chatGPT); const commands = new CommandHandler(client, chatGPT);
await commands.start(); await commands.start();
LogService.info("index", "Starting bot..."); LogService.info("index", `Starting bot using ChatGPT model ${CHATGPT_MODEL}`);
await client.start() await client.start()
LogService.info("index", "Bot started!"); 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 Markdown from 'markdown-it';
import { MatrixClient } from "matrix-bot-sdk"; import { MatrixClient } from "matrix-bot-sdk";
import { MessageEvent, StoredConversation } from "./interfaces.js"; 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); await client.sendEvent(roomId, "m.room.message", finalContent);
} }
export async function sendChatGPTMessage(chatGPT: ChatGPTAPIBrowser, question: string, storedConversation: StoredConversation) { export async function sendChatGPTMessage(chatGPT: ChatGPTAPI, question: string, storedConversation: StoredConversation) {
let result: ChatResponse let result: ChatMessage
if (storedConversation !== undefined) { if (storedConversation !== undefined) {
result = await chatGPT.sendMessage(question, { result = await chatGPT.sendMessage(question, {
timeoutMs: CHATGPT_TIMEOUT, timeoutMs: CHATGPT_TIMEOUT,