goose/services/ask-ai-bot/events/messageCreate.ts
BestCodes 45327dae5f
refactor: misc ask ai bot updates (#10332)
Signed-off-by: BestCodes <106822363+The-Best-Codes@users.noreply.github.com>
2026-07-09 01:31:02 +00:00

137 lines
4 KiB
TypeScript

import {
ChannelType,
Client,
Events,
Message,
type OmitPartialGroupDMChannel,
} from "discord.js";
import { answerQuestion } from "../utils/ai";
import { buildServerContext } from "../utils/discord/server-context";
import { logger } from "../utils/logger";
const followUpInstructions = {
embeds: [
{
title: "Want to ask a follow-up?",
description:
"Reply to one of my messages or @mention me in this thread so I know to answer.",
color: 0x6a9f58,
},
],
};
export default {
event: Events.MessageCreate,
handler: async (
_client: Client,
message: OmitPartialGroupDMChannel<Message<boolean>>,
) => {
if (message.author.bot) return;
const questionChannelId = process.env.QUESTION_CHANNEL_ID;
const guild = message.guild;
const serverContext = guild ? await buildServerContext(guild) : "";
// Handle messages in threads
if (message.channel.isThread()) {
const parentChannelId =
message.channel.parent?.id ?? message.channel.parentId;
if (!questionChannelId) {
logger.verbose(
"QUESTION_CHANNEL_ID is not configured; ignoring thread message",
);
return;
}
if (!parentChannelId || parentChannelId !== questionChannelId) {
logger.verbose(
`Ignoring thread message from ${message.author.username} (thread not in question channel)`,
);
return;
}
try {
// Check if the bot was mentioned or replied to
const isMentioned = message.mentions.has(message.client.user?.id || "");
let isReplyToBot = false;
if (message.reference?.messageId) {
isReplyToBot = await message.channel.messages
.fetch(message.reference.messageId)
.then((msg) => msg.author.bot)
.catch(() => false);
}
if (!isMentioned && !isReplyToBot) {
logger.verbose(
`Ignoring thread message from ${message.author.username} (not mentioned or replied to)`,
);
return;
}
await message.channel.sendTyping();
// Fetch last 10 messages from the thread for context
const messages = await message.channel.messages.fetch({ limit: 10 });
const sortedMessages = Array.from(messages.values())
.reverse()
.map((msg) => ({
author:
msg.author?.displayName || msg.author?.username || "Unknown",
content: msg.content,
isBot: msg.author.bot,
}));
await answerQuestion({
question: message.content,
thread: message.channel,
userId: message.author.id,
messageHistory: sortedMessages,
serverContext,
});
logger.verbose(
`Answered follow-up question for ${message.author.username} in thread`,
);
} catch (error) {
logger.error(`Error handling thread message: ${error}`);
}
return;
}
// Handle initial questions in the question channel
if (questionChannelId && message.channelId === questionChannelId) {
if (message.channel.type === ChannelType.GuildText) {
try {
let threadName = message.content.trim();
if (threadName.length > 100) {
threadName = threadName.substring(0, 97) + "...";
}
const thread = await message.startThread({
name: threadName,
autoArchiveDuration: 60,
});
// Send status message that will be updated as tools are called
const statusMessage = await thread.send("Just a sec...");
await answerQuestion({
question: message.content,
thread,
userId: message.author.id,
statusMessage,
serverContext,
});
await thread.send(followUpInstructions);
logger.verbose(`Answered question for ${message.author.username}`);
} catch (error) {
logger.error(`Error handling question: ${error}`);
}
}
}
},
};