matrix-chatgpt-bot/src/handlers.ts

61 lines
3.5 KiB
TypeScript
Raw Normal View History

import { ChatGPTAPIBrowser, ChatResponse } from "chatgpt";
2022-12-09 09:41:17 +00:00
import { MatrixClient } from "matrix-bot-sdk";
2022-12-29 11:11:52 +00:00
import { CHATGPT_TIMEOUT, MATRIX_BOT_USERNAME, MATRIX_DEFAULT_PREFIX_REPLY, MATRIX_DEFAULT_PREFIX} from "./env.js";
2023-01-03 02:49:59 +00:00
import { RelatesTo, StoredConversation, StoredConversationConfig } from "./interfaces.js";
2022-12-29 11:11:52 +00:00
import { isEventAMessage, sendError, sendThreadReply } from "./utils.js";
2022-12-09 09:41:17 +00:00
/**
* Run when *any* room event is received. The bot only sends a message if needed.
* @param client
* @returns Room event handler, which itself returnings nothing
*/
export async function handleRoomEvent(client: MatrixClient, chatGPT: ChatGPTAPIBrowser): Promise<(roomId: string, event: any) => Promise<void>> {
2022-12-09 09:41:17 +00:00
return async (roomId: string, event: any) => {
if (isEventAMessage(event)) {
2022-12-29 11:11:52 +00:00
try {
const relatesTo: RelatesTo | undefined = event.content["m.relates_to"];
const rootEventId: string = (relatesTo !== undefined && relatesTo.event_id !== undefined) ? relatesTo.event_id : event.event_id;
const storedValue: string = await client.storageProvider.readValue('gpt-' + rootEventId)
const storedConversation: StoredConversation = (storedValue !== undefined) ? JSON.parse(storedValue) : undefined;
2023-01-03 02:49:59 +00:00
const config: StoredConversationConfig = (storedConversation !== undefined && storedConversation.config !== undefined) ? storedConversation.config : {};
const MATRIX_PREFIX_REPLY:boolean = (config.MATRIX_PREFIX_REPLY === undefined) ? MATRIX_DEFAULT_PREFIX_REPLY : config.MATRIX_PREFIX_REPLY
2023-01-03 02:31:06 +00:00
const shouldBePrefixed: boolean = ((MATRIX_DEFAULT_PREFIX) && (relatesTo === undefined)) || (MATRIX_PREFIX_REPLY && (relatesTo !== undefined));
2022-12-29 11:11:52 +00:00
if (event.sender === MATRIX_BOT_USERNAME) return; // Don't reply to ourself
if (Date.now() - event.origin_server_ts > 10000) return; // Don't reply to old messages
if (shouldBePrefixed && !event.content.body.startsWith(MATRIX_DEFAULT_PREFIX)) return; // Don't reply without prefix if prefixed
await Promise.all([client.sendReadReceipt(roomId, event.event_id), client.setTyping(roomId, true, 10000)]);
const trimLength: number = shouldBePrefixed ? MATRIX_DEFAULT_PREFIX.length : 0
const question: string = event.content.body.slice(trimLength).trimStart();
if ((question === undefined) || !question) {
await sendError(client, "Error with question: " + question, roomId, event.event_id);
2022-12-28 23:16:21 +00:00
return;
}
2022-12-29 11:11:52 +00:00
let result: ChatResponse
if (storedConversation !== undefined) {
result = await chatGPT.sendMessage(question, {
timeoutMs: CHATGPT_TIMEOUT,
conversationId: storedConversation.conversationId,
parentMessageId: storedConversation.messageId
});
} else {
2022-12-29 11:11:52 +00:00
result = await chatGPT.sendMessage(question, {timeoutMs: CHATGPT_TIMEOUT});
}
2022-12-29 11:11:52 +00:00
await Promise.all([client.setTyping(roomId, false, 500), sendThreadReply(client, `${result.response}`, roomId, rootEventId)]);
2022-12-29 11:11:52 +00:00
await client.storageProvider.storeValue('gpt-' + rootEventId, JSON.stringify({
conversationId: result.conversationId,
messageId: result.messageId,
config: ((storedConversation !== undefined && storedConversation.config !== undefined) ? storedConversation.config : {}),
}));
2022-12-09 09:41:17 +00:00
} catch (e) {
console.error(e);
2022-12-29 11:11:52 +00:00
await sendError(client, "Bot error, terminating.", roomId, event.event_id);
2022-12-09 09:41:17 +00:00
}
}
}
}