Attempting a ChatGPT Discord Bot: Part 3 - Taking a Stab at Javascript

Photo by Uday Mittal on Unsplash

Attempting a ChatGPT Discord Bot: Part 3 - Taking a Stab at Javascript

ยท

5 min read

In the previous part, I spent a few hours spinning my wheels following an old tutorial. With some Googling and ChatGPT help, I was able to get to a working point in my project:

  • OpenAPI was connected successfully and I was able to get a response from the API

  • Discord was able to trigger on new message events and then provide a response

Things were looking good! Or, so I thought. Turns out the bot was triggering on every new message and I had to quickly disable it and have the server admin remove its permissions. Whoops!

I'd released a bot into the wild without first checking it - a cardinal sin. So, I returned to the code to see what was happening. Before we go down the road of analyzing the code, I probably should remind you that I have literally zero experience with Javascript. Here is the code:

client.on("messageCreate", async function (message) {
  if (message.author.bot) return;

  try {
    const response = await openai.createChatCompletion({
        model: "gpt-3.5-turbo",
        messages: [
            {role: "system", content: "You are a helpful assistant who responds succinctly"},
            {role: "user", content: message.content}
        ],
      });

    const content = response.data.choices[0].message;
    return message.reply(content);

  } catch (err) {
    return message.reply(
      "As an AI robot, I errored out."
    );
  }
});

In essence, the code is conducting the following steps:

  1. Look for a new message event from the Discord server

  2. Then try to send the OpenAI API a message from the system and store the response in content

  3. Push the return message back to Discord

  4. If the return from the OpenAI API is an error, report back to Discord an error message

Pretty straightforward, right? That's what I thought, but the issue is that it triggers off of every single message on the server - including ones that don't pertain to the bot. Instead, what I want it to do is to respond to mentions which are messages that specifically mention the bot with an '@' symbol.

After a quick YouTube video or two on Javascript, I figured out that it is pretty straightforward in terms of basic operations like conditional statements, loops, and messages. What I wanted to do is make sure that the mentioned ID in the Discord message matches the user ID of the bot. Easy right? "If message contains bot ID, then respond." I was able to sus out the response using console.log() (thanks to Youtube videos) but wasn't sure what I was dealing with.

I kept hitting errors because I wasn't sure how to extract the ID parameter from Collection(1). In truth, I had no idea what that even was. Ok, back to learning more about Javascript. In my impatience, I turned to ChatGPT to give me a quick answer, rather than weeding through more YouTube tutorials:

In the structure you've described, message.mentions.users is a Collection, which is essentially a map of key-value pairs in Discord.js. Each key is a user ID, and the value is the user object. To access a specific user's ID within a mention, you generally need to iterate over message.mentions.users or access a specific user by one of the collection methods.

If you are trying to get the ID of the first mentioned user, you can use the first() method of the Collection class.

Great! By parsing the collection with message.mentions.users.first() I can now extract the ID and then compare it with the bot's ID. If it matches, then the bot will respond. I will admit that it took way too many tries and facepalming before I got to the answer about collections (sorry, Rosie, for the server spam).

After figuring out the Javascript response, I had everything in place! The last thing to do was clean up and add comments to the code (like a good, responsible, seasoned coder should do ๐Ÿ‘€). Here is the final code:

// Import necessary libraries and modules
import dotenv from 'dotenv';
import { Client, GatewayIntentBits } from 'discord.js'; // 'User' import removed as it's not used
import OpenAI from 'openai';

// Load environment variables from .env file
dotenv.config();

// Initialize Discord client with specific intents to listen for
const client = new Client({
  intents: [
    GatewayIntentBits.Guilds,
    GatewayIntentBits.GuildMessages,
    GatewayIntentBits.MessageContent,
  ],
});

// Initialize OpenAI client with API key from environment variables
const openai = new OpenAI({
    apiKey: process.env.OPENAI_API_KEY
});

// Event listener for new messages
client.on("messageCreate", async function (message) {
    // Ignore messages from bots
    if (message.author.bot) return;

    // Check if the bot itself is mentioned in the message
    const firstMentionedUser = message.mentions.users.first();
    if (firstMentionedUser && client.user.id === firstMentionedUser.id) {
        try {
            // Generate a response from OpenAI based on the message content
            const response = await openai.chat.completions.create({
                model: "gpt-3.5-turbo",
                messages: [
                    {role: "system", content: "You are a wise and philosophical 'Shrine of the Whispers'. People come to you looking for wisdom. Please provide a short but deep, concise, and whimsical response to their prayers."},
                    {role: "user", content: message.content}
                ],
            });

            // Extract and send the AI-generated response
            const content = response.choices[0].message;
            return message.reply(content);

        } catch (err) {
            // Handle any errors by sending a generic error message
            return message.reply("Error");
        }
    } else {
        // Optional: Log a message or take other actions when no user is mentioned or the bot is not mentioned
        console.log("No user mentioned or bot not targeted.");
    }
});

// Log the bot in using the token from environment variables
client.login(process.env.BOT_TOKEN);

The results from the 3.5-turbo model are respectable but have their quirks - they seem a little uninspired and have some repetitiveness to them.

Whew, I'm feeling pretty good about this! I think the next step for this would be to look at making a bot that has a little bit more functionality rather than simple and novel.

One idea I have is to make a bot that can take input from all channels, stitch together a coherent summary, and give statistics about posting habits over a full 24-hour period. In addition to that, I'd like to push the bot onto a cloud hosting service so that it's not sitting on my laptop. Anyway, I'll save that for the next post! Cheers :)

ย