I think at some point in pretty much every hobby developer’s career, they get a really awful idea that they absolutely have to see through to completion. Mine, or at least my most recent one, is Vore-bot . Off the top, I want to make it clear that I won’t be explaining what vore is, if you want to know you can Google it yourself (or don’t. It’s NSFW, and doesn’t really matter for my project).

At the time the idea came to me, a few Discord servers I frequented found themselves in a bit of an odd situation: we found ourselves in an (I hope) ironic loop of making vore jokes and calling out people for making vore jokes. I of course thought “Hey self, you’ve wanted to make a Discord bot for a while. Why not make a bot to call people out for making these jokes?” Thusly, I set out to learn how to make a Discord bot. I wanted this bot to be able to do a few pretty simple things: read chat to see if someone references vore (or any other banned word), send a message calling out the user for it, and then update its per-server timer to indicate the last time someone referenced vore. My hope, of course, being that the implication of ruining a reference-free streak would keep people from doing it (spoilers: it didn’t work).

I ended up building my bot in Python, using Discord.py which has a pretty good set of instructions on how to set up included in its readme, and hosted it using Heroku (not something I’d repeat for a bot like this, but that’s for later). But first, I needed to create a bot account.

Using this link you can create your bot application, then create a bot user to inhabit it. I had to make my bot public, but did not require OAuth2 code grant to be enabled. The token associated with your bot user will have to be added to your client.run() call, and must be kept private – I forgot this originally and included it on Github, don’t make my mistake. Lastly, I invited my new bot into a private server I started.

Building off of the example in Discord.py’s readme, I now had a functional bot I could run from my own command line, and had to implement the logic I was interested in.

The first step was to decide how to determine if a message was referencing vore or not. For this, I decided to use a case insensitive RegEx and simply check every message sent by another user to see if it matched this RegEx. Code for this follows as part of the body of the on_message() function, which will be executed everytime the bot sees a message being sent:


pattern = re.compile(r'\b[V|v][O|Ò|Ó|Ô|Õ|Ö|o|ò|ó|ô|õ|ö][R|r][E|È|É|Ê|Ë|e|è|é|ê|ë][S|s]?\b')
...
elif ((pattern.search(message.content) is not None) and 
      (message.author.id != client.user.id)):
    serverAndDate[message.server.id] = currentTime
    writeTimesToFile()
    print((currentTime - lastMention).total_seconds())
    if (awake and (currentTime - lastMention).total_seconds() >= 1800):
        await client.send_message(message.channel, 
              '{} referenced the forbidden word, setting the counter back to 0. ' +
              'I\'ll wait a half hour before warning you again.\n The server went' +
              '{}{}{}{} without mentioning it.'.format(message.author.mention,
                                                       dt, ht, mt, st))
        lastMention = currentTime

You may note that this code checks to make sure that the message was sent by someone other than the bot by checking the message author, prints a message to the chat, and also calls a function called writeTimesToFile(). This gets paired with a readTimesFromFile() function to update information for each server, and is done as follows:


def readTimesFromFile():
    global serverAndDate
    with open("timeStamps.txt", "r") as target:
        for line in target:
            tmp = line.split(',')
            tmp[1] = tmp[1][0:-1]
            serverAndDate[tmp[0]] = datetime.datetime.strptime(tmp[1],
                                                   "%Y-%m-%d %H:%M:%S")
            

def writeTimesToFile():
    with open('timeStamps.txt', 'w') as target:
        for serverId in serverAndDate:
            target.write('{},{}\n'.format(serverId, 
                                          serverAndDate[serverId].strftime("%Y-%m-%d %H:%M:%S")))

At this point, I wanted to add in some additional commands to the bot. Namely: a way to silence (and unmute) the bot, restricted to administrators; a way to check the current time without reseting it; and a help function to indicate what options are available. These are all handled similarly, and the code for the more interesting silence command follows:


permission = message.author.server_permissions

if message.content.startswith('!vtsilence') and permission.administrator:
    await client.send_message(message.channel, 
          "Ok {}, I'll be quiet now. use '!vtalert' to wake me back " + 
          "up!".format(message.author.mention))
    awake = False

When you put out an invitation link for your bot, make sure it asks for the permissions it needs and no more. You can generate these links from this link.

Lastly, I had to go about moving my hosting from being local to using Heroku, which I did using the information in this Reddit Post. Like mentioned before, were I to start the bot up again today, I wouldn’t run the Heroku instance directly off the Github repository (or at least not a public one) for the sake of not including the token in a public place, instead opting to grab the code from my own computer. Unfortunately, I haven’t done that yet and can’t speak about it.

As always, you can find the source code for my project here free for you to peruse and base your own works off of.

Mistakes Were Made

Leave a Reply

Your email address will not be published. Required fields are marked *