You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
120 lines
3.3 KiB
120 lines
3.3 KiB
import * as dotenv from "dotenv"; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
|
dotenv.config();
|
|
|
|
import {
|
|
ChannelType,
|
|
Client,
|
|
Colors,
|
|
EmbedBuilder,
|
|
GatewayIntentBits,
|
|
Partials,
|
|
SnowflakeUtil,
|
|
} from "discord.js";
|
|
import {
|
|
type ChatCompletionRequestMessage,
|
|
Configuration,
|
|
OpenAIApi,
|
|
} from "openai";
|
|
|
|
const SYSTEM_MESSAGE =
|
|
"You are Smolhaj, a Discord bot. You are helpful and friendly, and answers concisely. Due to the group nature of Discord, Messages will be prefixed with a username, followed by a colon.";
|
|
|
|
const client = new Client({
|
|
intents: [
|
|
GatewayIntentBits.MessageContent,
|
|
GatewayIntentBits.GuildMessages,
|
|
GatewayIntentBits.Guilds
|
|
],
|
|
});
|
|
|
|
const configuration = new Configuration({
|
|
apiKey: process.env.OPENAI_API_KEY,
|
|
});
|
|
const openai = new OpenAIApi(configuration);
|
|
|
|
client.on("ready", () => {
|
|
console.log(`Logged in as ${client.user?.tag}!`);
|
|
});
|
|
|
|
client.on("messageCreate", async (message) => {
|
|
if (message.channelId != process.env.CHANNEL) return;
|
|
if (message.webhookId) return;
|
|
if (message.author.bot) return;
|
|
if (message.content.startsWith("\\")) return;
|
|
if (message.channel.type != ChannelType.GuildText) return;
|
|
|
|
await message.channel.sendTyping();
|
|
const typingTimer = setInterval(() => {
|
|
if (message.channel.type == ChannelType.GuildText)
|
|
message.channel.sendTyping();
|
|
}, 5000);
|
|
|
|
try {
|
|
const msgs = await message.channel.messages.fetch({
|
|
after: SnowflakeUtil.generate({
|
|
timestamp: Date.now() - 5 * 60 * 1000,
|
|
}).toString(),
|
|
});
|
|
|
|
const context = [
|
|
...msgs
|
|
.filter((msg) => {
|
|
if (message.webhookId) return false;
|
|
if (message.author.bot && msg.author !== msg.author.client.user)
|
|
return false;
|
|
if (message.content.startsWith("\\")) return false;
|
|
return true;
|
|
})
|
|
.mapValues<ChatCompletionRequestMessage>((msg) => {
|
|
if (msg.author === msg.author.client.user) {
|
|
return { role: "assistant", content: msg.content };
|
|
}
|
|
return {
|
|
role: "user",
|
|
content: `${msg.member?.displayName ?? msg.author.username}:${
|
|
msg.content
|
|
}`,
|
|
};
|
|
})
|
|
.values(),
|
|
].reverse();
|
|
|
|
const response = await openai.createChatCompletion({
|
|
model: "gpt-3.5-turbo",
|
|
messages: [{ role: "system", content: SYSTEM_MESSAGE }, ...context],
|
|
});
|
|
|
|
const responseMessage = response.data.choices[0].message;
|
|
if (!responseMessage) return;
|
|
|
|
const isAppropriate = await openai
|
|
.createModeration({ input: responseMessage.content })
|
|
.then(({ data }) => !data.results[0].flagged);
|
|
|
|
if (isAppropriate) {
|
|
await message.channel.send({
|
|
content: responseMessage.content,
|
|
allowedMentions: { parse: ["users"] },
|
|
});
|
|
} else {
|
|
await message.channel.send({
|
|
embeds: [
|
|
new EmbedBuilder()
|
|
.setTitle("Response flagged!")
|
|
.setDescription(
|
|
"The generated response may have been inappropriate."
|
|
)
|
|
.setColor(Colors.Red),
|
|
],
|
|
});
|
|
}
|
|
|
|
clearInterval(typingTimer);
|
|
} catch (e) {
|
|
clearInterval(typingTimer);
|
|
throw e;
|
|
}
|
|
});
|
|
|
|
client.login(process.env.TOKEN);
|