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 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 ( 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 < 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 ) ;