matrix-chatgpt-bot/src/index.ts

95 lines
4.3 KiB
TypeScript
Raw Normal View History

2023-02-06 14:08:45 +00:00
import ChatGPTClient from '@waylaidwanderer/chatgpt-api';
2023-02-04 16:58:43 +00:00
import Keyv from 'keyv'
import { KeyvFile } from 'keyv-file';
2022-12-09 09:41:17 +00:00
import {
2023-02-04 16:58:43 +00:00
MatrixAuth, MatrixClient, AutojoinRoomsMixin,
LogService, LogLevel, RichConsoleLogger,
2023-02-06 13:47:09 +00:00
RustSdkCryptoStorageProvider, IStorageProvider, SimpleFsStorageProvider,
2022-12-09 09:41:17 +00:00
} from "matrix-bot-sdk";
2022-12-29 02:48:50 +00:00
2022-12-29 02:38:29 +00:00
import * as path from "path";
2023-02-06 00:30:19 +00:00
import { DATA_PATH, KEYV_URL, 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, KEYV_BOT_STORAGE, KEYV_BACKEND } from './env.js'
2023-01-06 23:59:52 +00:00
import CommandHandler from "./handlers.js"
2023-02-06 00:30:19 +00:00
import { KeyvStorageProvider } from './storage.js'
2023-02-04 16:58:43 +00:00
import { parseMatrixUsernamePretty } from './utils.js';
2022-12-09 09:41:17 +00:00
LogService.setLogger(new RichConsoleLogger());
2023-02-06 13:47:09 +00:00
// LogService.setLevel(LogLevel.DEBUG); // Shows the Matrix sync loop details - not needed most of the time
2022-12-09 09:41:17 +00:00
LogService.setLevel(LogLevel.INFO);
// LogService.muteModule("Metrics");
LogService.trace = LogService.debug;
2023-02-04 16:58:43 +00:00
if (KEYV_URL && KEYV_BACKEND === 'file') LogService.warn('config', 'KEYV_URL is ignored when KEYV_BACKEND is set to `file`')
let chatgptStore:Keyv
if (KEYV_BACKEND === 'file'){
chatgptStore = new Keyv({store: new KeyvFile({ filename: path.join(DATA_PATH, `chatgpt-bot-api.json`),})})
} else {
chatgptStore = new Keyv(KEYV_URL, { namespace: 'chatgpt-bot-api' });
}
let storage: IStorageProvider
if (KEYV_BOT_STORAGE) {
storage = new KeyvStorageProvider('chatgpt-bot-storage');
} else {
storage = new SimpleFsStorageProvider(path.join(DATA_PATH, "bot.json")); // /storage/bot.json
}
2022-12-09 09:41:17 +00:00
2023-02-06 13:42:17 +00:00
const cryptoStore = MATRIX_ENCRYPTION ? new RustSdkCryptoStorageProvider(path.join(DATA_PATH, "encrypted")) : undefined; // /storage/encrypted
2022-12-09 09:41:17 +00:00
async function main() {
2022-12-29 11:11:52 +00:00
if (!MATRIX_ACCESS_TOKEN){
const botUsernameWithoutDomain = parseMatrixUsernamePretty(MATRIX_BOT_USERNAME);
2022-12-29 11:11:52 +00:00
const authedClient = await (new MatrixAuth(MATRIX_HOMESERVER_URL)).passwordLogin(botUsernameWithoutDomain, MATRIX_BOT_PASSWORD);
console.log(authedClient.homeserverUrl + " token: \n" + authedClient.accessToken)
2022-12-29 11:11:52 +00:00
console.log("Set MATRIX_ACCESS_TOKEN to above token, MATRIX_BOT_PASSWORD can now be blank")
return;
}
if (!MATRIX_THREADS && CHATGPT_CONTEXT !== "room") throw Error("You must set CHATGPT_CONTEXT to 'room' if you set MATRIX_THREADS to false")
const client: MatrixClient = new MatrixClient(MATRIX_HOMESERVER_URL, MATRIX_ACCESS_TOKEN, storage, cryptoStore);
2023-02-06 14:08:45 +00:00
const clientOptions = { // (Optional) Parameters as described in https://platform.openai.com/docs/api-reference/completions
modelOptions: {
model: CHATGPT_MODEL, // The model is set to text-chat-davinci-002-20221122 by default
2023-02-04 16:58:43 +00:00
},
2023-02-06 14:08:45 +00:00
// (Optional) Set custom instructions instead of "You are ChatGPT...".
// promptPrefix: 'You are Bob, a cowboy in Western times...',
// (Optional) Set a custom name for the user
// userLabel: 'User',
// (Optional) Set a custom name for ChatGPT
// chatGptLabel: 'ChatGPT',
// (Optional) Set to true to enable `console.debug()` logging
debug: false,
};
const cacheOptions = { // Options for the Keyv cache, see https://www.npmjs.com/package/keyv
store: chatgptStore,
};
const chatgpt = new ChatGPTClient(OPENAI_API_KEY, clientOptions, cacheOptions);
2022-12-09 09:41:17 +00:00
// Automatically join rooms the bot is invited to
2023-02-06 14:08:45 +00:00
if (MATRIX_AUTOJOIN) AutojoinRoomsMixin.setupOnClient(client);
2022-12-09 09:41:17 +00:00
client.on("room.failed_decryption", async (roomId, event, error) => {
// handle `m.room.encrypted` event that could not be decrypted
LogService.error("index", `Failed decryption event!\n${{ roomId, event, error }}`);
await client.sendText(roomId, `I couldn't decrypt the message :( Please add me to an unencrypted room.`);
});
client.on("room.join", async (roomId: string, _event: any) => {
LogService.info("index", `Bot joined room ${roomId}`);
await client.sendMessage(roomId, {
"msgtype": "m.notice",
2023-02-06 13:42:17 +00:00
"body": `👋 Hello, I'm ChatGPT bot! Matrix E2EE: ${MATRIX_ENCRYPTION}`,
2022-12-09 09:41:17 +00:00
});
});
2023-01-06 23:59:52 +00:00
// Prepare the command handler
2023-02-06 14:08:45 +00:00
const commands = new CommandHandler(client, chatgpt);
2023-01-06 23:59:52 +00:00
await commands.start();
2022-12-09 09:41:17 +00:00
2023-02-06 13:42:17 +00:00
LogService.info("index", `Starting bot using ChatGPT model: ${CHATGPT_MODEL}`);
2022-12-09 09:41:17 +00:00
await client.start()
LogService.info("index", "Bot started!");
}
main();