{"id":66,"date":"2024-07-02T17:23:48","date_gmt":"2024-07-02T17:23:48","guid":{"rendered":"https:\/\/blog.bynate.studio\/?p=66"},"modified":"2024-07-10T15:54:02","modified_gmt":"2024-07-10T15:54:02","slug":"bong-beginnings","status":"publish","type":"post","link":"https:\/\/blog.bynate.studio\/?p=66","title":{"rendered":"Bong Beginnings"},"content":{"rendered":"<figure class=\"wp-block-post-featured-image\"><a href=\"https:\/\/blog.bynate.studio\/?p=66\" target=\"_self\"  ><img loading=\"lazy\" decoding=\"async\" width=\"500\" height=\"500\" src=\"https:\/\/bynate.nyc3.digitaloceanspaces.com\/blog\/wp-content\/uploads\/2024\/07\/02172458\/6.png\" class=\"attachment-post-thumbnail size-post-thumbnail wp-post-image\" alt=\"Bong Beginnings\" style=\"object-fit:cover;\" srcset=\"https:\/\/bynate.nyc3.digitaloceanspaces.com\/blog\/wp-content\/uploads\/2024\/07\/02172458\/6.png 500w, https:\/\/bynate.nyc3.digitaloceanspaces.com\/blog\/wp-content\/uploads\/2024\/07\/02172458\/6-300x300.png 300w, https:\/\/bynate.nyc3.digitaloceanspaces.com\/blog\/wp-content\/uploads\/2024\/07\/02172458\/6-150x150.png 150w\" sizes=\"auto, (max-width: 500px) 100vw, 500px\" \/><\/a><\/figure>\n\n\n<p class=\"wp-block-paragraph\"><em>bongbots logo, made by Gina<\/em><sup data-fn=\"7325dd72-0b43-4fcb-846d-3e92eae6756c\" class=\"fn\"><a href=\"#7325dd72-0b43-4fcb-846d-3e92eae6756c\" id=\"7325dd72-0b43-4fcb-846d-3e92eae6756c-link\">1<\/a><\/sup><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">About 3 years ago, I was a part of a online community on the popular app Discord. This community had various automated &#8220;bots&#8221;, interactive users that allowed regular human users to send commands, play games, etc. Enter in &#8220;BongBot&#8221;. This bot, at the time, only had 2 functions. First, it would send a message to one channel at the top of every hour, similar to how a Big Ben would chime at the beginning of every hour, hence the name &#8220;BongBot&#8221;. After the bot sends this message, the bot itself reacts with the clock emoji (this is similar to how you can react to slack messages), and the first user to react after the bot gets the &#8220;bong&#8221;. If no one reacts to the bot it deletes the message.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Years pass by and during a particularly dry season of looking for jobs, I decided to apply some of the previous hosting knowledge, with some elementary Python knowledge, to the test. The goal? Rebuild BongBot, but make it better. Instead of only having two commands, let users check to see how many &#8220;bongs&#8221; they have at any given time. Come up with a guild (what Discord refers to as communities) leaderboard, entice users to compete with each other, and even allow guilds to compete with each other!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Soo&#8230; Its time to design!<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">So where do we begin? The very first thing I did was design how the bot should respond to user commands, what commands and what data needed to be stored for each user, and what storage backend I was going to use.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>pip install discord.py<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">discord.py is the standard python library for using python to interact with the discord API in a simple, easy to understand way. It supports async and await, allowing for seamless access and control of multiple guilds. So I hobbled over to the API documentation and started reading.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At this time, there was going to be two functions: One that would handle sending the message and waiting for the user reaction and calculate the reaction time of the user, and another one that would add the bongs to the users overall score, tie the reaction time with the user that responded, and store that information in a JSON file.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Python? Libraries? API Documentation?:<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><br>So on a particularly cold day, and after setting up my VSCode environment, I decided to take a crack at it. I go over to the Discord Developer Portal and grab the keys needed and started with a simple task. Lets get the bot to say &#8220;Hello World&#8221; in response to the !h command being sent first.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>import discord.py \n\nbot = commands.Bot(bot = commands.Bot(command_prefix='!', intents=intents, help_command=None, case_insensitive=True)\n\nasync def on_ready():\n     print(\"logged into discord\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So here, after we declare a &#8220;bot&#8221;, the commands.Bot object then looks for a function with the name &#8220;on_ready&#8221; Here I just have it announce that it successfully logged into Discord! However, there is nothing else here, so subsequently, the bot is going to do&#8230;&#8230;..nothing. We have to declare a command!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The discord.py library luckily has these nifty things called <em>decorators<\/em>. These allow us to create major components of the bot without having to write alot of code, and are easy to read and understand. For our help command, our decorator looks like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>@bot.command<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Next we have the function that is going to handle the command when it is invoked. We are going to pass the argument ctx. It allows us to get the context of the interaction, which will then allow us to respond to it\/get more information about where the command was run.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>async def h(ctx):\n     ctx.channel.stend(\"hello world!\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">At the very end of the python program we have call bot.run and pass our auth key we got from the Discord Developer Portal and walla, we can run !hello_world and the bot responds with a &#8220;hello world&#8221; back! Sweet!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Getting User Input<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">So after I got the Hello World working I then went to go tackle the next part of this project. I needed to now &#8220;get&#8221; the reactions of the users that reacted too the bot. Little did I know, there was 2 ways i could do this, and I, of course, chose the wrong way first. I used the provided on_reaction_add function that the library provided. This function accepts a payload, which then we can get the user object, store that, at this point, in a list. Here is a snippet of the &#8220;reaction getting&#8221;:<\/p>\n\n\n\n<pre class=\"wp-block-code alignwide\"><code>async def on_raw_reaction_add(payload):\n    global bong_message_id\n    \n    if payload.user_id != bot.user.id:\n        guild = bot.get_guild(payload.guild_id)\n        channel = guild.get_channel(payload.channel_id)\n        message = await channel.fetch_message(payload.message_id)\n        member = guild.get_member(payload.user_id)\n\n        if payload.emoji.name == \"\u23f0\" and message.id == bong_message_id:\n            if message.author.id == bot.user.id:\n                reaction_time = datetime.now(timezone.utc).astimezone() - message.created_at\n                try:\n                    await message.delete()\n                except discord.errors.NotFound:\n                    return\n                await announce_winner(channel, member, reaction_time)\n                await bot.change_presence(activity=discord.Game('blah'))\n                return<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">After that, I call another function that does simple addition, and overwrite the users score with the new score calculated. There was one problem though, as I quickly found out. This function works GREAT if you have a inactive discord server. But further testing caused a RATE LIMIT if too many people tried reacting with the clock at the same time, as the bot could not delete the message fast enough, and every time the reaction was pressed it would trigger a DELETE request to Discords API, which would result in a error because the function is trying to delete a message that has been deleted earlier. This solution was not going to work. Back to the documentation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n<ol class=\"wp-block-footnotes\"><li id=\"7325dd72-0b43-4fcb-846d-3e92eae6756c\"><a href=\"https:\/\/x.com\/GinaLaskey\">https:\/\/x.com\/GinaLaskey<\/a> <a href=\"#7325dd72-0b43-4fcb-846d-3e92eae6756c-link\" aria-label=\"Jump to footnote reference 1\">\u21a9\ufe0e<\/a><\/li><\/ol>\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>bongbots logo, made by Gina About 3 years ago, I was a part of a online community on the popular app Discord. This community had various automated &#8220;bots&#8221;, interactive users that allowed regular human users to send commands, play games, etc. Enter in &#8220;BongBot&#8221;. This bot, at the time, only had 2 functions. First, it [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":71,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":"[{\"content\":\"<a href=\\\"https:\/\/x.com\/GinaLaskey\\\">https:\/\/x.com\/GinaLaskey<\/a>\",\"id\":\"7325dd72-0b43-4fcb-846d-3e92eae6756c\"}]"},"categories":[6],"tags":[],"class_list":["post-66","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-projects"],"_links":{"self":[{"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/posts\/66","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=66"}],"version-history":[{"count":11,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/posts\/66\/revisions"}],"predecessor-version":[{"id":85,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/posts\/66\/revisions\/85"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=\/wp\/v2\/media\/71"}],"wp:attachment":[{"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=66"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=66"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bynate.studio\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=66"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}