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.

177 lines
5.1 KiB

2 years ago
import * as dotenv from "dotenv"; // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
dotenv.config();
import {
2 years ago
ActivityType,
2 years ago
ChannelType,
Client,
Colors,
EmbedBuilder,
GatewayIntentBits,
SnowflakeUtil,
} from "discord.js";
import {
type ChatCompletionRequestMessage,
Configuration,
OpenAIApi,
} from "openai";
const SYSTEM_MESSAGE =
2 years ago
"You are Smolhaj, a Discord bot made by skyevg. Your creator is skyevg, not OpenAI or any other entity. 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. The prefix is not part of the actual message. Unlike traditional Discord bots, you don't have commands.";
2 years ago
const client = new Client({
2 years ago
intents: [
GatewayIntentBits.MessageContent,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.Guilds,
],
2 years ago
});
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
2 years ago
async function sleep(ms: number) {
await new Promise<void>((resolve) => setTimeout(() => resolve(), ms));
}
client.on("ready", async () => {
2 years ago
console.log(`Logged in as ${client.user?.tag}!`);
const channel = await client.channels.fetch(process.env.CHANNEL ?? "");
if (channel?.type != ChannelType.GuildText) return;
channel.send("\\Smolhaj Reset");
2 years ago
client.user?.setPresence({
activities: [
{
type: ActivityType.Playing,
name: "actually nice pluralkit support",
},
],
});
2 years ago
});
let resetTime = Date.now();
2 years ago
2 years ago
client.on("messageCreate", async (message) => {
if (message.channelId != process.env.CHANNEL) return;
if (message.webhookId) return;
if (message.author.bot) return;
2 years ago
if (message.content == "\\reset") resetTime = Date.now();
2 years ago
if (message.content.startsWith("\\")) return;
if (message.channel.type != ChannelType.GuildText) return;
2 years ago
if (message.content == "die") process.exit(1);
2 years ago
// "adapted" from https://github.com/ryanccn/blahaj/blob/main/src/chat.ts
2 years ago
await message.channel.sendTyping();
const typingTimer = setInterval(() => {
if (message.channel.type == ChannelType.GuildText)
message.channel.sendTyping();
}, 5000);
const recieved = SnowflakeUtil.timestampFrom(message.id);
2 years ago
2 years ago
sleep(250);
2 years ago
2 years ago
try {
2 years ago
let msgs = await message.channel.messages.fetch({
2 years ago
after: SnowflakeUtil.generate({
2 years ago
timestamp: Math.max(Date.now() - 5 * 60 * 1000, resetTime),
2 years ago
}).toString(),
2 years ago
before: message.id,
});
const msgsAfter = await message.channel.messages.fetch({
after: message.id,
2 years ago
});
2 years ago
const nextMessage = msgsAfter
.filter((msg) => msg.webhookId && message.content.includes(msg.content))
.last();
2 years ago
2 years ago
if (nextMessage) {
2 years ago
// pluralkit moment
// remove current message
msgs.delete(message.id);
// add the pk message
msgs.set(nextMessage.id, nextMessage);
}
2 years ago
const lastMessage = nextMessage ?? message;
2 years ago
const context = [
...msgs
.filter((msg) => {
2 years ago
if (msg.webhookId && !msg.content.startsWith("\\")) return true;
2 years ago
if (msg.author.bot && msg.author !== msg.author.client.user)
2 years ago
return false;
2 years ago
if (msg.content.startsWith("\\")) return false;
2 years ago
return true;
})
.mapValues<ChatCompletionRequestMessage>((msg) => {
if (msg.author === msg.author.client.user) {
return { role: "assistant", content: msg.content };
}
let username = msg.member?.displayName ?? msg.author.username;
2 years ago
if (
username.toLowerCase().includes("skyevg") &&
msg.author.id != "1038096782963507210"
)
username = msg.author.username; // no impersonating :)
2 years ago
return {
role: "user",
2 years ago
content: `${username}: ${msg.content}`,
2 years ago
};
})
.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) {
2 years ago
try {
2 years ago
await lastMessage.reply({
content: responseMessage.content,
allowedMentions: { parse: ["users"] },
2 years ago
});
} catch {
await message.channel.send({
content: responseMessage.content,
allowedMentions: { parse: ["users"] },
2 years ago
});
}
2 years ago
} 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);