Discord AI Chatbot: Part 1
I have always dreamed of creating a very advanced AI chatbot that acts as similar to a human as possible. I wanted to do this because I found it extremely funny that someone could either befriend or block an AI bot in anger, thinking it was a real person. My first idea to accomplish this was very outlandish and unrealistic. Essentially, I wanted everything to come from scratch, including the data, the architecture, and whatnot. My first attempt at this, which was a few years ago, was using a simple Python chatterbot module. I then took a ton of messages from a very toxic Discord server I was in, collected about 10 thousand, and prayed...
After deploying, I realized that the chatbot would only send messages that it had heard before, making it not that fun. I integrated it into a Discord self-bot to fool people, but they caught on instantly when they realized the bot was just repeating things they said 10 minutes back. I needed another approach, and that is when I learned about OpenAI's finetuning playground. This was a game changer for my project, and it allowed me to have an extremely advanced, funny, and witty chatbot in minutes. It worked because I would supply 30-60 messages in JSON format to the OpenAI format. The data I provided looked like this:
{"messages": [{"role": "system", "content": "PROMPT"}, {"role": "user", "content":"CONTENT"}]}
Using this approach worked pretty well. I could mess around with the bot for a bit, and I tricked many people. I had friends who started questioning every "person" I introduced to them, thinking it was an AI bot. But for me, it was not good enough. I noticed that sometimes the bot would spit out random Chinese or Russian characters, probably due to the temperature being turned up too high.
Additionally, it could not react to messages and understand much context. I edited the source code for version 2 of this AI Bot and fine-tuned the OpenAI model to understand JSON inside the content property. I told the model that it could include "additional messages" and "emojis" to react to other people. I really liked the innovation, and I had no issues with it. Occasionally, the bot would mess up the JSON formatting, and it would spit back out, causing the message to send back to fail.
Something was missing; however, it felt too easy. I wanted to have something that was completely customizable, extremely smart, and, most importantly, cheap. After some planning with ChatGPT, I decided to work on a new AI Chatbot that would be better than all of the rest: the most impressive and data-heavy version 3. My first step was to get about 100k messages of unsupervised data for my first test iteration. To get this amount of data without any human intervention, I needed a script to automatically process all of the Discord messages coming to my client automatically process all of the Discord messages.
Completing said script took me about 5 hours, and I'm still making minor changes. It works like this: when a new message arrives, I check if the message is from a user and is in one of the channels that I actually want data from. After, it is sent to a map that stores all the channels I am listening to. The message is sent to an event log array.
for (const channelID of channelWhitelist){
logger.debug(`Adding channel ${channelID} to the Map memory`);
conversationMap.set(channelID, {
mostRecentMessage: null,
eventLog: []
});
}
I then check if there has been a message in the past 7 minutes in that channel, and if there has been, I add it to the event log. If there has not been a message in the past 7 minutes OR the event log has 100 elements, I answer all the messages in the array and push them to a .txt file.
if(Date.now() - channel.mostRecentMessage > 1000 * 60 * minuteCooldown){
if(channel.eventLog.length < 2) {
channel.mostRecentMessage = null;
channel.eventLog.push({type, data});
logger.info("Holding off registration for more messages");
return;
}
logger.debug(`Log was sent after the cooldown, pushing to registry...`)
await parseMessages(channel.eventLog, client)
channel.mostRecentMessage = null;
channel.eventLog.length = 0;
channel.eventLog.push({type, data});
return;
}
if(channel.eventLog > conversationLimit){
logger.debug("Enough messages were sent for 1 conversation");
await parseMessages(channel.eventLog, client);
channel.mostRecentMessage = null;
channel.eventLog.length = 0;
channel.eventLog.push({type, data});
}
What is significant about this data collection is that all users mentioned in the dataset are anonymized in real-time. I have a Mongo database that stores each user mentioned in the dataset and converts them to a unique ID. This is then referenced for cleaning text like channel IDs and pinging users. For example, the message
yooo <@1164755746890723388> meet me at <#1296335493172039711>
would be turned into
yooo @user1 meet me at #staff-chat
This was essential to my script to ensure all data was anonymous and the model understood the context. After parsing the messages, it would finally add them to the dataset. A sample conversation would look like this:
The issues I have run into are random errors where the message object is null (why...) and the fact that I have discord servers that literally have had a consistent conversation for 2 hours without a single 7-minute break. That is why I implemented the 100-message limit. Transformer models have a contextual limit for training; the model I want only takes 2048 tokens per chunk for context, so if I want to plug in a conversation at a time, I have to make some sacrifices.
I am excited to see how this project turns out, and I will create a part 2 once training begins.