This week I built my own Discord Bot
So I had this idea with my friends from Discord the other day of making a custom Discord bot for our server and, since I’ve got a VPC in Vultr and the bot itself barely consumes any memory to run, I decided to go and make it myself.
Idea and API
I decided to use the discord.py API, since it is very well documented and has tons of examples. My ideas for this bot for now is fairly simple:
- Integrate my OpenAI API key to talk to it with a custom prompt to make it sound like Jarvis (yes, the one from Iron Man)
- React to messages with specific keywords with custom emojis from the server
- Count how many times @everyone were mentioned by one specific user (he is known for doing that a lot)
- Some slash commands, like:
- Store and list funny quotes some members have said
- List games that we have played and refunded (we do that a lot)
I implemented some other things along the way, but these were the ones that first came to my mind.
Setup
First I had to create my API key and set OAuth2 for my server at the Dev Dashboard. After that, set the both discord
and OpenAI keys in an .env file and install discord.py and OpenAI libs and we’re good to go. Here we initialize everything:
1import discord
2from dotenv import load_dotenv
3from openai import OpenAI
4import os
5
6load_dotenv()
7
8TOKEN = os.getenv("TOKEN")
9OPENAI_KEY = os.getenv("OPEN_AI_KEY")
10
11intents = discord.Intents.default()
12intents.message_content = True
13GUILD = discord.Object(id=GUILD_ID)
14
15bot = commands.Bot(command_prefix="!", intents=intents, help_command=None)
16openai_client = OpenAI(api_key=OPENAI_KEY)
The premise here is set your intents, which is basically what are the permissions of the bot, what it can do. I’m defaulting it here. Next
we create out guild object, which has my guild id on a constant set before. That’s basically it. Now we are set to implementing our stuff.
Slash commands
Let’s do something simple, the add quote function I talked about earlier. We must use the decorator @bot.tree.command to add a slash command.
Then we add the command itself add_quote along with its description and the guild object.
1@bot.tree.command(
2 name="add_quote", description="Adiciona uma citação de alguém", guild=GUILD
3)
4async def add_quote(
5 interaction: discord.Interaction, member: discord.Member, quote: str
6):
7 with open("data/quotes.json", "r") as file:
8 quotes = json.load(file)
9 quotes.append(
10 {
11 "member_id": member.id,
12 "member_name": member.display_name,
13 "quote": quote,
14 "date": datetime.now().strftime("%d/%m/%Y"),
15 }
16 )
17 with open("data/quotes.json", "w") as file:
18 json.dump(quotes, file)
19 await interaction.response.send_message(
20 f'Citação adicionada: "{quote}" - {member.display_name}'
21 )
We then define the function (with async) that gets the member and the quote as input. I then opens a json file with all the quotes and add it. Here, I
am using the json lib as well.
Let’s now implement some reactions to messages with certain keywords. First we set a json that we will call KEYWORDS with our set of keywords and respective emojis. Should look
something like this:
1{
2 "other": 123456789,
3 "something": 1459574930294444124,
4}
Now we implement the listener:
1@bot.event
2async def on_message(message: Message):
3 if message.author == bot.user:
4 return
5
6 content = message.content.lower()
7
8 for keyword, emojis in KEYWORDS.items():
9 if keyword in content or keyword in message.content:
10 if not isinstance(emojis, list):
11 emojis = [emojis]
12 for emoji in emojis:
13 if isinstance(emoji, int):
14 emoji = bot.get_emoji(emoji)
15 if emoji:
16 await message.add_reaction(emoji)
This will listen to every message and react accordingly. We can also add here our LLM integration, which will be called when the bot has been
mentioned. I defined a SYSTEM_PROMPT beforehand with some information about the server, its members and a custom funny Jarvis prompt.
1 if bot.user and bot.user.mentioned_in(message) and not message.mention_everyone:
2 user_message = message.content.replace(f"<@{bot.user.id}>", "").strip()
3 if user_message:
4 try:
5 response = openai_client.chat.completions.create(
6 model="gpt-4o-mini",
7 messages=[
8 {"role": "system", "content": SYSTEM_PROMPT},
9 {"role": "user", "content": user_message},
10 ],
11 max_tokens=300,
12 )
13 reply = response.choices[0].message.content
14 finish_reason = response.choices[0].finish_reason
15 print(f"Reply received: {reply}")
16 print(f"Finish reason: {finish_reason}")
17 print(f"Full response: {response.choices[0]}")
18
19 if reply:
20 await message.reply(reply)
21 except Exception as e:
22 await message.reply("Desculpe, não consegui processar sua mensagem.")
23 print(f"OpenAI error: {e}")
With that, I intend to implement some cooler stuff in the future with custom voice and stuff, but this is ok for now. Checkout the repo if you’d like.