What is a solution if it’s not over-engineered? The answer is “probably a better solution than the one you over-engineered”.
So what have we done this time? Well, we created a Discord bot. No, it’s not available for download, but I will provide the code for those of you who also like over-engineering dumb solutions to things that could have been solved far easier.
What does the bot do? One thing. Sends a ping to me, asking a simple question, then checks that the input matches the type it expects, then logs it to a separate channel. Here’s a screenshot of all it does:
Impressive, I know. Wait until you see the terrible code responsible for this.
import datetime
from datetime import timezone
import discord
from discord.ext import tasks
import os
from dotenv import load_dotenv, find_dotenv
load_dotenv(find_dotenv())
TOKEN = os.getenv("DISCORD_TOKEN")
UID = os.getenv("DISCORD_USER_ID")
intents = discord.Intents.default()
intents.message_content = True
class RemBotClient(discord.Client):
async def on_ready(self):
# if not already running, start the task
called_once_a_day.start()
# currently any message will be logged as a pushup count
async def on_message(self, message):
content = message.content
if message.author == self.user:
return
if content.isdigit():
message_channel = client.get_channel(<PUT_CHANNEL2_ID_HERE>)
await message_channel.send(f'''Exercise Log\nDate: {datetime.datetime.now().strftime("%d %b %Y")}\nPushups: {content}''')
print("Log response success")
else:
message_channel = client.get_channel(<PUT_CHANNEL1_ID_HERE>)
await message_channel.send(f'{content} is not a number. Try again')
client = RemBotClient(intents=intents)
# this is the time for the ping. Timezone is UTC and based on 24 hour clock. The current set time is 10pm Eastern Time, which is 3am UTC
msgTime = datetime.time(hour=3, minute=00, second=0, tzinfo=timezone.utc)
# Task create
@tasks.loop(time=msgTime)
async def called_once_a_day():
message_channel = client.get_channel(<PUT_CHANNEL1_ID_HERE>)
await message_channel.send(f'<@{UID}> how many pushups did you do today?')
print("Prompt success")
client.run(TOKEN)
Alright, so let’s go through this:
We’re importing a few libraries that we need, like datetime, discord.py, os, and dotenv. From there, we go ahead and pull in our .env file (you thought I’d just accidentally give you the token? My UID? My channel IDs [that you can’t do anything with?]? Ha. I’m not THAT dumb. Sometimes.), we then set some static variables from out .env file that we’ll use later.
Here’s where the magic starts and ends, the next bit is all about setting up the bot. So we’re using one variant of a “bot” from discord.py, a client. A client is basically just a bot that doesn’t require user interaction, so it has no commands or ways to actually call it. To use the bot we have to declare its intents, which are basically pre-defined sets of things bots can interact with and do in a server, like reading message content etc. Some events are privileged and require explicit calling, like getting message content and interacting with it, so we had to set intents.message_content to true in order to actually be able to retrieve the content. I’m not going to pretend I’m an expert on all this, because I’m not. I re-read the documentation on discord.py’s site while doing this so I may have botched a few things in this code, who knows. It works, so that’s all I care about.
Anywho, now we actually set up the client itself. I decided to just use a class so that I could expand on it later if I wanted to. The first function is just an on_ready and all it does is starts the task that we define later if it’s not running (which it won’t be if the bot is just coming online). The next function looks for any message in a specified text channel NOT sent by the bot itself, grabs the content, checks if it’s a number, if not tells the user, if so it sends it to a second channel with formatted text.
Now we just define an instance of client, we define a time using tzinfo and datetime (I went with UTC because I was too lazy to bother handling daylight savings time. So there will be times where this fires at a different time than I originally set. Oh well.) Then we set up the actual task which just runs a function to send a specific message, tagging my userID so I’m sure to see it.
A cool thing about discord clients is that they’re basically just running in the background, especially if you’re using tasks and tasks.loop to run them on schedules. The similar feature that bots have is commands. I found out the hard way that you can’t use client and bot in the same file, which is unfortunate but understandable I guess.
Anywho, yeah, that’s that. That’s all it does. It just asks me how many pushups I did today. That’s it. I mean, I could make it do more, but like, this was literally the entire purpose I had in mind. It’s a very very rudimentary reminder system that I might use to keep myself accountable for things. I could have just used something like Google Calendar or like a notepad or literally anything else, but where’s the fun in that? I now have a semi-functioning discord bot that does a thing and I’ve established the baseline for future things and potentially future bots (lol maybe…).
Also, no, I don’t understand asyncIO. I did once upon a time but I don’t use it often enough to keep it in my brain. I just followed the documentation and experimented until it stopped breaking. As far as I know, asyncIO is the defacto for server-client stuff because it allows for handling stuff and things and stuff. Events? Yeah, those.
Anyway, that’s it. That’s all this post was. Just me doing a thing that really doesn’t serve much purpose but it works, sorta, and it’s neat so there ya go. Feel free to copy the code and fiddle with it yourself, it’s pretty neat because you can register the bot on discord’s developer site and then locally run the code and actually invite it to servers you have permission to do so in and it’ll run. So you could run your bot’s code on like a raspberry pi locally and invite it to a server you and your friends have and it would work.