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, 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 not by you will be prefixed with the username of the message author, 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; // "adapted" from https://github.com/ryanccn/blahaj/blob/main/src/chat.ts 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 (msg.webhookId) return false; if (msg.author.bot && msg.author !== msg.author.client.user) return false; if (msg.content.startsWith("\\")) return false; return true; }) .mapValues((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);