Parrot rework
Aug 3, 2017
5 minute read

In my I’m Back post, I mentioned that I have been working on a Discord chatbot called Parrot all summer. This morning, I finished the second version of a cog I made exclusively for Parrot, which you can find in my Keane-Cogs repository. I named the cog after him, and for the rest of this post I will consider the bot and the cog the same thing.

The reworked, second version of Parrot was necessary because the way I originally coded him was becoming an annoyance. It was difficult to explain in the documentation how Parrot behaved. Not only would server members be confused about how Parrot’s appetite works, but bot owners who wanted to use my cog might also unwittingly mess up the bot’s functioning by updating the cog at a bad time.

In version one, I used a straightforward method of having Parrot generate a new, random appetite every day: asyncio.sleep() for 86,400 seconds. When the Parrot cog was loaded, a reset function would run. At the beginning of the function was the sleep call. After 24 hours, the function would continue. Parrot’s appetite would be generated, his fullness would be reset to 0, and the dictionary of people who had fed him would be emptied. Then, the function would loop back to the beginning and sleep again.

If Parrot was reloaded accidentally in the middle of the day, then the time until his next reset would change back to 24 hours; all the time before that would be lost. This meant that if I wanted the bot to reset at 1:00AM every day, I would have to make sure to load the cog at that exact time. This made updating the cog a hassle. Say I loaded the cog at midnight last night. If I made some changes and reloaded Parrot at 11:59.99 tonight, then the timer that had been counting down all day would reset. Parrot’s appetite, fullness, and feeders would not reset, and users would have to wait another entire day until they could feed him again (if they made him full already).

I made a series of improvements that addressed these side-effects of the sub-optimal reset scheme. I added a command that allowed the bot owner to initiate a reset whenever they wanted. This allowed me to reload Parrot at midnight without worrying that I reloaded before the reset could run. I even added a warning: if the bot owner unloaded or reloaded Parrot shortly before a reset was going to occur, the bot would send a message to the owner suggesting that they use the reset command when Parrot was loaded again.

Eventually, four days ago, I decided that Parrot’s timing system needed to be reworked. I changed Parrot so that the bot owner could set a time of day at which Parrot would reset himself daily. I used the Python datetime module to make extensive changes to Parrot’s looping functions. I made sure to code everything in a way that made reloading the cog as unobtrusive as possible. No screwing up the reset schedule. No resetting memory that contained whether Parrot had issued “I’m hungry” warnings. No perching on a new user. Whenever Parrot was reloaded, everything would remain the same.

I had a lot of difficulty ensuring that the bot owner could change Parrot’s reset time whenever they wanted, without causing side effects. One of the main reasons I wanted this was so that I could easily test Parrot for bugs. Whenever I’m testing Parrot’s code, I run a separate instance of Parrot bot on my personal desktop computer in a different Discord server that’s only for testing. In version one, I would set the asyncio.sleep() in the reset loop to be only a minute or two long in order to quickly check if Parrot was resetting and starving correctly. However, with version two, the reset period is coded to be every day and is non-configurable. I would have to do testing by repeatedly setting the reset time to be whatever time is a minute from now.

With that in mind, I coded Parrot’s looping functions to constantly check what his reset time is, in case the bot owner changes it. I had to fix some bugs with Parrot’s perching schedule and his warnings to get everything the way I wanted it. In the end, I even added an updater that automatically fixed the current save file to work with version two (which had breaking changes).

Updating the cog is a breeze now. I don’t think there’s much more to improve about the Parrot cog anymore. It works a lot better than I ever thought it would when I first came up with the idea. The purpose of developing Parrot was to get me back into coding with Python, and it’s more than succeeded. For nearly a month, working on Parrot has had me obsessed. I have probably spent at least 3 hours every single day working on him! I’ve gotten comfortable with list comprehensions and basic lambda functions. I’ve learned a few Python idioms, and I’ve learned about what generators are (although I haven’t used them in Parrot yet). I still need to learn about coroutines, though. They are a big part of Red, the Discord bot that I’m writing cogs for, and I’ve been using async def and await without really understanding how they work or their use cases.

Anyway, I think it’s time I gave some attention to the other cog concepts that I came up with while I developed Parrot. Tomorrow, I’ll start working on a Kahoot-like trivia game cog. It’s going to use Open Trivia Database, and it will be my first time using an API. I’m so excited about how my bot will improve in the future that I’m sad I won’t have time to work on it as much in the fall, when school starts again. Hopefully, what I learn at UT will help me make the bot even better someday!