<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ discord - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ discord - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Thu, 28 May 2026 04:41:51 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/discord/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Ask a Great (Technical) Question: The Art of Getting Help Debugging Code ]]>
                </title>
                <description>
                    <![CDATA[ Computers will, 100% of the time, without fail, do exactly what you tell them to do. It's getting them to do what you want them to do that's the real trick. The gap between what you tell the computer  ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-ask-a-great-technical-question/</link>
                <guid isPermaLink="false">69b997b8c22d3eeb8ae5a273</guid>
                
                    <category>
                        <![CDATA[ questions ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Pete Lamonica ]]>
                </dc:creator>
                <pubDate>Tue, 17 Mar 2026 18:04:40 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/00747953-54ab-4027-8eaa-6f85a4e74b51.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Computers will, 100% of the time, without fail, do exactly what you <em>tell</em> them to do. It's getting them to do what you <em>want</em> them to do that's the real trick.</p>
<p>The gap between what you <em>tell</em> the computer to do and what you <em>want</em> the computer to do is where bugs are born and thrive.</p>
<p>As a moderator on the <a href="https://discord.gg/KVUmVXA">freeCodeCamp Discord</a>, I frequently see people pop in to say, “I did everything exactly right but my code isn’t working!” These people know what they <em>want</em> their code to do. But they didn’t <em>tell</em> the computer how to do it properly.</p>
<p>To resolve their bug, they must align their desires — their mental model of their code’s behavior — with their code. <strong>Assumptions are the truses that preserve that gap.</strong></p>
<h2 id="heading-the-problem-with-assumptions">The Problem with Assumptions</h2>
<p>Let’s say that I'm working on a webpage and I want to set an element’s background to be blue. It’s not working as I desire. It’s setting the text color instead of the background color.</p>
<p>I engage my coworker for help. I say to her, “I told the computer to make the background blue. But it’s setting the text color instead!”</p>
<p>My problem is that I <em>didn’t</em> tell the computer to change the <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/background-color">background color</a>. I instead mistakenly used the <code>color</code> <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/color">property</a>. There’s no way that my coworker could know that I was using the wrong property. So my assumption prevented her from helping me.</p>
<h2 id="heading-the-components-of-a-great-question">The Components of a Great Question</h2>
<p>Getting help takes effort. The more work you put into seeking help, the more likely you’ll be to obtain the help you need. In my experience, there are 3 components to a great question when you need technical assistance. <strong>Great questions contain <em>all</em> of these components</strong>.</p>
<ol>
<li><p>The code that contains the problem</p>
</li>
<li><p>Any error messages you’re seeing</p>
</li>
<li><p>A description of the issue containing <em>all</em> of the following:</p>
<ul>
<li><p>The exact steps to reproduce the issue</p>
</li>
<li><p>The expected result</p>
</li>
<li><p>The actual result</p>
</li>
</ul>
</li>
</ol>
<p>Below, I’ll break down each component in detail in case you’re not sure how to provide it when asking for help.</p>
<h3 id="heading-the-code-that-contains-the-problem">The Code that Contains the Problem</h3>
<p>This is harder than it sounds. Frequently, the reason I fail to fix a bug by myself is that I’m looking in the wrong place. I may be looking in the wrong function, file, or even project! If I go to my coworker for help and only provide her the code that I <em>think</em> contains the problem, then she may not be able to help me out.</p>
<p>This means that it’s important to provide as much context as possible. This increases the chances that the code you share with the people helping you will contain the information they need to help you solve your problem.</p>
<p>Even with decades of experience coding, I frequently have doubts about how certain code will behave. Being able to run the code and experiment with it (add logs, attach a debugger, try different inputs, and so on) is a big part of my process.</p>
<p>There’s lots of ways you might transmit your code to someone to get some help. Here’s some examples:</p>
<h4 id="heading-for-large-projects">For Large Projects</h4>
<p>If you’re having trouble with a large project (something more than one file or more than a couple hundred lines of code). Then your best bet is to share a <a href="https://github.com">GitHub</a> repo containing all the code.</p>
<p>Alternatively, you might create a “minimal reproduction.” If you can make the problem happen in the context of a small amount of code, then you can share that code instead. This can dramatically reduce the amount of work others need to do to help you and increase your chances of getting the answers you’re seeking.</p>
<p>Note: When working on a project for a client or employer, you might not be able to get help from outside sources due to your inability to share the relevant code. Don't share your employer’s or clients’ code publicly unless you have explicit permission to do so!</p>
<h4 id="heading-for-single-file-projects">For Single File Projects</h4>
<p>If your project is just one or two files or you’re creating a minimal reproduction of the issue, then you might be able to in-line your code in a chat message or create a <a href="https://gist.github.com">GitHub Gist</a> containing the relevant code.</p>
<p>Alternatively, you could use a tool like <a href="https://codepen.io">CodePen</a> or <a href="https://stackblitz.com">StackBlitz</a> to quickly generate a reproduction of the problem you’re facing.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736374915253/1aad0c65-c5dc-41df-9eff-d8cb7c0f9a84.png" alt="A not-yet-sent Discord message containing a code block. It starts with three backticks and the letters &quot;js&quot; on their own line, then contains some JavaScript code followed by three more backticks on their own line at the end." style="display:block;margin:0 auto" width="600" height="400" loading="lazy">

<h4 id="heading-for-freecodecamp-lessons">For freeCodeCamp Lessons</h4>
<p>Many of the questions I get on the freeCodeCamp Discord are regarding lessons in the learning platform (the curriculum). When asking a question about one of these lessons, make sure you include:</p>
<ol>
<li><p>All the code from the editor panel (that is, all the code you have written or modified)</p>
</li>
<li><p>A link to the step</p>
</li>
</ol>
<p>Without these two things, providing help gets dramatically harder.</p>
<h3 id="heading-error-messages">Error Messages</h3>
<p>When you’re a newer developer, error messages can be <em>very intimidating.</em> It’s understandable that you might skim over them as they don’t make sense and you’re not sure how to leverage them to solve your problem.</p>
<p><strong>Reading your error messages carefully</strong> can be one of the first steps you take to becoming a stronger developer. But even if you don’t know what to make of the message, when seeking help, make sure you give others with experience in reading those messages the opportunity to do so. If there are any error messages in your <a href="https://developer.chrome.com/docs/devtools/console">console</a> or terminal, copy/paste them <em>in their entirety</em> to your post asking for help.</p>
<h3 id="heading-description-of-the-issue">Description of the Issue</h3>
<p>When asking for help, you should <strong>consider that the person or people you’re asking might not have any context on your situation</strong>. It’s up to you to give them the context they need. To make sure you have given that context, include each of the following:</p>
<h4 id="heading-steps-to-reproduce-the-issue">Steps to Reproduce the Issue</h4>
<p>What, specifically, did you do that led you to encounter the problem you have? Be as detailed as possible here. Include things like, “I clicked the ‘Submit’ button,” or “I opened <code>https://www.example.com</code> in my browser.”</p>
<h4 id="heading-the-expected-result">The Expected Result</h4>
<blockquote>
<p>Me: “My text is blue!”<br>My coworker: “That’s nice.”</p>
</blockquote>
<p>My coworker wasn’t being dismissive of my problem. She just didn’t know whether it <em>was</em> a problem or not. Maybe I <em>wanted</em> the text to be blue. Maybe the blue text had nothing to do with my problem. Maybe the text only turns blue under certain circumstances.</p>
<p>If I had told my coworker that I expected the background to be blue rather than the text, she might have quickly realized what I did wrong and offered up a good suggestion on how to fix it.</p>
<h4 id="heading-the-actual-result">The Actual result</h4>
<blockquote>
<p>Me: “I wanted my background to be blue!”<br>My coworker: “That’s nice.”</p>
</blockquote>
<p>Forgetting to give the actual result is pretty much just as useless as forgetting to give the expected result. The expected and actual result of your code are <em>both</em> critical to a well-composed question.</p>
<p>When it comes to sharing actual results, screenshots or videos can be quite handy. Just make sure you’re not relying on them to the exclusion of the other components of a good question.</p>
<h2 id="heading-example-question">Example Question</h2>
<p>Here’s an example of what putting all this guidance together might yield:</p>
<blockquote>
<p>I'm on this step: <a href="https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures-v8/learn-introductory-javascript-by-building-a-pyramid-generator/step-33">https://www.freecodecamp.org/learn/javascript-algorithms-and-data-structures-v8/learn-introductory-javascript-by-building-a-pyramid-generator/step-33</a></p>
<p>I tried putting in this code:</p>
<pre><code class="language-javascript">for (iterator; condition; iteration) {

}
</code></pre>
<p>I expected it to pass, but instead, it gave me an error reading:</p>
<pre><code class="language-plaintext">ReferenceError: iterator is not defined
</code></pre>
<p>Can anyone help me figure out what I'm doing wrong? Here’s a screenshot of the step and error:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736374001287/ed083d56-ae60-40f8-a985-e9ce21a919ad.png" alt="A step on the freeCodeCamp website showing instructions and errors." style="display:block;margin:0 auto" width="600" height="400" loading="lazy"></blockquote>
<p>The question isn’t long, but it contains all the elements of a quality question as described by this article. It gives the code that contains the problem (including a link to the freeCodeCamp step), the error message that is shown, and the expectations (that it should pass) and actual result (that an error message is shown). It even includes a helpful screenshot showing how the error and result are presented.</p>
<h2 id="heading-closing-thoughts">Closing Thoughts</h2>
<p>This feels like a whole lot of effort. I just want my problem solved. Do I <strong>really</strong> need to do all of this???</p>
<p>Short answer? Yes. But only because <strong>it’s <em>less</em> effort than engaging in a prolonged back and forth as the people trying to help you attempt to get this information.</strong> Not just that, but if you go through these steps and collect this information, you might even find that you solve your problem yourself without needing to engage others.</p>
<p>This process might feel tedious at first, but like many things in life, if you practice it regularly then it becomes easier.</p>
<p>You’ve got this. 😎</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Integrate Discord Webhooks with Next.js 15 – Example Project ]]>
                </title>
                <description>
                    <![CDATA[ You’ve likely seen (or used) contact forms on websites that ask for customer feedback, potential work opportunities, customer contact info, and so on. But do you know what’s required to get all that feedback, contact info, and data sent to a private ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/integrate-discord-webhooks-with-nextjs-15-example-project/</link>
                <guid isPermaLink="false">678fa927b9b8fe257214e1be</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tapas Adhikary ]]>
                </dc:creator>
                <pubDate>Tue, 21 Jan 2025 14:03:19 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1737468174839/93832182-b4a9-48c0-aee5-cd8d716fc47a.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You’ve likely seen (or used) contact forms on websites that ask for customer feedback, potential work opportunities, customer contact info, and so on. But do you know what’s required to get all that feedback, contact info, and data sent to a private Discord text channel the moment the user submits it?</p>
<p>In this case, there is no database to store the information. Rather, you just have a free Discord text channel that keeps the data as a sequence of messages in chat. And an admin/moderator/user with the required access rights can read through these messages and take the appropriate action.</p>
<p>In case you are new to Discord, it’s a great platform for chatting, playing video games, having calls, and even running a virtual team for your startup. It’s free, and you can download it from <a target="_blank" href="https://discord.com/">here</a>.</p>
<p>Alright, coming back to the topic, I had a requirement like this to implement using Discord’s webhook and Next.js. I learned a lot from the activity. So I wrote this step-by-step tutorial about that process.</p>
<p>The primary objective here is to understand:</p>
<ul>
<li><p>What is a Webhook and what are the use cases?</p>
</li>
<li><p>How to Integrate a webhook with a web application framework like Next.js</p>
</li>
<li><p>How to build a user interface with the latest APIs and components from Next.js</p>
</li>
</ul>
<p>I hope you’ll like it. If you want to learn from a video, the article is also available as a video tutorial:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/6h-y1aRzvhY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-what-is-a-webhook">What is a Webhook?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-configure-a-webhook-on-discord">How to Configure a Webhook on Discord</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-create-a-nextjs-project">How to Create a Next.js Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-environment-variables">How to Set Environment Variables</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-integrate-the-webhook-with-the-nextjs-form-component">How to Integrate the Webhook with the Next.js Form Component</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-create-the-form">Create the Form</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-create-the-server-action">Create the Server Action</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-the-app-page">How to Update the App Page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-test-it-out">Let’s Test it Out</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-improve-the-message-feedback-using-react-19s-useactionstate-hook">How to Improve the Message feedback using React 19’s useActionState Hook</a></p>
<ul>
<li><p><a class="post-section-overview" href="#heading-set-up-a-toaster">Set up a toaster</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-enhance-the-form-with-the-useactionstate-hook">Enhance the Form with the useActionState Hook</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-update-the-server-action-to-return-results">How to Update the Server Action to Return Results</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#heading-lets-test-it-one-more-time">Let’s Test it One More Time</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-source-code-and-resources">Source Code and Resources</a></p>
</li>
</ol>
<h2 id="heading-what-is-a-webhook">What is a Webhook?</h2>
<p>Assume we have an email service that connects to a third-party email sender to send emails to users. Traditionally, there will be an API gateway to talk to the email service from the client devices. When the request is received, the email service will contact the email-sending provider.</p>
<p>While the email-sending provider processes the emails, the email service needs to know their status. One generic way to handle this is for the email service to poll for the status at a short frequency and update the client as and when the status changes. But this method has many drawbacks, such as resource mis-utilization, unnecessary connections, and poor performance.</p>
<p>On the contrary, how about the email service registers a callback URL that the email sender can call with relevant information when the email-sending action has been completed? This way, there is no unnecessary polling. Rather, the email sender can proactively inform the email service of a success or a failure after attempting to send the email. Then, the email service can respond to the clients regarding their status accordingly.</p>
<p>This registered callback is called a <code>Webhook</code>. Webhooks are widely used today in various industries like payments and checkout, system health monitoring, and third-party app integrations.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735270052821/366027ef-bd79-4825-ba51-58cdaf3d3705.png" alt="Webhook flow" class="image--center mx-auto" width="1637" height="644" loading="lazy"></p>
<h2 id="heading-how-to-configure-a-webhook-on-discord">How to Configure a Webhook on Discord</h2>
<p>You should now have an idea of how webhooks work, so let’s configure one with Discord.</p>
<p>Create or select a text channel on your Discord server. Click on the <code>Edit Channel</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206358936/d94926f1-381a-451b-95f8-ddb7747dba70.png" alt="edit channel" class="image--center mx-auto" width="306" height="382" loading="lazy"></p>
<p>Then, from the left-side navigation menu, select the <code>Integration</code> option. You should see the <code>Webhooks</code> option listed there.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206416398/eedf690f-1f75-48f2-8da7-ef1e9c9558da.png" alt="Integration of Webhooks" class="image--center mx-auto" width="1194" height="472" loading="lazy"></p>
<p>Click on the <code>Webhooks</code> option and then click on the <code>New Webhook</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206460104/540097f7-8e1e-41db-b2d6-d2ef1f14055b.png" alt="New Webhook" class="image--center mx-auto" width="896" height="408" loading="lazy"></p>
<p>Give your webhook a name, and you can optionally upload a photo. This can be helpful when you have multiple webhooks and you want to quickly identify them by their photo and name.</p>
<p>Now, click on the <code>Copy Webhook URL</code> button to copy the webhook URL. Keep it safe somewhere, as we will be using it shortly in our application.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735206520757/0718e9e0-b16b-4156-a1bd-40ce28ed2ff4.png" alt="Edit the Webhook" class="image--center mx-auto" width="770" height="380" loading="lazy"></p>
<p>That’s it. Now let’s create a Next.js 15 application so we can integrate the webhook with it.</p>
<h2 id="heading-how-to-create-a-nextjs-project">How to Create a Next.js Project</h2>
<p>Open the terminal and use the following command to create a Next.js application:</p>
<pre><code class="lang-bash">npx create-next-app@latest
</code></pre>
<p>You’ll have to provide a few inputs for the initial project to create it. I will be using JavaScript (as opposed to TypeScript), Tailwind CSS, App Router, and Turbopack for this project. So I’ve opted for those choices by providing <code>Yes</code> as the response.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736133367746/03ebe408-3d6f-44ac-9629-5d08a4f30eab.png" alt="Nexy.js project creation" class="image--center mx-auto" width="3015" height="1763" loading="lazy"></p>
<p>With that, you’ve created a Next.js project you can use for the rest of the tutorial.</p>
<h2 id="heading-how-to-set-environment-variables">How to Set Environment Variables</h2>
<p>Create a <code>.env</code> file at the root of your project. We will now create an environment variable secret with the webhook URL you had copied before. Create an entry in the .env file like this:</p>
<pre><code class="lang-bash">DISCORD_WEBHOOK_URL=&lt;YOUR_DISCORD_WEBHOOK_URL&gt;
</code></pre>
<p>Make sure you replace the <code>&lt;YOUR_DISCORD_WEBHOOK_URL&gt;</code> with your actual webhook URL in the <code>.env</code> file. Remember, you must not commit and push this file to your version control. So, make sure that the <code>.env</code> file has been added to the <code>.gitignore</code> file of the project.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736912358788/d98af5b1-ab61-46c3-92d5-a8772d34e4f2.png" alt=".gitignore" class="image--center mx-auto" width="1282" height="708" loading="lazy"></p>
<h2 id="heading-how-to-integrate-the-webhook-with-the-nextjs-form-component">How to Integrate the Webhook with the Next.js <code>Form</code> component</h2>
<p>Now, we will create the user interface to capture inputs from the user and send those to the Discord text channel using the webhook.</p>
<p>Let’s create a simple message form using the <code>&lt;Form /&gt;</code> component from Next.js. The <code>&lt;Form/&gt;</code> component is an extension of HTML’s native form with more flexibilities and features introduced by the release of Next.js 15. I would suggest that you <a target="_blank" href="https://www.youtube.com/watch?v=vl_aGFMShg0">go over this project-based video</a> tutorial if you are interested in learning more about this new addition to Next.js.</p>
<p>Our strategy here is very straightforward:</p>
<ul>
<li><p>We will create a form and add an action to it using the form’s <code>action</code> prop.</p>
</li>
<li><p>We will then create a <a target="_blank" href="https://www.youtube.com/watch?v=gQ2bVQPFS4U">Server Action</a> using the webhook URL to communicate with Discord.</p>
</li>
<li><p>The action will be invoked when the user fills up the form and submits it. Thus the webhook communication will be done.</p>
</li>
</ul>
<p>Let’s write the code for these functionalities now.</p>
<h3 id="heading-create-the-form">Create the Form</h3>
<p>Create a folder called <code>_components</code> under the <code>app/</code> directory. Now, create a file <code>message-form.jsx</code> under the <code>app/_components/</code> folder with the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"next/form"</span>;
<span class="hljs-keyword">import</span> { sendDiscordMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../actions"</span>;

<span class="hljs-keyword">const</span> MessageForm = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{sendDiscordMessage}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your name"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your e-mail"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Image Url"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"dp"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"type"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 rounded border my-2 w-[300px]"</span>
        <span class="hljs-attr">required</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>&gt;</span>Message Type<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"thanks"</span>&gt;</span>Say, Thanks!<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"qa"</span>&gt;</span>QA<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"general"</span>&gt;</span>General<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"What do you want to say?"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 w-[70px] text-white rounded-md"</span>
      &gt;</span>
        Send
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Form</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MessageForm;
</code></pre>
<p>Here, we have created a basic form with five fields for users to enter their name, email, profile image URL, message type (general, thanks, qa), and the message. There is a send button as well to submit the form.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735271967544/3313c084-1526-435b-9288-bfa93cdbcb05.png" alt="The simple form we've created" class="image--center mx-auto" width="502" height="466" loading="lazy"></p>
<p>We have used the <code>Form</code> component from the <code>next/form</code> package. The <code>Form</code> component allows us to execute a function on the server (server action) with the help of its <code>action</code> attribute. As you can see in the code above, we have imported a server action <code>sendDiscordMessage</code> and used that as the value for the <code>action</code> attribute of the form.</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{sendDiscordMessage}</span>&gt;</span>
</code></pre>
<p>Now, let’s create the server action so that we can submit the form with it.</p>
<h3 id="heading-create-the-server-action">Create the Server Action</h3>
<p>We’ll use a server action to send messages to Discord using webhooks. Create a folder called <code>actions</code> under the <code>app/</code> directory. It’s a convention to keep all the actions colocated under the <code>actions/</code> directory. Now, create a file called <code>index.js</code> under the <code>app/actions/</code> directory with the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sendDiscordMessage = <span class="hljs-keyword">async</span> (formData) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> rawFormEntries = <span class="hljs-built_in">Object</span>.fromEntries(formData);

    <span class="hljs-built_in">console</span>.log(rawFormEntries);

    <span class="hljs-keyword">await</span> fetch(process.env.DISCORD_WEBHOOK_URL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">username</span>: rawFormEntries?.username,
        <span class="hljs-attr">avatar_url</span>: rawFormEntries?.dp || <span class="hljs-string">"https://i.imgur.com/mDKlggm.png"</span>,
        <span class="hljs-attr">content</span>: rawFormEntries?.message,
        <span class="hljs-attr">embeds</span>: [
          {
            <span class="hljs-attr">fields</span>: [
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Email"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.email,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Message Type"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.type,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
            ],
          },
        ],
      }),
    })
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(err.message);
  }
};
</code></pre>
<p>Let’s understand what’s going on in the above code:</p>
<ul>
<li><p>In Next.js (or with React 19), a server function (aka server action) needs a special directive called <code>”use server”</code> at the top of the file. So we have declared that.</p>
</li>
<li><p>A server action is an async function that gets the <code>formData</code> as a parameter. The <code>formData</code> will contain the values of all the form fields submitted by the user. We can use the <code>Object.formEntries()</code> API to get a key-value pair from the <code>formData</code>. <a target="_blank" href="https://www.youtube.com/shorts/gNIO_6FcRrE">Check this out</a> to learn the best way to handle formData in JavaScript.</p>
</li>
<li><p>Next, we used the Discord webhook URL to make a <code>POST</code> call with the required payload to create the message.</p>
</li>
<li><p>Let’s understand the payload format well. We need to follow a specific payload structure for the discord webhook to create the message. It follows a schema to have the following:</p>
<ul>
<li><p><code>username</code>: the name of the message sender. It appears at the top of the message. We’re reading the name field from the submitted data and populating the <code>username</code> field.</p>
</li>
<li><p><code>avatar_url</code>: the profile photo of the message sender. We have a <code>dp</code> field in the form to capture the profile photo URL. We’re using that, and in case the user does not provide it, we’re using a default image as the profile photo.</p>
</li>
<li><p><code>content</code>: The <code>content</code> field is the actual message content. We are reading the value of the <code>message</code> field and populating the value.</p>
</li>
<li><p><code>embeds</code>: In the Discord message, you can use embeds. These embeds could be text messages, images, or videos. We will utilize the embeds to show the email and message type information. The <code>embeds</code> is an array of fields. In each of the fields, we have passed the <code>email</code> and message <code>type</code> values as the <code>inline</code> values. Inline values will appear in line, side by side.</p>
</li>
</ul>
</li>
</ul>
<h2 id="heading-how-to-update-the-app-page">How to Update the App Page</h2>
<p>Finally, let’s update the application’s page with our form component so that everything gets stitched together. Open the <code>page.js</code> file under the <code>app/</code> directory and paste in the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> MessageForm <span class="hljs-keyword">from</span> <span class="hljs-string">"./_components/message-form"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">return</span> (
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col justify-center items-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl my-3"</span>&gt;</span>Send a message to tapaScript<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">MessageForm</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      ) 
}
</code></pre>
<p>Here we have integrated the form with the app page so that we can access it on the browser.</p>
<h2 id="heading-lets-test-it-out">Let’s Test it Out</h2>
<p>Now you can run the app with the command <code>yarn dev</code> from your terminal. The app will be available on <code>localhost:3000</code> by default. Open a browser tab and hit the URL <code>http://localhost:3000</code>. You should see a form like the one below. Fill it out with values and hit the <code>Send</code> button.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735272054425/12e3894b-534a-436b-8e18-e076799e5a4f.png" alt="form" class="image--center mx-auto" width="585" height="489" loading="lazy"></p>
<p>Go to the text channel of your Discord server and check for a message that should appear there momentarily. Check that the fields of the message match the input values you provided in the form.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1735272074694/b795bf9f-adfd-4ff8-aed5-5fe07c49e47b.png" alt="output" class="image--center mx-auto" width="1080" height="428" loading="lazy"></p>
<p>TADA! We have done it. But hold on – we can make it even better. Did you notice that we aren’t showing any kind of feedback saying the “Message has been sent successfully”, or “There are issues in sending the message”? We’re not taking care of providing the result of the server action to the form component. Let’s fix that.</p>
<h2 id="heading-how-to-improve-the-message-feedback-using-react-19s-useactionstate-hook">How to Improve the Message Feedback using React 19’s useActionState Hook</h2>
<p><code>React 19</code> introduced a hook called <a target="_blank" href="https://www.youtube.com/watch?v=PWFKgdGmhxg">useActionState</a> that helps you update the state based on the result of a server action. Let’s use this hook to enhance the message form and the server action so that when the action gets executed successfully (or fails), we can notify the form component to change its state and show the success/error messages accordingly.</p>
<h3 id="heading-set-up-a-toaster">Set up a toaster</h3>
<p>We will use a toast message to show the success/error messages. Let’s use the toast library called <code>sonner</code>. First, install it with this command:</p>
<pre><code class="lang-bash">yarn add sonner
</code></pre>
<p>Now, we have to add the <code>Toaster</code> from <code>sonner</code> to our app. The best place to add it is at the root level Layout of the application. Open the <code>layout.js</code> file and import the <code>Toaster</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Toaster } <span class="hljs-keyword">from</span> <span class="hljs-string">'sonner'</span>;
</code></pre>
<p>Then, use the <code>Toaster</code> in the JSX of the layout:</p>
<pre><code class="lang-xml"> <span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">geistSans.variable</span>} ${<span class="hljs-attr">geistMono.variable</span>} <span class="hljs-attr">antialiased</span>`}
      &gt;</span>
        {children}
        <span class="hljs-tag">&lt;<span class="hljs-name">Toaster</span> <span class="hljs-attr">richColors</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
 <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>That’s it. The toaster set-up is done. Now we will be able to use it in our components to show the toast messages.</p>
<h3 id="heading-enhance-the-form-with-the-useactionstate-hook">Enhance the Form with the <code>useActionState</code> Hook</h3>
<p>First, import the <code>useActionState</code> and <code>useEffect</code> hooks into the <code>message-form.jsx</code> file. Also, import the <code>toast</code> from <code>sonner</code>. As we will be using the hooks now, we have to make the <code>message-form</code> component a client component. So, add the <code>”use client”</code> directive at the top of the file.</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"next/form"</span>;
<span class="hljs-keyword">import</span> { useActionState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { sendDiscordMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../actions"</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"sonner"</span>;
</code></pre>
<p>Next, use the <code>useActionState</code> hook to get the <code>formState</code> updated from the server action result. An <code>isPending</code> state tells us if the form submission has been completed. The <code>useActionState</code> returns a new <code>formAction</code> from the existing server action instance.</p>
<p>The syntax goes like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> [formState, formAction, isPending] = useActionState(sendDiscordMessage,<span class="hljs-literal">null</span>);
</code></pre>
<p>Now, we need to use this <code>formAction</code> as the value of the <code>action</code> attribute of the <code>&lt;Form /&gt;</code> component:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{formAction}</span>&gt;</span>
</code></pre>
<p>Next, use the <code>useEffect</code> hook to track the <code>formState</code> changes and show the toast message accordingly. The shape of the <code>formState</code> can be customized based on our needs. We will see shortly how the server action can return this state value.</p>
<pre><code class="lang-javascript">  useEffect (<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (formState?.success) {
      toast.success(formState?.message);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (formState?.success === <span class="hljs-literal">false</span>) {
      toast.error(formState?.message);
    }
  },[formState?.success])
</code></pre>
<p>The last thing here is to improve the UX of the form using the <code>isPending</code> state we got from the <code>useActionState</code> hook. The value of the <code>isPending</code> state will be true if the form is still in transition and being submitted. It will be changed to false when the form is submitted. So we can use the state value to customize the submit button text.</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
  <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
  <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 w-[70px] text-white rounded-md"</span>&gt;</span>
    {isPending ? "Sending..." : "Send"}
<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
</code></pre>
<p>Those are all the changes we need to make in the form component. Here’s the complete code of the modified form component for you to check out and use:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use client"</span>;

<span class="hljs-keyword">import</span> Form <span class="hljs-keyword">from</span> <span class="hljs-string">"next/form"</span>;
<span class="hljs-keyword">import</span> { useActionState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { sendDiscordMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"../actions"</span>;
<span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>
<span class="hljs-keyword">import</span> { toast } <span class="hljs-keyword">from</span> <span class="hljs-string">"sonner"</span>;

<span class="hljs-keyword">const</span> MessageForm = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">const</span> [formState, formAction, isPending] = useActionState(
    sendDiscordMessage,
    <span class="hljs-literal">null</span>
  );

  useEffect (<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (formState?.success) {
      toast.success(formState?.message);
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (formState?.success === <span class="hljs-literal">false</span>) {
      toast.error(formState?.message);
    }
  },[formState?.success])



  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Form</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span> <span class="hljs-attr">action</span>=<span class="hljs-string">{formAction}</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your name"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"username"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your e-mail"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"email"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your Image Url"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"dp"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">select</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"type"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"p-1 rounded border my-2 w-[300px]"</span>
        <span class="hljs-attr">required</span>
      &gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">""</span>&gt;</span>Message Type<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"thanks"</span>&gt;</span>Say, Thanks!<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"qa"</span>&gt;</span>QA<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">option</span> <span class="hljs-attr">value</span>=<span class="hljs-string">"general"</span>&gt;</span>General<span class="hljs-tag">&lt;/<span class="hljs-name">option</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">select</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
        <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"What do you want to say?"</span>
        <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"border p-1 rounded w-[300px] my-2"</span>
        <span class="hljs-attr">required</span>
      /&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-blue-500 w-[70px] text-white rounded-md"</span>
      &gt;</span>
        {isPending ? "Sending..." : "Send"}
      <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Form</span>&gt;</span></span>
  );
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> MessageForm;
</code></pre>
<h3 id="heading-how-to-update-the-server-action-to-return-results">How to Update the Server Action to Return Results</h3>
<p>The changes in the server action should be as follows:</p>
<ol>
<li><p>When you use the <code>useActionState</code> hook, pass a returned <code>formAction</code> to the form. We have seen this before. It helps in multiple ways, and one of them is getting the previous state value of the <code>formState</code> inside the server action. You must be mindful to pass it as the first argument to the server function, and then the <code>formData</code>.</p>
</li>
<li><p>We can now return the result of the server action so that the <code>formState</code> gets updated with the result. We will create a result structure with a boolean <code>success</code> field indicating if it is a success or error scenario and a <code>message</code> field with the actual message.</p>
</li>
</ol>
<p>Here’s the changed code with the above two updates:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"use server"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> sendDiscordMessage = <span class="hljs-keyword">async</span> (prevState, formData) =&gt; {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> rawFormEntries = <span class="hljs-built_in">Object</span>.fromEntries(formData);

    <span class="hljs-built_in">console</span>.log(rawFormEntries);

    <span class="hljs-keyword">await</span> fetch(process.env.DISCORD_WEBHOOK_URL, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        <span class="hljs-attr">username</span>: rawFormEntries?.username,
        <span class="hljs-attr">avatar_url</span>: rawFormEntries?.dp || <span class="hljs-string">"https://i.imgur.com/mDKlggm.png"</span>,
        <span class="hljs-attr">content</span>: rawFormEntries?.message,
        <span class="hljs-attr">embeds</span>: [
          {
            <span class="hljs-attr">fields</span>: [
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Email"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.email,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
              {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"Message Type"</span>,
                <span class="hljs-attr">value</span>: rawFormEntries?.type,
                <span class="hljs-attr">inline</span>: <span class="hljs-literal">true</span>,
              },
            ],
          },
        ],
      }),
    });

    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">success</span>: <span class="hljs-literal">true</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">`Your message has been sent successfully.`</span>,
    };
  } <span class="hljs-keyword">catch</span> (err) {
    <span class="hljs-built_in">console</span>.log(err.message);
    <span class="hljs-keyword">return</span> {
      <span class="hljs-attr">success</span>: <span class="hljs-literal">false</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">`Problem is sending message <span class="hljs-subst">${err.message}</span>`</span>,
    };
  }
};
</code></pre>
<p>As you must have noticed, the returned result is the <code>formState</code> we have used inside the <code>form</code> component to show the toast messages.</p>
<h2 id="heading-lets-test-it-one-more-time">Let’s Test it One More Time</h2>
<p>Let’s test things out with all the changes.</p>
<ol>
<li>First, fill out the form with the details. Now we’re using a profile picture URL rather than keeping it empty. Click on the <code>Send</code> button.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922583168/b044cca3-bb45-48d2-83bc-ec9459d86206.png" alt="Updated Form" class="image--center mx-auto" width="901" height="594" loading="lazy"></p>
<ol start="2">
<li>You should see the that button text gets changed to <code>Sending…</code> while the form is being submitted.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922728585/afdbe8cc-fdc3-4be1-8676-ffed5160168a.png" alt="Sending State" class="image--center mx-auto" width="703" height="574" loading="lazy"></p>
<ol start="3">
<li>After the submission, you should get the success message in the toast. Also, the form state should be restored to its original state.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922801497/518fa3ec-16d1-44a7-aa42-a647512379b5.png" alt="Success Message" class="image--center mx-auto" width="1426" height="1136" loading="lazy"></p>
<ol start="4">
<li>On the Discord text channel, you should see the message posted successfully.</li>
</ol>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736922367821/41cd6329-b6e5-45e7-8674-df7cff8e932d.png" alt="Updated message" class="image--center mx-auto" width="848" height="366" loading="lazy"></p>
<p>That’s amazing! We have now made the app much better with the message and error handling.</p>
<h2 id="heading-source-code-and-resources">Source Code and Resources</h2>
<p>All the source code used in this article is in the GitHub repository. You can take a look, follow the README to set it up, and run it locally.</p>
<ul>
<li><a target="_blank" href="https://github.com/tapascript/next-js-discord">The next-js-discord GitHub repository</a></li>
</ul>
<p>Here are the resources I mentioned in the article that you may find helpful:</p>
<ul>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=vl_aGFMShg0">Next.js 15 Form Component - All That You Need To Know</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=gQ2bVQPFS4U">Next.js Server Actions || Learn Patterns &amp; Project Building</a></p>
</li>
<li><p><a target="_blank" href="https://www.youtube.com/watch?v=PWFKgdGmhxg">MASTER React 19 useActionState Hook with Project &amp; Usecases</a></p>
</li>
<li><p><a target="_blank" href="https://react.dev/reference/react/useActionState">The useActionState official doc</a></p>
</li>
</ul>
<p>Also, you can connect with me by:</p>
<ul>
<li><p>Subscribing to my <a target="_blank" href="https://www.youtube.com/tapasadhikary?sub_confirmation=1">YouTube Channel</a>. If you are willing to learn <code>React</code> and its ecosystem, like <code>Next.js</code>, with both fundamental concepts and projects, I have great news for you: you can <a target="_blank" href="https://www.youtube.com/watch?v=VSB2h7mVhPg&amp;list=PLIJrr73KDmRwz_7QUvQ9Az82aDM9I8L_8">check out this playlist on my YouTube</a> channel with 30+ video tutorials and 20+ hours of engaging content so far, for free. I hope you like them as well.</p>
</li>
<li><p><a target="_blank" href="https://twitter.com/tapasadhikary">Following me on X (Twitter</a>) or <a target="_blank" href="https://www.linkedin.com/in/tapasadhikary/">LinkedIn</a> if you don't want to miss the daily dose of up-skilling tips.</p>
</li>
<li><p>Checking out and following my Open Source work on <a target="_blank" href="https://github.com/atapas">GitHub</a>.</p>
</li>
<li><p>I regularly publish meaningful posts on my <a target="_blank" href="https://blog.greenroots.info/">GreenRoots Blog</a>, you may find them helpful, too.</p>
</li>
</ul>
<p>See you soon with my next article. Until then, please take care of yourself, and keep learning.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Claim Your Supporter Role on Discord ]]>
                </title>
                <description>
                    <![CDATA[ If you have donated to support freeCodeCamp's efforts, you can now claim a special Supporter role in our Discord community. Claim Your Role In any channel within our Discord server, type /supporter and you should see a command window pop up. Click C... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/discord-supporter-role/</link>
                <guid isPermaLink="false">66198b0581dc24f50402c10a</guid>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ community ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Fri, 12 Apr 2024 19:27:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713297019795/f3010d96-e0e8-440b-a8f7-38f080e9b261.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you have donated to support freeCodeCamp's efforts, you can now claim a special Supporter role in our <a target="_blank" href="https://discord.gg/KVUmVXA">Discord community</a>.</p>
<h3 id="heading-claim-your-role">Claim Your Role</h3>
<p>In any channel within our Discord server, type <code>/supporter</code> and you should see a command window pop up.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712942511612/54b1e532-4246-416c-8f11-b8c96d901b22.png" alt="&quot;/supporter&quot; has been entered into the Discord chat box. A list is positioned above the chat box, indicating &quot;Commands Matching /supporter&quot;. CamperChan's &quot;/supporter&quot; command is listed and highlighted." class="image--center mx-auto" width="287" height="196" loading="lazy"></p>
<p>Click CamperChan's <code>/supporter</code> command, and enter the email address <strong>associated with your freecodecamp.org/learn account</strong>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712942602136/38acb38d-e617-47c8-a8e9-3ad40056827d.png" alt="Discord chat box with CamperChan's /supporter command populated. The email input field has been focused, with the text &quot;naomi@freecodecamp.org&quot; entered." class="image--center mx-auto" width="551" height="99" loading="lazy"></p>
<p>Then, hit enter to submit the command and you should see a success message! You'll now have the role with access to the supporter-only channels.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712943254909/988af1b4-4755-49e5-952a-9008730f6634.png" alt="CamperChan /supporter command response: &quot;Congrats! You now have the supporter role, with access to special channels.&quot;" class="image--center mx-auto" width="1153" height="125" loading="lazy"></p>
<h3 id="heading-error-messages">Error Messages</h3>
<p>There are a few error messages you may encounter when claiming this role. For any of these messages, please ping Naomi directly in the server to assist you.</p>
<h4 id="heading-already-have-role">Already Have Role</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712942696023/81047dac-769c-48fb-b12d-c008bb24e6c4.png" alt="CamperChan /supporter command response: &quot;You have already claimed the supporter role.&quot;" class="image--center mx-auto" width="539" height="142" loading="lazy"></p>
<p>This error message means you have already been assigned the Supporter role in the Discord server.</p>
<h4 id="heading-record-for-discord-server-exists">Record for Discord Server Exists</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712942812020/41aae705-cb11-4360-9488-6eb494284e99.png" alt="CamperChan /supporter command response: &quot;A supporter record already exists on your Discord account. If you believe this is an error, please contact Naomi.&quot;" class="image--center mx-auto" width="1156" height="119" loading="lazy"></p>
<p>This error message means your Discord account has already been granted the Supporter role previously, but you have somehow lost it (for example by leaving the server and rejoining later).</p>
<p>Naomi will need to reset the database entry for you.</p>
<h4 id="heading-record-for-email-exists">Record for Email Exists</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712943013577/c0bd880f-3d30-4fdf-82e5-5633d2d58646.png" alt="CamperChan /supporter command response: &quot;A supporter record already exists on your email. If you believe this is an error, please contact Naomi.&quot;" class="image--center mx-auto" width="1153" height="125" loading="lazy"></p>
<p>This means your email address has already been used by another Discord account to claim the Supporter role. Naomi will give you instructions on how to verify ownership of the email address.</p>
<h4 id="heading-not-a-supporter">Not a Supporter</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712942928427/5fc4ada9-b3e6-4a05-baf7-1037957b5c8d.png" alt="CamperChan /supporter command response: &quot;You do not appear to be actively supporting freeCodeCamp at this time. If you believe this is an error, please contact Naomi.&quot;" class="image--center mx-auto" width="1147" height="151" loading="lazy"></p>
<p>This means your curriculum account has not been flagged as having donated. Reach out to Naomi, and she will provide you instructions so she can fix that - make sure to have your latest donation receipt ready.</p>
<h4 id="heading-no-freecodecamp-account">No freeCodeCamp account</h4>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1712943178704/69a6f828-f495-4c0e-a63d-2a36d7dc8da3.png" alt="CamperChan /supporter command response: &quot;There does not appear to be a learn account associated with example@naomi.lgbt. If you believe this is an error, please contact Naomi.&quot;" class="image--center mx-auto" width="1153" height="125" loading="lazy"></p>
<p>This means the email address you entered does not have a curriculum account associated with it. Double check the spelling, and make sure you are using the email you use to sign in at freecodecamp.org</p>
<h3 id="heading-thank-you">Thank You</h3>
<p>Thanks again for supporting our charity's mission. Your donation will help us continue to provide free learning resources to millions of people around the world.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Discord Update Failed – How to Fix the Error on a Windows 10 PC ]]>
                </title>
                <description>
                    <![CDATA[ Discord is a popular chat app for gamers and anyone else who wants to create an online community. Despite its popularity, one major problem users report about Discord is the update failed loop. Discord has to update often because the Discord team imp... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/discord-update-failed-how-to-fix-the-error-on-a-windows-10-pc/</link>
                <guid isPermaLink="false">66adf0b3febac312b73075a6</guid>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ update  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows 10 ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Thu, 18 Aug 2022 15:31:45 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/08/discord--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Discord is a popular chat app for gamers and anyone else who wants to create an online community. Despite its popularity, one major problem users report about Discord is the update failed loop.</p>
<p>Discord has to update often because the Discord team implements new features and bug fixes regularly. </p>
<p>In addition, the app itself must load new messages from chat rooms and DMs. Once the update fails, Discord gets stuck in an update failed loop.</p>
<p>In this guide, I will show you 4 ways you can fix the Discord update failed error on a Windows 10 computer.</p>
<h2 id="heading-what-well-cover">What We'll Cover</h2>
<ul>
<li><a class="post-section-overview" href="#heading-solution-1-double-check-your-internet-connection">Double-check your Internet Connection</a></li>
<li><a class="post-section-overview" href="#heading-solution-2-run-discord-as-an-administrator">Run Discord as an Administrator</a></li>
<li><a class="post-section-overview" href="#heading-solution-3-rename-discords-updateexe-file">Rename Discord’s Update.exe File</a></li>
<li><a class="post-section-overview" href="#heading-solution-4-temporarily-disable-your-antivirus-program-and-vpn">Temporarily Disable your Antivirus Program and VPN</a></li>
<li><a class="post-section-overview" href="#heading-solution-5-uninstall-and-reinstall-discord">Uninstall and Reinstall Discord</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-solution-1-double-check-your-internet-connection">Solution 1: Double-check your Internet Connection</h2>
<p>The first thing I would suggest you do is to check your internet connection.</p>
<p>That’s because Discord needs an internet connection to update, as the update has to be done over the internet. Once there’s no internet connection, the update won’t happen.</p>
<p>Make sure your Windows 10 PC is connected to the internet and the internet connection is strong enough.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss1-4.png" alt="ss1-4" width="600" height="400" loading="lazy">
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss2-4.png" alt="ss2-4" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-solution-2-run-discord-as-an-administrator">Solution 2: Run Discord as an Administrator</h2>
<p>A common solution to this issue is to run the Discord app as an admin. </p>
<p>This could fix the issue because any app you want to install needs admin privileges. Discord is not an exception, so granting it admin privileges can make it dig deeper into the issue and resolve it.</p>
<p>To run Discord as administrator, search for Discord and select Run as Administrator on the right:
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss3-4.png" alt="ss3-4" width="600" height="400" loading="lazy"></p>
<h2 id="heading-solution-3-rename-discords-updateexe-file">Solution 3: Rename Discord’s Update.exe File</h2>
<p>There’s a separate executable for updating Discord. This is the update.exe file in the Discord folder. </p>
<p>Renaming this file can force Discord to download a fresh one and subsequently fix the issue for you.</p>
<p><strong>Follow these steps to rename Discord’s update.exe file</strong>
Press <code>WIN</code> (Windows logo key) + <code>R</code> on your keyboard and type in <code>%localappdata%</code>.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss4-5.png" alt="ss4-5" width="600" height="400" loading="lazy"></p>
<p>Look for the Discord folder and open it.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss5-5.png" alt="ss5-5" width="600" height="400" loading="lazy"></p>
<p>Right-click on the <code>Update.exe</code> file and select rename. Then rename it to something like “Updater.exe”. You must not change the extension.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss6-4.png" alt="ss6-4" width="600" height="400" loading="lazy"> </p>
<p>Open the app again and see if the problem gets fixed.</p>
<h2 id="heading-solution-4-temporarily-disable-your-antivirus-program-and-vpn">Solution 4: Temporarily Disable your Antivirus Program and VPN</h2>
<p>Antivirus programs and VPNs are known for interfering with the normal functioning of computers and internet connections. So if you have one or both of them on your PC, it might be stopping Discord from updating.</p>
<p>To disable your antivirus and VPN, open Task Manager, right-click on <code>WIN</code> (Windows logo key) and select Task Manager.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss7-3.png" alt="ss7-3" width="600" height="400" loading="lazy"></p>
<p>Under processes, right-click on Discord and select “End Task”.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss8-3.png" alt="ss8-3" width="600" height="400" loading="lazy"> </p>
<h2 id="heading-solution-5-uninstall-and-reinstall-discord">Solution 5: Uninstall and Reinstall Discord</h2>
<p>If the solutions already discussed fail to work for you, the last resort is to uninstall Discord and reinstall it.</p>
<p><strong>Step 1</strong>: Head over to Control Panel and select Uninstall a Program.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss9-2.png" alt="ss9-2" width="600" height="400" loading="lazy"> </p>
<p><strong>Step 2</strong>: Look for Discord, right-click on it and select uninstall.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss10-2.png" alt="ss10-2" width="600" height="400" loading="lazy"> </p>
<p><strong>Step 3</strong>: To completely remove Discord, press <code>WIN</code> + <code>R</code> on your keyboard, type <code>%localappdata%</code>, and click OK.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss4-5.png" alt="ss4-5" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: Right-click on the Discord folder and select Delete.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss11-2.png" alt="ss11-2" width="600" height="400" loading="lazy"> </p>
<p><strong>Step 4</strong>: <a target="_blank" href="https://discord.com/download">Download Discord</a> again and install it.
<img src="https://www.freecodecamp.org/news/content/images/2022/08/ss12-2.png" alt="ss12-2" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope the solutions discussed above help you get Discord to update so you can start using it again.</p>
<p>Thank you for reading.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use RegEx to Match Emoji – Discord Emotes Regular Expression Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ Emoji are special Unicode characters that render pictographs. But these characters can be very tricky to identify with regular expressions (RegEx).  I was recently working on a Discord bot that had to detect the number of emotes in a given message. T... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-use-regex-to-match-emoji-including-discord-emotes/</link>
                <guid isPermaLink="false">66ac7f4eed08c5b0125be18f</guid>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ emoji ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Regex ]]>
                    </category>
                
                    <category>
                        <![CDATA[ unicode ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Wed, 13 Jul 2022 23:04:07 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/07/pexels-roman-odintsov-6898861.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Emoji are special Unicode characters that render pictographs. But these characters can be very tricky to identify with regular expressions (RegEx). </p>
<p>I was recently working on a Discord bot that had to detect the number of emotes in a given message. Today I'll share my process with you, including the newer JavaScript RegEx feature that finally solved the issues I was having.</p>
<h2 id="heading-how-unicode-emoji-work">How Unicode Emoji Work</h2>
<p>The Unicode Consortium defines specific character codes for each emoji. They even maintain a <a target="_blank" href="https://unicode.org/emoji/charts/full-emoji-list.html">helpful emoji chart</a> as a reference. As an example, <code>U+1F600</code> corresponds to the 😀 emoji.</p>
<p>Some emoji consist of multiple Unicode characters. This is most common with the flag emoji, which consists of the "regional indicators" that make up the country's two-letter country code. </p>
<p>This means the United States flag, 🇺🇸, consists of the two Unicode characters <code>U+1F1FA</code> and <code>U+1F1F8</code>, which correspond to the regional indicators <code>U</code> and <code>S</code>.</p>
<blockquote>
<p>As a fun fact, it is up to the operating system to determine <strong>how</strong> to render an emoji. If you are on Windows, for example, you won't see a flag above. You'll see <code>US</code>.</p>
</blockquote>
<h2 id="heading-what-are-discord-emotes">What are Discord Emotes?</h2>
<p>One of Discord's many features is allowing communities to upload their own custom emotes. These emotes are identified by a name, and are used with the syntax <code>:emote_name:</code>.</p>
<p>However, the way they are identified by the client/API is different. Each emote has a unique ID, and they're sent in the message content as <code>&lt;:emote_name:1234567890&gt;</code>, or <code>&lt;a:emote_name:1234567890&gt;</code> for animated emotes.</p>
<p>You can see this in Discord by putting a backslash <code>\</code> before the emote and sending it. It will render something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/image-162.png" alt="A Discord message showing an emote's raw value `<:NaomiGrin:938275644092063784>`" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-match-emoji-and-emotes-with-regex">How to Match Emoji and Emotes with RegEx</h2>
<p>My original approach had two different RegEx phrases.</p>
<p>I was using <code>/(&lt;a?)?:\w+:(\d{18}&gt;)?/g</code> to catch the Discord emotes. This RegEx was successfully picking up Discord emotes, which was great! </p>
<p>I paired it with <code>/:[^:\s]*(?:::[^:\s]*)*:/g</code> to match the Unicode emoji, which only partially worked. The problem here was that I was seeing some emotes being counted twice – because the Discord RegEx was matching them. And others were being missed entirely.</p>
<p>So, with RegEx being what it is, I tried to make it more complex. <code>&lt;:[^:\s]+:\d+&gt;|&lt;a:[^:\s]+:\d+&gt;|(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff]|\ufe0f)/g</code> was a bit more successful in matching the built-in emoji, but still wasn't perfect. This RegEx was designed to match unicode characters specifically.</p>
<p>I played around with trimming whitespace, using the word boundary <code>\b</code> character, and a few other tweaks, before finally giving up and doing some research. </p>
<p>And then I discovered <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Unicode_Property_Escapes">Unicode Property Escapes</a>. This RegEx feature allows you to add the <code>u</code> flag to your RegEx, unlocking the Unicode Properties denoted with the <code>\p</code> character.</p>
<p>With some additional research, I was able to find the <a target="_blank" href="https://unicode.org/reports/tr51/#Emoji_Properties">Emoji Character properties</a> – specifically, the <code>Extended_Pictograph</code> property. This enabled me to update the RegEx to a final, functional value:</p>
<pre><code class="lang-js">/<span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">a?:.+?:\d{18}</span>&gt;</span>|\p{Extended_Pictographic}/gu</span>
</code></pre>
<p>The <code>\p{Extended_Pictographic}</code> property seems to match Unicode emotes as well as character modifiers (often used for skin tones in emoji).</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This RegEx is currently running in my production code and hasn't shown any issues yet. </p>
<p>Hopefully this article has helped you. If you are interested in exploring Unicode Property Escapes further, the Unicode Consortium offers a <a target="_blank" href="https://www.unicode.org/Public/UCD/latest/ucd/PropertyValueAliases.txt">full list</a> of the available values.</p>
<p>Happy Coding!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a 100 Days of Code Discord Bot with TypeScript, MongoDB, and Discord.js 13 ]]>
                </title>
                <description>
                    <![CDATA[ The 100 Days of Code challenge is very popular among new coders and developers looking to level up their skills. It's so popular that our Discord server has an entire channel dedicated to it. By popular demand, we built a Discord bot that helps peopl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-100-days-of-code-discord-bot-with-typescript-mongodb-and-discord-js-13/</link>
                <guid isPermaLink="false">66ac7eac11cd6758aec202a8</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding challenge ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Mon, 31 Jan 2022 21:41:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/pexels-kindel-media-8566473.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The <a target="_blank" href="https://www.freecodecamp.org/news/the-crazy-history-of-the-100daysofcode-challenge-and-why-you-should-try-it-for-2018-6c89a76e298d/">100 Days of Code challenge</a> is very popular among new coders and developers looking to level up their skills. It's so popular that our <a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-discord-chat-room-server/">Discord server</a> has an entire channel dedicated to it.</p>
<p>By popular demand, we built a Discord bot that helps people track their progress in the challenge.</p>
<p>Today I am going to show you how to build your own 100 Days of Code bot.</p>
<blockquote>
<p>Note that discord.js has released version 14, which includes breaking changes. For compatibility with this tutorial, you will want to ensure you are using discord.js 13 - you can install this with <code>npm install discord.js@13</code>. If you have any questions, feel free to join my <a target="_blank" href="https://chat.nhcarrigan.com">discord server</a>. </p>
</blockquote>
<details>
    <summary>Contents</summary>
    <ul>
        <li>
            <a href="#create-a-discord-bot-application">Create a Discord Bot Application</a>
        </li>
        <li>
            <a href="#set-up-your-project">Set Up Your Project</a>
        </li>
        <li>
            <a href="#create-the-discord-bot">Create the Discord Bot</a>
        </li>
        <li>
            <a href="#gateway-events-in-discord">Gateway Events in Discord</a>
        </li>
        <li>
            <a href="#connect-to-the-database">Connect to the Database</a>
        </li>
        <li>
            <a href="#environment-variable-validation">Environment Variable Validation</a>
        </li>
        <li>
            <a href="#the-interaction-event">The "interaction" Event</a>
        </li>
        <li>
            <a href="#prepare-for-commands">Prepare for Commands</a>
        </li>
        <li>
            <a href="#database-model">Database Model</a>
        </li>
        <li>
            <a href="#write-bot-commands">Write Bot Commands</a>
        </li>
    </ul>
</details>

<h2 id="heading-create-a-discord-bot-application">Create a Discord Bot Application</h2>
<p>Your first step is to set up a Discord bot application. Head over to the <a target="_blank" href="https://discord.dev">Discord Developer Portal</a>, sign in if needed, and select "Applications" from the sidebar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-76.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the Developer Portal. If this is your first bot, you will not have any applications here.</em></p>
<p>Click the "New Application" button. Give it a name, and set it as a "Personal" application. You will now be taken to the application's settings. Here you can change the name, or give it an avatar.</p>
<p>Select "Bot" from the side bar, then click the "Add Bot" button. This will create a Discord Bot account for your application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-77.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the Bot settings page. If you did not set an avatar, you will see a default based on your bot's name.</em></p>
<p>This is the screen where you will get the bot token. It is <em>very</em> important to keep this token secret, as the token allows your code to connect to your bot. Keep it safe and do not share it with anyone.</p>
<p>Now you need to add the bot to a server to interact with it. Click the "OAuth2" option on the sidebar, then select "URL Generator".</p>
<p>Under "Scopes", select <code>bot</code> and <code>application.commands</code>. The <code>bot</code> scope allows your bot account to join the server, and the <code>application.commands</code> scope allows you to update the slash commands (more on this later).</p>
<p>When you select <code>bot</code>, a new section for "Bot Permissions" will appear. Select the following permissions:</p>
<ul>
<li>Send Messages</li>
<li>Embed Links</li>
<li>Read Messages/View Channels</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-78.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the OAuth screen with the required settings.</em></p>
<p>Copy the generated URL, and paste it into your browser. This will take you through Discord's process to add your new bot to a server. </p>
<p>Note that you must have the Manage Server permission in the server you want to add the bot to. If you do not have this permission, you can create a server to test your bot in.</p>
<p>Now you are ready to write some code!</p>
<h2 id="heading-set-up-your-project">Set Up Your Project</h2>
<p>You will first need to set up the infrastructure and tooling for your project.</p>
<p>Ensure that you have Node.js <strong>version 16</strong> and <code>npm</code> installed. Note that the packages you will use do not support earlier versions of Node.</p>
<h3 id="heading-prepare-the-packagejson">Prepare the <code>package.json</code></h3>
<p>Create a directory, or folder, for your project. Open your terminal pointing to that new folder. Run the command <code>npm init</code> to set up your <code>package.json</code> file. For this tutorial, the default values are sufficient, but feel free to edit them as you wish.</p>
<p>You should end up with a <code>package.json</code> similar to this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"100doc-tutorial"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>
}
</code></pre>
<p>Now you need to make a couple of changes to get ready for the TypeScript implementation.</p>
<p>First, replace the <code>main</code> value of <code>index.js</code> with <code>./prod/index.js</code> – you will be setting your TypeScript to compile to a <code>prod</code> directory.</p>
<p>Then remove the <code>test</code> script and add the following two scripts:</p>
<pre><code class="lang-json"><span class="hljs-string">"build"</span>: <span class="hljs-string">"tsc"</span>,
<span class="hljs-string">"start"</span>: <span class="hljs-string">"node -r dotenv/config ./prod/index.js"</span>
</code></pre>
<p>The <code>build</code> script will compile your TypeScript into JavaScript so Node can run it, and the <code>start</code> script will run the <code>index.js</code> entrypoint file.</p>
<p>Adding the <code>-r dotenv/config</code> here will dynamically import and run the <code>config</code> method in the <code>dotenv</code> package, which loads your environment variables from the <code>.env</code> file.</p>
<p>Speaking of packages, your next step is to install dependencies. Using <code>npm install</code>, install these dependencies:</p>
<ul>
<li><code>discord.js</code> – this is the library that will handle connecting to the gateway and managing the Discord API calls.</li>
<li><code>@discordjs/builders</code> – the discord.js package for constructing application commands</li>
<li><code>@discordjs/rest</code> – a custom API client for interacting with the Discord REST API.</li>
<li><code>discord-api-types</code> – Type definitions and handlers for the Discord REST API.</li>
<li><code>dotenv</code> – a package that loads <code>.env</code> values into the Node process.</li>
<li><code>mongoose</code> – A wrapper for the MongoDB connection which offers tools for structuring your data.</li>
</ul>
<p>Finally, install the development dependencies with <code>npm install --save-dev</code>. Development dependencies are packages that are required for working on your project in a development environment, but not required for running the codebase in production.</p>
<ul>
<li><code>typescript</code> – This is the package for the TypeScript language, which includes everything needed to write code in TypeScript and compile it into JavaScript.</li>
<li><code>@types/node</code> – TypeScript relies on type definitions to understand the code you write. This package defines the types for the Node.js runtime environment, such as the <code>process.env</code> object.</li>
</ul>
<p>With these packages installed, you should now have a <code>package.json</code> similar to this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"100doc-tutorial"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"./prod/index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node -r dotenv/config ./prod/index.js"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"@discordjs/builders"</span>: <span class="hljs-string">"^0.11.0"</span>,
    <span class="hljs-attr">"@discordjs/rest"</span>: <span class="hljs-string">"^0.2.0-canary.0"</span>,
    <span class="hljs-attr">"discord.js"</span>: <span class="hljs-string">"^13.6.0"</span>,
    <span class="hljs-attr">"dotenv"</span>: <span class="hljs-string">"^14.2.0"</span>,
    <span class="hljs-attr">"mongoose"</span>: <span class="hljs-string">"^6.1.7"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^17.0.10"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^4.5.4"</span>
  }
}
</code></pre>
<h3 id="heading-prepare-typescript">Prepare TypeScript</h3>
<p>TypeScript's compiler offers a number of different settings to maximise your control over the resulting JavaScript.</p>
<p>You can typically modify the compiler settings through a <code>tsconfig.json</code> file at the root of your project. You can generate the default boilerplate for this file with <code>npx tsc --init</code>, use an existing one if you set one up in another project, or even write one from scratch.</p>
<p>Because the compiler settings can significantly change the behaviour of TypeScript, it is best to use the same settings when following this tutorial. Here are the settings you should use:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES6"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"CommonJS"</span>,
    <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"./src"</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./prod"</span>,
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"resolveJsonModule"</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p>The most important settings here are the <code>rootDir</code> and <code>outDir</code> settings. These tell the compiler that all of your code will be in the <code>src</code> directory, and the resulting JavaScript should go in the <code>prod</code> directory.</p>
<p>If you would like to test your settings, create a <code>src</code> directory and place an <code>index.ts</code> file inside. Write some code (such as a <code>console.log</code> statement) and run <code>npm run build</code> in your terminal. You should see a <code>prod</code> directory get created, with an <code>index.js</code> containing your compiled code.</p>
<h3 id="heading-additional-setup-notes">Additional Setup Notes</h3>
<p>If you are using <code>git</code> as a version control, you want to avoid pushing secrets and unnecessary code to your repository. Create a <code>.gitignore</code> file in your root project directory, and add the following content:</p>
<pre><code class="lang-txt">/node_modules/
/prod/
.env
</code></pre>
<p>The <code>.gitignore</code> file tells <code>git</code> not to track files/folders that match the patterns you enter. Ignoring the <code>node_modules</code> folder keeps your repository from becoming bloated.</p>
<p>Pushing the compiled JavaScript is also unnecessary, as your project is typically compiled in production before runtime. <code>.env</code> files contain secret values, such as API keys and tokens, so they should not be committed to a repository.</p>
<h2 id="heading-create-the-discord-bot">Create the Discord Bot</h2>
<p>Your next step is to prepare the initial bot connection. If you did not do so earlier, create a <code>src</code> directory and an <code>index.ts</code> file within.</p>
<p>Start with an anonymous immediately-invoked function expression (IIFE) to allow for top-level <code>await</code> use:</p>
<pre><code class="lang-ts">(<span class="hljs-keyword">async</span> () =&gt; {

})();
</code></pre>
<p>Within this function you are going to instantiate your Discord bot. At the top of the file, import the <code>Client</code> class with <code>import { Client } from "discord.js";</code>. The <code>Client</code> class represents your Discord bot's session.</p>
<p>Inside your function, construct a new <code>Client</code> instance and assign it to a <code>BOT</code> variable with <code>const BOT = new Client();</code>. Now the <code>BOT</code> variable will represent your bot.</p>
<p>To connect your bot to the Discord gateway and begin receiving events, you will need to use the <code>.login()</code> method on your bot instance. The <code>.login()</code> method takes a single argument, which is the token for the bot application you created earlier.</p>
<p>Many of the methods in <code>discord.js</code> are asynchronous, so you will need to use <code>await</code> here. Add the line <code>await BOT.login(process.env.BOT_TOKEN);</code> to your IIFE.</p>
<p>Your <code>index.ts</code> file should now look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> BOT = <span class="hljs-keyword">new</span> Client();

  <span class="hljs-keyword">await</span> BOT.login(process.env.BOT_TOKEN);
})();
</code></pre>
<p>If you try to run <code>npm run build</code>, you will see an error: <code>An argument for 'options' was not provided.</code></p>
<p>In discord.js 13, you are required to specify Gateway Intents when you instantiate your bot. Gateway Intents tell Discord what events your bot should receive.</p>
<p>In your <code>src</code> folder, create a <code>config</code> folder - then in <code>config</code>, create an <code>IntentOptions.ts</code> file.</p>
<p>Within that new file, add the line <code>export const IntentOptions = ["GUILDS"]</code>. This will tell Discord your bot should receive the Guild events.</p>
<p>Then, back in your <code>index.ts</code> file, add an argument to your <code>new Client()</code> call: <code>new Client({intents: IntentOptions})</code>. You'll need to import it at the top of your file with <code>import { IntentOptions } from "./config/IntentOptions;</code>. </p>
<p>It seems you still have an error: <code>Type 'string' is not assignable to type 'number |</code>${bigint}<code>| IntentsString | Readonly&lt;BitField&lt;IntentsString, number&gt;&gt; | RecursiveReadonlyArray&lt;number |</code>${bigint}<code>| IntentsString | Readonly&lt;...&gt;&gt;'.</code></p>
<p>TypeScript is inferring your <code>IntentOptions</code> array as a string, but the <code>Client</code> constructor is expecting more specific types. </p>
<p>Head back to your <code>config/IntentOptions.ts</code> file and add another import: <code>import { IntentsString } from "discord.js"</code>. Then update your variable with the new type definition: <code>export const IntentOptions: IntentsString[] = ["GUILDS"];</code>.</p>
<p>Now <code>npm run build</code> should be successful. If you have added your new bot to a Discord server, running <code>npm start</code> will show your bot come online in that server. However, the bot is not going to respond to anything yet, because you have not started listening to events.</p>
<h2 id="heading-gateway-events-in-discord">Gateway Events in Discord</h2>
<p>Gateway "events" are generated when an action happens on Discord, and are typically sent to clients (including your bot) as JSON payloads. You can listen to those events with the <code>.on()</code> method, allowing you to write logic for your bot to follow when specific events occur.</p>
<p>The first event to listen to is the "ready" event. This event fires when your bot has connected to the gateway and is <em>ready</em> to process events. Above your <code>.login()</code> call, add <code>BOT.on("ready", () =&gt; console.log("Connected to Discord!"));</code>. </p>
<p>For your changes to take effect, use <code>npm run build</code> again to compile the new code. Now if you try <code>npm run start</code>, you should see "Connected to Discord!" print in your terminal.</p>
<h2 id="heading-connect-to-the-database">Connect to the Database</h2>
<p>You'll be using the <code>mongoose</code> package to connect to a MongoDB instance. If you prefer, you can run MongoDB locally, or you can use the MongoDB Atlas free tier for a cloud-based solution. </p>
<p>If you do not have a MongoDB Atlas account, freeCodeCamp has a <a target="_blank" href="https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/">great tutorial on setting one up</a>.</p>
<p>Grab your connection string for your database and add it to your <code>.env</code> file as <code>MONGO_URI=""</code>, with the connection string going between the quotes. For the database name, use <code>oneHundredDays</code>.</p>
<p>Create a directory called <code>database</code> to hold the files that contain your database logic. Within that directory, create a file called <code>connectDatabase.ts</code>. You will be writing your logic to initiate the database connection here.</p>
<p>Start with an exported function declaration:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> connectDatabase = <span class="hljs-keyword">async</span> () =&gt; {

}
</code></pre>
<p><code>mongoose</code> offers a <code>connect</code> method for connecting to the database. Import it with <code>import { connect } from "mongoose";</code> at the top of your file.</p>
<p>Then use the method inside your function with <code>await connect(process.env.MONGO_URI);</code>. Add a <code>console.log</code> statement after that so you can identify that your bot has connected to the database. </p>
<p>Your <code>connectDatabase.ts</code> file should now look something like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { connect } <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> connectDatabase = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> connect(process.env.MONGO_URI);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Database Connected!"</span>)
}
</code></pre>
<p>Now, within your <code>index.ts</code> file, import this function with <code>import { connectDatabase } from "./database/connectDatabase"</code> and add <code>await connectDatabase()</code> to your IIFE, just before the <code>.login()</code> method. Go ahead and run <code>npm run build</code> again.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-157.png" alt="Image" width="600" height="400" loading="lazy">
<em>A compiler error, indicating that: Argument of type string or undefined is not assignable to parameter of type string.</em></p>
<p>Oh no – an error!</p>
<h2 id="heading-environment-variable-validation">Environment Variable Validation</h2>
<p>The problem with environment variables is that they can all be <code>undefined</code>. This often happens if you make a typo in your environment variable name, or mix the name up with another name (a mistake I made when writing this tutorial, using <code>TOKEN</code> instead of <code>BOT_TOKEN</code> in some places).</p>
<p>TypeScript is warning you that the <code>connect</code> method takes a string, and that an <code>undefined</code> value will break things. You can fix this, but first you will want to write a function to handle validating your environment variables.</p>
<p>Within your <code>src</code> directory, create a <code>utils</code> directory to contain your utility functions. Add a <code>validateEnv.ts</code> file there.</p>
<p>Create a function in the file called <code>validateEnv</code>. This function will be synchronous and does not need the <code>async</code> keyword. Within that function, add conditions to check for your two environment variables. If either one is missing, return <code>false</code>. Otherwise, return <code>true</code>.</p>
<p>Your code might look something like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> validateEnv = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">if</span> (!process.env.BOT_TOKEN) {
    <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Missing Discord bot token."</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-keyword">if</span> (!process.env.MONGO_URI) {
    <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Missing MongoDB connection."</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
};
</code></pre>
<p>Head back to your <code>index.ts</code> file and import this validation function with <code>import { validateEnv } from "./utils/validateEnv"</code>. Then at the beginning of your IIFE, use an if statement to return early if the function returns false. Your <code>index.ts</code> should look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { connectDatabase } <span class="hljs-keyword">from</span> <span class="hljs-string">"./database/connectDatabase"</span>;
<span class="hljs-keyword">import</span> { validateEnv } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/validateEnv"</span>;

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">if</span> (!validateEnv()) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">const</span> BOT = <span class="hljs-keyword">new</span> Client();

  BOT.on(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Connected to Discord!"</span>));

  <span class="hljs-keyword">await</span> connectDatabase();

  <span class="hljs-keyword">await</span> BOT.login(process.env.BOT_TOKEN);
})();
</code></pre>
<p>If you try <code>npm run build</code> again, you will see the same error message as before. This is because while we know the environment variable exists, TypeScript still cannot infer it. The validation function is set up to exit the process if the environment variable is missing, so we are going to tell TypeScript that it is definitely a string.</p>
<p>Back in your <code>connectDatabase.ts</code> file, within the <code>connect</code> function use <code>process.env.MONGO_URI as string</code> to coerce the type into <code>string</code>. The error should go away, and you can now run <code>npm run build</code> and <code>npm start</code>. </p>
<p>You should see the messages you wrote for both the Discord and MongoDB connections print in your terminal.</p>
<h2 id="heading-the-interaction-event">The "interaction" Event</h2>
<p>While you are making great progress on your bot, it still does not <em>do</em> anything. In order to receive commands, you will need to create another event listener.</p>
<p>Discord rolled out slash commands, featuring a new UI and a new gateway event. The <code>interactionCreate</code> event is triggered when someone uses a slash command with your bot. This is the event you will want to listen to. Because the logic is a bit more complicated than the <code>ready</code> event, you will want to create a separate file.</p>
<p>Within your <code>src</code> directory, create an <code>events</code> directory, and an <code>onInteraction.ts</code> file in there. Start by defining an exported function <code>onInteraction</code>. This should be an asynchronous function, with a single parameter called <code>interaction</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onInteraction = <span class="hljs-keyword">async</span> (interaction) =&gt; {

};
</code></pre>
<p>To provide a type definition for your parameter, import the <code>Interaction</code> type from <code>discord.js</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Interaction } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onInteraction = <span class="hljs-keyword">async</span> (interaction: Interaction) =&gt; {

};
</code></pre>
<p>The <code>interaction</code> event actually triggers on any command interaction, which includes things like button clicks and select menus, as well as the slash commands we want. </p>
<p>Because you will only be writing slash commands for this bot, you can filter out any other interaction type and help TypeScript understand the data you are working with.</p>
<p>In your new function, add a condition to check <code>interaction.isCommand()</code>. You will be writing logic within this block later.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Interaction } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onInteraction = <span class="hljs-keyword">async</span> (interaction: Interaction) =&gt; {
  <span class="hljs-keyword">if</span> (interaction.isCommand()) {
  }
};
</code></pre>
<p>Now, back in your <code>index.ts</code> file, you can mount another listener. Next to your <code>.on("ready")</code> listener, add a <code>BOT.on("interactionCreate")</code> listener. For this event, the callback takes an <code>interaction</code> argument which you can pass to your new <code>onInteraction</code> function.</p>
<pre><code class="lang-ts">  BOT.on(
    <span class="hljs-string">"interactionCreate"</span>,
    <span class="hljs-keyword">async</span> (interaction) =&gt; <span class="hljs-keyword">await</span> onInteraction(interaction)
  );
</code></pre>
<p>Great! You can run <code>npm run build</code> to confirm that TypeScript doesn't throw any errors, but without actual commands to use you can't quite test this code yet.</p>
<h2 id="heading-prepare-for-commands">Prepare for Commands</h2>
<p>I maintain a few Discord bots, and one thing I've discovered that helps keep code maintainable and readable is making the components modular.</p>
<h3 id="heading-define-an-interface">Define an Interface</h3>
<p>You will first need to define a common structure for your commands. Create an <code>interfaces</code> folder in <code>src</code>. Then inside <code>interfaces</code> create a file called <code>Command.ts</code>.</p>
<p>Now you are going to create an interface. In TypeScript, an interface is often used to define the structure of an object, and is one of many tools available for declaring a variable's type.</p>
<p>In your <code>Command.ts</code> file, create an exported interface called <code>Command</code>:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Command {

}
</code></pre>
<p>Your interface will have two properties – <code>data</code>, which will hold the command data to send to Discord, and <code>run</code>, which will hold the callback function and command logic.</p>
<p>For the <code>data</code> property, import <code>SlashCommandBuilder</code> and <code>SlashCommandSubcommandsOnlyBuilder</code> from <code>@discordjs/builders</code>. Define the <code>data</code> property as either one of those two types.</p>
<p>For the <code>run</code> property, import the <code>CommandInteraction</code> type from <code>discord.js</code>. Define <code>run</code> as a function which takes a <code>CommandInteraction</code> typed parameter and returns a <code>void</code> Promise.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> {
  SlashCommandBuilder,
  SlashCommandSubcommandsOnlyBuilder,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { CommandInteraction } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CommandInt {
  data: SlashCommandBuilder | SlashCommandSubcommandsOnlyBuilder;
  run: <span class="hljs-function">(<span class="hljs-params">interaction: CommandInteraction</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;
}
</code></pre>
<h3 id="heading-create-a-command-list">Create a Command List</h3>
<p>Next you need a place to store all of your commands. Create a folder called <code>commands</code> in the <code>src</code> directory, and add a file called <code>_CommandList.ts</code>. The underscore here will keep this file at the top of the list.</p>
<p>The <code>_CommandList.ts</code> file will need two lines. First, import your <code>Command</code> interface, then declare a <code>CommandList</code> array. The array will be empty for now, but give it a <code>Command[]</code> type so TypeScript knows it will eventually hold your command objects. The file should look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList: Command[] = [];
</code></pre>
<p>The purpose of this file is to create an array of your bot's commands which you will iterate over in the interaction event listener. <a target="_blank" href="https://github.com/BeccaLyria/discord-bot/blob/main/src/utils/readDirectory.ts">There are ways to automate this</a>, but they tend to be unnecessarily complex for smaller bots.</p>
<h3 id="heading-check-for-commands">Check for Commands</h3>
<p>Back in your <code>onInteraction.ts</code> file, you should start working on the logic for finding and running a command.</p>
<p>In your <code>interaction.isCommand()</code> condition block, loop through the <code>CommandList</code> array (remember to import it!) with a <code>for...of</code> loop. </p>
<pre><code class="lang-ts"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> Command <span class="hljs-keyword">of</span> CommandList) {

}
</code></pre>
<p>The interaction payload received from Discord includes a <code>commandName</code> property, which you can use to find the command that a user has selected. To check this, compare <code>interaction.commandName</code> with the <code>Command.data.name</code> property.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">if</span> (interaction.commandName === Command.data.name) {

}
</code></pre>
<p>Now, if you have found the command the user has chosen, you need to run the logic for that command. This is achieved with a <code>Command.run(interaction)</code> call – passing the interaction payload into the command.</p>
<p>Your final file should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Interaction } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { CommandList } <span class="hljs-keyword">from</span> <span class="hljs-string">"../commands/_CommandList"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onInteraction = <span class="hljs-keyword">async</span> (interaction: Interaction) =&gt; {
  <span class="hljs-keyword">if</span> (interaction.isCommand()) {
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> Command <span class="hljs-keyword">of</span> CommandList) {
      <span class="hljs-keyword">if</span> (interaction.commandName === Command.data.name) {
        <span class="hljs-keyword">await</span> Command.run(interaction);
        <span class="hljs-keyword">break</span>;
      }
    }
  }
};
</code></pre>
<h2 id="heading-database-model">Database Model</h2>
<p>There's one more step before you are ready to start writing commands. This bot will track your community members' 100 Days of Code progress. And you need to store that progress in the database.</p>
<p><code>mongoose</code> helps structure your MongoDB records to prevent you from passing malformed or incomplete data into your database.</p>
<p>Start by creating a <code>models</code> folder in your <code>database</code> directory. In that <code>models</code> folder, create a <code>CamperModel.ts</code> file. This will be your structure for the user objects.</p>
<p>You first need to import the necessary values from the <code>mongoose</code> library. Add <code>import { Document, model, Schema } from "mongoose";</code> at the top of the file.</p>
<p>Because you are using TypeScript, you need to create a type definition for your database objects. Create another interface, like you did for your commands, named <code>CamperInt</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CamperInt <span class="hljs-keyword">extends</span> Document {

}
</code></pre>
<p>Your database model will have four properties. Add these to your interface:</p>
<ul>
<li><code>discordId: string;</code> – Every user object in Discord has a unique identifier, called a Snowflake, which is used to distinguish them from other users. Unlike a username or discriminator (the four digit number after the username), the <code>id</code> value cannot be changed. This makes it the ideal value for linking your stored data to a Discord user.</li>
<li><code>round: number;</code> – This will represent the "round" the user is on in the challenge. When someone completes 100 days of the challenge, they may choose to undertake the challenge again. When they do, they often refer to it as "round 2", for example. </li>
<li><code>day: number;</code> – This represents the day the user is on in the challenge.</li>
<li><code>timestamp: number;</code> – You will use this value to track when the user last submitted a 100 Days of Code post.</li>
</ul>
<p>Great! Now you need to define the Schema for your database entries. <code>mongoose</code> uses a Schema object to define the shape of the documents that go in to your database collection. The <code>Schema</code> import has a constructor, which you will assign to a variable.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Camper = <span class="hljs-keyword">new</span> Schema();
</code></pre>
<p>This constructor takes an object as its argument, and that object defines the database keys and types. Go ahead and pass in an object similar to what your interface looks like.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Camper = <span class="hljs-keyword">new</span> Schema({
    discordId: <span class="hljs-built_in">String</span>,
    round: <span class="hljs-built_in">Number</span>,
    day: <span class="hljs-built_in">Number</span>,
    timestamp: <span class="hljs-built_in">Number</span>,
})
</code></pre>
<p>Next you need to create the <code>model</code>. In <code>mongoose</code>, the <code>model</code> object serves to create, read, and update your documents in the MongoDB database. Add <code>export default model();</code> at the bottom of your file.</p>
<p>The <code>model</code> function takes a few parameters. The first is a string, and is the name to use for the documents in your database. For this collection, use <code>"camper"</code>. The second argument is the schema to use for the data – use your <code>Camper</code> schema there.</p>
<p>By default, <code>mongoose</code> will use the plural version of your <code>model</code> name for the collection. In our case, that would be "campers". If you want to change that, you can pass in a third argument of <code>{ collection: "name" }</code> to set the collection to <code>name</code>.</p>
<p>If you were using JavaScript, this would be enough to get your database model set up. However, because you are using TypeScript, you should take advantage of the type safety. <code>model()</code> by default returns a <code>Document</code> type of <code>any</code>. </p>
<p>To resolve this, you can pass a generic type into the <code>model</code> function. Generic types serve as variables for type definitions, in a sense. You need to set the generic type for your <code>model</code> to use your interface. Add the generic type by changing <code>model</code> to <code>model&lt;CamperInt&gt;</code>.</p>
<p>Just one more step here. Your <code>CamperInt</code> interface only defines the properties you set in the MongoDB document, but doesn't include the standard properties. </p>
<p>Change your <code>export interface CamperInt</code> to <code>export interface CamperInt extends Document</code>. This tells TypeScript that your type definition is an extension of the existing <code>Document</code> type definition – you are essentially adding properties to that structure.</p>
<p>Your final file should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Document, model, Schema } <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CamperInt {
  discordId: <span class="hljs-built_in">string</span>;
  round: <span class="hljs-built_in">number</span>;
  day: <span class="hljs-built_in">number</span>;
  timestamp: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Camper = <span class="hljs-keyword">new</span> Schema({
  discordId: <span class="hljs-built_in">String</span>,
  round: <span class="hljs-built_in">Number</span>,
  day: <span class="hljs-built_in">Number</span>,
  timestamp: <span class="hljs-built_in">Number</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> model&lt;CamperInt&gt;(<span class="hljs-string">"camper"</span>, Camper);
</code></pre>
<p>As a safety check, use <code>npm run build</code> again. You should not see any errors in the terminal.</p>
<h2 id="heading-write-bot-commands">Write Bot Commands</h2>
<p>You are finally ready to start writing some commands! As this is a 100 Days of Code bot, you should start with the command for creating a 100 Days of Code update.</p>
<h3 id="heading-100-command">100 Command</h3>
<p>Within your <code>commands</code> folder, create a <code>oneHundred.ts</code> file. This will hold your 100 Days of Code command. Import your command interface with <code>import { Command } from "../interfaces/Command;</code>.</p>
<p>Now declare an exported variable <code>oneHundred</code> and give it the <code>Command</code> type:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: Command = {

};
</code></pre>
<p>First, create the <code>data</code> property. You will be using the <code>@discordjs/builders</code> package to build a slash command.</p>
<p>Start by importing the <code>SlashCommandBuilder()</code> from the <code>@discordjs/builders</code> package. Then, construct a new instance in the <code>data</code> property with <code>new SlashCommandBuilder()</code>. You're going to chain some methods here to pass the information you want into the builder.</p>
<p>The <code>.setName()</code> method allows you to set the name of your slash command. Set the name to <code>"100"</code>. The <code>setDescription()</code> option allows you to display a description of the command in Discord's UI. Set the description to <code>"Check in for the 100 Days of Code challenge."</code>.</p>
<p>Slash commands can also accept <code>option</code> values. These are used to take arguments from the user, and come in various types. For this command, you'll want a string option with the <code>addStringOption()</code> method. Option methods take a callback function, with an <code>option</code> parameter.</p>
<p>You can then chain methods on the <code>option</code> parameter to configure the information for the argument. Use the <code>.setName()</code> method to give the option a name of <code>"message"</code>, and the <code>.setDescription()</code> method to give it a description of <code>"The message to go in your 100 Days of Code update."</code>. Finally, use the <code>.setRequired()</code> method to set the option to be required.</p>
<p>Here's what you should have now:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"100"</span>)
    .setDescription(<span class="hljs-string">"Check in for the 100 Days of Code challenge."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
};
</code></pre>
<p>If you are coding in an IDE with Intellisense enabled, you may have noticed that this will throw a type error on the <code>data</code> property. This is because the <code>SlashCommandBuilder</code> actually returns an <code>Omit</code> type! An <code>Omit</code> type is used to tell TypeScript that the type is <em>almost</em> the same as another type, but with specific properties removed.</p>
<p>Head over to your <code>interfaces/Command.ts</code> file to update the type. Replace the <code>SlashCommandBuilder</code> type with <code>Omit&lt;SlashCommandBuilder, "addSubcommandGroup" | "addSubcommand"&gt;</code>. This will tell TypeScript that <code>data</code> should be a <code>SlashCommandBuilder</code>, but without those two specific properties.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> {
  SlashCommandBuilder,
  SlashCommandSubcommandsOnlyBuilder,
} <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { CommandInteraction } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> Command {
  data:
    | Omit&lt;SlashCommandBuilder, <span class="hljs-string">"addSubcommandGroup"</span> | <span class="hljs-string">"addSubcommand"</span>&gt;
    | SlashCommandSubcommandsOnlyBuilder;
  run: <span class="hljs-function">(<span class="hljs-params">interaction: CommandInteraction</span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;;
}
</code></pre>
<p>Great! Now that your type error is resolved, head back over to your <code>oneHundred.ts</code> command file – it is time to write the command logic.</p>
<p>All of your bot's logic for responding to the command will go in the <code>run</code> property. As you did in your interface, start by creating an async function which takes an <code>interaction</code> argument. Then, let the first line of your function be <code>await interaction.deferReply();</code>.</p>
<p>Discord expects a bot to respond to a command within three seconds. Because this command may take longer to process, using the <code>.deferReply()</code> method sends an acknowledgement response that gives you a full 15 minutes to send the actual response.</p>
<p>Next, you need to extract some data from the command. First, destructure the <code>user</code> object out of the interaction payload with <code>const { user } = interaction;</code>. The <code>user</code> object represents the Discord user that called the command. </p>
<p>Then get the <code>message</code> option you sent with <code>const text = interaction.options.getString("message", true);</code>. With this line, you are accessing the <code>options</code> property of the interaction. The <code>.getString()</code> method specifically grabs a string option (remember that you created the option in <code>data</code>), and <code>"message"</code> is the <strong>name</strong> of the option. The <code>true</code> argument indicates that this is a required option, so TypeScript won't consider it nullable.</p>
<p>Your file should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"100"</span>)
    .setDescription(<span class="hljs-string">"Check in for the 100 Days of Code challenge."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> { user } = interaction;
    <span class="hljs-keyword">const</span> text = interaction.options.getString(<span class="hljs-string">"message"</span>, <span class="hljs-literal">true</span>);
  },
};
</code></pre>
<p>The next step in this command would be to fetch data from your database. Because many of your commands will need to do this, you should create a module for it.</p>
<h3 id="heading-handling-the-database-logic">Handling the Database Logic</h3>
<p>Create a <code>src/modules</code> directory, and add a <code>getCamperData.ts</code> file within. Create an exported async function named <code>getCamperData</code>, and give it a string parameter named <code>id</code>. Then, within the function, you can query the database.</p>
<p>Import your <code>CamperModel</code> from the <code>database</code> directory, and use the <code>findOne()</code> method to query by the camper's <code>id</code>: <code>const camperData = await CamperModel.findOne({ discordId: id });</code>. </p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> CamperModel <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getCamperData = <span class="hljs-keyword">async</span> (id: <span class="hljs-built_in">string</span>) =&gt; {
  <span class="hljs-keyword">const</span> camperData = <span class="hljs-keyword">await</span> CamperModel.findOne({ id });
};
</code></pre>
<p>We still have one more step here. If the camper has not used the bot before, they won't have an existing database record. <code>findOne()</code> would return <code>null</code> in this case – so you can add a fallback value.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> CamperModel <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getCamperData = <span class="hljs-keyword">async</span> (id: <span class="hljs-built_in">string</span>) =&gt; {
  <span class="hljs-keyword">const</span> camperData =
    (<span class="hljs-keyword">await</span> CamperModel.findOne({ discordId: id })) ||
    (<span class="hljs-keyword">await</span> CamperModel.create({
      discordId: id,
      round: <span class="hljs-number">1</span>,
      day: <span class="hljs-number">0</span>,
      date: <span class="hljs-built_in">Date</span>.now(),
    }));
};
</code></pre>
<p>Finally, you need to <code>return</code> your data. Add <code>return camperData</code> at the end of the function. For extra type safety, define the return type of your function as <code>Promise&lt;CamperData&gt;</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> CamperModel, { CamperInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> getCamperData = <span class="hljs-keyword">async</span> (id: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">Promise</span>&lt;CamperInt&gt; =&gt; {
  <span class="hljs-keyword">const</span> camperData =
    (<span class="hljs-keyword">await</span> CamperModel.findOne({ discordId: id })) ||
    (<span class="hljs-keyword">await</span> CamperModel.create({
      discordId: id,
      round: <span class="hljs-number">1</span>,
      day: <span class="hljs-number">0</span>,
      date: <span class="hljs-built_in">Date</span>.now(),
    }));
  <span class="hljs-keyword">return</span> camperData;
};
</code></pre>
<p>You now have a way to get camper data from the database, but you need a way to update it as well. Create another file in your <code>/src/modules</code> directory called <code>updateCamperData.ts</code>. This will handle the logic to increment a camper's progress.</p>
<p>Start with an exported async function called <code>updateCamperData</code>. It should take a <code>Camper</code> parameter, which would be the data you fetch from MongoDB.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CamperInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateCamperData = <span class="hljs-keyword">async</span> (Camper: CamperInt) =&gt; {

};
</code></pre>
<p>The only time you will update data is within the <code>/100</code> command – where you'll want to increment the camper's day count, check if they have started a new round, and update the timestamp.</p>
<p>First, increment the day count with <code>Camper.day++;</code>. With the way the 100 Days of Code challenge works, if a camper has passed day 100 then they have started a new "round". You'll need a condition to check if <code>Camper.day &gt; 100</code>, and if so, reset the day to 1 and increment the round. </p>
<p>After that condition, update the timestamp with <code>Camper.timestamp = Date.now();</code> and save the data with <code>await Camper.save();</code>. Finally, return the modified data object so you can use it in the command.</p>
<p>Your final file should look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CamperInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> updateCamperData = <span class="hljs-keyword">async</span> (Camper: CamperInt) =&gt; {
  Camper.day++;
  <span class="hljs-keyword">if</span> (Camper.day &gt; <span class="hljs-number">100</span>) {
    Camper.day = <span class="hljs-number">1</span>;
    Camper.round++;
  }
  Camper.timestamp = <span class="hljs-built_in">Date</span>.now();
  <span class="hljs-keyword">await</span> Camper.save();
  <span class="hljs-keyword">return</span> Camper;
};
</code></pre>
<h3 id="heading-100-command-continued">100 Command Continued</h3>
<p>Now that your database logic is ready, return to your <code>oneHundred.ts</code> file. As a reminder, that file should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"100"</span>)
    .setDescription(<span class="hljs-string">"Check in for the 100 Days of Code challenge."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> { user } = interaction;
    <span class="hljs-keyword">const</span> text = interaction.options.getString(<span class="hljs-string">"message"</span>, <span class="hljs-literal">true</span>);
  },
};
</code></pre>
<p>Import your two new modules at the top of the file. Then, after your logic that extracts the values from the interaction object, fetch the camper's data from the database with <code>const targetCamper = await getCamperData(user.id);</code>. Update the data with <code>const updatedCamper = await updateCamperData(targetCamper);</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;
<span class="hljs-keyword">import</span> { getCamperData } <span class="hljs-keyword">from</span> <span class="hljs-string">"../modules/getCamperData"</span>;
<span class="hljs-keyword">import</span> { updateCamperData } <span class="hljs-keyword">from</span> <span class="hljs-string">"../modules/updateCamperData"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"100"</span>)
    .setDescription(<span class="hljs-string">"Check in for the 100 Days of Code challenge."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> { user } = interaction;
    <span class="hljs-keyword">const</span> text = interaction.options.getString(<span class="hljs-string">"message"</span>, <span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> targetCamper = <span class="hljs-keyword">await</span> getCamperData(user.id);
    <span class="hljs-keyword">const</span> updatedCamper = <span class="hljs-keyword">await</span> updateCamperData(targetCamper);
  },
};
</code></pre>
<p>Now you need to construct the response to send back to the camper when they use the command. </p>
<p>For this, you'll be using Discord's message embed feature. Start by importing the <code>MessageEmbed</code> constructor from discord.js, and creating a new embed with <code>const oneHundredEmbed = new MessageEmbed();</code>. The <code>MessageEmbed</code> class has a few methods to use for building the content of the embed.</p>
<p>Use the <code>.setTitle()</code> method to set the title of the embed to <code>"100 Days of Code"</code>. </p>
<p>Use the <code>.setDescription()</code> method to set the description of the embed to the message the camper provided in the command (remember that you extracted this to the <code>text</code> variable earlier). The author of the embed can be set, and will display at the top of the embed. </p>
<p>Use the <code>.setAuthor()</code> method to pass an object with a <code>name</code> property set to <code>user.tag</code> (which will display the camper's username and discriminator, like <code>nhcarrigan#0001</code>), and an <code>iconURL</code> property set to <code>user.displayAvatarUrl()</code> (which will attach the camper's avatar to the embed).</p>
<p>Embeds also accept fields, which are smaller blocks of text that have their own title and description. The <code>.addField()</code> method takes two or three arguments, the first being the field title, the second being the field description, and the third being an optional boolean to set the field as inline. </p>
<p>Use the <code>.addField()</code> method to add two fields. The first should have the title set to <code>"Round"</code> and the description set to <code>updatedCamper.round.toString()</code>. The second should have the title set to <code>"Day"</code> and the description set to <code>updatedCamper.day.toString()</code>. Both fields should be inline.</p>
<p>For the last part of your embed, use the <code>.setFooter()</code> method to add small footer text. Pass an object with a <code>text</code> property set to <code>"Day completed: " + new Date(updatedCamer.timestamp).toLocaleDateString()</code> to show the time the camper reported their progress.</p>
<p>Finally, you need to send this new embed back to the camper. Because you have already sent a response with the <code>interaction.deferReply()</code> call, you cannot send another response. Instead, you need to edit the one you sent.</p>
<p>Use <code>await interaction.editReply()</code> to edit the response. The <code>.editReply()</code> method takes an object with various properties – in this case, you are sending an embed. Pass an object with an <code>embeds</code> property set to <code>[oneHundredEmbed]</code>. </p>
<p>Note that this is an array containing your embed. Discord messages can contain up to 10 embeds, and the API expects an array of embed objects to match.</p>
<p>Your final command file should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { MessageEmbed } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;
<span class="hljs-keyword">import</span> { getCamperData } <span class="hljs-keyword">from</span> <span class="hljs-string">"../modules/getCamperData"</span>;
<span class="hljs-keyword">import</span> { updateCamperData } <span class="hljs-keyword">from</span> <span class="hljs-string">"../modules/updateCamperData"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"100"</span>)
    .setDescription(<span class="hljs-string">"Check in for the 100 Days of Code challenge."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> { user } = interaction;
    <span class="hljs-keyword">const</span> text = interaction.options.getString(<span class="hljs-string">"message"</span>, <span class="hljs-literal">true</span>);

    <span class="hljs-keyword">const</span> targetCamper = <span class="hljs-keyword">await</span> getCamperData(user.id);
    <span class="hljs-keyword">const</span> updatedCamper = <span class="hljs-keyword">await</span> updateCamperData(targetCamper);

    <span class="hljs-keyword">const</span> oneHundredEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    oneHundredEmbed.setTitle(<span class="hljs-string">"100 Days of Code"</span>);
    oneHundredEmbed.setDescription(text);
    oneHundredEmbed.setAuthor({
      name: user.tag,
      iconURL: user.displayAvatarURL(),
    });
    oneHundredEmbed.addField(<span class="hljs-string">"Round"</span>, updatedCamper.round.toString(), <span class="hljs-literal">true</span>);
    oneHundredEmbed.addField(<span class="hljs-string">"Day"</span>, updatedCamper.day.toString(), <span class="hljs-literal">true</span>);
    oneHundredEmbed.setFooter({
      text:
        <span class="hljs-string">"Day completed: "</span> +
        <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(updatedCamper.timestamp).toLocaleDateString(),
    });

    <span class="hljs-keyword">await</span> interaction.editReply({ embeds: [oneHundredEmbed] });
  },
};
</code></pre>
<h3 id="heading-registering-commands">Registering Commands</h3>
<p>If you run <code>npm run build</code> and <code>npm start</code>, everything starts up – but you have no way to actually use your new command. This is because Discord requires that commands be registered so they are made available in the application UI. There are a few steps we'll need to take to do this.</p>
<p>First, head over to your <code>_CommandList.ts</code> file and import your <code>oneHundred</code> command. Add this to your <code>CommandList</code> array so it's available elsewhere. </p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;
<span class="hljs-keyword">import</span> { oneHundred } <span class="hljs-keyword">from</span> <span class="hljs-string">"./oneHundred"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList: Command[] = [oneHundred];
</code></pre>
<p>Now it is time to add the logic to send the command information to Discord. In your <code>src/events</code> directory, add an <code>onReady.ts</code> file. We'll be using this with the <code>"ready"</code> event.</p>
<p>Create an exported async function named <code>onReady</code>, and give it a single parameter called <code>BOT</code>. Import the <code>Client</code> type from discord.js and set the <code>BOT</code> typedef to <code>Client</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onReady = <span class="hljs-keyword">async</span> (BOT: Client) =&gt; {};
</code></pre>
<p>Now import the <code>REST</code> module from <code>@discordjs/rest</code>. This will allow you to instantiate an API client, which you'll use to send the commands. Construct a new instance with <code>const rest = new REST();</code>. </p>
<p>There are a couple of things you'll need to configure with your REST client. First, pass an object into the <code>REST()</code> constructor with a <code>version</code> property set to <code>"9"</code>. This tells the client to use version 9 of Discord's API, which is currently the latest version. </p>
<p>Then, chain a <code>.setToken()</code> call on the constructor to set the API token to <code>process.env.BOT_TOKEN</code> – you'll have to coerce this to a <code>string</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { REST } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/rest"</span>;
<span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onReady = <span class="hljs-keyword">async</span> (BOT: Client) =&gt; {
  <span class="hljs-keyword">const</span> rest = <span class="hljs-keyword">new</span> REST({ version: <span class="hljs-string">"9"</span> }).setToken(
    process.env.BOT_TOKEN <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>
  );
};
</code></pre>
<p>The API expects command data to be sent in a specific JSON format, but thankfully the slash command builder we are using has a method just for that. Import your <code>CommandList</code>, then create a new array and map your command data.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> commandData = CommandList.map(<span class="hljs-function">(<span class="hljs-params">command</span>) =&gt;</span> command.data.toJSON());
</code></pre>
<p>Before you send the commands to Discord, it's important to note that there are two types of commands. "Global Commands" are available everywhere your bot is used, but take about an hour to update. "Guild Commands" are available only in a single server, but update immediately. Because this bot is designed to run in a single server, we're going to use guild commands.</p>
<p>You'll need to get the ID of the server you are using the bot in. To do this, make sure you have enabled developer mode in your Discord application, then right click on your server icon and select "Copy ID". In your <code>.env</code> file, add a <code>GUILD_ID</code> variable and assign it the ID you copied. It should look something like <code>GUILD_ID="778130114772598785"</code>.</p>
<p>Back in your <code>onReady.ts</code> file, start your API call with <code>await rest.put()</code>. Sending a <code>PUT</code> request will update any existing commands, where a <code>POST</code> will attempt to create new commands and error if commands share a name. Import <code>Routes</code> from <code>discord-api-types/v9</code>, and within the <code>rest.put()</code> call pass a <code>Routes.applicationGuildCommands()</code> call. This will be used to construct the API endpoint to send the commands to.</p>
<p>The <code>applicationGuildCommands()</code> call will take two arguments. </p>
<p>The first is the application ID to associate the commands with. You can get this from the <code>BOT.user.id</code> value – but <code>user</code> is potentially undefined, so you'll need to optionally chain it. Use <code>BOT.user?.id || "missing id"</code> to add a fallback value that will error out – this will allow us to know if the bot's ID is missing. </p>
<p>The second argument is the server ID, which you set up as <code>process.env.GUILD_ID</code> (remember to coerce the type!).</p>
<p>The <code>.put()</code> call needs a second argument as well, which is the data you want to send. Pass this as <code>{ body: commandData }</code> to match the expected format. </p>
<p>Finally, add a <code>console.log("Discord ready!")</code> at the end of the file to indicate your bot is online.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { REST } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/rest"</span>;
<span class="hljs-keyword">import</span> { Routes } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord-api-types/v9"</span>;
<span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { CommandList } <span class="hljs-keyword">from</span> <span class="hljs-string">"../commands/_CommandList"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onReady = <span class="hljs-keyword">async</span> (BOT: Client) =&gt; {
  <span class="hljs-keyword">const</span> rest = <span class="hljs-keyword">new</span> REST({ version: <span class="hljs-string">"9"</span> }).setToken(
    process.env.BOT_TOKEN <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>
  );

  <span class="hljs-keyword">const</span> commandData = CommandList.map(<span class="hljs-function">(<span class="hljs-params">command</span>) =&gt;</span> command.data.toJSON());

  <span class="hljs-keyword">await</span> rest.put(
    Routes.applicationGuildCommands(
      BOT.user?.id || <span class="hljs-string">"missing id"</span>,
      process.env.GUILD_ID <span class="hljs-keyword">as</span> <span class="hljs-built_in">string</span>
    ),
    { body: commandData }
  );

  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Discord ready!"</span>);
};
</code></pre>
<p>Switch to your <code>index.ts</code> file and locate your <code>"ready"</code> event listener. Replace the <code>console.log</code> call with your new <code>onReady</code> function – remember to import it, and make the callback asynchronous.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { IntentOptions } <span class="hljs-keyword">from</span> <span class="hljs-string">"./config/IntentOptions"</span>;
<span class="hljs-keyword">import</span> { connectDatabase } <span class="hljs-keyword">from</span> <span class="hljs-string">"./database/connectDatabase"</span>;
<span class="hljs-keyword">import</span> { onInteraction } <span class="hljs-keyword">from</span> <span class="hljs-string">"./events/onInteraction"</span>;
<span class="hljs-keyword">import</span> { onReady } <span class="hljs-keyword">from</span> <span class="hljs-string">"./events/onReady"</span>;
<span class="hljs-keyword">import</span> { validateEnv } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/validateEnv"</span>;

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">if</span> (!validateEnv()) <span class="hljs-keyword">return</span>;
  <span class="hljs-keyword">const</span> BOT = <span class="hljs-keyword">new</span> Client({ intents: IntentOptions });

  BOT.on(<span class="hljs-string">"ready"</span>, <span class="hljs-keyword">async</span> () =&gt; <span class="hljs-keyword">await</span> onReady(BOT));

  BOT.on(
    <span class="hljs-string">"interactionCreate"</span>,
    <span class="hljs-keyword">async</span> (interaction) =&gt; <span class="hljs-keyword">await</span> onInteraction(interaction)
  );

  <span class="hljs-keyword">await</span> connectDatabase();

  <span class="hljs-keyword">await</span> BOT.login(process.env.BOT_TOKEN);
})();
</code></pre>
<p>Now run <code>npm run build</code> and <code>npm start</code>, and head to your server in Discord. If you type <code>/</code> you should see your new <code>/100</code> command show up. Try using the command and checking the response.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-122.png" alt="Image" width="600" height="400" loading="lazy">
<em>If you see this response, then you've successfully created your first command!</em></p>
<p>Congratulations! You have your first successful command. With all of the infrastructure you've built, adding additional commands will be much smoother. Let's go ahead and do that now.</p>
<h3 id="heading-edit-command">Edit Command</h3>
<p>What happens if a camper makes a typo in their <code>/100</code> message? Because the bot sends the response, the camper cannot edit it (Discord does not allow you to edit messages you did not send). You should create a command that will allow a camper to do this.</p>
<p>Create an <code>edit.ts</code> file in your <code>src/commands</code> directory. Like you did with the <code>/100</code> command, import your <code>SlashCommandBuilder</code> and <code>Command</code> interface, and export an <code>edit</code> object with the <code>Command</code> type.</p>
<p>Use the <code>SlashCommandBuilder</code> to prepare the <code>data</code> property. Give the command the name <code>edit</code> and the description <code>Edit a previous 100 days of code post.</code>, then add two string options. The first string option should have a name <code>embed-id</code> and a description of <code>ID of the message to edit.</code>, and the second should have a name of <code>message</code> and a description of <code>The message to go in your 100 Days of Code update.</code>. Both options should be required.</p>
<p>Your code should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> edit: Command = {
    data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"edit"</span>)
    .setDescription(<span class="hljs-string">"Edit a previous 100 days of code post."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"embed-id"</span>)
        .setDescription(<span class="hljs-string">"ID of the message to edit."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    )
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
}
</code></pre>
<p>Create your <code>run</code> property with an async function and an <code>interaction</code> parameter. Destructure the <code>channel</code> and <code>user</code> from the interaction, and grab the <code>embed-id</code> and <code>message</code> options. Don't forget to defer the response!</p>
<pre><code class="lang-js">    run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
        <span class="hljs-keyword">await</span> interaction.deferReply();
        <span class="hljs-keyword">const</span> { channel, user } = interaction;
        <span class="hljs-keyword">const</span> targetId = interaction.options.getString(<span class="hljs-string">"embed-id"</span>, <span class="hljs-literal">true</span>);
        <span class="hljs-keyword">const</span> text = interaction.options.getString(<span class="hljs-string">"message"</span>, <span class="hljs-literal">true</span>);
    }
</code></pre>
<p>The <code>channel</code> property is nullable (in cases where an interaction is sent via a DM, for example), so you'll want to check that it exists. If it does not, respond with a message that the command is missing parameters.</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">if</span> (!channel) {
      <span class="hljs-keyword">await</span> interaction.editReply({
        content: <span class="hljs-string">"Missing channel parameter."</span>,
      });
      <span class="hljs-keyword">return</span>;
    }
</code></pre>
<p>Now that you know the channel exists, you can fetch the message that the camper wants to edit based on the ID they provided. Use <code>channel.messages.fetch()</code> to do this, passing in the <code>targetId</code> as the argument.</p>
<p>Because it is possible that the target message does not exist, you need to account for that in your code. Add a condition that checks for this, and if the message is not found respond with an explanation.</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">const</span> targetMessage = <span class="hljs-keyword">await</span> channel.messages.fetch(targetId);

    <span class="hljs-keyword">if</span> (!targetMessage) {
      <span class="hljs-keyword">await</span> interaction.editReply({
        content:
          <span class="hljs-string">"That does not appear to be a valid message ID. Be sure that you are using this command in the same channel as the message."</span>,
      });
      <span class="hljs-keyword">return</span>;
    }
</code></pre>
<p>The last thing you need to check is that the message the camper is editing actually belongs to them. You can access the embed with the <code>.embeds</code> property – much like how you sent it, the property is returned as an array of embed objects.</p>
<p>Grab the first embed from the array, and then check that the embed author matches the user's tag. If not, let them know they cannot edit this post.</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">const</span> targetEmbed = targetMessage.embeds[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">if</span> (targetEmbed.author?.name !== user.tag) {
        <span class="hljs-keyword">await</span> interaction.editReply({
            content: <span class="hljs-string">"This does not appear to be your 100 Days of Code post. You cannot edit it."</span>
        })
    }
</code></pre>
<p>Now that you have confirmed everything is correct, you can use <code>.setDescription()</code> on the embed to update the text. Then, edit the message with the new embed, and respond to the interaction with a confirmation.</p>
<p>Your full code should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> edit: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"edit"</span>)
    .setDescription(<span class="hljs-string">"Edit a previous 100 days of code post."</span>)
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"embed-id"</span>)
        .setDescription(<span class="hljs-string">"ID of the message to edit."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    )
    .addStringOption(<span class="hljs-function">(<span class="hljs-params">option</span>) =&gt;</span>
      option
        .setName(<span class="hljs-string">"message"</span>)
        .setDescription(<span class="hljs-string">"The message to go in your 100 Days of Code update."</span>)
        .setRequired(<span class="hljs-literal">true</span>)
    ),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> { channel, user } = interaction;
    <span class="hljs-keyword">const</span> targetId = interaction.options.getString(<span class="hljs-string">"embed-id"</span>, <span class="hljs-literal">true</span>);
    <span class="hljs-keyword">const</span> text = interaction.options.getString(<span class="hljs-string">"message"</span>, <span class="hljs-literal">true</span>);

    <span class="hljs-keyword">if</span> (!channel) {
      <span class="hljs-keyword">await</span> interaction.editReply({
        content: <span class="hljs-string">"Missing channel parameter."</span>,
      });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> targetMessage = <span class="hljs-keyword">await</span> channel.messages.fetch(targetId);

    <span class="hljs-keyword">if</span> (!targetMessage) {
      <span class="hljs-keyword">await</span> interaction.editReply({
        content:
          <span class="hljs-string">"That does not appear to be a valid message ID. Be sure that you are using this command in the same channel as the message."</span>,
      });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> targetEmbed = targetMessage.embeds[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">if</span> (targetEmbed.author?.name !== user.tag) {
      <span class="hljs-keyword">await</span> interaction.editReply({
        content:
          <span class="hljs-string">"This does not appear to be your 100 Days of Code post. You cannot edit it."</span>,
      });
    }

    targetEmbed.setDescription(text);
    <span class="hljs-keyword">await</span> targetMessage.edit({ embeds: [targetEmbed] });
    <span class="hljs-keyword">await</span> interaction.editReply({ content: <span class="hljs-string">"Updated!"</span> });
  },
};
</code></pre>
<p>Add your new <code>edit</code> command to your <code>CommandList</code> array, then build and run your bot and you should see the new command. Try editing the embed you sent earlier.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-123.png" alt="Image" width="600" height="400" loading="lazy">
<em>You should see your embed update, and a confirmation from the bot!</em></p>
<h3 id="heading-view-command">View Command</h3>
<p>Campers should have a way to view their current progress, so we'll need to create a command to do so. By now, you should be comfortable with the command structure – we encourage you to follow these instructions but attempt to write the code without looking at the final result.</p>
<p>Create a <code>view.ts</code> file in your commands directory, and set up your command variable. Create the <code>data</code> property with a command that has the name <code>view</code> and the description <code>Shows your latest 100 days of code check in.</code> This command does not need any options.</p>
<p>Set up your async function in the <code>run</code> property, and defer the interaction response. Extract the <code>user</code> object from the interaction. Use your <code>getCamperData</code> module to fetch the camper's data from the database. Then, check if the data's <code>day</code> property has a non-zero value. If it does not, let the camper know that they have not started the 100 Days of Code challenge, and can do so with the <code>/100</code> command.</p>
<p>Create an embed with a title set to <code>My 100DoC Progress</code>. Set the description to <code>Here is my 100 Days of Code progress. I last reported an update on:</code> and add the camper's timestamp. Add a <code>Round</code> and <code>Day</code> field, and set the author for the embed. Then send the embed in the interaction response.</p>
<p>Remember to add your new command to the <code>CommandList</code>, then try building and starting your bot. You should see the command available, and be able to get a response from it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-125.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you did not get the response, here is what your code should look like.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { MessageEmbed } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;
<span class="hljs-keyword">import</span> { getCamperData } <span class="hljs-keyword">from</span> <span class="hljs-string">"../modules/getCamperData"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> view: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"view"</span>)
    .setDescription(<span class="hljs-string">"Shows your latest 100 Days of Code check in."</span>),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> { user } = interaction;
    <span class="hljs-keyword">const</span> targetCamper = <span class="hljs-keyword">await</span> getCamperData(user.id);

    <span class="hljs-keyword">if</span> (!targetCamper.day) {
      <span class="hljs-keyword">await</span> interaction.editReply({
        content:
          <span class="hljs-string">"It looks like you have not started the 100 Days of Code challenge yet. Use `/100` and add your message to report your first day!"</span>,
      });
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> camperEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    camperEmbed.setTitle(<span class="hljs-string">"My 100DoC Progress"</span>);
    camperEmbed.setDescription(
      <span class="hljs-string">`Here is my 100 Days of Code progress. I last reported an update on <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(
        targetCamper.timestamp
      ).toLocaleDateString()}</span>.`</span>
    );
    camperEmbed.addField(<span class="hljs-string">"Round"</span>, targetCamper.round.toString(), <span class="hljs-literal">true</span>);
    camperEmbed.addField(<span class="hljs-string">"Day"</span>, targetCamper.day.toString(), <span class="hljs-literal">true</span>);
    camperEmbed.setAuthor({
      name: user.tag,
      iconURL: user.displayAvatarURL(),
    });

    <span class="hljs-keyword">await</span> interaction.editReply({ embeds: [camperEmbed] });
  },
};
</code></pre>
<h3 id="heading-help-command">Help Command</h3>
<p>The last thing you need to build is a help command, which will explain how campers can interact with the bot. </p>
<p>Create your <code>help.ts</code> file in the command directory, and create your <code>data</code> property. Give the command the name <code>help</code> and the description <code>Provides information on using this bot.</code></p>
<p>Set up your <code>run</code> property with the async function, and remember to defer the reply. Create an embed, and use the description and fields to provide the information you would like to share with your campers. Send the embed in the interaction response. </p>
<p>Load your new help command into the <code>CommandList</code>, and build + start your bot to test it. You should see a response with the embed you created.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/01/image-126.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Your embed might look different, depending on what information you chose to share. Here's the code that we used for the above embed:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { SlashCommandBuilder } <span class="hljs-keyword">from</span> <span class="hljs-string">"@discordjs/builders"</span>;
<span class="hljs-keyword">import</span> { MessageEmbed } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { Command } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/Command"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> help: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"help"</span>)
    .setDescription(<span class="hljs-string">"Provides information on using this bot."</span>),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> helpEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    helpEmbed.setTitle(<span class="hljs-string">"100 Days of Code Bot!"</span>);
    helpEmbed.setDescription(
      <span class="hljs-string">"This discord bot is designed to help you track and share your 100 Days of Code progress."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Create today's update"</span>,
      <span class="hljs-string">"Use the `/100` command to create your update for today. The `message` will be displayed in your embed."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Edit today's update"</span>,
      <span class="hljs-string">"Do you see a typo in your embed? Right click it and copy the ID (you may need developer mode on for this), and use the `/edit` command to update that embed with a new message."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Show your progress"</span>,
      <span class="hljs-string">"To see your current progress in the challenge, and the day you last checked in, use `/view`."</span>
    );
    helpEmbed.setFooter({ text: <span class="hljs-string">`Version <span class="hljs-subst">${process.env.npm_package_version}</span>`</span> });
    <span class="hljs-keyword">await</span> interaction.editReply({ embeds: [helpEmbed] });
    <span class="hljs-keyword">return</span>;
  },
};
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You have successfully built a Discord bot for the 100 Days of Code challenge.</p>
<p>If you are interested in exploring further, you can view <a target="_blank" href="https://github.com/nhcarrigan/100-days-of-code-bot">the source code</a> for the live bot that inspired this tutorial, which includes custom error logging, external error reporting, and a documentation site.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ RTC Connecting Discord – How to Fix the Server Error ]]>
                </title>
                <description>
                    <![CDATA[ Discord is an instant messaging app that lets you communicate through voice, video, and texts. It is available in a web-based form, a desktop app, and a mobile app. But sometimes, when you're trying to establish a voice call connection, you'll get an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rtc-connecting-discord-how-to-fix-the-server-error/</link>
                <guid isPermaLink="false">66adf20af452caf50fb1fe1f</guid>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ messaging ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Thu, 02 Dec 2021 17:10:47 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/12/Discord.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Discord is an instant messaging app that lets you communicate through voice, video, and texts. It is available in a web-based form, a desktop app, and a mobile app.</p>
<p>But sometimes, when you're trying to establish a voice call connection, you'll get an error that says “RTC Connecting Discord”. This message will keep showing without any meaningful progress.</p>
<p>So what does this error mean?</p>
<p>Discord uses the Real-time Chat protocol (RTC) to run concurrent communication. So if you are experiencing the “RTC Connecting Discord” problem, it is a network issue.</p>
<p>In this article, I will show you 5 changes you can make to your network configurations to fix the RTC Connecting Discord issue. 3 of the fixes involve your computer network settings, while the remaining 2 happen right in your Discord app. </p>
<h2 id="heading-how-to-fix-rtc-connecting-discord-by-updating-your-network-driver">How to Fix RTC Connecting Discord by Updating your Network Driver</h2>
<p>If your device relies on an outdated network adapter driver for internet connections, it could have a negative effect on your internet experience – and could cause this issue as well. </p>
<p>So, updating your network driver can fix the issue.</p>
<p><strong>The steps below take you through how you can update your network adapter driver.</strong></p>
<p><strong>Step 1</strong>: Click on Start (Windows logo) and search for "device manager". Hit <code>ENTER</code> to open the first search result – which is always Device Manager. You can click on the "Device Manager" search result, too.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-1.jpg" alt="ss-1" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Expand "Network Adapters."</p>
<p><strong>Step 3</strong>: Look for the adapter in use, right-click on it and select "update driver".
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-2.jpg" alt="ss-2" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: Select "Search automatically for drivers".
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-3.jpg" alt="ss-3" width="600" height="400" loading="lazy"></p>
<p>Windows will now search the internet for an updated driver and install it for you.</p>
<h2 id="heading-how-to-fix-rtc-connecting-discord-by-changing-your-domain-name-server">How to Fix RTC Connecting Discord by Changing your Domain Name Server</h2>
<p>A domain name server (DNS) is assigned to you by your ISP (Internet Service Provider). Domain name servers make it possible to reach websites by typing addresses (URLs) to the browser instead of some unreadable numbers.</p>
<p>Changing this DNS to a widely used DNS like that of Google or Cloudflare can help you fix the RTC Connecting Discord issue.</p>
<p><strong>To change your DNS to Google’s, follow the steps below.</strong></p>
<p><strong>Step 1:</strong> Right-click on Start and choose “Run” to open the Run dialogue.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-4.jpg" alt="ss-4" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Type in <code>“Control ncpa.cpl”</code> (without quotes) and hit <code>ENTER</code>. This will open up your network connection devices.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-5-2.png" alt="ss-5-2" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: Right-click on your current network and select “Properties”.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-6-1.jpg" alt="ss-6-1" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: Look for Internet Protocol Version 4 (TCP/IPv4) and double-click it.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-7.jpg" alt="ss-7" width="600" height="400" loading="lazy"></p>
<p><strong>Step 5</strong>: Click on the “Use the following DNS server address” radio button and type in the following values:</p>
<ul>
<li>8.8.8.8 for Preferred DNS Server</li>
<li>8.8.4.4 for Alternate DNS Server</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-8.jpg" alt="ss-8" width="600" height="400" loading="lazy"></p>
<p><strong>Step 6</strong>: Click Ok.</p>
<p>Complete the setup with the next fix.</p>
<h2 id="heading-how-to-fix-rtc-connecting-discord-by-clearing-your-computer-network-cache-in-the-command-line">How to Fix RTC Connecting Discord by Clearing your Computer Network Cache in the Command Line</h2>
<p>If you’re using the web-based Discord, this fix can work for you.</p>
<p>You can clear your network cache on your browser, but a more effective way to do it is to clear it right on your Windows 10 computer in the command line.</p>
<p>The steps below show you how to do it.</p>
<p><strong>Step 1</strong>: Hit the <code>WIN</code> (Windows logo) key on your keyboard and search for "cmd". </p>
<p>You have to use the Command Prompt as an administrator, so you should select "Run as Administrator" on the right instead of just hitting <code>ENTER</code> to open it.</p>
<p><strong>Step 2</strong>: Enter and execute the following commands one after the other:</p>
<ul>
<li><code>ipconfig /release</code></li>
<li><code>ipconfig /flushdns</code></li>
<li><code>ipconfig /renew</code></li>
</ul>
<p><strong>Step 3</strong>: Restart your computer.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-9.png" alt="ss-9" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-10.png" alt="ss-10" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-fix-rtc-connecting-discord-by-disabling-qos">How to Fix RTC Connecting Discord by Disabling QoS</h2>
<p>Discord’s QoS (Quality of Service) communicates to your router that the units of data being transmitted are of high priority. This could make your router misbehave, and cause the RTC Connecting Discord issue. </p>
<p>So, disabling the QoS might fix it for you in case you have it enabled.</p>
<p><strong>Follow the steps below to disable QoS on Discord</strong>.</p>
<p><strong>Step 1</strong>: Launch Discord, then click on Settings on the bottom left corner. 
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-11.jpg" alt="ss-11" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Select Voice and Video on the left panel.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-12.jpg" alt="ss-12" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: Scroll down to the QoS section and disable it.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-13.jpg" alt="ss-13" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: Restart the Discord app.</p>
<h2 id="heading-how-to-fix-rtc-connecting-discord-by-changing-the-audio-subsystem-in-discord">How to Fix RTC Connecting Discord by Changing the Audio Subsystem in Discord</h2>
<p>In Discord, the Legacy audio subsystem has always been suggested as the best because it is the highest quality compared to the Standard and Experimental audio subsystems.</p>
<p>Changing your Audio subsystem to Legacy can make you establish quality audio – which can end up fixing the RTC Connecting Discord issue.</p>
<p><strong>These steps take you through how to change your Audio subsystem to Legacy</strong>.</p>
<p><strong>Step 1</strong>: Open Discord and click on Settings on the bottom left corner.<br><img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-11.jpg" alt="ss-11" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Select Voice and Video.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-12.jpg" alt="ss-12" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: Scroll down to the Audio Subsystem dropdown and select “Legacy”.
<img src="https://www.freecodecamp.org/news/content/images/2021/12/ss-14.jpg" alt="ss-14" width="600" height="400" loading="lazy"></p>
<p><strong>Step 4</strong>: Restart Discord. </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This article showed you how you can fix the RTC Connecting Discord problem you might experience when you are trying to use Discord's audio call feature.</p>
<p>Apart from the solutions explained in this article, you can try other minor fixes such as: </p>
<ul>
<li>Restarting your devices – computer and router</li>
<li>Double-checking internet connection</li>
<li>Using a VPN</li>
</ul>
<p>Thanks a lot for reading this article. If you find it helpful, share it with your friends and loved ones.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Discord Won't Open on my PC [Solved in Windows 10] ]]>
                </title>
                <description>
                    <![CDATA[ Discord is an instant messaging app you can use to communicate through text messages, voice calls, and video calls. You can also use it to share files.  Discord was originally created for gamers, but many other people now use it these days. It has be... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/discord-wont-open-on-my-pc-solved-in-windows-10/</link>
                <guid isPermaLink="false">66adf0b6febac312b73075a8</guid>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ how-to ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Problem Solving ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kolade Chris ]]>
                </dc:creator>
                <pubDate>Tue, 09 Nov 2021 14:57:04 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/11/Discord.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Discord is an instant messaging app you can use to communicate through text messages, voice calls, and video calls. You can also use it to share files. </p>
<p>Discord was originally created for gamers, but many other people now use it these days. It has become an alternative to Slack for many users – especially for those who want to have an online community.</p>
<p>But sometimes, Discord might not open when you launch it. This could be due to pending updates, running games, and other causes.</p>
<p>In this article, I will show you 5 quick ways you can make Discord open again on a Windows 10 computer.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-how-to-fix-discord-not-opening-in-the-command-line">How to Fix Discord Not Opening in the Command Line</a></li>
<li><a class="post-section-overview" href="#heading-how-to-fix-discord-not-opening-by-clearing-appdata">How to Fix Discord Not Opening by Clearing AppData</a></li>
<li><a class="post-section-overview" href="#heading-how-to-fix-discord-not-opening-by-clearing-localappdata">How to Fix Discord Not Opening by Clearing LocalAppData</a></li>
<li><a class="post-section-overview" href="#heading-how-to-fix-discord-not-opening-by-closing-background-applications">How to Fix Discord Not Opening by Closing Background Applications</a></li>
<li><a class="post-section-overview" href="#heading-how-to-fix-discord-not-opening-with-the-task-manager">How to Fix Discord Not Opening with the Task Manager</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-how-to-fix-discord-not-opening-in-the-command-line">How to Fix Discord Not Opening in the Command Line</h2>
<p>The number one fix I would recommend to make Discord open again is to kill the Discord task with the Command Prompt.</p>
<p>This is how to do it:</p>
<p><strong>Step 1</strong>: Click on Start or press <code>WIN</code> (Windows key) on your keyboard, then search for "cmd".</p>
<p><strong>Step 2</strong>: Hit <code>ENTER</code> or select the first search result to open the Command Prompt.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-1-5.jpg" alt="ss-1-5" width="600" height="400" loading="lazy"></p>
<p><strong>Step</strong>: Type in <code>taskkill /F /IM discord.exe</code> and hit <code>ENTER</code>.</p>
<p>You should get a message that the Discord process has been terminated.
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-2-1.png" alt="ss-2-1" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-fix-discord-not-opening-by-clearing-appdata">How to Fix Discord Not Opening by Clearing AppData</h2>
<p>When you clear an app's AppData, all cache files are cleared – which can fix some problems, including loading.</p>
<p>Go through the following steps to clear your Discord's AppData:</p>
<p><strong>Step 1</strong>: Press <code>WIN</code> (Windows key) + R to open the Run dialogue.</p>
<p><strong>Step 2</strong>: Type in "%appdata%" (without quotes) and press <code>ENTER</code> on your keyboard. This will open up the AppData folder.
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-3-1.png" alt="ss-3-1" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: Locate the Discord folder and delete it. Delete it from your Recycle Bin too.
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-4-6.jpg" alt="ss-4-6" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-fix-discord-not-opening-by-clearing-localappdata">How to Fix Discord Not Opening by Clearing LocalAppData</h2>
<p>Clearing the Discord's LocalAppData can make it open again. It can also fix the common JavaScript error associated with Discord as well. </p>
<p>To clear LocalAppData, follow these steps:</p>
<p><strong>Step 1</strong>: Press <code>WIN</code> (Windows key) + R to open the Run dialogue.</p>
<p><strong>Step 2</strong>: Type in "%localappdata%" (without quotes) and press <code>ENTER</code> on your keyboard. This will open up the LocalAppData folder.
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-5.png" alt="ss-5" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: Look for the Discord folder and delete it. Go to your Recycle Bin and delete it too.
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-6-5.jpg" alt="ss-6-5" width="600" height="400" loading="lazy"></p>
<p>Note that you might have to reinstall Discord to get it running again after clearing its LocalAppData. I can attest that this solves the issue as I recently had to do it.</p>
<h2 id="heading-how-to-fix-discord-not-opening-by-closing-background-applications">How to Fix Discord Not Opening by Closing Background Applications</h2>
<p>A lot of games run in the background and this could have a negative effect on your Discord app.</p>
<p>Use the steps below to fix the issue only if you don't have useful apps running in the background.</p>
<p><strong>Step 1</strong>: Click on Start or press <code>WIN</code> (Windows key) on your keyboard and select Settings.
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-7-1.jpg" alt="ss-7-1" width="600" height="400" loading="lazy"></p>
<p><strong>Step 2</strong>: Select "Privacy".
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-8-2.jpg" alt="ss-8-2" width="600" height="400" loading="lazy"></p>
<p><strong>Step 3</strong>: Click on Background apps on the left, then turn off the toggle under "Let apps run in the background".
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-9-1.jpg" alt="ss-9-1" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-fix-discord-not-opening-with-the-task-manager">How to Fix Discord Not Opening with the Task Manager</h2>
<p>Stopping the Discord process with the Task Manager can make Discord open again as this refreshes the app.</p>
<p><strong>Step 1</strong>: Press <code>CTRL</code> + <code>SHIFT</code> + <code>ESC</code> to open the Task Manager</p>
<p><strong>Step 2</strong>: Make sure you are under the <code>Processes</code>. Right-click on Discord and select "End Task". 
<img src="https://www.freecodecamp.org/news/content/images/2021/11/ss-10-2.jpg" alt="ss-10-2" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, you learned how to fix Discord when it won't open in a few different ways.</p>
<p>Apart from the methods discussed in this guide, you can also fix the issue by uninstalling and reinstalling Discord. </p>
<p>Note that you can also fix another popular issue with Discord – JavaScript error – by using one of the fixes suggested in this article – clearing LocalAppData. I've had to do this myself.</p>
<p>Thanks a lot for reading. If you find this article helpful, consider sharing it with your friends. That's very much appreciated.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Discord AI Chatbot that Talks Like Your Favorite Character ]]>
                </title>
                <description>
                    <![CDATA[ Would you like to talk to a chatbot that speaks like your favorite character, fictional or non-fictional? Let's build one! In case you've seen my previous tutorial on this topic, stick with me as this version features lots of updates. You can follow ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/discord-ai-chatbot/</link>
                <guid isPermaLink="false">66d460199f2bec37e2da0645</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Thu, 26 Aug 2021 19:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/lynns-thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Would you like to talk to a chatbot that speaks like your favorite character, fictional or non-fictional? Let's build one!</p>
<p>In case you've seen my previous tutorial on this topic, stick with me as this version features lots of updates.</p>
<p>You can follow along with this tutorial using the code on my GitHub:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/RuolinZheng08/twewy-discord-chatbot">https://github.com/RuolinZheng08/twewy-discord-chatbot</a></div>
<p> </p>
<p>If you want, you can dive right into my video tutorial on YouTube – or read on for more details. 😎</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Rk8eM1p_xgM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-what-to-expect-from-this-tutorial">What to Expect from this Tutorial</h2>
<p>Here is an example of the Discord AI chatbot that we will have built by the end of this tutorial.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/discord.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Chat demo with my bot in Discord. I drew the bot's icon 😊</em></p>
<p>My chatbot project started as a joke with a friend when we were playing video games.</p>
<p>I'm honestly surprised by how popular it became – there were 5.9k views of my previous tutorial, plus, when I deployed my bot to a 1k+ user server, people flooded it with 300+ messages in an hour, effectively crashing the bot. 😳 You can <a target="_blank" href="https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/">read more about my deployment post-mortem in this post.</a></p>
<p>Since a lot of people are interested in building their own bots based on their favorite characters, I updated my tutorial to include an in-depth explanation on how to gather text data for any character, fictional or non-fictional.</p>
<p>You may also create a custom dataset that captures the speech between you and your friends and build a chatbot that speaks like yourself!</p>
<p>Other updates in this tutorial address changes in Hugging Face's model hosting services, including API changes that affect how we push the model to Hugging Face's model repositories.</p>
<h2 id="heading-outline-of-this-tutorial">Outline of this Tutorial</h2>
<p>The video version of this tutorial runs for a total of one hour and features the following topics:</p>
<ol>
<li><p>Gather text data for your character using one of these two methods: find pre-made datasets on <strong>Kaggle</strong> or make custom datasets from raw transcripts.</p>
</li>
<li><p>Train the model in <strong>Google Colab,</strong> a cloud-based Jupyter Notebook environment with free GPUs.</p>
</li>
<li><p>Deploy the model to <strong>Hugging Face,</strong> an AI model hosting service.</p>
</li>
<li><p>Build a Discord bot in either <strong>Python</strong> or <strong>JavaScript</strong>, your choice! 🤩</p>
</li>
<li><p>Set up the Discord bot's permissions so they don't spam non-bot channels</p>
</li>
<li><p>Host the bot on <strong>Repl.it.</strong></p>
</li>
<li><p>Keep the bot running indefinitely with <strong>Uptime Robot.</strong></p>
</li>
</ol>
<p>To learn more about how to build Discord bots, you may also find these two freeCodeCamp posts useful – there's a <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">Python version</a> and a <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/">JavaScript version</a>.</p>
<h2 id="heading-how-to-prepare-the-data">How to Prepare the Data</h2>
<p>For our chatbot to learn to converse, we need text data in the form of dialogues. This is essentially how our chatbot is going to respond to different exchanges and contexts.</p>
<h3 id="heading-is-your-favorite-character-on-kaggle">Is Your Favorite Character on Kaggle?</h3>
<p>There are a lot of interesting datasets on Kaggle for popular cartoons, TV shows, and other media. For example:</p>
<ul>
<li><p><a target="_blank" href="https://www.kaggle.com/andradaolteanu/rickmorty-scripts">Rick and Morty</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/gulsahdemiryurek/harry-potter-dataset?select=Harry+Potter+1.csv">Harry Potter</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/mitramir5/the-big-bang-theory-series-transcript">The Big Bang Theory</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/anderfj/game-of-thrones-series-scripts-breakdowns">Game of Thrones</a></p>
</li>
</ul>
<p>We only need two columns from these datasets: <strong>character name</strong> and <strong>dialogue line</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.07.59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example dataset: Harry Potter movie transcript</em></p>
<h3 id="heading-cant-find-your-favorite-character-on-kaggle">Can't Find Your Favorite Character on Kaggle?</h3>
<p>Can't find your favorite character on Kaggle? No worries. We can create datasets from raw transcripts. A great place to look for transcripts is <a target="_blank" href="https://transcripts.fandom.com/wiki/Transcripts_Wiki">Transcript Wiki</a>. For example, check out <a target="_blank" href="https://transcripts.fandom.com/wiki/Peppa_Pig">this Peppa Pig transcript.</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.13.57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example: Peppa Pig transcript</em></p>
<p>Using a regular expression like <code>([a-zA-Z|\s]+): (.+)</code>, we can extract out the two columns of interest, character name, and dialogue line.</p>
<p><a target="_blank" href="https://pythex.org/?regex=\(%5Ba-zA-Z%7C%5Cs%5D%2B\)%3A%20\(.%2B\)&amp;test_string=Peppa%20Pig%3A%20George%2C%20I%20could%20see%20you%20too%20easily.%0A%0ANarrator%3A%20Now%20it%20is%20Peppa%27s%20turn%20to%20hide.%0A%0AGeorge%3A%20One...%20um...%20three.%0A%0AMummy%20Pig%3A%20I%27ll%20help%20George%20to%20count.%20&amp;ignorecase=0&amp;multiline=0&amp;dotall=0&amp;verbose=0">Try it out on this Python regex website yourself!</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.58.35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-train-the-model">How to Train the Model</h2>
<p>Under the hood, our model will be a <strong>Generative Pre-trained Transfomer (GPT),</strong> the most popular language model these days.</p>
<p>Instead of training from scratch, we will load <a target="_blank" href="https://huggingface.co/microsoft/DialoGPT-small">Microsoft's pre-trained GPT</a>, <code>DialoGPT-small</code>, and fine-tune it using our dataset.</p>
<p>My GitHub repo for this tutorial contains <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/model_train_upload_workflow.ipynb">the notebook file</a> named <code>model_train_upload_workflow.ipynb</code> to get you started. All you need to do is the following: (please refer to the video for a detailed walkthrough)</p>
<ol>
<li><p>Upload the file to <a target="_blank" href="https://colab.research.google.com/">Google Colab</a></p>
</li>
<li><p>Select <strong>GPU</strong> as the runtime, which will speed up our model training.</p>
</li>
<li><p>Change the dataset and the target character in code snippets like:</p>
</li>
</ol>
<pre><code class="lang-python">data = pd.read_csv(<span class="hljs-string">'MY-DATASET.csv'</span>)
CHARACTER_NAME = <span class="hljs-string">'MY-CHARACTER'</span>
</code></pre>
<p>Running through the training section of the notebook should take less than half an hour. I have about 700 lines and the training takes less than ten minutes. The model will be stored in a folder named <code>output-small</code> .</p>
<p>Want an even smarter and more eloquent model? Feel free to train a larger model like <code>DialoGPT-medium</code> or even <code>DialoGPT-large</code>. Model size here refers to the number of parameters in the model. More parameters will allow the model to pick up more complexity from the dataset.</p>
<p>You may also increase the number of training epochs by searching for <code>num_train_epochs</code> in the notebook. This is the number of times that the model will cycle through the training dataset. The model will generally get smarter when it has more exposure to the dataset.</p>
<p>However, do take care not to overfit the model: If the model is trained for too many epochs, it may memorize the dataset and recite back lines from the dataset when we try to converse with it. This isn't ideal as we want the conversation to be more organic.</p>
<h2 id="heading-how-to-host-the-model">How to Host the Model</h2>
<p>We will host the model on Hugging Face, which provides a free API for us to query the model.</p>
<p>Sign up for <a target="_blank" href="https://huggingface.co/">Hugging Face</a> and create a new model repository by clicking on <strong>New model.</strong> Obtain your API token by going to <strong>Edit profile &gt; API Tokens.</strong> We will need this token when we build the Discord bot.</p>
<p>Follow along with this section in my video to push the model. Also, remember to tag it as <strong>conversational</strong> in its Model Card (equivalently its <code>README.md</code>):</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">---</span>
tags:
- conversational
<span class="hljs-comment">---</span>

# My Awesome Model
</code></pre>
<p>You will know that everything works fine if you are able to chat with the model in the browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/huggingface3.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-build-the-discord-bot">How to Build the Discord Bot</h2>
<p>Go to the <a target="_blank" href="https://discord.com/developers/applications">Discord Developer's page</a>, create an application, and add a bot to it. Since our chatbot is only going to respond to user messages, checking <strong>Text Permissions &gt; Send Messgaes</strong> in the Bot Permissions Setting is sufficient. Copy the bot's API token for later use.</p>
<p>Sign up for <a target="_blank" href="https://repl.it/">Repl.it</a> and create a new Repl, <strong>Python</strong> or <strong>Node.js</strong> for JavaScript, whichever you are working with.</p>
<p>Let's store our API tokens for <strong>Hugging Face</strong> and <strong>Discord</strong> as environment variables, named <code>HUGGINGFACE_TOKEN</code> and <code>DISCORD_TOKEN</code> respectively. This helps keep them secret.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/repl.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Copy <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/discord_bot.py">my Python script</a> for a Python bot and <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/discord_bot.js">my JS script</a> for a JS bot. Note that for the JS bot, because of a version incompatibility with Repl.it's Node and NPM, we will need to explicitly specify a lower version of the Discord API in <code>package.json</code>.</p>
<pre><code class="lang-pgsql">"dependencies": {
    "discord.js": "^12.5.3",
}
</code></pre>
<p>With that, our bot is ready to go! Start the Repl script by hitting <strong>Run</strong>, add the bot to a server, type something in the channel, and enjoy the bot's witty response.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/discord-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-keep-the-bot-online">How to Keep the Bot Online</h2>
<p>One problem with our bot is that it halts as soon as we <strong>stop</strong> the running Repl (equivalently, if we close the Repl.it browser window).</p>
<p>To get around this and keep our bot running indefinitely, we will set up a web server to contain the bot script, and use a service like <a target="_blank" href="https://uptimerobot.com/">Uptime Robot</a> to pin our server every five minutes so that our server stays alive.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-15.29.06.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In my video tutorial, I copied the server code from these two freeCodeCamp posts (<a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">Python version</a>, <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/">JavaScript version</a>). Then, I set up the monitor on Uptime Robot. Now my bot continues to reply to my messages even if I close the browser (or shut down my computer all together).</p>
<p>Congratulations on reaching the end of this tutorial! I hope you enjoyed creating the bot and have fun chatting with your favorite character! 🥳</p>
<h2 id="heading-tutorial-video-link">Tutorial Video Link</h2>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Rk8eM1p_xgM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-more-about-me-and-my-chatbot-project">More About Me and My Chatbot Project</h2>
<p>I'm Lynn, a software engineer at Salesforce. I graduated from the University of Chicago in 2021 with a joint BS/MS in Computer Science, specializing in Machine Learning. <a target="_blank" href="https://ruolinzheng08.github.io/">Come say hi on my personal website!</a></p>
<p>I post fun project tutorials like this on my YouTube channel. Feel free to subscribe to catch up on my latest content. 😃</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Want to learn more about my bot? Check out this 15-minute real-time chat demo featuring me, my friend, and my bot!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/-n6uWu8PZzo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Interested in the model I trained? Check it out on Hugging Face:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://huggingface.co/r3dhummingbird/DialoGPT-medium-joshua">https://huggingface.co/r3dhummingbird/DialoGPT-medium-joshua</a></div>
<p> </p>
<p>My chatbot was so popular on a 1k+ user server that... it crashed. 🤯 Read about my deployment post-mortem in this post:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/">https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/</a></div>
<p> </p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Make a Discord Chat Bot That Talks Like Rick Sanchez ]]>
                </title>
                <description>
                    <![CDATA[ You can use artificial intelligence to make a Discord chat bot talk like your favorite character. It could be a Rick and Morty character, Harry Potter, Peppa Pig, or someone else. We just released a tutorial on the freeCodeCamp.org YouTube channel th... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/make-a-discord-bot-that-talks-like-rick-sanchez/</link>
                <guid isPermaLink="false">66b2059643f24c1bb159817c</guid>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Thu, 26 Aug 2021 13:59:54 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/rick.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You can use artificial intelligence to make a Discord chat bot talk like your favorite character. It could be a Rick and Morty character, Harry Potter, Peppa Pig, or someone else.</p>
<p>We just released a tutorial on the freeCodeCamp.org YouTube channel that will teach you how to create a discord chat bot that talks like a character of your choice. You will learn to create the bot in both Python and JavaScript.</p>
<p>Lynn Zheng created this course. Lynn is a software engineer at Salesforce and a hobbyist game developer. She is also a great teacher!</p>
<p>Here are the sections in this course:</p>
<ul>
<li>Gather data</li>
<li>Train the model</li>
<li>Deploy the model</li>
<li>Build the Discord bot in Python</li>
<li>Build the Discord bot in JavaScript</li>
<li>Keep the bots online</li>
</ul>
<p>Creating this awesome bot will make you feel like a programming wizard.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/giphy-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Watch the course below or <a target="_blank" href="https://www.youtube.com/watch?v=UjDpW_SOrlw">on the freeCodeCamp.org YouTube channel</a> (1-hour watch).</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/UjDpW_SOrlw" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-transcript">Transcript</h2>
<p>(autogenerated)</p>
<p>Want to make a discord bot that talks like characters from Rick and Morty or Harry Potter? Maybe you want to make a talk like another favorite character.</p>
<p>In this course Lynn will show you how to create a discord bot that uses artificial intelligence to talk like a character of your choice.</p>
<p>Hi there. I'm Lynn. I'm a software engineer, hobbyist game developer and recent graduate from the University of Chicago.</p>
<p>In this tutorial, we're going to build a discord AI chat bot that can speak like your favorite character.</p>
<p>Before we start, if you haven't seen that video, by any chance, know that this video is something different and more comprehensive.</p>
<p>So keep watching to the end, the original tutorial and my first discord bot started as a joke between me and my friends when we were playing video games, and it really surprised me how popular it became and a lot of people wanted to build their own bot based on that tutorial.</p>
<p>Therefore, I decided to update that tutorial to include more characters, as well as to show you how to find data for your favorite character.</p>
<p>Other topics that we will cover here include, but are not limited to how to train the model, how to deploy the model, come the errors you might see during this model, training and deployment pipeline and how to solve them.</p>
<p>Moreover, we will cover how to build the bot in Python and how to build it in JavaScript.</p>
<p>Lastly, we will cover how to properly deploy the bot to a Discord server and to limit it to certain channels, and how to keep the bot running indefinitely.</p>
<p>I hope you're excited.</p>
<p>And let's jump into the tutorial.</p>
<p>First, we're going to find data for our character.</p>
<p>My favorite sources aren't kaggle transcript wiki, and just random fandom websites that came out from a Google search.</p>
<p>And my research process goes like this, I first search on kaggle to see if there are pre made dialog data set.</p>
<p>For example, if we search for Rick and Morty, we get this nicely formatted data set that includes the name of the character and the lines are speaking.</p>
<p>If we search for Harry Potter, here is another data set that includes the character and the sentence they're speaking.</p>
<p>Since we're building a chatbot, we need only these two columns in our data, set the character name and align their speaking.</p>
<p>So these dialog data sets on kaggle are perfect for our requirement.</p>
<p>Alright, if we succeeded in finding some data on kaggle, we can move on to the model training step.</p>
<p>But what if we cannot find a data set for our character on kaggle.</p>
<p>For example, if I want to find a data set for Peppa Pig, it looks like there is no data set for the character.</p>
<p>In this case, we may need to find a raw transcript of the media, be it a video game, a cartoon on a show.</p>
<p>And I've found that transcript wiki has some great resources.</p>
<p>So here we have a list of movies shows, video games, musicals commercials.</p>
<p>For example, I was able to find the transcript for Peppa Pig.</p>
<p>And also movies like Batman on transcript wiki.</p>
<p>The transcript looks like this.</p>
<p>So we have their character name and their actions or the lines they're speaking.</p>
<p>We will see shortly how to turn a raw transcript like this into a data set like those we saw on kaggle.</p>
<p>Besides transcript wiki, you may also just Google the name of your media with the keyword transcript.</p>
<p>For example, my first thought was based on the game, the word ends with you.</p>
<p>And it has no results either on kaggle or transcript wiki.</p>
<p>So what I did was to just Google the name of the game and game transcript.</p>
<p>And it just happened that this fandom website has the full game transcript.</p>
<p>So Be sure to utilize your Google Search skills to find data for your character.</p>
<p>If rather than a fictional character, you are more interested in real life character, you may search for interview scripts as your data source.</p>
<p>If you want to create a chatbot that speaks like yourself or your friends, you can treat text messages between you and your friends as dialogues, and handcraft, your data set.</p>
<p>There are tons of ways to get data for your character.</p>
<p>So be creative.</p>
<p>And now we will look at how to turn raw transcripts into data set.</p>
<p>Now, suppose we have found our raw transcript, let's see how we can turn it into a two column character line data set.</p>
<p>Suppose we take this Peppa Pig transcripts and copy them into a text file.</p>
<p>Now we go to Google colab and upload our data file.</p>
<p>Now we create a Google colab notebook.</p>
<p>And use those to parse our script.</p>
<p>So I'm going to name this parse script ipnb.</p>
<p>And we are going to from Google colab can import drive and then called drive about mount content drive.</p>
<p>This will allow us to read the data from our Google Drive.</p>
<p>Alright, now that our drive has been mounted, let's import OS and then OS change directory into content, drive by drive.</p>
<p>And then we see if there's anything in it.</p>
<p>Yeah, we have our Peppa Pig dot txt.</p>
<p>So here we are going to import regular expression to parse our transcript, put the parsed result into a panda's data frame, and export it as a CSV file just like those we saw on kaggle.</p>
<p>This is going to be our regular expression pattern.</p>
<p>You don't have to be a pro a regular expression to understand this part.</p>
<p>If we take the pattern to this site, and our testing is Peppa Pig you will see that we have two match capture groups, the first being the character's name, the second being the line spoken.</p>
<p>And also for the second line mama pig, we have the character name and the line being spoken.</p>
<p>Right so that's all right, regular expression.</p>
<p>Now, let's define a dictionary that will store our data.</p>
<p>So we know that we need the column name and the column line in our result data frame and then we open and read the file For each line, we match it with our regular expression pattern.</p>
<p>If there is a match, we extract name and light from this regular expression match, and then append it to our dictionary here.</p>
<p>And then we convert this dictionary into a data frame.</p>
<p>Now we can inspect the data frame, quote.</p>
<p>So we have the name is Peppa Pig saying that I'm Peppa Pig.</p>
<p>And George makes the sound that mama pig makes the sound great.</p>
<p>We can also count the number of lines that belong to our character.</p>
<p>So we do some the F name is equal to Peppa Pig.</p>
<p>And we saw that Peppa Pig has 38 lines in our entire data frame.</p>
<p>So the length of our data frame is over 100 and Peppa Pig has 1/3 of the lines.</p>
<p>The last step will be to export the data frame.</p>
<p>So df.to, CSV, the name will be Peppa Pig.</p>
<p>csv and and we will drop the index.</p>
<p>Cool.</p>
<p>Now we should have a Peppa Pig dot CSV in overdrive.</p>
<p>And hear this name and line.</p>
<p>This is how we parse those raw transcripts into a file that can be used in our model training.</p>
<p>So next, let's proceed on to the exciting step model training.</p>
<p>Now we're going to train the model.</p>
<p>Go to my GitHub repository linked to in the description below and download the content.</p>
<p>We're going to use those model train upload workflow the IP one MB which looks like this.</p>
<p>Alright, now our file has been downloaded, we unzip the content.</p>
<p>And in here we have a model training upload workflow dot ipnb.</p>
<p>We upload this notebook file to Google Drive and open it in Google colab.</p>
<p>We are going to train a GPT model which is short for generative pre trained transformer.</p>
<p>In the runtime change runtime type.</p>
<p>Make sure to select GPU because this will accelerate our model training.</p>
<p>So now here we mount the drive.</p>
<p>We install the Transformers module that we'll be using.</p>
<p>And we change directory into my drive.</p>
<p>Here are all the modules that we are importing.</p>
<p>If we're using the data set from kaggle, we need to obtain our API key from kaggle.</p>
<p>So go to our kaggle profile, go to account.</p>
<p>Scroll down to the API key section.</p>
<p>Create a new API token and download this file as kaggle dot JSON.</p>
<p>We'll go back to Google Drive and upload kaggle dot JSON.</p>
<p>Now we can download our data set from kaggle, we're going to use the Harry Potter data set as the example.</p>
<p>So grab those user name and the data set name.</p>
<p>And our file is here part one dot CSV.</p>
<p>Not not because there are white spaces, in our file name, we got special characters in the file name.</p>
<p>So let's inspect the contents of our data file.</p>
<p>Well, CSV files are usually separated by commas, hence the name CSV.</p>
<p>However, this one looks like it's separated by semicolons.</p>
<p>So we need to take care of the semi colons where we're reading the data into a panda's data frame.</p>
<p>So separation is semicolon instead of a comma.</p>
<p>Cool.</p>
<p>So let's sample data to see what's inside.</p>
<p>Alright, so we have character and sentence.</p>
<p>Notice that these two column names aren't exactly what we need.</p>
<p>We want the two columns of our data frame to be named as name and line as use down here in this cell, so we need to change the name of our columns.</p>
<p>Alright, let's resample our data.</p>
<p>Looks like we have successfully changed the name of our columns.</p>
<p>Now let's see how big our data is.</p>
<p>So he only has 1000 or so lines.</p>
<p>And let's see how many lines our character has.</p>
<p>Our character has 155 lines.</p>
<p>So here we change our character name to Harry.</p>
<p>And we now run this cell to create a context data frame that includes the current line our characters speaking, and several lines directly preceding the line.</p>
<p>Context data frame is useful here because we are creating a conversational chatbot.</p>
<p>And we want to generate a response based on the conversation context.</p>
<p>So let's sample our context data frame.</p>
<p>So in the context of clarity, something something our character respond with seems a pity not to ask her.</p>
<p>Great, now we have our data set, which split the data set into a training set and a test set.</p>
<p>This is because we don't want to overfit the model.</p>
<p>In the case of overfitting, the model will just memorize the lines from the data set and talk back to us using the exact lines, we don't want that we want the conversation to be more organic.</p>
<p>So we're only training the model on a training set and evaluating the model on the test set.</p>
<p>So we continue running these cells to build data sets, caching checkpoints, and down here we built the model.</p>
<p>We'll build our model by fine tuning Microsoft's pre trained GPT small.</p>
<p>Small here refers to the number of parameters in the model.</p>
<p>There are also a medium and a large model.</p>
<p>Generally, the larger the model, the longer it takes to train, but the smarter the model can get.</p>
<p>I would recommend that training a medium model, as it's pretty smart and not too hard to change.</p>
<p>My production chatbot that is currently running on a server with 1000 plus users is also a medium model.</p>
<p>For this tutorial for the sake of time, I'm training just a small model.</p>
<p>You can see here it's downloading the model.</p>
<p>And this may take some time because it's essentially 300 megabytes.</p>
<p>Here are some hyper parameters that you may find useful.</p>
<p>For example, num train aprox is the number of training aprox.</p>
<p>This is defined to be four here, and this is the number of times that the model will cycle through the training set.</p>
<p>As long as the model is not overfitting, increasing the number of training epochs usually results in smarter models.</p>
<p>Because the model has more time to cycle through the data set and pick up the nitty gritty details.</p>
<p>There's another hyper parameter called the batch size.</p>
<p>This is the number of training examples that the model will see in the batch before he updates his gradient.</p>
<p>I wouldn't recommend changing this unless you know what you're doing, since other hyper parameters like learning rate and temperature might be sensitive to this change in batch size.</p>
<p>However, if you're training a larger model on a larger data set and are running into memory errors, to make the error go away a might help to decrease the batch size.</p>
<p>The remaining cells have been configured to taking this context data frame we've created, trained the model and save it to a folder called output small.</p>
<p>Now let's run this main function.</p>
<p>training will take some time, I trained my medium model for 12 epochs, and it took around two hours.</p>
<p>So do sit back and grab a snack while the model is training.</p>
<p>You can see the progress in the progress bars about Alright, here we get back a perplexing cancer.</p>
<p>This usually refers to how confused the model is.</p>
<p>If a model has a large complexity, it means that the model is pretty confused as to which words to choose to respond to a given situation.</p>
<p>And the model might not be very smart.</p>
<p>In our case, our data set is pretty small.</p>
<p>It only has 150 plus lines.</p>
<p>So it makes sense about the perplexity is high to decrease the perplexity we might need to train for more epochs.</p>
<p>Cool.</p>
<p>But now that the training is complete, we can load and chat with the model here.</p>
<p>That's changed the name of the Hello, fellow read writer.</p>
<p>So let's ask pause.</p>
<p>Quidditch.</p>
<p>There's no such thing as a bad read writer.</p>
<p>Great, it looks like our chat bots is capable of making and maintaining a conversation.</p>
<p>Now we can push the model to Huggy face and start building our discord chat bot.</p>
<p>Alright, now let's change directory just into the content folder.</p>
<p>Because we'll be doing our push there.</p>
<p>And we do pip install hugging face command line client.</p>
<p>And then we're logging using our credentials.</p>
<p>Right after logging were assigned this token, we need to grab this token Go for the song that we need to do afterwards.</p>
<p>So we can create a repository to store all models from the command line, my is going to be called dialogue up at small Harry Potter.</p>
<p>And our empty model repository is right here.</p>
<p>There's nothing except for the get attributes file, but we will be adding the model file soon.</p>
<p>Now we still get Fs, which stands for get large file storage, this wall allows us to push and pull all models.</p>
<p>And we've replaced this token with the takeaway just copied from the above.</p>
<p>So here's my username and my token.</p>
<p>And we call it our training result is stored in this output small directory.</p>
<p>And then we change directory into our dialog up the small directory because we need to do git add and permit from there, we saw the get Fs and inspect the contents our current directory, which should be dialogue GPT, small her father and also just printed out the working directory where to make sure that we are inside content.</p>
<p>Cool.</p>
<p>Now we check the file status on Git.</p>
<p>So these files not we need to add to get.</p>
<p>So we do a git add this will take some time because the pytorch model dot being is pretty large.</p>
<p>And we configured a global username and user email.</p>
<p>These are just my hug and based credentials.</p>
<p>That would come in with message initial comment.</p>
<p>And finally, we'll push the model.</p>
<p>It's about 400 megabytes because the pytorch model is itself about 400 megabytes.</p>
<p>Alright, looks like the push is complete.</p>
<p>Now we see our pytorch model here.</p>
<p>However, there's one more thing that we need to do before we can converse with the model on Huggy face.</p>
<p>That is, you see here, it's tagged as text generation.</p>
<p>However, we know that we are training a chatbot model, and we want our model to be conversational.</p>
<p>For that purpose, we need to edit the model card.</p>
<p>So we create a model cart here and we're putting our desired model tax so our tax is conversational.</p>
<p>We come in our model card.</p>
<p>And now our model is correctly tagged as conversational.</p>
<p>If we go to the main model page, we can start chatting with the model here.</p>
<p>Alright, now that we have pushed our model to hugging face, we're ready to use it in our discord chat bots.</p>
<p>Now we have our model let's build the discord bot here on Discord.</p>
<p>I have my server lease dev lab.</p>
<p>I have two channels, one for the Python bot and one for the job as robot The reason why we have separate channel for the bots is because we don't want the bots to be talking to each other.</p>
<p>So after we built the bot, we will learn how to set their permissions correctly so that they don't go outside of their dedicated channel.</p>
<p>So we go to discord, developers page, create an application, we need one application per bot.</p>
<p>So our name will be chatted about Python.</p>
<p>So here we create a bot.</p>
<p>And I'm going to name this Harry Potter, bot, Python and upload an icon.</p>
<p>We will be using this API token here.</p>
<p>When we create our bot in Python, we're going to host our bot our rapida it so sign up for repple.id and create a new Python repple here going to name those chatty, but I thought and in here, we will need to store our API tokens for Huggy face underscore us environmental variables.</p>
<p>So here is the top for the secrets for the environment variables.</p>
<p>So the first one will be hugging face token.</p>
<p>And for the value, we'll go to our hugging face profile or the profile API tokus, copy the API token, come back here and fill in that value.</p>
<p>Next, we'll created this core token.</p>
<p>And for this value, would go to this discord developers portal and copy the token.</p>
<p>Three, add the token here.</p>
<p>And our environment variables are all set.</p>
<p>Next, I have the Python file in my GitHub repository called this court bot dot p y.</p>
<p>So we brought the code from here, and I'll explain the code line by line.</p>
<p>Starting from line one, we're first import the OS module that will help us reading our environment variables.</p>
<p>Next, we import modules that are useful for querying the Huggy face model.</p>
<p>Finally, we'll import the discount module.</p>
<p>And here I have my API URL pointing to my username.</p>
<p>And with the Find a bot as follows.</p>
<p>In the init function, it takes in a model name, which for me will be dialogue GPT small Harry Potter.</p>
<p>Then we store this API endpoint by concatenating this API URL, which is my profile link with the model name.</p>
<p>Now we retrieve the secret API token from the system environment by looking at Oh s dot environment hugging face token.</p>
<p>Next, we format the header, you know request to Huggy face.</p>
<p>For the authorization part.</p>
<p>We're putting bearer and the hugging face token.</p>
<p>Next, we'll define the quorum method that takes in the payload.</p>
<p>we dump the payload as a JSON string.</p>
<p>And use the request module to make an HTTP POST request to the API endpoint using our defined request headers, which contains our hugging face API key and passing the data.</p>
<p>Once the request finishes, it should give us a response object and we decode it from UTF eight and load the result as us rate and return to string.</p>
<p>Next, would you find an asynchronous function named already.</p>
<p>The next two function definitions are based on the discord API.</p>
<p>Both are asynchronous function.</p>
<p>The first one is already, this function will be called when the bot is logging in.</p>
<p>So when the bot is logging, we will print out logged as print out the bots name and a bot ID so that we know that the bot is functioning.</p>
<p>Next, because our bot is a chatbot, it needs to respond to messages.</p>
<p>So our message is a method that will be called each time the bot sees a message in the channel.</p>
<p>So given the message, if the message is coming from the bot itself, the bot ignores the message and does not reply to it.</p>
<p>Otherwise, it will form a Korea payload with the content of the message.</p>
<p>And to make the bot more user friendly.</p>
<p>While the bot is waiting for the HTTP response from the model, we set its status as typing so that the user will know that the bot is generating its response.</p>
<p>So this is a synchronous call with a message.channel.tv.</p>
<p>We call it soft query using the payload and get back the response.</p>
<p>If there is a valid generated response, there will be a generated tax field in this response.</p>
<p>And we'll be able to get that out as a bots response.</p>
<p>Otherwise, there might be an error in the response, we just log out the error message so that we can debug later on.</p>
<p>Finally, we use another asynchronous method to send the bots response to the channel using message dot channel dot send.</p>
<p>And that's it about our bot definition.</p>
<p>In the main function we just created about passing the model name.</p>
<p>So for me, this is style gptc small Harry Potter and us client that run looking up the score token from the environment variables.</p>
<p>Great now that our bot should be all set up that seemed like the bot to our channel.</p>
<p>In the OAuth two tab, we are going to select the bot.</p>
<p>And for the bot permissions.</p>
<p>The only thing you need is to send messages.</p>
<p>So we copy this URL, paste it in a new browser window and invited to my server.</p>
<p>Alright, now that we see that our bot has appeared, however, it shows as offline.</p>
<p>So we need to run the repple.</p>
<p>So we hit run here.</p>
<p>And rapport is installing all our dependencies and imports.</p>
<p>Great now that our bot has logged in as Harry Potter about Python, and this is its unique ID.</p>
<p>Let's go to the server.</p>
<p>And now that the bot is online don't want the bot to appear in the general channel.</p>
<p>So we go to the channel setting permissions.</p>
<p>Advanced permissions add a bot and we remove it permission to send messages and save the changes.</p>
<p>Now, let's see what happens if I type something in the general channel, nothing should happen because the bot shouldn't be able to send a message.</p>
<p>Nothing happens, although the bot is online.</p>
<p>And now this bot should work in this Python bots channel.</p>
<p>So let's do Hello.</p>
<p>And we briefly saw not there's a typing prompt.</p>
<p>Cool.</p>
<p>So yeah, this is how we built a bot in Python.</p>
<p>One thing to know, you know, raphoe, though it is not, because we took away the bots permission to send the messages in the general channel, it is showing an exception, and this is totally okay.</p>
<p>If you don't like seeing this exception, you can use the try, accept block and log out this exception.</p>
<p>Great.</p>
<p>So now we're going to repeat the process for the JavaScript bot.</p>
<p>So we go back to the discord developer portal, and create a new application.</p>
<p>This time, I'm going to call it chatty bots, JavaScript.</p>
<p>And I'll create a bot.</p>
<p>Back to wrap it, we create a node.js app.</p>
<p>going to call it chadic, thought js.</p>
<p>We again create two environmental variables.</p>
<p>The first one is hugging face token, copying my API token from my profile Edit Profile, putting here and copy my discord bot token.</p>
<p>Call this one this court token and adding the value.</p>
<p>Great.</p>
<p>Now we have our environment variable set up.</p>
<p>Go to my GitHub repository, there is a discord bots.js file that contains the code that we will use for this JavaScript chatbot.</p>
<p>Let's copy paste.</p>
<p>And I'll go through the code line by line.</p>
<p>So first, we import the discord CPI for the JavaScript module.</p>
<p>And we import fetch for making HTTP requests, just like with it in Python.</p>
<p>And we initialize a new discord client and define the model URL just as my user name and the model name.</p>
<p>So this guy is the LGBT small Harry Potter.</p>
<p>And this is the same callback that is called when the bot is ready, just like the already function we saw in Python.</p>
<p>So when the bot is ready, and logged in, we print out logged in as client user dot Tak.</p>
<p>And here is another callback.</p>
<p>This time all message, we use an asynchronous callback, because we are making HTTP requests.</p>
<p>Like in the Python script, we ignore the message if the message is from the bot itself.</p>
<p>By checking if message dot author is the bot.</p>
<p>Now we formed the payload.</p>
<p>So the payload is a dictionary containing inputs with text message content, which is the message that the bot has received.</p>
<p>And we formed the request headers by again, using the Huggy face API key.</p>
<p>So we read the Huggy face token from the environment, process dot m dot hugging face token and form the headers.</p>
<p>Right before we start making the HTTP request, we set the bot status to typing.</p>
<p>Now record a server.</p>
<p>So the response is the result from this call to fetch using HTTP POST, given the payload as the body and the headers using the Huggy face token.</p>
<p>And we convert the response into JSON format, and extract out the generated text field.</p>
<p>If there isn't a generate a text field in the response, but is that the response contains a narrower field.</p>
<p>This means that the board has encountered some errors, and we may want to print out the error for further debugging.</p>
<p>Now that we have the bots response, we can clear out his typing status and sent the message to the channel as a reply.</p>
<p>This ends the definition of our client dot our message call.</p>
<p>Down here, we log in using the discord token.</p>
<p>Now let's invite the boss to our server.</p>
<p>So we go to o auth.</p>
<p>Check the bot, it only permission is to send messages.</p>
<p>We copy this paste in a new browser window and invite it to our server.</p>
<p>Great looks like we have another bot.</p>
<p>Remember to click on save changes.</p>
<p>Otherwise, the bots icon wouldn't be showing.</p>
<p>Now that we have our bot, however, it's not logged in.</p>
<p>So we need to go back to the repple to run our script.</p>
<p>But before we run our repple, let's make sure that the bot doesn't have access to the general channel.</p>
<p>Nor does it have access to the Python channel because it's not supposed to go there.</p>
<p>So in permissions, we find this chat about JavaScript.</p>
<p>Remove its permission to send messages.</p>
<p>And always remember to save the changes.</p>
<p>We do the same thing for it on the Python channel and go to the jazz channel.</p>
<p>This time, the one we need to remove is the Python bot.</p>
<p>So this Python bot shouldn't be able to send messages to this JavaScript channel.</p>
<p>Now we go back to run our raphoe dotnet.</p>
<p>If you see this error, this means that the discord version that NPM is trying to install is wrong.</p>
<p>You can see that there are those warnings that the newest discord module is not compatible with rapida its version of node, or NPM.</p>
<p>So we need to manually change something in package dot JSON.</p>
<p>So here, we just use the older version and rerun it.</p>
<p>Now that we are logged in as chatty about JavaScript 1048 let's go back to our discord channel.</p>
<p>Call the chat about is also online.</p>
<p>Let's see if a response to our messages.</p>
<p>Alright, right.</p>
<p>So this is now an error message telling us that the model is still loading.</p>
<p>The model will usually take one or two minutes to load.</p>
<p>So let's give it some time.</p>
<p>Great looks like our bot is responding to us.</p>
<p>And because we have set the bots permissions correctly, the Python bot is not responding to any messages here.</p>
<p>And the JavaScript bots shouldn't be able to talk here.</p>
<p>And in our general channel, nobody is ever allowed to talk here.</p>
<p>Cool.</p>
<p>So now we have successfully built the bot both in Python and in JavaScript.</p>
<p>One thing to note is that if I close the browser tab for the Python bot, the bot is no longer responding, although it still shows that the bot is online.</p>
<p>So in the next part, we're going to look at how to keep the bots running indefinitely in the browser, even when we close the browser tab.</p>
<p>In order to get our bot to run indefinitely, we need to create a web server in raphoe dotnet, and set up a service called uptime robot to continuously ping the web server.</p>
<p>So this is for the Python bot.</p>
<p>And we create a new file called people live dot p y.</p>
<p>And we add the code for a web server like this.</p>
<p>And in our main.py, we import that part.</p>
<p>And down here in the main function, right before the bot runs.</p>
<p>We ask it to be kept alive.</p>
<p>We run it.</p>
<p>When the code runs, we see a URL shown in this tab.</p>
<p>And we copy this URL and bring it to our uptime robot service.</p>
<p>So here is the uptime robots website.</p>
<p>And I already have an account.</p>
<p>So I'll just go to my dashboard and add a new monitor.</p>
<p>Monitor montra type is going to be HTTPS friendly name, this court, Python but the URL is the one we copied from here.</p>
<p>And monetary level will be a ping every five minutes that should be sufficient.</p>
<p>And finally we create, monitor and close it.</p>
<p>Now let's see if our Python bot is capable of running indefinitely.</p>
<p>Alright, I'm going to close this top containing my Python script.</p>
<p>And it looks like our model is still up.</p>
<p>It's just that after some time, the model or Huggy face backend will reload because the bot itself is responding.</p>
<p>We know that our web server approach has worked Now let's repeat this process for the JavaScript bot.</p>
<p>We create a new file called server.js and copy paste base code waiting for import this part from the file that we just created.</p>
<p>Finally, right before to bought Roz, we are going to call keep alive.</p>
<p>Stop this service.</p>
<p>And right.</p>
<p>Alright, the server is now ready.</p>
<p>We copy this URL, go to uptime robot and add a new monitor.</p>
<p>It's again HTTP monitor this cord.</p>
<p>js bot, and the URL is like this.</p>
<p>We create a monitor.</p>
<p>Now we can safely close this browser window and go back to our discord chat about is still running.</p>
<p>Great.</p>
<p>Now we're all done.</p>
<p>We have a cool Python chat bot and a cool JavaScript chat bot not can run indefinitely.</p>
<p>I hope you enjoyed this video.</p>
<p>Please subscribe for more content like this and I'll see you in the next one. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use TypeScript and MongoDB to Build a 100 Days of Code Discord Bot ]]>
                </title>
                <description>
                    <![CDATA[ The 100 Days of Code challenge is very popular among new coders and developers looking to level up their skills. It's so popular that our Discord server has an entire channel dedicated to it.  By popular demand, we recently built a Discord bot that h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-100-days-of-code-bot-for-discord-using-typescript-and-mongodb/</link>
                <guid isPermaLink="false">66ac7f0d297ff4b6f39a55f1</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Tue, 22 Jun 2021 16:20:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/news-header.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The <a target="_blank" href="https://www.freecodecamp.org/news/the-crazy-history-of-the-100daysofcode-challenge-and-why-you-should-try-it-for-2018-6c89a76e298d/">100 Days of Code challenge</a> is very popular among new coders and developers looking to level up their skills. It's so popular that our <a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-discord-chat-room-server/">Discord server</a> has an entire channel dedicated to it. </p>
<p>By popular demand, we recently built a Discord bot that helps people track their progress in the challenge.</p>
<p>Today I am going to show you how to build your own 100 Days of Code bot.</p>
<h2 id="heading-how-to-create-a-discord-bot-application">How to Create a Discord Bot Application</h2>
<p>Your first step is to set up a Discord bot application. Head over to the <a target="_blank" href="https://discord.com/developers">Discord Developer Portal</a>, sign in if needed, and select "Applications" from the sidebar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-158.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the Developer Portal. If this is your first bot, you will not have any applications here.</em></p>
<p>Click the "New Application" button. Give it a name, and set it as a "Personal" application. You will now be taken to the application's settings. Here you can change the name, or give it an avatar.</p>
<p>Select "Bot" from the side bar, then click the "Add Bot" button. This will create a Discord Bot account for your application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-99.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the Bot settings page. If you did not set an avatar, you will see a default based on your bot's name.</em></p>
<p>This is the screen where you will get the bot token. It is <em>very</em> important to keep this token secret, as the token allows your code to connect to your bot. Keep it safe and do not share it with anyone.</p>
<p>Now you need to add the bot to a server to interact with it. Click the "OAuth2" option on the side bar. You should see a form under the "OAuth2 URL Generator" section. Leave the "Select Redirect URL" dropdown blank, and check the box for the "bot" scope.</p>
<p>An option to select permissions will appear. Check the boxes for the following permissions:</p>
<ul>
<li>Send Messages</li>
<li>Manage Messages</li>
<li>Embed Links</li>
<li>Read Message History</li>
<li>View Channels</li>
</ul>
<p>Above that section, you should see a URL generated. Click the "Copy" button to copy it, then paste it into your browser and go. </p>
<p>This will take you through Discord's process to add your new bot to a server. Note that you must have the Manage Server permission in the server you want to add the bot to. If you do not have this permission in any servers, you can create a server to test your bot in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-156.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the OAuth screen with the correct settings marked.</em></p>
<p>Now you are ready to write some code!</p>
<h2 id="heading-how-to-set-up-your-project">How to Set Up Your Project</h2>
<p>You need to set up the infrastructure and tooling for your project.</p>
<h3 id="heading-prepare-the-packagejson">Prepare the <code>package.json</code></h3>
<p>Create a directory, or folder, for your project. Open your terminal pointing to that new folder. Run the command <code>npm init</code> to set up your <code>package.json</code> file. For this tutorial, the default values are sufficient, but feel free to edit them as you wish.</p>
<p>You should end up with a <code>package.json</code> similar to this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"tutorial"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>
}
</code></pre>
<p>Now you need to make a couple of changes to get ready for the TypeScript implementation.</p>
<p>First, replace the <code>main</code> value of <code>index.js</code> with <code>./prod/index.js</code> – you will be setting your TypeScript to compile to a <code>prod</code> directory.</p>
<p>Then, remove the <code>test</code> script and add the following two scripts:</p>
<pre><code class="lang-json"><span class="hljs-string">"build"</span>: <span class="hljs-string">"tsc"</span>,
<span class="hljs-string">"start"</span>: <span class="hljs-string">"node -r dotenv/config ./prod/index.js"</span>
</code></pre>
<p>The <code>build</code> script will compile your TypeScript into JavaScript so <code>node</code> can run it, and the <code>start</code> script will run the <code>index.js</code> entrypoint file.</p>
<p>Adding the <code>-r dotenv/config</code> here will dynamically import and run the <code>config</code> method in the <code>dotenv</code> package, which loads your environment variables from the <code>.env</code> file.</p>
<p>Speaking of packages, your next step is to install dependencies. Using <code>npm install</code>, install these dependencies:</p>
<ul>
<li><code>discord.js</code> – this is the library that will handle connecting to the gateway and managing the Discord API calls.</li>
<li><code>dotenv</code> – a package that loads <code>.env</code> values into the node process.</li>
<li><code>mongoose</code> – A wrapper for the MongoDB connection which offers tools for structuring your data.</li>
</ul>
<p>Finally, install the development dependencies with <code>npm install --save-dev</code>. Development dependencies are packages that are required for working on your project in a development environment, but not required for running the codebase in production.</p>
<ul>
<li><code>typescript</code> – This is the package for the TypeScript language, which includes everything needed to write code in TypeScript and compile it into JavaScript.</li>
<li><code>@types/node</code> – TypeScript relies on type definitions to help understand the code you write. This package defines the types for the Node.js runtime environment, such as the <code>process.env</code> object.</li>
</ul>
<p>With these packages installed, you should now have a <code>package.json</code> similar to this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"tutorial"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node -r dotenv/config ./prod/index.js"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"discord.js"</span>: <span class="hljs-string">"^12.5.3"</span>,
    <span class="hljs-attr">"dotenv"</span>: <span class="hljs-string">"^10.0.0"</span>,
    <span class="hljs-attr">"mongoose"</span>: <span class="hljs-string">"^5.12.14"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^15.12.2"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^4.3.4"</span>
  }
}
</code></pre>
<h3 id="heading-prepare-typescript">Prepare TypeScript</h3>
<p>TypeScript's compiler offers a number of different settings to maximize your control over the resulting JavaScript. </p>
<p>You can typically modify compiler settings through a <code>tsconfig.json</code> file at the root of your project. You can generate the default boilerplate for this file with <code>npx tsc --init</code>, use an existing one if you set one up in another project, or even write one from scratch.</p>
<p>Because the compiler settings can significantly change the behaviour of TypeScript, it is best to use the same settings when following this tutorial. Here are settings you should use:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES6"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"CommonJS"</span>,
    <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"./src"</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./prod"</span>,
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"resolveJsonModule"</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p>The most important settings here are the <code>rootDir</code> and <code>outDir</code> settings. These tell the compiler that all of your code will be in the <code>src</code> directory, and the resulting JavaScript should go in the <code>prod</code> directory.</p>
<p>If you would like to test your settings, create a <code>src</code> directory and place an <code>index.ts</code> file inside. Write some code (such as a <code>console.log</code> statement) and run <code>npm run build</code> in your terminal. You should see a <code>prod</code> directory get created, with an <code>index.js</code> containing your compiled code.</p>
<h3 id="heading-additional-setup-notes">Additional Setup Notes</h3>
<p>If you are using <code>git</code> as a version control, you want to avoid pushing secrets and unnecessary code to your repository. Create a <code>.gitignore</code> file in your root project directory, and add the following content:</p>
<pre><code class="lang-txt">/node_modules/
/prod/
.env
</code></pre>
<p>The <code>.gitignore</code> file tells <code>git</code> not to track files/folders that match the patterns you enter. Ignoring the node modules folder keeps your repository from becoming bloated. </p>
<p>Pushing the compiled JavaScript is also unnecessary, as your project is typically compiled in production before runtime. <code>.env</code> files typically contain your secret values, such as API keys and tokens, so they should not be committed to a repository.</p>
<p>If you are using a UNIX based environment (such as Linux, or Git Bash on Windows), you can also add a <code>prebuild</code> script to your <code>package.json</code>. The <code>prebuild</code> script will automatically run before the <code>build</code> script when you use <code>npm run build</code>. I set mine to clean up the existing <code>prod</code> directory with <code>rm -r ./prod</code>.</p>
<h2 id="heading-how-to-create-the-discord-bot">How to Create the Discord Bot</h2>
<p>Your next step is to prepare the initial bot connection. If you did not do so earlier, create a <code>src</code> directory and an <code>index.ts</code> file within.</p>
<p>Start with an anonymous immediately-invoked function expression (IIFE) to allow for top-level <code>await</code> use:</p>
<pre><code class="lang-ts">(<span class="hljs-keyword">async</span> () =&gt; {

})();
</code></pre>
<p>Within this function you are going to instantiate your Discord bot. At the top of the file, import the <code>Client</code> class with <code>import { Client } from "discord.js;"</code>. The <code>Client</code> class represents your Discord bot's session.</p>
<p>Inside your function, construct a new <code>Client</code> instance and assign it to a <code>BOT</code> variable with <code>const BOT = new Client();</code>. Now the <code>BOT</code> variable will represent your bot.</p>
<p>To connect your bot to Discord's gateway and begin receiving events, you will need to use the <code>.login()</code> method on your bot instance. The <code>.login()</code> method takes a single argument, which is the token for the bot application you created earlier. </p>
<p>Many of the methods in <code>discord.js</code> are asynchronous, so you will need to use <code>await</code> here. Add the line <code>await BOT.login(process.env.BOT_TOKEN);</code> to your IIFE.</p>
<p>The <code>process.env</code> object will contain the environment variables for your Node.js runtime environment. With the <code>dotenv</code> package, this will also include any variables you set in your <code>.env</code> secrets file. </p>
<p>Create that <code>.env</code> file in the root of your project, and add <code>BOT_TOKEN=""</code> as the first line. Between the quotes, paste the bot token from the bot page on the Discord Developer Portal.</p>
<p>Your <code>index.ts</code> file should now look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> BOT = <span class="hljs-keyword">new</span> Client();

  <span class="hljs-keyword">await</span> BOT.login(process.env.BOT_TOKEN);
})();
</code></pre>
<p>Assuming you added your new bot to a server, if you run <code>npm run build</code> and <code>npm start</code> you should see your bot come online in the server. However, the bot will not respond to anything yet, because we have not started listening to events.</p>
<h2 id="heading-gateway-events-in-discord">Gateway Events in Discord</h2>
<p>Gateway "events" are generated when an action happens on Discord, and are typically sent to clients (including your bot) as JSON payloads. You can listen to those events with the <code>.on()</code> method, allowing you to write logic for your bot to follow when specific events occur.</p>
<p>The first event to listen to is the "ready" event. This event fires when your bot has connected to the gateway and is <em>ready</em> to process events. Above your <code>.login()</code> call, add <code>BOT.on("ready", () =&gt; console.log("Connected to Discord!"));</code>. </p>
<p>For your changes to take effect, use <code>npm run build</code> again to compile the new code. Now if you try <code>npm run start</code>, you should see "Connected to Discord!" print in your terminal.</p>
<h2 id="heading-how-to-connect-to-the-database">How to Connect to the Database</h2>
<p>You'll be using the <code>mongoose</code> package to connect to a MongoDB instance. If you prefer, you can run MongoDB locally, or you can use the MongoDB Atlas free tier for a cloud-based solution. </p>
<p>If you do not have a MongoDB Atlas account, freeCodeCamp has a <a target="_blank" href="https://www.freecodecamp.org/news/get-started-with-mongodb-atlas/">great tutorial on setting one up</a>.</p>
<p>Grab your connection string for your database and add it to your <code>.env</code> file as <code>MONGO_URI=""</code>, with the connection string going between the quotes. For the database name, use <code>oneHundredDays</code>.</p>
<p>Create a directory called <code>database</code> to hold the files that contain your database logic. Within that directory, create a file called <code>connectDatabase.ts</code>. You will be writing your logic to initiate the database connection here.</p>
<p>Start with an exported function declaration:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> connectDatabase = <span class="hljs-keyword">async</span> () =&gt; {

}
</code></pre>
<p><code>mongoose</code> offers a <code>connect</code> method for connecting to the database. Import it with <code>import { connect } from "mongoose";</code> at the top of your file.</p>
<p>Then use the method inside your function with <code>await connect(process.env.MONGO_URI);</code>. Add a <code>console.log</code> statement after that so you can identify that your bot has connected to the database. </p>
<p>Your <code>connectDatabase.ts</code> file should now look something like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { connect } <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> connectDatabase = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">await</span> connect(process.env.MONGO_URI);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Database Connected!"</span>)
}
</code></pre>
<p>Now, within your <code>index.ts</code> file, import this function with <code>import { connectDatabase } from "./database/connectDatabase"</code> and add <code>await connectDatabase()</code> to your IIFE, just before the <code>.login()</code> method. Go ahead and run <code>npm run build</code> again.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-157.png" alt="Image" width="600" height="400" loading="lazy">
<em>A compiler error, indicating that: Argument of type string or undefined is not assignable to parameter of type string.</em></p>
<p>Oh no – an error!</p>
<h2 id="heading-environment-variable-validation">Environment Variable Validation</h2>
<p>The problem with environment variables is that they can all be <code>undefined</code>. This often happens if you make a typo in your environment variable name, or mix the name up with another name (a mistake I made when writing this tutorial, using <code>TOKEN</code> instead of <code>BOT_TOKEN</code> in some places).</p>
<p>TypeScript is warning you that the <code>connect</code> method takes a string, and that an <code>undefined</code> value will break things. You can fix this, but first you will want to write a function to handle validating your environment variables.</p>
<p>Within your <code>src</code> directory, create a <code>utils</code> directory to contain your utility functions. Add a <code>validateEnv.ts</code> file there.</p>
<p>Create a function in the file called <code>validateEnv</code>. This function will be synchronous and does not need the <code>async</code> keyword. Within that function, add conditions to check for your two environment variables. If either one is missing, return <code>false</code>. Otherwise, return <code>true</code>.</p>
<p>Your code might look something like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> validateEnv = <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-keyword">if</span> (!process.env.BOT_TOKEN) {
    <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Missing Discord bot token."</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }

  <span class="hljs-keyword">if</span> (!process.env.MONGO_URI) {
    <span class="hljs-built_in">console</span>.warn(<span class="hljs-string">"Missing MongoDB connection."</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
  }
  <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
};
</code></pre>
<p>Head back to your <code>index.ts</code> file and import this validation function with <code>import { validateEnv } from "./utils/validateEnv"</code>. Then at the beginning of your IIFE, use an if statement to return early if the function returns false. Your <code>index.ts</code> should look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Client } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { connectDatabase } <span class="hljs-keyword">from</span> <span class="hljs-string">"./database/connectDatabase"</span>;
<span class="hljs-keyword">import</span> { validateEnv } <span class="hljs-keyword">from</span> <span class="hljs-string">"./utils/validateEnv"</span>;

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">if</span> (!validateEnv()) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">const</span> BOT = <span class="hljs-keyword">new</span> Client();

  BOT.on(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Connected to Discord!"</span>));

  <span class="hljs-keyword">await</span> connectDatabase();

  <span class="hljs-keyword">await</span> BOT.login(process.env.BOT_TOKEN);
})();
</code></pre>
<p>If you try <code>npm run build</code> again, you will see the same error message as before. This is because while we know the environment variable exists, TypeScript still cannot infer it. The validation function is set up to exit the process if the environment variable is missing, so we are going to tell TypeScript that it is definitely a string.</p>
<p>Back in your <code>connectDatabase.ts</code> file, within the <code>connect</code> function use <code>process.env.MONGO_URI as string</code> to coerce the type into <code>string</code>. The error should go away, and you can now run <code>npm run build</code> and <code>npm start</code>. </p>
<p>You should see the messages you wrote for both the Discord and MongoDB connections print in your terminal.</p>
<h2 id="heading-the-message-event">The "message" Event</h2>
<p>While you are making great progress on your bot, it still does not <em>do</em> anything. In order for the bot to respond to messages, you will need another event handler. </p>
<p>The logic in this one will be a bit more complicated, so you should create a separate module for it. Create an <code>events</code> folder in the <code>src</code> directory.</p>
<p>Within your <code>events</code> folder, create an <code>onMessage.ts</code> file. At the top, import the <code>Message</code> class from discord.js with <code>import { Message } from "discord.js";</code>. This class will serve as your type definition.</p>
<p>Then create an exported function called <code>onMessage</code>. The function should be asynchronous and take a <code>message</code> parameter with the <code>Message</code> type. Your function will look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Message } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> onMessage = <span class="hljs-keyword">async</span> (message: Message) =&gt; {

};
</code></pre>
<p>Before diving in to the logic for this function, you should attach it to the event listener. Back in your <code>index.ts</code> file, import your new function with <code>import { onMessage } from "./events/onMessage";</code>.</p>
<p>Next to your existing <code>.on("ready")</code> listener, add a <code>BOT.on("message")</code> listener. For the "message" event, the callback takes a <code>message</code> argument which you can pass to your new <code>onMessage</code> function:</p>
<pre><code class="lang-ts">BOT.on(<span class="hljs-string">"message"</span>, <span class="hljs-keyword">async</span> (message) =&gt; <span class="hljs-keyword">await</span> onMessage(message));
</code></pre>
<p>We should test that this works. Head back to your <code>onMessage.ts</code> file. Inside your <code>onMessage</code> function, add <code>console.log(message.content)</code>. The <code>.content</code> property on the <code>Message</code> class contains the text content sent in the message.</p>
<p>Use <code>npm run build</code> and <code>npm start</code> to get your bot running again, and then send "Hello" in a Discord channel the bot can see. You should see "Hello" print to your terminal.</p>
<h2 id="heading-how-to-prepare-for-commands">How to Prepare for Commands</h2>
<p>I maintain a few Discord bots, and one thing I've discovered that helps keep code maintainable and readable is making the components modular.</p>
<h3 id="heading-define-an-interface">Define an Interface</h3>
<p>You will first need to define a common structure for your commands. Create an <code>interfaces</code> folder in <code>src</code>. Then inside <code>interfaces</code> create a file <code>CommandInt.ts</code>.</p>
<p>Now you are going to create an interface. In TypeScript, an interface is often used to define the structure of an object, and is one of many tools available for declaring a variable's type.</p>
<p>In your <code>CommandInt.ts</code> file, import the Message class from Discord, then declare an interface called <code>CommandInt</code> with this syntax:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Message } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CommandInt {

}
</code></pre>
<p>Inside this interface, you are going to add three properties:</p>
<ul>
<li><code>name: string;</code> – the <code>name</code> value will be your command's name. You will use this to trigger the command in the Discord server.</li>
<li><code>description: string;</code> – the <code>description</code> value explains what the command does. You will used this in one of the commands.</li>
<li><code>run: (message: Message) =&gt; Promise&lt;void&gt;</code> – this is the property that will hold the command's logic.</li>
</ul>
<p>The <code>run</code> type definition is a bit tricky, so let's break it down. You have typed it as a function which takes one argument, <code>message</code>. That argument should be the <code>Message</code> type. You then set the function's <code>return</code> type to <code>Promise&lt;void&gt;</code>. This means your function will be asynchronous (this is important later) and does not return a value.</p>
<h3 id="heading-create-a-command-list">Create a Command List</h3>
<p>Next you need a place to store all of your commands. Create a folder called <code>commands</code> in the <code>src</code> directory, and add a file called <code>_CommandList.ts</code>. The underscore here will keep this file at the top of the list.</p>
<p>The <code>_CommandList.ts</code> file will need two lines. First, import your <code>CommandInt</code> interface, then declare a <code>CommandList</code> array. The array will be empty for now, but give it a <code>CommandInt[]</code> type so TypeScript knows it will eventually hold your command objects. The file should look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList: CommandInt[] = [];
</code></pre>
<p>The purpose of this file is to create an array of your bot's commands which you will iterate over in the message event listener. <a target="_blank" href="https://github.com/BeccaLyria/discord-bot/blob/main/src/utils/readDirectory.ts">There are ways to automate this</a>, but they tend to be unnecessarily complex for smaller bots.</p>
<h3 id="heading-check-for-commands">Check for Commands</h3>
<p>Back in your <code>onMessage.ts</code> file, you should start working on the logic to check messages for commands.</p>
<p>The first step is to ensure that your bot ignores its own messages, as well as the messages of other bots. This helps prevent endless cycles where the bot is responding to itself. </p>
<p>The <code>message</code> object has an <code>author</code> property, which represents the Discord user that sent the message. The <code>author</code> property has a <code>bot</code> property, which is a Boolean that indicates the author is a bot account. Add a step to check if this property is true:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">if</span> (message.author.bot) {
  <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>You also want to prevent people from accidentally calling your bot's commands. For example, if you have a <code>help</code> command, you would not want the bot to respond when someone says "help me please".</p>
<p>This can be avoided by setting a prefix for the bot to detect. Most bots use <code>!</code>, but you are welcome to choose whichever prefix you would like. </p>
<p>Declare a variable <code>prefix</code> and assign it your chosen prefix, such as <code>const prefix = "!";</code>. Then add a condition to check if the <code>message.content</code> does not start with that prefix, and if so <code>return</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> prefix = <span class="hljs-string">"!"</span>;

<span class="hljs-keyword">if</span> (!message.content.startsWith(prefix)) {
  <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>Now that you have verified that the message came from a user and is intentionally triggering your bot, you can check to see if the command is valid. </p>
<p>Using the (currently empty) <code>CommandList</code> array will facilitate this process, so import it at the top of your file with <code>import { CommandList } from "../commands/_CommandList";</code>.</p>
<p>There are a few ways to iterate through an array – for the live bot, I used a <code>for..of</code> loop. Regardless of the loop approach, you will want to check each command in the array against the message content. Here is a loop example:</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> Command <span class="hljs-keyword">of</span> CommandList) {
    <span class="hljs-keyword">if</span> (message.content.startsWith(prefix + Command.name)) {
      <span class="hljs-keyword">await</span> Command.run(message);
      <span class="hljs-keyword">break</span>;
    }
  }
</code></pre>
<p>This loop iterates through the list of commands, and if the message content starts with the prefix and command name, the bot will call the command's <code>run</code> method. </p>
<p>Remember that you declared the <code>run</code> property to be an async function that took the message as an argument. Then, to save on compute time, the loop breaks when it finds a matching command.</p>
<h2 id="heading-database-model">Database Model</h2>
<p>There's one more step before you are ready to start writing commands. This bot will track your community members' 100 Days of Code progress. And you need to store that progress in the database.</p>
<p><code>mongoose</code> helps structure your MongoDB records to prevent you from passing malformed or incomplete data into your database.</p>
<p>Start by creating a <code>models</code> folder in your <code>database</code> directory. In that <code>models</code> folder, create a <code>CamperModel.ts</code> file. This will be your structure for the user objects.</p>
<p>You first need to import the necessary values from the <code>mongoose</code> library. Add <code>import { Document, model, Schema } from "mongoose";</code> at the top of the file.</p>
<p>Because you are using TypeScript, you need to create a type definition for your database objects. Create another interface, like you did for your commands, named <code>CamperInt</code>.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CamperInt {

}
</code></pre>
<p>Your database model will have four properties. Add these to your interface:</p>
<ul>
<li><code>discordId: string;</code> – Every user object in Discord has a unique identifier, called a Snowflake, which is used to distinguish them from other users. Unlike a username or discriminator (the four digit number after the username), the <code>id</code> value cannot be changed. This makes it the ideal value for linking your stored data to a Discord user.</li>
<li><code>round: number;</code> – This will represent the "round" the user is on in the challenge. When someone completes 100 days of the challenge, they may choose to undertake the challenge again. When they do, they often refer to it as "round 2", for example. </li>
<li><code>day: number;</code> – This represents the day the user is on in the challenge.</li>
<li><code>timestamp: number;</code> – You will use this value to track when the user last submitted a 100 Days of Code post.</li>
</ul>
<p>Great! Now you need to define the Schema for your database entries. <code>mongoose</code> uses a Schema object to define the shape of the documents that go in to your database collection. The <code>Schema</code> import has a constructor, which you will assign to a variable.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Camper = <span class="hljs-keyword">new</span> Schema();
</code></pre>
<p>This constructor takes an object as its argument, and that object defines the database keys and types. Go ahead and pass in an object similar to what your interface looks like.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Camper = <span class="hljs-keyword">new</span> Schema({
    discordId: <span class="hljs-built_in">String</span>,
    round: <span class="hljs-built_in">Number</span>,
    day: <span class="hljs-built_in">Number</span>,
    timestamp: <span class="hljs-built_in">Number</span>,
})
</code></pre>
<p>Next you need to create the <code>model</code>. In <code>mongoose</code>, the <code>model</code> object serves to create, read, and update your documents in the MongoDB database. Add <code>export default model();</code> at the bottom of your file.</p>
<p>The <code>model</code> function takes a few parameters. The first is a string, and is the name to use for the documents in your database. For this collection, use <code>"camper"</code>. The second argument is the schema to use for the data – use your <code>Camper</code> schema there.</p>
<p>By default, <code>mongoose</code> will use the plural version of your <code>model</code> name for the collection. In our case, that would be "campers". If you want to change that, you can pass in a third argument of <code>{ collection: "name" }</code> to set the collection to <code>name</code>.</p>
<p>If you were using JavaScript, this would be enough to get your database model set up. However, because you are using TypeScript, you should take advantage of the type safety. <code>model()</code> by default returns a <code>Document</code> type of <code>any</code>. </p>
<p>To resolve this, you can pass a generic type into the <code>model</code> function. Generic types serve as variables for type definitions, in a sense. You need to set the generic type for your <code>model</code> to use your interface. Add the generic type by changing <code>model</code> to <code>model&lt;CamperInt&gt;</code>.</p>
<p>Just one more step here. Your <code>CamperInt</code> interface only defines the properties you set in the MongoDB document, but doesn't include the standard properties. </p>
<p>Change your <code>export interface CamperInt</code> to <code>export interface CamperInt extends Document</code>. This tells TypeScript that your type definition is an extension of the existing <code>Document</code> type definition – you are essentially adding properties to that structure.</p>
<p>Your final file should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { Document, model, Schema } <span class="hljs-keyword">from</span> <span class="hljs-string">"mongoose"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CamperInt {
  discordId: <span class="hljs-built_in">string</span>;
  round: <span class="hljs-built_in">number</span>;
  day: <span class="hljs-built_in">number</span>;
  timestamp: <span class="hljs-built_in">number</span>;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> Camper = <span class="hljs-keyword">new</span> Schema({
  discordId: <span class="hljs-built_in">String</span>,
  round: <span class="hljs-built_in">Number</span>,
  day: <span class="hljs-built_in">Number</span>,
  timestamp: <span class="hljs-built_in">Number</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> model&lt;CamperInt&gt;(<span class="hljs-string">"camper"</span>, Camper);
</code></pre>
<p>As a safety check, use <code>npm run build</code> again. You should not see any errors in the terminal.</p>
<h2 id="heading-how-to-write-bot-commands">How to Write Bot Commands</h2>
<p>You are finally ready to start writing some commands! As this is a 100 Days of Code bot, you should start with the command for creating a 100 Days of Code update.</p>
<h3 id="heading-100-command">100 command</h3>
<p>Within your <code>commands</code> folder, create a <code>oneHundred.ts</code> file. This will hold your 100 Days of Code command. Import your command interface with <code>import { CommandInt } from "../interfaces/CommandInt";</code>.</p>
<p>Now declare an exported variable <code>oneHundred</code> and give it the <code>CommandInt</code> type:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: CommandInt = {

}
</code></pre>
<p>Set the <code>name</code> property to <code>"100"</code>, give it a <code>description</code> property similar to <code>"Creates a 100 Days of Code update"</code>, and set up the <code>run</code> function as <code>async (message) =&gt; {}</code>.</p>
<p>Now for the logic within the function. Your logic will need a few properties from the <code>message</code> object to work, so go ahead and destructure those out: <code>const{ author, channel, content } = message;</code>.</p>
<p>When a user calls this command, it should look something like this:</p>
<blockquote>
<p>!100 Here is my 100 Days of Code update.</p>
</blockquote>
<p>You will want to extract that text without the <code>!100</code> part. There are a few was to do this – we are going to slice it out with <code>const text = content.split(" ").slice(1).join(" ")</code>.  Using the previous example, <code>text</code> would now hold the string <code>"Here is my 100 Days of Code update."</code>.</p>
<p>Time for some database work. Import your <code>CamperModel</code> with <code>import CamperModel from "../database/models/CamperModel"</code>. Note that you are importing the default export, instead of a module.</p>
<p>Now you need to see if the user has a record in your database. Use <code>let targetCamperData = await CamperModel.findOne()</code> to prepare for this. </p>
<p>The <code>.findOne()</code> method is used to query the collection for a single record, and takes an object to filter the query. These queries support MongoDB's syntax for advanced searching, but in this case you only need to find the record by the user's <code>discordId</code>. Add <code>{discordId: author.id}</code> as the parameter for the <code>findOne()</code>.</p>
<p>What happens if the user's record does not exist yet? If this is their first time using the command, they will not have a document in the database. Add an <code>if</code> condition to check if <code>targetCamperData</code> does not exist:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">if</span> (!targetCamperData) {

}
</code></pre>
<p>In this block, you are going to reassign <code>targetCamperData</code> to a new document with <code>targetCamperData = await CamperModel.create()</code>. You use the <code>.create()</code> method to generate and save a new document. The method takes an object as the first argument – that object is the document to create. Pass the following object to the method:</p>
<pre><code class="lang-ts">targetCamperData = <span class="hljs-keyword">await</span> CamperModel.create({
  discordId: author.id,
  round: <span class="hljs-number">1</span>,
  day: <span class="hljs-number">0</span>,
  timestamp: <span class="hljs-built_in">Date</span>.now()
});
</code></pre>
<p>Whether the record exists already or has just been created, your next step is to update it. After your <code>if</code> block, add a line to increment the <code>day</code> value: <code>targetCamperData.day++</code>.</p>
<p>What happens if the user is on day 100? They should not be able to go to day 101, as the challenge is only a hundred days long. You will need to add logic for that. If the user is above day 100, you want to set their day to 1 and increase their round:</p>
<pre><code class="lang-ts">targetCamperData.day++;
<span class="hljs-keyword">if</span> (targetCamperData &gt; <span class="hljs-number">100</span>) {
  targetCamperData.day = <span class="hljs-number">1</span>;
  targetCamperData.round++;
}
</code></pre>
<p>Now update the timestamp with <code>targetCamperData.timestamp = Date.now();</code>. This may seem redundant, since you did this step in the <code>create</code> method, but this ensures that the timestamp is updated if the data already existed.</p>
<p>You need to save the changes you made to the document. Add <code>await targetCamperData.save();</code> to do this – <code>mongoose</code> will then save your changes to the document in MongoDB.</p>
<p>Now you will construct the message the bot should send. To do this, you are going to use a message embed. Message embeds are special message formats that are available to Discord bots, which offer additional formatting options and styling.</p>
<p>Start by adding the <code>MessageEmbed</code> class to your imports with <code>import { MessageEmbed } from "discord.js";</code>. Then, after your database logic, create a new message embed with <code>const oneHundredEmbed = new MessageEmbed();</code>. Time to start setting the values of the embed.</p>
<p>The embed title appears as large text at the top of the embed. Set the title to "100 Days of Code" with <code>oneHundredEmbed.setTitle("100 Days of Code");</code>.</p>
<p>The embed description appears as standard text below the title. Set this to the user-provided text with <code>oneHundredEmbed.setDescription(text);</code>.</p>
<p>The embed author appears above the title, and is used to indicate who generated the embed. You will set this with <code>oneHundredEmbed.setAuthor()</code>. </p>
<p>This method takes a few arguments, and you will use the first two. The first argument is the author's name. Set this to <code>author.username + "#" + author.discriminator</code>.  This will display in the same way that you see a user in Discord: <code>nhcarrigan#0001</code>.  </p>
<p>Set the second argument to <code>author.displayAvatarUrl()</code>. This is a method provided by discord.js to fetch the URL for the author's avatar image.</p>
<p>Embed fields are additional title-description pairs that can be nested within the embed, and optionally inlined. These can be created with the <code>.addField()</code> method, which takes up to three arguments. The first argument is the field title, the second argument is the field description, and the third argument is an optional Boolean to set the field as inline. </p>
<p>Add two fields to your embed. The first is <code>oneHundredEmbed.addField("Round", targetCamperData.round, true);</code>, and the second is <code>oneHundredEmbed.addField("Day", targetCamperData.day, true);</code>.</p>
<p>You can add a footer to an embed and appears at the bottom in small text. Set the footer to the data's timestamp with <code>oneHundredEmbed.setFooter("Day completed: " + new Date(targetCamperData.timestamp).toLocaleDateString();</code>.  The <code>toLocaleDateString()</code> method will take a <code>Date</code> object and convert it to a locale-specific string based on the location of your bot's server.</p>
<p>Now you need to send that message embed. The <code>channel</code> property you extracted from the <code>message</code> value earlier represents the Discord channel in which the message was sent. This object has a <code>.send()</code> method, which you can use to have the bot send a message back to that channel. Use <code>await channel.send(oneHundredEmbed)</code> to send your new embed to that channel.</p>
<p>To keep the channel clean, add an <code>await message.delete()</code> to have the bot delete the message that triggered the command. Your final code should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;
<span class="hljs-keyword">import</span> CamperModel <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;
<span class="hljs-keyword">import</span> { MessageEmbed } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> oneHundred: CommandInt = {
  name: <span class="hljs-string">"100"</span>,
  description: <span class="hljs-string">"Creates a 100 Days of Code update"</span>,
  run: <span class="hljs-keyword">async</span> (message) =&gt; {
    <span class="hljs-keyword">const</span> { author, channel, content } = message;
    <span class="hljs-keyword">const</span> text = content.split(<span class="hljs-string">" "</span>).slice(<span class="hljs-number">1</span>).join(<span class="hljs-string">" "</span>);

    <span class="hljs-keyword">let</span> targetCamperData = <span class="hljs-keyword">await</span> CamperModel.findOne({ discordId: author.id });

    <span class="hljs-keyword">if</span> (!targetCamperData) {
      targetCamperData = <span class="hljs-keyword">await</span> CamperModel.create({
        discordId: author.id,
        round: <span class="hljs-number">1</span>,
        day: <span class="hljs-number">0</span>,
        timestamp: <span class="hljs-built_in">Date</span>.now(),
      });
    }

    targetCamperData.day++;
    <span class="hljs-keyword">if</span> (targetCamperData.day &gt; <span class="hljs-number">100</span>) {
      targetCamperData.day = <span class="hljs-number">1</span>;
      targetCamperData.round++;
    }
    targetCamperData.timestamp = <span class="hljs-built_in">Date</span>.now();
    <span class="hljs-keyword">await</span> targetCamperData.save();

    <span class="hljs-keyword">const</span> oneHundredEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    oneHundredEmbed.setTitle(<span class="hljs-string">"100 Days of Code"</span>);
    oneHundredEmbed.setDescription(text);
    oneHundredEmbed.setAuthor(
      author.username + <span class="hljs-string">"#"</span> + author.discriminator,
      author.displayAvatarURL()
    );
    oneHundredEmbed.addField(<span class="hljs-string">"Round"</span>, targetCamperData.round, <span class="hljs-literal">true</span>);
    oneHundredEmbed.addField(<span class="hljs-string">"Day"</span>, targetCamperData.day, <span class="hljs-literal">true</span>);
    oneHundredEmbed.setFooter(
      <span class="hljs-string">"Day completed: "</span> +
        <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(targetCamperData.timestamp).toLocaleDateString()
    );

    <span class="hljs-keyword">await</span> channel.send(oneHundredEmbed);
    <span class="hljs-keyword">await</span> message.delete();
  },
};
</code></pre>
<p>If you remember, you created a list to hold all of your commands. You need to add your new command to that list. Head back to your <code>_CommandList.ts</code> file. Import your new command with <code>import { oneHundred } from "./oneHundred";</code>, then add <code>oneHundred</code> to your empty <code>CommandList</code> array:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;
<span class="hljs-keyword">import</span> { oneHundred } <span class="hljs-keyword">from</span> <span class="hljs-string">"./oneHundred"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList: CommandInt[] = [oneHundred];
</code></pre>
<p>Now you can test it out! Use <code>npm run build</code> and <code>npm start</code> to get the bot started. Try sending <code>!100 This is my first post!</code> in the channel. The bot should respond with an embed and delete your message.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-160.png" alt="Image" width="600" height="400" loading="lazy">
<em>You can see the embed, with the author, title, description, fields, and footer.</em></p>
<h3 id="heading-view-command">View command</h3>
<p>What happens if a user forgets if they submitted or not, or wants to see what day they are on? You should add a command to view current 100 Days of Code progress.</p>
<p>In your <code>commands</code> directory, create a <code>view.ts</code> file. Like before, import your command interface and CamperModel, and create a new command called <code>view</code>. Set the <code>name</code> to <code>"view"</code>, the <code>description</code> to something like "View your current 100 Days of Code progress", and the <code>run</code> command to <code>async (message) =&gt; {}</code>.</p>
<p>You won't need the message content for this command, so extract the <code>author</code> and <code>channel</code> values from the <code>message</code> like you did before: <code>const { author, channel } = message;</code>.</p>
<p>Just like the 100 command, you need to fetch the user's data from the database. This time, however, if the data does not exist you will not be creating it – so you can use <code>const</code> here instead of <code>let</code>: <code>const targetCamperData = await CamperModel.findOne({ discordId: author.id });</code> </p>
<p>Now, if the user doesn't have a data record yet, they haven't started the challenge with the bot. You should send a message to let them know how to do this. </p>
<pre><code class="lang-ts"><span class="hljs-keyword">if</span> (!targetCamperData) {
  <span class="hljs-keyword">await</span> channel.send(<span class="hljs-string">"You have not started the challenge yet."</span>);
  <span class="hljs-keyword">return</span>;
}
</code></pre>
<p>Construct an embed, similar to the one you built for the 100 command. Don't forget to import the <code>MessageEmbed</code> class!</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">const</span> camperEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    camperEmbed.setTitle(<span class="hljs-string">"My 100DoC Progress"</span>);
    camperEmbed.setDescription(
      <span class="hljs-string">`Here is my 100 Days of Code progress. I last reported an update on <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(
        targetCamperData.timestamp
      ).toLocaleDateString()}</span>.`</span>
    );
    camperEmbed.addField(<span class="hljs-string">"Round"</span>, targetCamperData.round, <span class="hljs-literal">true</span>);
    camperEmbed.addField(<span class="hljs-string">"Day"</span>, targetCamperData.day, <span class="hljs-literal">true</span>);
    camperEmbed.setAuthor(
      author.username + <span class="hljs-string">"#"</span> + author.discriminator,
      author.displayAvatarURL()
    );
</code></pre>
<p>A couple of key differences here. Instead of taking a text input from the user, you are using a fixed description value to indicate this is a <code>view</code> embed instead of a <code>100</code> embed. Since you use the timestamp in the description, you do not need to add a footer.</p>
<p>Just like before, send the embed to the message channel and delete the original message. Your final file should be:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;
<span class="hljs-keyword">import</span> CamperModel <span class="hljs-keyword">from</span> <span class="hljs-string">"../database/models/CamperModel"</span>;
<span class="hljs-keyword">import</span> { MessageEmbed } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> view: CommandInt = {
  name: <span class="hljs-string">"view"</span>,
  description: <span class="hljs-string">"Views your 100 Days of Code progress."</span>,
  run: <span class="hljs-keyword">async</span> (message) =&gt; {
    <span class="hljs-keyword">const</span> { author, channel } = message;

    <span class="hljs-keyword">const</span> targetCamperData = <span class="hljs-keyword">await</span> CamperModel.findOne({
      discordId: author.id,
    });

    <span class="hljs-keyword">if</span> (!targetCamperData) {
      <span class="hljs-keyword">await</span> channel.send(<span class="hljs-string">"You have not started the challenge yet."</span>);
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> camperEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    camperEmbed.setTitle(<span class="hljs-string">"My 100DoC Progress"</span>);
    camperEmbed.setDescription(
      <span class="hljs-string">`Here is my 100 Days of Code progress. I last reported an update on <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>(
        targetCamperData.timestamp
      ).toLocaleDateString()}</span>.`</span>
    );
    camperEmbed.addField(<span class="hljs-string">"Round"</span>, targetCamperData.round, <span class="hljs-literal">true</span>);
    camperEmbed.addField(<span class="hljs-string">"Day"</span>, targetCamperData.day, <span class="hljs-literal">true</span>);
    camperEmbed.setAuthor(
      author.username + <span class="hljs-string">"#"</span> + author.discriminator,
      author.displayAvatarURL()
    );

    <span class="hljs-keyword">await</span> channel.send(camperEmbed)
    <span class="hljs-keyword">await</span> message.delete();
  },
};
</code></pre>
<p>Add your new <code>view</code> command to your <code>_CommandList.ts</code> file with an import, and put the command in the <code>CommandList</code> array. Then use <code>npm run build</code> and <code>npm start</code> to test your new changes. Send "!view" in your channel and you should see the bot respond:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-161.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-edit-command">Edit command</h3>
<p>Unfortunately, if a user makes a typo in their 100 Days of Code post, they can't edit the message because the bot sent it. But you can add a command that will allow them to do this.</p>
<p>Create an <code>edit.ts</code> file in your <code>commands</code> directory. Import your command interface and declare a new command called <code>edit</code>. Set the <code>name</code> to <code>"edit"</code>, the <code>description</code> to something like "Edits a previous 100 Days of Code post", and prepare the <code>run</code> function as you have before.</p>
<p>Within the function, extract the <code>author</code>, <code>channel</code>, and <code>content</code> properties from the <code>message</code> object.</p>
<p>The <code>edit</code> command will take a Discord message id, followed by the updated text to use. You can destructure those from the message content with <code>const [, targetId, ...text] = content.split(" ");</code>. </p>
<p>The first element in the array would be the <code>!edit</code> command call, which is not needed for this command so you do not need to assign it to a value. The <code>targetId</code> element would be the id of the message to edit. <code>...text</code> that uses the spread operator to assign the remaining message content to the <code>text</code> variable, as an array.</p>
<p>Now you need to use the <code>targetId</code> to get the actual message from Discord. The <code>channel</code> value has a <code>messages</code> property which represents all of the messages sent in that channel. You can use the <code>fetch</code> method on that <code>messages</code> property to get a specific message (or multiple messages). Set this up as <code>const targetMessage = await channel.messages.fetch()</code>. </p>
<p>The <code>.fetch()</code> method can take an object containing the options for the fetch request, or it can take a string as the <code>id</code> of the message to fetch. Because you have the <code>id</code>, and are only fetching one message, you can pass <code>targetId</code> to the <code>.fetch()</code> method as the only parameter.</p>
<p>It is possible that the <code>targetMessage</code> does not exist. For example, if the user provided an invalid id string (or no id string at all). You'll need to add logic to check if the <code>targetMessage</code> is not found:</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">if</span> (!targetMessage) {
        <span class="hljs-keyword">await</span> channel.send(<span class="hljs-string">"That does not appear to be a valid message ID."</span>);
        <span class="hljs-keyword">return</span>;
    }
</code></pre>
<p>Now that you have asserted that the message exists, you can start working with the properties. Because your bot sends the message as an embed, the <code>content</code> property you are used to working with will be empty. Instead, you can find the embed within the <code>embeds</code> property.</p>
<p>The <code>embeds</code> property is an array of <code>MessageEmbed</code> objects. Since you wrote the bot's code to only send one embed, you can access that embed with <code>const targetEmbed = targetMessage.embeds[0];</code>.</p>
<p>Now that you have the embed, you need to confirm that the embed is from one of that user's 100 Days of Code posts. Thankfully, you set the user as the author of the embed. You can check if the embed's author information does not match the message author's information:</p>
<pre><code class="lang-ts">    <span class="hljs-keyword">if</span> (
      targetEmbed.author?.name !==
      author.username + <span class="hljs-string">"#"</span> + author.discriminator
    ) {
      <span class="hljs-keyword">await</span> channel.send(
        <span class="hljs-string">"This does not appear to be your 100 Days of Code post. You cannot edit it."</span>
      );
      <span class="hljs-keyword">return</span>;
    }
</code></pre>
<p>You have accounted for the message belonging to a different user (or not having the correct embed entirely), so now you can edit the embed. </p>
<p>Like you did before, set the description of the embed with the <code>.setDescription()</code> method. You'll need to use <code>.join(" ")</code> on the <code>text</code> variable this time, since it is currently an array. <code>targetEmbed.setDescription(text.join(" "));</code></p>
<p>Rather than sending a new message, you need to edit the existing message. You have the existing message stored in <code>targetMessage</code>, so you can use the <code>.edit()</code> method to change that message directly. </p>
<p><code>await targetMessage.edit(targetEmbed);</code> will change the message's embed to your modified version. Then delete the message that triggered this command with <code>await message.delete();</code>. Your command should look like this:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> edit: CommandInt = {
  name: <span class="hljs-string">"edit"</span>,
  description: <span class="hljs-string">"Edits a previous 100 Days of Code post."</span>,
  run: <span class="hljs-keyword">async</span> (message) =&gt; {
    <span class="hljs-keyword">const</span> { author, channel, content } = message;
    <span class="hljs-keyword">const</span> [, targetId, ...text] = content.split(<span class="hljs-string">" "</span>);

    <span class="hljs-keyword">const</span> targetMessage = <span class="hljs-keyword">await</span> channel.messages.fetch(targetId);

    <span class="hljs-keyword">if</span> (!targetMessage) {
      <span class="hljs-keyword">await</span> channel.send(<span class="hljs-string">"That does not appear to be a valid message ID."</span>);
      <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">const</span> targetEmbed = targetMessage.embeds[<span class="hljs-number">0</span>];

    <span class="hljs-keyword">if</span> (
      targetEmbed.author?.name !==
      author.username + <span class="hljs-string">"#"</span> + author.discriminator
    ) {
      <span class="hljs-keyword">await</span> channel.send(
        <span class="hljs-string">"This does not appear to be your 100 Days of Code post. You cannot edit it."</span>
      );
      <span class="hljs-keyword">return</span>;
    }

    targetEmbed.setDescription(text.join(<span class="hljs-string">" "</span>));

    <span class="hljs-keyword">await</span> targetMessage.edit(targetEmbed);
    <span class="hljs-keyword">await</span> message.delete();
  },
};
</code></pre>
<p>Add the command to your <code>_CommandList.ts</code> file, importing it and adding the variable to the array. Then use <code>npm run build</code> and <code>npm start</code> to run the bot again.</p>
<p>To grab a message ID, you should have Developer Mode enabled in your Discord client. If you have not done so, visit your settings and select the "Advanced" section. Toggle "Developer Mode" on:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-162.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Then head back to your channel and right click on your original 100 Days of Code message. You should see an option in the context menu to copy the message ID:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-164.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Select that option and you will get an ID (mine was <code>855559921666621441</code>). Then in the same channel, use <code>!edit 855559921666621441 This is an edited post!</code>, replacing my value with the one you got from the "Copy ID" option. The bot should edit the existing embed with your new content.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-165.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-help-command">Help command</h3>
<p>You are almost there! One more command to go. Many bots have a <code>help</code> command, which returns a list of available commands. You should add one to your bot as well.</p>
<p>One last time, create a <code>help</code> file in your <code>commands</code> directory. Import your <code>CommandInt</code> interface and set up your command as <code>help</code>. Set the <code>name</code> to <code>"help"</code>, and the <code>description</code> to something like <code>"Returns information on the bot's available commands."</code>. Set up your <code>run</code> function. </p>
<p>This time you only need the message's <code>channel</code> property, so no need to destructure anything here. Instead, import the <code>MessageEmbed</code> class from <code>discord.js</code>, and go ahead and import your command list too: <code>import { CommandList } from "./_CommandList";</code>.</p>
<p>Construct a new <code>MessageEmbed</code> and assign it to a <code>helpEmbed</code> variable. Set the <code>title</code> to <code>"Available Commands:"</code> and the description to something similar to <code>"These are the available commands for this bot."</code>.</p>
<p>Now you need to add a field to the embed and dynamically generate the list of commands. Start by adding the field with <code>helpEmbed.addField()</code>. Use the first parameter to set the field name to <code>"Commands:"</code>. For the description (the second parameter), you will use the <code>CommandList</code> array to generate a readable list of commands.</p>
<pre><code class="lang-ts">    helpEmbed.addField(
      <span class="hljs-string">"Commands:"</span>,
      CommandList.map(<span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> <span class="hljs-string">`\`!<span class="hljs-subst">${el.name}</span>\`: <span class="hljs-subst">${el.description}</span>`</span>).join(<span class="hljs-string">"\n"</span>)
    );
</code></pre>
<p>The process here is two-part. First, using the built-in array method <code>.map</code>, you are creating a new array from your array of <code>CommandInt</code> objects. This array contains strings formatted using Markdown so the command name and description are readable. The string for your help command would look like:</p>
<blockquote>
<p><code>!help</code>: Returns information on the bot's available commands.</p>
</blockquote>
<p>You are then joining that array of strings with a new-line separator, which will create a vertical list of commands in a single string (embed fields require strings for the description).</p>
<p>Send the embed to the channel. Because you did not destructure the <code>channel</code> property out of the <code>message</code> object, you will need to use <code>message.channel.send(helpEmbed);</code> directly. </p>
<p>This time, do not delete the original message – you did not add an author to the help embed, so preserving the original message helps moderators see who used the command. Your help command should look like:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;
<span class="hljs-keyword">import</span> { MessageEmbed } <span class="hljs-keyword">from</span> <span class="hljs-string">"discord.js"</span>;
<span class="hljs-keyword">import</span> { CommandList } <span class="hljs-keyword">from</span> <span class="hljs-string">"./_CommandList"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> help: CommandInt = {
  name: <span class="hljs-string">"help"</span>,
  description: <span class="hljs-string">"Returns information on the bot's available commands."</span>,
  run: <span class="hljs-keyword">async</span> (message) =&gt; {
    <span class="hljs-keyword">const</span> helpEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    helpEmbed.setTitle(<span class="hljs-string">"Available Commands!"</span>);
    helpEmbed.setDescription(
      <span class="hljs-string">"These are the available commands for this bot."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Commands:"</span>,
      CommandList.map(<span class="hljs-function">(<span class="hljs-params">el</span>) =&gt;</span> <span class="hljs-string">`\`!<span class="hljs-subst">${el.name}</span>\`: <span class="hljs-subst">${el.description}</span>`</span>).join(<span class="hljs-string">"\n"</span>)
    );

    <span class="hljs-keyword">await</span> message.channel.send(helpEmbed);
  },
};
</code></pre>
<p>Import your <code>help</code> command into your <code>_CommandList.ts</code> file and add the command to your array. With this final command, your <code>_CommandList.ts</code> file should be:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;
<span class="hljs-keyword">import</span> { oneHundred } <span class="hljs-keyword">from</span> <span class="hljs-string">"./oneHundred"</span>;
<span class="hljs-keyword">import</span> { view } <span class="hljs-keyword">from</span> <span class="hljs-string">"./view"</span>;
<span class="hljs-keyword">import</span> { edit } <span class="hljs-keyword">from</span> <span class="hljs-string">"./edit"</span>;
<span class="hljs-keyword">import</span> { help } <span class="hljs-keyword">from</span> <span class="hljs-string">"./help"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList: CommandInt[] = [oneHundred, view, edit, help];
</code></pre>
<p>Use <code>npm run build</code> and <code>npm start</code> one last time to test this feature. Send <code>!help</code> in your channel and the bot should respond:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-167.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You have successfully built a Discord bot for the 100 Days of Code challenge.</p>
<p>If you are interested in exploring further, you can view <a target="_blank" href="https://github.com/nhcarrigan/100-days-of-code-bot">the source code</a> for the live bot that inspired this tutorial, which includes custom error logging, external error reporting, and a documentation site.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Recover from Deployment Hell – What I Learned After My Discord Bot Crashed on a 1,000+ User Server ]]>
                </title>
                <description>
                    <![CDATA[ I built a Discord AI Chatbot in my last blog post and then, to challenge myself, proceeded to stress-test it on a Discord server with 1,000+ users. In the first hour, Deployment Hell struck and I had to take the bot down for maintenance. Another hour... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/</link>
                <guid isPermaLink="false">66d46025264384a65d5a9594</guid>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ lessons learned ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Life lessons ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Fri, 04 Jun 2021 20:41:29 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/Untitled126_20210603134910.PNG" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I built <a target="_blank" href="https://www.freecodecamp.org/news/discord-ai-chatbot/">a Discord AI Chatbot in my last blog post</a> and then, to challenge myself, proceeded to stress-test it on a Discord server with 1,000+ users.</p>
<p>In the first hour, Deployment Hell struck and I had to take the bot down for maintenance. Another hour passed and I managed to patch up my bot and send it back.</p>
<p>Now my bot is up and running and more resilient than ever. As for me, I survived and even thrived in Deployment Hell 🔥. In this deployment postmortem, I'm going to show you how.</p>
<p>I sent my AI chatbot into the server at 2 pm on June 2nd, and hype started gathering around it. The chat went on for a while and all was nice and smooth.</p>
<p>At 3:20 pm, everything started falling apart: My free-tier API key reached its hourly request limit, and I had no choice but to take down the bot and the server for maintenance.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/IMG_0676.PNG" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Never mind the typos in my message 😅 This incident caught me totally unprepared</em></p>
<p>Despite being struck by Deployment Hell in the first hour on the first day of my project debut, I knew I need to sit down, take a deep breath, and recover from this incident.</p>
<h1 id="heading-what-went-well-with-deployment">What Went Well with Deployment</h1>
<p>As a first step, I didn't forget to congratulate myself on what went well despite this mishap. Clearly, people were enthusiastic about chatting with my chatbot, to the extent that we went over rate limit.</p>
<p>Moreover, in the brief hour when I observed people interact with my bot in real time, I discovered several good design choices that I've consciously, unconsciously, or subconsciously made.</p>
<h2 id="heading-avoid-feature-creep-at-all-costs-during-development">Avoid Feature Creep At All Costs During Development</h2>
<p>I originally developed my code for a tutorial, so I kept my code as simple and readable as possible, without complicated features that won't serve my bot's main use case: chatting.</p>
<p>That said, I marked down <strong>TODOs and stretch goals</strong> in my code, hoping to get back to those if necessary. For example:</p>
<pre><code class="lang-python"> <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> cache chat history in DB and load</span>
 <span class="hljs-comment"># <span class="hljs-doctag">TODO:</span> after each user input and bot input,</span>
 <span class="hljs-comment"># append them to conversation history for the next query</span>
 ...
 <span class="hljs-comment"># <span class="hljs-doctag">FIXME:</span> better make this try-except block more fine-grained</span>
</code></pre>
<p>As I observed users interacting with the bot, I'm actually relieved that I didn't implement the <strong>memory cache feature</strong>. Several users were talking with the bot at once, each along their different conversation thread. If I were to keep track of the conversation history, I would have to create a unique log for each user, further complicating database operations.</p>
<h2 id="heading-abide-by-the-principle-of-least-privilege">Abide by the Principle of Least Privilege</h2>
<p>One thing I learned in my Computer Security class is the Principle of Least Privilege (PoLP) – granting an application the minimum amount of access it needs to do its job.</p>
<p>My chatbot only needs two low-level security permissions: <strong>View Channel</strong> for reading users' messages, and <strong>Send Text Messages</strong> for replying to users.</p>
<p>Of course I could have given it more fancy permissions like those shown below in the image, just in case it needs any. But that would have violated PoLP and who knows whether my malfunctioning bot will bring down anything else with it when it fails?</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/Screen-Shot-2021-06-03-at-14.21.52-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Permission settings for Discord bots</em></p>
<h2 id="heading-other-observations-from-deploying-my-bot">Other Observations from Deploying My Bot</h2>
<p>Despite its ephemeral lifespan, my bot offered me an opportunity to conduct user research in the real world. This is a server with 1000+ users, not my cozy dev server where my friends and I hang out and take turns to politely exchange lines with the bot.</p>
<p>Here, I observed several interesting user behaviors:</p>
<ul>
<li><p>People tend to ask the bot <strong>open-ended questions</strong> instead of <strong>factual ones</strong>. Because I built my bot based off a video game character, when developing the AI model, I was keen on making sure the model learns the canon information about the character, like name, age, and role in the game. I was relieved when I saw that people are much more curious about the bot character's preference for ice cream flavor than their factual place of birth.</p>
</li>
<li><p>People use a lot of emoticons :), emojis 😃 and GIFs as they text. However, these will most likely be treated as <code>&lt;UNKNOWN&gt;</code> tokens in the AI model's tokenizer, which means I should <strong>sanitize</strong> user inputs.</p>
</li>
</ul>
<p>I was also very fortunate to receive direct feedback from friendly people on the server. One feature request that I got was to make the bot attach its response to a user's message thread, instead of just dumping its response in the channel.</p>
<p>Buzzing with excitement from people's enthusiasm and armed with insights from user research, I was ready to patch up my bot and send it back as soon as possible.</p>
<h1 id="heading-what-i-needed-to-fix">What I Needed to Fix</h1>
<p>As a good development habit, I kept my code well-organized and modularized, so switching from the production bot back to my development bot requires no more than copy-pasting the dev bot's API key.</p>
<p>Once I was running on my dev server, I sat down to identify the types of problems I needed to tackle.</p>
<h2 id="heading-fatal-crashes">Fatal Crashes</h2>
<p>The fatal bug that caused me to take down my bot was that I hit an hourly API rate limit. I took the obvious approach of keeping <strong>redundancy</strong> in my system: Keep an alternative API key, and once the primary one runs out, switch over to the alternative, and then switch back at the turn of the hour.</p>
<p>Workaround aside, I noted that this is a short-term solution. If I need to properly scale up my system, I should make an estimate of the number of requests per hour, as I'll discuss at the end of this section.</p>
<h2 id="heading-new-features-for-usability">New Features for Usability</h2>
<p>I decided on several new features that will make my bot more user-friendly. Some highlights are:</p>
<ul>
<li><p>As some server people suggested, I re-programmed the bot so that instead of dumping responses to different user messages into the channel, it directly responds to each user message in the message's thread.</p>
</li>
<li><p>I sanitized user inputs by removing Unicode emojis and Discord-specific <code>&lt;:some_hilarious_gif&gt;</code> tags. This will limit <code>&lt;UNKNOWN&gt;</code> tokens that my AI model will receive and help it generate better responses.</p>
</li>
<li><p>I implemented a magic command <code>$ignore [message]</code> that allow users to send a message to the channel without triggering a bot response. This feature comes from my observation that, whenever the bot says something funny or smart (or both!), users will remark on that by sending a text intended for their friends (and not the bot) to the channel. It'd be annoying to still receive a bot response on a remark intended for a friend. Hence, I hope that this magic command will address this user pain point.</p>
</li>
<li><p>I implemented magic commands for the server moderators to interface with my bot (stopping or rebooting) so that they can keep the bot under control without having to access my Repl.it server. This makes both my job and theirs easier.</p>
</li>
</ul>
<h2 id="heading-future-proof-logging">Future-proof Logging</h2>
<p>In my cozy dev server, there is little complexity, whereas on this 1000+ user server where 40% of users are online at any given moment, complexity explodes.</p>
<p>There are multiple channels besides the chat channel dedicated to my bot, multiple user roles and permission levels, multiple users typing at the same time, and so on.</p>
<p>While I certainly cannot prevent all possible failure scenarios, what I could do is to protect the important part of my code with a try-except block and log out all information that might reveal the cause of a failure. Since real-time system bugs are subtle and difficult to reproduce, logging will save me lots of headaches down the road.</p>
<pre><code class="lang-python"><span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            print(e, <span class="hljs-string">'Offending channel'</span>, message.channel, 
            <span class="hljs-string">'Offending message'</span>, message.content, 
            <span class="hljs-string">'Offending bot response'</span>, bot_response, 
            sep=<span class="hljs-string">'\n'</span>, end=<span class="hljs-string">'\n\n'</span>)
</code></pre>
<h2 id="heading-scalability">Scalability</h2>
<p>Estimating under some system constraints is where basic statistics and heuristics come in. Hugging Face's model Inference API imposes two limits on the scalability of my system:</p>
<ol>
<li><p>A 10k tokens (characters) per hour rate limit, which is about 300 queries.</p>
</li>
<li><p>A 30k tokens per month quota for free-tier accounts, which is about 900 queries.</p>
</li>
</ol>
<blockquote>
<p>Wonder how I get the numbers? Fun fact: 1) <a target="_blank" href="https://capitalizemytitle.com/character-count/10000-characters/">10k characters is between 1430 and 2500 words</a>. We will take 2100 since Discord messages usually use simple, short words. 2) T<a target="_blank" href="https://crushhapp.com/texting-tidbits/the-average-text-message-length-is-around-7-words">he average length of a text message is 7 words</a>. 2100 / 7 = 300 messages</p>
</blockquote>
<p>After processing these numbers, the fact that I hit the per-hour rate limit during the first hour of my bot debut is quite a remarkable feat. People are clearly hyped about my witty chatbot. 🥳</p>
<p>Suppose the hype recedes and life goes on, consider now a hypothetical scenario where 20 users (2% of the 1000+ on the server) regularly chat with my bot, each for 25 lines, in the two hours following dinner. This produces a total of 500 queries in two hours (or 250 per hour), meaning that my bot is safe from the per-hour rate limit of 300.</p>
<p>However, in a month, 500 * 30 = 15,000 queries, 15 times more than my quota of 900. If my bot is indeed this popular, I would need to switch to a higher-tier subscription plan to ensure that it remains available.</p>
<h2 id="heading-from-tutorial-code-to-production-code">From Tutorial Code to Production Code</h2>
<p>Compared to my tutorial code which strives to be simple, readable, and educative, my production code is longer, more complicated, but also more robust.</p>
<h1 id="heading-what-makes-a-great-side-project">What Makes a Great Side Project</h1>
<p>Having emerged from Deployment Hell, like my bot, I'm more resilient than ever and have gained new insights about the principles and challenges in real-world software engineering.</p>
<p>As a final takeaway, I reflect on what makes my Discord AI chatbot this popular. (On June 3rd, a day when it's up 24 hours, it had already busted the monthly 30k quota on both my account and one I borrowed from a friend, totaling 2,000+ messages. 🤓)</p>
<p>I have completed and polished various side projects that received positive feedback, but none of them were as half popular as this one. In retrospect, it's not too hard to see why.</p>
<p>Among projects that I'm most proud of, I built <a target="_blank" href="https://github.com/RuolinZheng08/renpy-chess">a chess engine</a> and <a target="_blank" href="https://github.com/RuolinZheng08/renpy-rhythm">a rhythm game engine</a> for <a target="_blank" href="https://renpy.org/">the Ren'Py Visual Novel (VN) game development engine.</a> Both are rated 5-star on <a target="_blank" href="https://itch.io/">itch.io</a>, a popular platform for publishing indie games.</p>
<p>These projects, however, are open-source engines intended for developers to integrate into their VN games more than standalone playables that can entertain players for hours.</p>
<p>In comparison, my Discord AI chatbot manages to capture each of the following elements that distinguishes a <strong>great</strong> side project from a good one:</p>
<ul>
<li><p><strong>Audience:</strong> I'm fortunate to have this friendly server with 1000+ users who are open to experimenting with the bot and provide me with helpful feedback.</p>
</li>
<li><p><strong>Accessibility:</strong> For people to enjoy my chabot, there is nothing special they need to add to their routine - not even opening up a new web app - they just log into Discord as usual, and voilà, the bot is here to chat!</p>
</li>
<li><p><strong>Interactivity:</strong> Without interactive components, even the most visually-astonishing game will fail to retain players' attention. Nothing to worry about for my chatbot though: Like a loyal friend, it always has something to quip about whenever you need a good chat.</p>
</li>
</ul>
<p>If you'd like to learn more about my methodology for working on side projects, check out my previous blog post:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/how-i-built-my-one-person-open-source-project/">https://www.freecodecamp.org/news/how-i-built-my-one-person-open-source-project/</a></div>
<p> </p>
<p>Also check out my chatbot tutorial!</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/discord-ai-chatbot/">https://www.freecodecamp.org/news/discord-ai-chatbot/</a></div>
<p> </p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/UBwvFuTC1ZE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>You can also try this out in JavaScript:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/XR6JFRLxe5A" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ JavaScript Discord Bot Tutorial – Code a Discord Bot And Host it for Free ]]>
                </title>
                <description>
                    <![CDATA[ This tutorial will show you how to use JavaScript and Node.js to build your own Discord bot completely in the cloud. You do not need to install anything on your computer, and you do not need to pay anything to host your bot. We are going to use a num... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/</link>
                <guid isPermaLink="false">66b20180a8b92c932923642b</guid>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Mon, 08 Mar 2021 14:45:50 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/discordjs.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This tutorial will show you how to use JavaScript and Node.js to build your own Discord bot completely in the cloud.</p>
<p>You do not need to install anything on your computer, and you do not need to pay anything to host your bot.</p>
<p>We are going to use a number of tools, including the Discord API, Node.js libraries, and a cloud computing platform called <a target="_blank" href="https://www.repl.it">Repl.it</a>.</p>
<p>If you'd rather code your discord bot using Python instead of JavaScript, <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">read this tutorial instead</a>.</p>
<p>There is also a video version of this written tutorial. The video is embedded below and the written version is after the video.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/7rU_KyudGBY" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-create-a-discord-bot-account">How to Create a Discord Bot Account</h2>
<p>In order to work with the Node.js library and the Discord API, we must first create a Discord Bot account.</p>
<p>Here are the step to creating a Discord Bot account.</p>
<ol>
<li><p>Make sure you’re logged on to the <a target="_blank" href="https://discord.com">Discord website</a>.</p>
</li>
<li><p>Navigate to the <a target="_blank" href="https://discord.com/developers/applications">application page</a>.</p>
</li>
<li><p>Click on the “New Application” button.</p>
</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-117.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Give the application a name and click “Create”.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-118.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="5">
<li>Go to the “Bot” tab and then click “Add Bot”. You will have to confirm by clicking "Yes, do it!"</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-119.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Keep the default settings for <strong>Public Bot</strong> (checked) and <strong>Require OAuth2 Code Grant</strong> (unchecked).</p>
<p>Your bot has been created. The next step is to copy the token.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-122.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This token is your bot's password so don't share it with anybody. It could allow someone to log in to your bot and do all sorts of bad things.</p>
<p>You can regenerate the token if it accidentally gets shared.</p>
<h2 id="heading-how-to-invite-your-bot-to-join-a-server">How to Invite Your Bot to Join a Server</h2>
<p>Now you have to get your Bot User into a server. To do this, you should create an invite URL for it.</p>
<p>Go to the "OAuth2" tab. Then select "bot" under the "scopes" section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-123.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now choose the permissions you want for the bot. Our bot is going to mainly use text messages so we don't need a lot of the permissions. You may need more depending on what you want your bot to do. Be careful with the "Administrator" permission.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/11/image-124.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After selecting the appropriate permissions, click the 'copy' button above the permissions. That will copy a URL which can be used to add the bot to a server.</p>
<p>Paste the URL into your browser, choose a server to invite the bot to, and click “Authorize”.</p>
<p>To add the bot, your account needs "Manage Server" permissions.</p>
<p>Now that you've created the bot user, we'll start writing the Python code for the bot.</p>
<h2 id="heading-how-to-code-a-basic-discord-bot-with-the-discordjs-library">How to Code a Basic Discord Bot with the discord.js Library</h2>
<p>We'll be using the discord.js Node library to write the code for the bot. discord.js is an API wrapper for Discord that makes it easier to create a Discord bot in Node.js / JavaScript.</p>
<h3 id="heading-how-to-create-a-repl-and-install-discordjs">How to Create a Repl and Install discord.js</h3>
<p>You can develop the bot on your local computer with any code editor. However, in this tutorial, we'll be using Repl.it because it will make it simpler for anyone to follow along. Repl.it is an online IDE that you can use in your web browser.</p>
<p>Start by going to <a target="_blank" href="https://repl.it">Repl.it</a>. Create a new Repl and choose "Node.js" as the language. This means the programming language will be JavaScript.</p>
<p>To use the discord.js library, just add <code>const Discord = require("discord.js");</code> at the top of <code>main.js</code>. Repl.it will automatically install this dependency when you press the "run" button.</p>
<h3 id="heading-how-to-set-up-discord-events-for-your-bot">How to Set Up Discord Events for Your Bot</h3>
<p>discord.js revolves around the concept of events. An event is something you listen to and then respond to. For example, when a message happens, you will receive an event about it that you can respond to.</p>
<p>Let’s make a bot that replies to a specific message. This simple bot code is taken directly from <a target="_blank" href="https://discord.js.org/#/docs/main/stable/general/welcome">the discord.js documentation</a>. We will be adding more features to the bot later.</p>
<p>Add this code to main.js. I'll explain what all this code does shortly.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Discord = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord.js"</span>)
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Discord.Client()

client.on(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Logged in as <span class="hljs-subst">${client.user.tag}</span>!`</span>)
})

client.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (msg.content === <span class="hljs-string">"ping"</span>) {
    msg.reply(<span class="hljs-string">"pong"</span>);
  }
})

client.login(process.env.TOKEN)
</code></pre>
<p>When you created your bot user on Discord, you copied a token. Now we are going to create a <code>.env</code> file to store the token. </p>
<p><code>.env</code> files are used for declaring environment variables. On Repl.it, most files you create are visible to anyone but <code>.env</code> files are only visible to you.  Other people viewing a public repl will not be able to see the contents of the <code>.env</code> file.</p>
<p>So if you are developing on Repl.it, only include private information like tokens or keys in a <code>.env</code> file.</p>
<p>Click the "Add file" button and create a file named <code>.env</code>.</p>
<p>Inside the file add the following line, including your actual token you copied previously:</p>
<pre><code>TOKEN=[paste token here]
</code></pre><p>Now let's go over what each line of code is doing in your Discord bot code.</p>
<p>The first line imports the discord.js library.  Next, we create an instance of a <a target="_blank" href="https://discordpy.readthedocs.io/en/latest/api.html#discord.Client"><code>Client</code></a>. This is the connection to Discord.</p>
<p>The <code>client.on()</code> is used to check for events.  It accepts an event name, and then a callback function to be called when the event takes place. In this code, the <code>ready</code> event is called when the bot is ready to start being used. Then, when the Discord server has a new message, the <code>message</code> event is called. </p>
<p>The code checks if the <a target="_blank" href="https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.content"><code>msg.content</code></a> equals <code>'ping'</code>. If so, then the bot replies with <code>'pong'</code> to the channel.</p>
<p>Now that the bot is set up, the final line runs the bot with the login token. It gets the token from out <code>.env</code> file.</p>
<p>We have the code for the bot so now we just have to run it.</p>
<h3 id="heading-how-to-run-the-bot">How to Run the Bot</h3>
<p>Now click run button on the top to run your bot in repl.it.</p>
<p>Now go to your Discord room and type "ping". Your bot should return "pong".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-improve-the-bot">How to Improve the Bot</h2>
<p>Now that we have a basic bot working, we'll improve it. It is called "Encourage Bot" for a reason. </p>
<p>This bot will respond with a message of encouragement whenever someone sends a message containing a sad or depressing word.</p>
<p>Anyone will be able to add encouraging messages for the bot to use and the user-submitted messages will be stored in the Repl.it database.</p>
<p>The bot will also return a random inspirational quote from an API when someone types the message "$inspire" into the chat.</p>
<p>We'll start with adding the "$inspire" feature.</p>
<h3 id="heading-how-to-add-inspirational-quotes-to-the-bot">How to Add Inspirational Quotes to the Bot</h3>
<p>We will get inspirational quotes from an API called zenquotes.io. We need to import the node-fetch module, add a <code>getQuote()</code> function, and update our bot code to call the function. </p>
<p>Here is the updated code. After the code, I'll explain the new parts.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Discord = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord.js"</span>)
<span class="hljs-keyword">const</span> fetch = <span class="hljs-built_in">require</span>(<span class="hljs-string">"node-fetch"</span>)
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Discord.Client()

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getQuote</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> fetch(<span class="hljs-string">"https://zenquotes.io/api/random"</span>)
    .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> res.json()
      })
    .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> data[<span class="hljs-number">0</span>][<span class="hljs-string">"q"</span>] + <span class="hljs-string">" -"</span> + data[<span class="hljs-number">0</span>][<span class="hljs-string">"a"</span>]
    })
}

client.on(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Logged in as <span class="hljs-subst">${client.user.tag}</span>!`</span>)
})

client.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (msg.author.bot) <span class="hljs-keyword">return</span>

  <span class="hljs-keyword">if</span> (msg.content === <span class="hljs-string">"$inspire"</span>) {
    getQuote().then(<span class="hljs-function"><span class="hljs-params">quote</span> =&gt;</span> msg.channel.send(quote))
  }
})

client.login(process.env.TOKEN)
</code></pre>
<p>We now have to import the <code>node-fetch</code> module. This module allows our code to make an HTTP request to get data from the API. </p>
<p>The <code>getQuote()</code> function is pretty straightforward. First, it uses the node-fetch module to request data from the API URL. The API returns a random inspirational quote. This function could easily be rewritten to get quotes from a different API, if the current one stops working.</p>
<p>Then the function converts the response from the API to JSON and creates a string to return. Through trial and error I figured out how to get the quote from the JSON into the string format I wanted. The quote is returned from the function as a string.</p>
<p>The final part updated in the code is toward the end. Previously it looked for the message "ping". Now it looks for "$inspire". Instead of returning "pong", it gets the quote with <code>getQuote()</code> and returns the quote. We use <code>msg.channel.send()</code> to send the message to the channel. Also, the code checks if the message comes from the bot itself and if it does, it leaves the function so it does not do anything.</p>
<p>At this point you can run your code and try it out.</p>
<h2 id="heading-how-to-add-encouraging-messages-to-the-bot">How to Add Encouraging Messages to the Bot</h2>
<p>Now we will implement the feature where the bot responds with encouraging messages when a user posts a message with a sad word.</p>
<h3 id="heading-how-to-add-sad-words-to-the-bot">How to Add Sad Words to the Bot</h3>
<p>First we need to create an array that contains the sad words that the bot will respond to.</p>
<p>Add the following line after the <code>client</code> variable is created:</p>
<p><code>sadWords = ["sad", "depressed", "unhappy", "angry", "miserable"]</code></p>
<p>Feel free to add more words to the list.</p>
<h3 id="heading-how-to-add-encouraging-messages-to-the-bot-1">How to Add Encouraging Messages to the Bot</h3>
<p>Now we'll add an array of encouraging messages that the bot will respond with.</p>
<p>Add the following array after the <code>sadWords</code> list you created:</p>
<pre><code class="lang-python">encouragements = [
  <span class="hljs-string">"Cheer up!"</span>,
  <span class="hljs-string">"Hang in there."</span>,
  <span class="hljs-string">"You are a great person / bot!"</span>
]
</code></pre>
<p>Like before, feel free to add more phrases of your choice to the array . I'm just using three items for now because later we'll add the ability for users to add more encouraging phrases for the bot to use.</p>
<h3 id="heading-how-to-respond-to-messages">How to Respond to Messages</h3>
<p>Now we need to update our bot to use the two lists we created. </p>
<p>Now we will update the <code>message</code> function to check all messages to see if they contain a word from the <code>sadWords</code> list. If a sad word is found, the bot will send a random message of encouragement. </p>
<p>Here is the updated code:</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (msg.content === <span class="hljs-string">"$inspire"</span>) {
    getQuote().then(<span class="hljs-function"><span class="hljs-params">quote</span> =&gt;</span> msg.channel.send(quote))
  }

  <span class="hljs-keyword">if</span> (sadWords.some(<span class="hljs-function"><span class="hljs-params">word</span> =&gt;</span> msg.content.includes(word))) {
    <span class="hljs-keyword">const</span> encouragement = encouragements[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * encouragements.length)]
    msg.reply(encouragement)
  }

})
</code></pre>
<p>This is a good time to test the bot. You know enough now to create your own bot. But next you'll learn how to implement more advanced features and store data using the Repl.it database.</p>
<h3 id="heading-how-to-enable-user-submitted-messages">How to Enable User-submitted Messages</h3>
<p>The bot is completely functional, but now let's make it possible to update the bot right from Discord. A user should be able to add more encouraging messages for the bot to use when it detects a sad word.</p>
<p>We are going to use Repl.it's built-in database to store user-submitted messages. This database is a key-value store that’s built into every repl.</p>
<p>At the top of the code, under the other import statements, add:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Database = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@replit/database"</span>)
<span class="hljs-keyword">const</span> db = <span class="hljs-keyword">new</span> Database()
</code></pre>
<p>This will allow us to use the Repl.it database. When you run the code, Repl.it should install the database module automatically. If for some reason it doesn't, you may have to go into the Shell tab (not the Console) and type "npm install @replit/database".</p>
<p>After where the <code>encouragements</code> array is created, insert the following code to add the encouragements to the database if needed:</p>
<pre><code class="lang-javascript">db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (!encouragements || encouragements.length &lt; <span class="hljs-number">1</span>) {
    db.set(<span class="hljs-string">"encouragements"</span>, starterEncouragements)
  }  
})
</code></pre>
<p>Also, rename the <code>encouragements</code> array toward the top to <code>starterEncouragements</code>.</p>
<p>Users will be able to add custom encouraging messages for the bot to use directly from the Discord chat. Before we add new commands for the bot, let's create two helper functions that will add custom messages to the database and delete them.</p>
<p>Add the following code after the <code>getQuote()</code> function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateEncouragements</span>(<span class="hljs-params">encouragingMessage</span>) </span>{
  db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
    encouragements.push([encouragingMessage])
    db.set(<span class="hljs-string">"encouragements"</span>, encouragements)
  })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deleteEncouragment</span>(<span class="hljs-params">index</span>) </span>{
  db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (encouragements.length &gt; index) {
      encouragements.splice(index, <span class="hljs-number">1</span>)
      db.set(<span class="hljs-string">"encouragements"</span>, encouragements)
    }
  })
}
</code></pre>
<p>The <code>updateEncouragements()</code> function accepts an encouraging message as an argument.</p>
<p>First it gets the "encouragements" from the database. Then, it adds the new encouragement to the array, and stores the updated array back in the database under the "encouragements" key. </p>
<p>The <code>deleteEncouragement()</code> function accepts an index as an argument.</p>
<p>It gets the list of encouragements from the database stored under the "encouragements" key. If the length is more than the index, then the list item at that index is deleted. Finally, the updated list is stored back in the database under the "encouragements" key.</p>
<p>Here is the updated code for the <code>message</code> function. After the code, I'll explain the new sections.</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (msg.content === <span class="hljs-string">"$inspire"</span>) {
    getQuote().then(<span class="hljs-function"><span class="hljs-params">quote</span> =&gt;</span> msg.channel.send(quote))
  }


  <span class="hljs-keyword">if</span> (sadWords.some(<span class="hljs-function"><span class="hljs-params">word</span> =&gt;</span> msg.content.includes(word))) {
    db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
      <span class="hljs-keyword">const</span> encouragement = encouragements[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * encouragements.length)]
      msg.reply(encouragement)
    })
  }

  <span class="hljs-keyword">if</span> (msg.content.startsWith(<span class="hljs-string">"$new"</span>)) {
    encouragingMessage = msg.content.split(<span class="hljs-string">"$new "</span>)[<span class="hljs-number">1</span>]
    updateEncouragements(encouragingMessage)
    msg.channel.send(<span class="hljs-string">"New encouraging message added."</span>)
  }

  <span class="hljs-keyword">if</span> (msg.content.startsWith(<span class="hljs-string">"$del"</span>)) {
    index = <span class="hljs-built_in">parseInt</span>(msg.content.split(<span class="hljs-string">"$del "</span>)[<span class="hljs-number">1</span>])
    deleteEncouragment(index)
    msg.channel.send(<span class="hljs-string">"Encouraging message deleted."</span>)
  }
})
</code></pre>
<p>The sad words section has been updated to use the encouraging messages from the database so user submitted messages can be used.</p>
<p>The next new section of code is used to add a new user-submitted message to the database. If a Discord message starts with "$new", then the text after "$new" will be used as a new encouraging message. </p>
<p>The code <code>msg.content.split('$new ')[1]</code> splits off the message from the "$new" command and stores the message in a variable. In that line of code, take note of the space in <code>'$new '</code>. We want everything <em>after</em> the space.</p>
<p>We call the <code>updateEncouragements</code> helper function with the new message, and then the bot sends a message to the discord chat confirming that the message was added.</p>
<p>The third new section (at the end of the code above) checks if a new Discord message starts with "$del". This is the command to delete an item from the "encouragements" list in the database.</p>
<p>The index is split off from the Discord message starting with "$del". Then, the <code>deleteEncouragement()</code> function is called passing in the index to delete. The updated list of encouragements is loaded into the <code>encouragements</code> variable, and then the bot sends a message to Discord with the current list.</p>
<h2 id="heading-final-bot-features">Final Bot Features</h2>
<p>The bot should work so this is a good time to test it. We will now add a few final features.</p>
<p>We will add the ability to get a list of user-submitted messages right from Discord and we will add the ability to turn off and on whether the bot responds to sad words.</p>
<p>I will give you the full final code of the program, and then I'll discuss the updates below the code.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Discord = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord.js"</span>)
<span class="hljs-keyword">const</span> fetch = <span class="hljs-built_in">require</span>(<span class="hljs-string">"node-fetch"</span>)
<span class="hljs-keyword">const</span> Database = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@replit/database"</span>)

<span class="hljs-keyword">const</span> db = <span class="hljs-keyword">new</span> Database()
<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Discord.Client()

<span class="hljs-keyword">const</span> sadWords = [<span class="hljs-string">"sad"</span>, <span class="hljs-string">"depressed"</span>, <span class="hljs-string">"unhappy"</span>, <span class="hljs-string">"angry"</span>, <span class="hljs-string">"miserable"</span>]

<span class="hljs-keyword">const</span> starterEncouragements = [
  <span class="hljs-string">"Cheer up!"</span>,
  <span class="hljs-string">"Hang in there."</span>,
  <span class="hljs-string">"You are a great person / bot!"</span>
]

db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(encouragements)
  <span class="hljs-keyword">if</span> (!encouragements || encouragements.length &lt; <span class="hljs-number">1</span>) {
    db.set(<span class="hljs-string">"encouragements"</span>, starterEncouragements)
  }  
})

db.get(<span class="hljs-string">"responding"</span>).then(<span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (value == <span class="hljs-literal">null</span>) {
    db.set(<span class="hljs-string">"responding"</span>, <span class="hljs-literal">true</span>)
  }  
})

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getQuote</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> fetch(<span class="hljs-string">"https://zenquotes.io/api/random"</span>)
    .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> res.json()
      })
    .then(<span class="hljs-function"><span class="hljs-params">data</span> =&gt;</span> {
      <span class="hljs-keyword">return</span> data[<span class="hljs-number">0</span>][<span class="hljs-string">"q"</span>] + <span class="hljs-string">" -"</span> + data[<span class="hljs-number">0</span>][<span class="hljs-string">"a"</span>]
    })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">updateEncouragements</span>(<span class="hljs-params">encouragingMessage</span>) </span>{
  db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
    encouragements.push([encouragingMessage])
    db.set(<span class="hljs-string">"encouragements"</span>, encouragements)
  })
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">deleteEncouragment</span>(<span class="hljs-params">index</span>) </span>{
  db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (encouragements.length &gt; index) {
      encouragements.splice(index, <span class="hljs-number">1</span>)
      db.set(<span class="hljs-string">"encouragements"</span>, encouragements)
    }
  })
}

client.on(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Logged in as <span class="hljs-subst">${client.user.tag}</span>!`</span>)
})

client.on(<span class="hljs-string">"message"</span>, <span class="hljs-function"><span class="hljs-params">msg</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (msg.content === <span class="hljs-string">"$inspire"</span>) {
    getQuote().then(<span class="hljs-function"><span class="hljs-params">quote</span> =&gt;</span> msg.channel.send(quote))
  }

  db.get(<span class="hljs-string">"responding"</span>).then(<span class="hljs-function"><span class="hljs-params">responding</span> =&gt;</span> {
    <span class="hljs-keyword">if</span> (responding &amp;&amp; sadWords.some(<span class="hljs-function"><span class="hljs-params">word</span> =&gt;</span> msg.content.includes(word))) {
      db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
        <span class="hljs-keyword">const</span> encouragement = encouragements[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * encouragements.length)]
        msg.reply(encouragement)
      })
    }
  })

  <span class="hljs-keyword">if</span> (msg.content.startsWith(<span class="hljs-string">"$new"</span>)) {
    encouragingMessage = msg.content.split(<span class="hljs-string">"$new "</span>)[<span class="hljs-number">1</span>]
    updateEncouragements(encouragingMessage)
    msg.channel.send(<span class="hljs-string">"New encouraging message added."</span>)
  }

  <span class="hljs-keyword">if</span> (msg.content.startsWith(<span class="hljs-string">"$del"</span>)) {
    index = <span class="hljs-built_in">parseInt</span>(msg.content.split(<span class="hljs-string">"$del "</span>)[<span class="hljs-number">1</span>])
    deleteEncouragment(index)
    msg.channel.send(<span class="hljs-string">"Encouraging message deleted."</span>)
  }

  <span class="hljs-keyword">if</span> (msg.content.startsWith(<span class="hljs-string">"$list"</span>)) {
    db.get(<span class="hljs-string">"encouragements"</span>).then(<span class="hljs-function"><span class="hljs-params">encouragements</span> =&gt;</span> {
      msg.channel.send(encouragements)
    })
  }

  <span class="hljs-keyword">if</span> (msg.content.startsWith(<span class="hljs-string">"$responding"</span>)) {
    value = msg.content.split(<span class="hljs-string">"$responding "</span>)[<span class="hljs-number">1</span>]

    <span class="hljs-keyword">if</span> (value.toLowerCase() == <span class="hljs-string">"true"</span>) {
      db.set(<span class="hljs-string">"responding"</span>, <span class="hljs-literal">true</span>)
      msg.channel.send(<span class="hljs-string">"Responding is on."</span>)
    } <span class="hljs-keyword">else</span> {
      db.set(<span class="hljs-string">"responding"</span>, <span class="hljs-literal">false</span>)
      msg.channel.send(<span class="hljs-string">"Responding is off."</span>)
    }
  }
})

client.login(process.env.TOKEN)
</code></pre>
<p>The first section added to the code is right under the <code>starterEncouragements</code> list:</p>
<pre><code class="lang-javascript">db.get(<span class="hljs-string">"responding"</span>).then(<span class="hljs-function"><span class="hljs-params">value</span> =&gt;</span> {
  <span class="hljs-keyword">if</span> (value == <span class="hljs-literal">null</span>) {
    db.set(<span class="hljs-string">"responding"</span>, <span class="hljs-literal">true</span>)
  }  
})
</code></pre>
<p>We create a new key in the database called "responding" and set it to "true". We'll use this to determine if the bot should respond to sad words or not. Since the database is saved even after the program stops running, we only create the new key if it doesn't already exist.</p>
<p>The next new part of the code is in the section that responds to sad words is now inside this if statement. The bot will only respond to sad words if <code>db.get("responding") = true</code>. The ability to update this value comes after this next section.</p>
<p>Next, after the code to make the bot respond to the "$del" command, there is new code to respond to the "$list" command when sent as a Discord message. </p>
<p>The bot sends the list of encouragements as a Discord message.</p>
<p>The final new section comes next. This code makes the bot respond to the "$responding" command. This command takes an argument of either "true" or "false". Here is a usage example: "$responding true". </p>
<p>The code first pulls off the argument with <code>value = msg.content.split("$responding ")[1]</code> (like before, note the space in <code>"$responding "</code>). Then there is an if/else statement that appropriately sets the "responding" key in the database and sends a notification message back to Discord. If the argument is anything but "true", the code assumes "false".</p>
<p>The code for the bot is complete! You can now run the bot and try it out. But there is one more important step that we will discuss next.</p>
<h2 id="heading-how-to-set-up-the-bot-to-run-continuously">How to Set Up the Bot to Run Continuously</h2>
<p>If you run your bot in repl.it and then close the tab it is running in, your bot will stop running.</p>
<p>But there are two ways you can keep your bot running continuously, even after you close your web bowser.</p>
<p>The first way and simplest way is to sign up for paid plan in Repl.it. Their cheapest paid plan is called the Hacker Plan and it includes five always-on Repls. </p>
<p>You can get three months free using this link (limited to first 1000 people):  https://repl.it/claim?code=tryalwayson2103</p>
<p>Once you have signed up for that plan, open your Repl and click the name at the top. Then select the "Always On" option.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-36.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>There is another way to keep your code running even on the free tier but it is a little more complicated. Repl.it will continue running a web server even after the tab is closed. But even a web server will only run for up to an hour without any use.</p>
<p>Here is what the repl.it docs say:</p>
<blockquote>
<p>Once deployed, the server will continue to run in the background, even after you close the browser tab. The server will stay awake and active until an hour after its last request, after which it will enter a sleeping stage. Sleeping repls will be woken up as soon as it receives another request; there is no need to re-run the repl. However, if you make changes to your server, you will need to restart the repl in order to see those changes reflected in the live version.</p>
</blockquote>
<p>To keep the bot running continuously, we'll use another free service called Uptime Robot at <a target="_blank" href="https://uptimerobot.com/">https://uptimerobot.com/</a>.</p>
<p>Uptime Robot can be set up to ping the bot's web server on repl.it every 5 minutes. With constant pings, the bot will never enter the sleeping stage and will just keep running.</p>
<p>So we have to do two more things to get our bot to run continuously:</p>
<ol>
<li>create a web server in repl.it and</li>
<li>set up Uptime Robot to continuously ping the web server.</li>
</ol>
<h3 id="heading-how-to-create-a-web-server-in-replit">How to Create a Web Server in repl.it</h3>
<p>Creating a web server is simpler than you may think. </p>
<p>To do it, create a new file in your project called <code>server.js</code>.</p>
<p>Then add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">"express"</span>)

<span class="hljs-keyword">const</span> server = express()

server.all(<span class="hljs-string">"/"</span>, <span class="hljs-function">(<span class="hljs-params">req, res</span>) =&gt;</span> {
  res.send(<span class="hljs-string">"Bot is running!"</span>)
})

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">keepAlive</span>(<span class="hljs-params"></span>) </span>{
  server.listen(<span class="hljs-number">3000</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server is ready."</span>)
  })
}

<span class="hljs-built_in">module</span>.exports = keepAlive
</code></pre>
<p>In this code, we use express to start a web server. The server returns "Bot is running!" to anyone who visits it. The server will run on a separate thread from our bot. We won't discuss everything here since the rest is not really relevant to our bot.</p>
<p>Now we just need the bot to run this web server.</p>
<p>Add the following line toward the top of <code>index.js</code>  to import the server.</p>
<pre><code class="lang-python">const keepAlive = require(<span class="hljs-string">"./server"</span>)
</code></pre>
<p>To start the web server when <code>index.js</code> is run, add the following line as the second-to-last line, right before the bot runs.</p>
<p><code>keepAlive()</code></p>
<p>When you run the bot on repl.it after adding this code, a new web server window will open up. There is a URL shown for the web server. Copy the URL so you can use it in the next section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-set-up-uptime-robot">How to Set Up Uptime Robot</h3>
<p>Now we need to set up Uptime Robot to ping the web server every five minutes. This will cause the bot to run continuously.</p>
<p>Create a free account on <a target="_blank" href="https://uptimerobot.com/">https://uptimerobot.com/</a>.</p>
<p>Once you are logged in to your account, click "Add New Monitor".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-21.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For the new monitor, select "HTTP(s)" as the Monitor Type and name it whatever you like. Then, paste in the URL of your web server from repl.it. Finally, click "Create Monitor".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-22.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We're done! Now the bot will run continuously so people can always interact with it on Repl.it.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You now know how to create a Discord bot with JavaScript, and run it continuously in the cloud. </p>
<p>There are a lot of other things that the discord.js library can do. So if you want to give a Discord bot even more features, your next step is to check out <a target="_blank" href="https://discord.js.org/#/docs/main/stable/general/welcome">the docs for discord.js.</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Python Discord Bot Tutorial – Code a Discord Bot And Host it for Free ]]>
                </title>
                <description>
                    <![CDATA[ This tutorial will show you how to build your own Discord bot completely in the cloud. You do not need to install anything on your computer, and you do not need to pay anything to host your bot. We are going to use a number of tools, including the Di... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-discord-bot-with-python/</link>
                <guid isPermaLink="false">66b2018325ef0bb2c5a51706</guid>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ youtube ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Beau Carnes ]]>
                </dc:creator>
                <pubDate>Tue, 15 Dec 2020 16:25:09 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/discordbot.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>This tutorial will show you how to build your own Discord bot completely in the cloud.</p>
<p>You do not need to install anything on your computer, and you do not need to pay anything to host your bot.</p>
<p>We are going to use a number of tools, including the Discord API, Python libraries, and a cloud computing platform called <a target="_blank" href="https://www.repl.it">Repl.it</a>.</p>
<p>There is also a video version of this written tutorial. The video is embedded below and the written version is after the video.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/SPTfmiYiuok" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-how-to-create-a-discord-bot-account">How to Create a Discord Bot Account</h2>
<p>In order to work with the Python library and the Discord API, we must first create a Discord Bot account.</p>
<p>Here are the step to creating a Discord Bot account.</p>
<ol>
<li><p>Make sure you’re logged on to the <a target="_blank" href="https://discord.com">Discord website</a>.</p>
</li>
<li><p>Navigate to the <a target="_blank" href="https://discord.com/developers/applications">application page</a>.</p>
</li>
<li><p>Click on the “New Application” button.</p>
</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-117.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="4">
<li>Give the application a name and click “Create”.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-118.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ol start="5">
<li>Go to the “Bot” tab and then click “Add Bot”. You will have to confirm by clicking "Yes, do it!"</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-119.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Keep the default settings for <strong>Public Bot</strong> (checked) and <strong>Require OAuth2 Code Grant</strong> (unchecked).</p>
<p>Your bot has been created. The next step is to copy the token.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-122.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This token is your bot's password so don't share it with anybody. It could allow someone to log in to your bot and do all sorts of bad things.</p>
<p>You can regenerate the token if it accidentally gets shared.</p>
<h2 id="heading-how-to-invite-your-bot-to-join-a-server">How to Invite Your Bot to Join a Server</h2>
<p>Now you have to get your Bot User into a server. To do this, you should create an invite URL for it.</p>
<p>Go to the "OAuth2" tab. Then select "bot" under the "scopes" section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-123.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now choose the permissions you want for the bot. Our bot is going to mainly use text messages so we don't need a lot of the permissions. You may need more depending on what you want your bot to do. Be careful with the "Administrator" permission.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-124.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After selecting the appropriate permissions, click the 'copy' button above the permissions. That will copy a URL which can be used to add the bot to a server.</p>
<p>Paste the URL into your browser, choose a server to invite the bot to, and click “Authorize”.</p>
<p>To add the bot, your account needs "Manage Server" permissions.</p>
<p>Now that you've created the bot user, we'll start writing the Python code for the bot.</p>
<h2 id="heading-how-to-code-a-basic-discord-bot-with-the-discordpy-library">How to Code a Basic Discord Bot with the discord.py Library</h2>
<p>We'll be using the discord.py Python library to write the code for the bot. discord.py is an API wrapper for Discord that makes it easier to create a Discord bot in Python.</p>
<h3 id="heading-how-to-create-a-repl-and-install-discordpy">How to Create a Repl and Install discord.py</h3>
<p>You can develop the bot on your local computer with any code editor. However, in this tutorial, we'll be using Repl.it because it will make it simpler for anyone to follow along. Repl.it is an online IDE that you can use in your web browser.</p>
<p>Start by going to <a target="_blank" href="https://repl.it">Repl.it</a>. Create a new Repl and choose "Python" as the language.</p>
<p>To use the discord.py library, just write <code>import discord</code> at the top of <code>main.py</code>. Repl.it will automatically install this dependency when you press the "run" button.</p>
<p>If you prefer to code the bot locally, you can use this command on MacOS to install discord.py:</p>
<p><code>python3 -m pip install -U discord.py</code></p>
<p>You may have to use <code>pip3</code> instead of <code>pip</code>.</p>
<p>If you are using Windows, then you should use the following line instead:</p>
<p><code>py -3 -m pip install -U discord.py</code></p>
<h3 id="heading-how-to-set-up-discord-events-for-your-bot">How to Set Up Discord Events for Your Bot</h3>
<p>discord.py revolves around the concept of events. An event is something you listen to and then respond to. For example, when a message happens, you will receive an event about it that you can respond to.</p>
<p>Let’s make a bot that replies to a specific message. This simple bot code, along with the code explanation, is taken from <a target="_blank" href="https://discordpy.readthedocs.io/en/latest/quickstart.html#a-minimal-bot">the discord.py documentation</a>. We will be adding more features to the bot later.</p>
<p>Add this code to main.py. (You can name the file something else if you like, just not discord.py.) I'll explain what all this code does shortly.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> discord
<span class="hljs-keyword">import</span> os

client = discord.Client()

<span class="hljs-meta">@client.event</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_ready</span>():</span>
    print(<span class="hljs-string">'We have logged in as {0.user}'</span>.format(client))

<span class="hljs-meta">@client.event</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_message</span>(<span class="hljs-params">message</span>):</span>
    <span class="hljs-keyword">if</span> message.author == client.user:
        <span class="hljs-keyword">return</span>

    <span class="hljs-keyword">if</span> message.content.startswith(<span class="hljs-string">'$hello'</span>):
        <span class="hljs-keyword">await</span> message.channel.send(<span class="hljs-string">'Hello!'</span>)

client.run(os.getenv(<span class="hljs-string">'TOKEN'</span>))
</code></pre>
<p>When you created your bot user on Discord, you copied a token. Now we are going to create a <code>.env</code> file to store the token. If you are running your code locally, you don't need the <code>.env</code> file. Just replace <code>os.getenv('TOKEN')</code> with the token.</p>
<p><code>.env</code> files are used for declaring environment variables. On Repl.it, most files you create are visible to anyone but <code>.env</code> files are only visible to you.  Other people viewing a public repl will not be able to see the contents of the <code>.env</code> file.</p>
<p>So if you are developing on Repl.it, only include private information like tokens or keys in a <code>.env</code> file.</p>
<p>Click the "Add file" button and create a file named <code>.env</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-19-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Inside the file add the following line, including your actual token you copied previously:</p>
<pre><code>TOKEN=[paste token here]
</code></pre><p>Now let's go over what each line of code is doing in your Discord bot code.</p>
<ol>
<li>The first line imports the discord.py library.</li>
<li>The second line imports the os library, but this is only used for getting the <code>TOKEN</code> variable from the <code>.env</code> file. If you are not using a <code>.env</code> file, you do not need this line.</li>
<li>Next, we create an instance of a <a target="_blank" href="https://discordpy.readthedocs.io/en/latest/api.html#discord.Client"><code>Client</code></a>. This is the connection to Discord.</li>
<li>The <code>[@client.event()](https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.event)</code> decorator is used to register an event. This is an asynchronous library, so things are done with callbacks. A callback is a function that is called when something else happens. In this code, the <code>[on_ready()](https://discordpy.readthedocs.io/en/latest/api.html#discord.on_ready)</code> event is called when the bot is ready to start being used. Then, when the bot receives a message, the <code>[on_message()](https://discordpy.readthedocs.io/en/latest/api.html#discord.on_message)</code> event is called.</li>
<li>The <code>[on_message()](https://discordpy.readthedocs.io/en/latest/api.html#discord.on_message)</code> event triggers each time a message is received but we don't want it to do anything if the message is from ourselves. So if the <code>[Message.author](https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.author)</code> is the same as the <code>[Client.user](https://discordpy.readthedocs.io/en/latest/api.html#discord.Client.user)</code> the code just returns.</li>
<li>Next, we check if the <a target="_blank" href="https://discordpy.readthedocs.io/en/latest/api.html#discord.Message.content"><code>Message.content</code></a> starts with <code>'$hello'</code>. If so, then the bot replies with <code>'Hello!'</code> to the channel it was used in.</li>
<li>Now that the bot is set up, the final line runs the bot with the login token. It gets the token from out <code>.env</code> file.</li>
</ol>
<p>We have the code for the bot so now we just have to run it.</p>
<h3 id="heading-how-to-run-the-bot">How to Run the Bot</h3>
<p>Now click run button on the top to run your bot in repl.it.</p>
<p>If you are writing the bot locally, you can use these commands in the terminal to run the bot:</p>
<p>On Windows:</p>
<p><code>py -3 main.py</code></p>
<p>On other systems:</p>
<p><code>python3 main.py</code></p>
<p>Now go to your Discord room and type "$hello". Your bot should return "Hello!".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-141.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-improve-the-bot">How to Improve the Bot</h2>
<p>Now that we have a basic bot working, we'll improve it. It is called "Encourage Bot" for a reason. </p>
<p>This bot will respond with a message of encouragement whenever someone sends a message containing a sad or depressing word.</p>
<p>Anyone will be able to add encouraging messages for the bot to use and the user-submitted messages will be stored in the Repl.it database.</p>
<p>The bot will also return a random inspirational quote from an API when someone types the message "$inspire" into the chat.</p>
<p>We'll start with adding the "$inspire" feature.</p>
<h3 id="heading-how-to-add-inspirational-quotes-to-the-bot">How to Add Inspirational Quotes to the Bot</h3>
<p>We will get inspirational quotes from an API called zenquotes.io. We need to import a couple more Python modules, add a <code>get_quote()</code> function, and update our bot code to call the function. </p>
<p>Here is the updated code. After the code, I'll explain the new parts.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> discord
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> json

client = discord.Client()

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_quote</span>():</span>
  response = requests.get(<span class="hljs-string">"https://zenquotes.io/api/random"</span>)
  json_data = json.loads(response.text)
  quote = json_data[<span class="hljs-number">0</span>][<span class="hljs-string">'q'</span>] + <span class="hljs-string">" -"</span> + json_data[<span class="hljs-number">0</span>][<span class="hljs-string">'a'</span>]
  <span class="hljs-keyword">return</span>(quote)

<span class="hljs-meta">@client.event</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_ready</span>():</span>
  print(<span class="hljs-string">'We have logged in as {0.user}'</span>.format(client))

<span class="hljs-meta">@client.event</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_message</span>(<span class="hljs-params">message</span>):</span>
  <span class="hljs-keyword">if</span> message.author == client.user:
    <span class="hljs-keyword">return</span>

  <span class="hljs-keyword">if</span> message.content.startswith(<span class="hljs-string">'$inspire'</span>):
    quote = get_quote()
    <span class="hljs-keyword">await</span> message.channel.send(quote)

client.run(os.getenv(<span class="hljs-string">'TOKEN'</span>))
</code></pre>
<p>We now have to import the <code>requests</code> module. This module allows our code to make an HTTP request to get data from the API. The API returns JSON, so the <code>json</code> module makes it easier to work with the data returned.</p>
<p>The <code>get_quote()</code> function is pretty straightforward. First, it uses the requests module to request data from the API URL. The API returns a random inspirational quote. This function could easily be rewritten to get quotes from a different API, if the current one stops working.</p>
<p>Next inside the function, we use <code>json.loads()</code> to convert the response from the API to JSON. Through trial and error I figured out how to get the quote from the JSON into the string format I wanted. The quote is returned from the function as a string.</p>
<p>The final part updated in the code is toward the end. Previously it looked for a message that started with "$hello". Now it looks for "$inspire". Instead of returning "Hello!", it gets the quote with <code>quote = get_quote()</code> and returns the quote.</p>
<p>At this point you can run your code and try it out.</p>
<h2 id="heading-how-to-add-encouraging-messages-to-the-bot">How to Add Encouraging Messages to the Bot</h2>
<p>Now we will implement the feature where the bot responds with encouraging messages when a user posts a message with a sad word.</p>
<h3 id="heading-how-to-add-sad-words-to-the-bot">How to Add Sad Words to the Bot</h3>
<p>First we need to create a Python list that contains the sad words that the bot will respond to.</p>
<p>Add the following line after the <code>client</code> variable is created:</p>
<p><code>sad_words = ["sad", "depressed", "unhappy", "angry", "miserable"]</code></p>
<p>Feel free to add more words to the list.</p>
<h3 id="heading-how-to-add-encouraging-messages-to-the-bot-1">How to Add Encouraging Messages to the Bot</h3>
<p>Now we'll add a list of encouraging messages that the bot will respond with.</p>
<p>Add the following list after the <code>sad_words</code> list you created:</p>
<pre><code class="lang-python">starter_encouragements = [
  <span class="hljs-string">"Cheer up!"</span>,
  <span class="hljs-string">"Hang in there."</span>,
  <span class="hljs-string">"You are a great person / bot!"</span>
]
</code></pre>
<p>Like before, feel free to add more phrases of your choice to the list. I'm just using three items for now because later we'll add the ability for users to add more encouraging phrases for the bot to use.</p>
<h3 id="heading-how-to-respond-to-messages">How to Respond to Messages</h3>
<p>Now we need to update our bot to use the two lists we created. First, import the random module because the bot will choose encouraging messages randomly. Add the following line to the import statements at the top of the code: <code>import random</code>.</p>
<p>Now we will update the <code>on_message()</code> function to check all messages to see if they contain a word from the <code>sad_words</code> list. If a sad word is found, the bot will send a random message of encouragement. </p>
<p>Here is the updated code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_message</span>(<span class="hljs-params">message</span>):</span>
  <span class="hljs-keyword">if</span> message.author == client.user:
    <span class="hljs-keyword">return</span>

  msg = message.content

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">'$inspire'</span>):
    quote = get_quote()
    <span class="hljs-keyword">await</span> message.channel.send(quote)

  <span class="hljs-keyword">if</span> any(word <span class="hljs-keyword">in</span> msg <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> sad_words):
    <span class="hljs-keyword">await</span> message.channel.send(random.choice(starter_encouragements))
</code></pre>
<p>This is a good time to test the bot. You know enough now to create your own bot. But next you'll learn how to implement more advanced features and store data using the Repl.it database.</p>
<h3 id="heading-how-to-enable-user-submitted-messages">How to Enable User-submitted Messages</h3>
<p>The bot is completely functional, but now let's make it possible to update the bot right from Discord. A user should be able to add more encouraging messages for the bot to use when it detects a sad word.</p>
<p>We are going to use Repl.it's built-in database to store user-submitted messages. This database is a key-value store that’s built into every repl.</p>
<p>At the top of the code, under the other import statements, add <code>from replit import db</code>. This will allow us to use the Repl.it database.</p>
<p>Users will be able to add custom encouraging messages for the bot to use directly from the Discord chat. Before we add new commands for the bot, let's create two helper functions that will add custom messages to the database and delete them.</p>
<p>Add the following code after the <code>get_quote()</code> function:</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_encouragements</span>(<span class="hljs-params">encouraging_message</span>):</span>
  <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
    encouragements = db[<span class="hljs-string">"encouragements"</span>]
    encouragements.append(encouraging_message)
    db[<span class="hljs-string">"encouragements"</span>] = encouragements
  <span class="hljs-keyword">else</span>:
    db[<span class="hljs-string">"encouragements"</span>] = [encouraging_message]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_encouragment</span>(<span class="hljs-params">index</span>):</span>
  encouragements = db[<span class="hljs-string">"encouragements"</span>]
  <span class="hljs-keyword">if</span> len(encouragements) &gt; index:
    <span class="hljs-keyword">del</span> encouragements[index]
  db[<span class="hljs-string">"encouragements"</span>] = encouragements
</code></pre>
<p>The <code>update_encouragements()</code> function accepts an encouraging message as an argument.</p>
<p>First it checks if "encouragements" is a key in the database. If so, it gets the list of encouragements already in the database, adds the new one to the list, and stores the updated list back in the database under the "encouragements" key. </p>
<p>If the database does not already contain "encouragements", a new key by that name is created and the new encouraging message is added as the first element in the list.</p>
<p>The <code>delete_encouragement()</code> function accepts an index as an argument.</p>
<p>It gets the list of encouragements from the database stored under the "encouragements" key. If the number of items in the encouragements list is greater than the index, then the list item at that index is deleted. </p>
<p>Finally, the updated list is stored back in the database under the "encouragements" key.</p>
<p>Here is the updated code for the <code>on_message()</code> function. After the code, I'll explain the new sections.</p>
<pre><code class="lang-python"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_message</span>(<span class="hljs-params">message</span>):</span>
  <span class="hljs-keyword">if</span> message.author == client.user:
    <span class="hljs-keyword">return</span>

  msg = message.content

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$inspire"</span>):
    quote = get_quote()
    <span class="hljs-keyword">await</span> message.channel.send(quote)

  options = starter_encouragements
  <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
    options = options + db[<span class="hljs-string">"encouragements"</span>]

  <span class="hljs-keyword">if</span> any(word <span class="hljs-keyword">in</span> msg <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> sad_words):
    <span class="hljs-keyword">await</span> message.channel.send(random.choice(options))

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$new"</span>):
    encouraging_message = msg.split(<span class="hljs-string">"$new "</span>,<span class="hljs-number">1</span>)[<span class="hljs-number">1</span>]
    update_encouragements(encouraging_message)
    <span class="hljs-keyword">await</span> message.channel.send(<span class="hljs-string">"New encouraging message added."</span>)

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$del"</span>):
    encouragements = []
    <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
      index = int(msg.split(<span class="hljs-string">"$del"</span>,<span class="hljs-number">1</span>)[<span class="hljs-number">1</span>])
      delete_encouragment(index)
      encouragements = db[<span class="hljs-string">"encouragements"</span>]
    <span class="hljs-keyword">await</span> message.channel.send(encouragements)
</code></pre>
<p>The first new line of code from above is <code>options = starter_encouragements</code>. We are making a copy of <code>starter_encouragements</code> because we are going to add the user-submitted messages to that list before choosing a random message for the bot to send.</p>
<p>We check if "encouragements" is already in the database keys (meaning that a user has submitted at least one custom message). If so, we add the user messages to the starter encouragements.</p>
<p>Then, instead of sending a random message from <code>starter_encouragements</code>, the bot now sends a random message from <code>options</code>.</p>
<p>The next new section of code is used to add a new user-submitted message to the database. If a Discord message starts with "$new", then the text after "$new" will be used as a new encouraging message. </p>
<p>The code <code>msg.split("$new ",1)[1]</code> splits off the message from the "$new" command and stores the message in a variable. In that line of code, take note of the space in <code>"$new "</code>. We want everything <em>after</em> the space.</p>
<p>We call the <code>update_encouragements</code> helper function with the new message, and then the bot sends a message to the discord chat confirming that the message was added.</p>
<p>The third new section (at the end of the code above) checks if a new Discord message starts with "$del". This is the command to delete an item from the "encouragements" list in the database.</p>
<p>First a new variable called <code>encouragements</code> is initialized as an empty array. The reason for this is that this section of code will send a message with an empty array if the database does not include an "encouragement" key.</p>
<p>If the "encouragement" key is in the database, the index will be split off from the Discord message starting with "$del". Then, the <code>delete_encouragement()</code> function is called passing in the index to delete. The updated list of encouragements is loaded into the <code>encouragements</code> variable, and then the bot sends a message to Discord with the current list.</p>
<h2 id="heading-final-bot-features">Final Bot Features</h2>
<p>The bot should work so this is a good time to test it. We will now add a few final features.</p>
<p>We will add the ability to get a list of user-submitted messages right from Discord and we will add the ability to turn off and on whether the bot responds to sad words.</p>
<p>I will give you the full final code of the program, and then I'll discuss the updates below the code.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> discord
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> random
<span class="hljs-keyword">from</span> replit <span class="hljs-keyword">import</span> db

client = discord.Client()

sad_words = [<span class="hljs-string">"sad"</span>, <span class="hljs-string">"depressed"</span>, <span class="hljs-string">"unhappy"</span>, <span class="hljs-string">"angry"</span>, <span class="hljs-string">"miserable"</span>]

starter_encouragements = [
  <span class="hljs-string">"Cheer up!"</span>,
  <span class="hljs-string">"Hang in there."</span>,
  <span class="hljs-string">"You are a great person / bot!"</span>
]

<span class="hljs-keyword">if</span> <span class="hljs-string">"responding"</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> db.keys():
  db[<span class="hljs-string">"responding"</span>] = <span class="hljs-literal">True</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_quote</span>():</span>
  response = requests.get(<span class="hljs-string">"https://zenquotes.io/api/random"</span>)
  json_data = json.loads(response.text)
  quote = json_data[<span class="hljs-number">0</span>][<span class="hljs-string">"q"</span>] + <span class="hljs-string">" -"</span> + json_data[<span class="hljs-number">0</span>][<span class="hljs-string">"a"</span>]
  <span class="hljs-keyword">return</span>(quote)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">update_encouragements</span>(<span class="hljs-params">encouraging_message</span>):</span>
  <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
    encouragements = db[<span class="hljs-string">"encouragements"</span>]
    encouragements.append(encouraging_message)
    db[<span class="hljs-string">"encouragements"</span>] = encouragements
  <span class="hljs-keyword">else</span>:
    db[<span class="hljs-string">"encouragements"</span>] = [encouraging_message]

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_encouragment</span>(<span class="hljs-params">index</span>):</span>
  encouragements = db[<span class="hljs-string">"encouragements"</span>]
  <span class="hljs-keyword">if</span> len(encouragements) &gt; index:
    <span class="hljs-keyword">del</span> encouragements[index]
  db[<span class="hljs-string">"encouragements"</span>] = encouragements

<span class="hljs-meta">@client.event</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_ready</span>():</span>
  print(<span class="hljs-string">"We have logged in as {0.user}"</span>.format(client))

<span class="hljs-meta">@client.event</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">on_message</span>(<span class="hljs-params">message</span>):</span>
  <span class="hljs-keyword">if</span> message.author == client.user:
    <span class="hljs-keyword">return</span>

  msg = message.content

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$inspire"</span>):
    quote = get_quote()
    <span class="hljs-keyword">await</span> message.channel.send(quote)

  <span class="hljs-keyword">if</span> db[<span class="hljs-string">"responding"</span>]:
    options = starter_encouragements
    <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
      options = options + db[<span class="hljs-string">"encouragements"</span>]

    <span class="hljs-keyword">if</span> any(word <span class="hljs-keyword">in</span> msg <span class="hljs-keyword">for</span> word <span class="hljs-keyword">in</span> sad_words):
      <span class="hljs-keyword">await</span> message.channel.send(random.choice(options))

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$new"</span>):
    encouraging_message = msg.split(<span class="hljs-string">"$new "</span>,<span class="hljs-number">1</span>)[<span class="hljs-number">1</span>]
    update_encouragements(encouraging_message)
    <span class="hljs-keyword">await</span> message.channel.send(<span class="hljs-string">"New encouraging message added."</span>)

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$del"</span>):
    encouragements = []
    <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
      index = int(msg.split(<span class="hljs-string">"$del"</span>,<span class="hljs-number">1</span>)[<span class="hljs-number">1</span>])
      delete_encouragment(index)
      encouragements = db[<span class="hljs-string">"encouragements"</span>]
    <span class="hljs-keyword">await</span> message.channel.send(encouragements)

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$list"</span>):
    encouragements = []
    <span class="hljs-keyword">if</span> <span class="hljs-string">"encouragements"</span> <span class="hljs-keyword">in</span> db.keys():
      encouragements = db[<span class="hljs-string">"encouragements"</span>]
    <span class="hljs-keyword">await</span> message.channel.send(encouragements)

  <span class="hljs-keyword">if</span> msg.startswith(<span class="hljs-string">"$responding"</span>):
    value = msg.split(<span class="hljs-string">"$responding "</span>,<span class="hljs-number">1</span>)[<span class="hljs-number">1</span>]

    <span class="hljs-keyword">if</span> value.lower() == <span class="hljs-string">"true"</span>:
      db[<span class="hljs-string">"responding"</span>] = <span class="hljs-literal">True</span>
      <span class="hljs-keyword">await</span> message.channel.send(<span class="hljs-string">"Responding is on."</span>)
    <span class="hljs-keyword">else</span>:
      db[<span class="hljs-string">"responding"</span>] = <span class="hljs-literal">False</span>
      <span class="hljs-keyword">await</span> message.channel.send(<span class="hljs-string">"Responding is off."</span>)

client.run(os.getenv(<span class="hljs-string">"TOKEN"</span>))
</code></pre>
<p>The first section added to the code is right under the <code>starter_encouragements</code> list:</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> <span class="hljs-string">"responding"</span> <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> db.keys():
  db[<span class="hljs-string">"responding"</span>] = <span class="hljs-literal">True</span>
</code></pre>
<p>We create a new key in the database called "responding" and set it to "True". We'll use this to determine if the bot should respond to sad words or not. Since the database is saved even after the program stops running, we only create the new key if it doesn't already exist.</p>
<p>The next new part of the code is that the section that responds to sad words is now inside this if statement: <code>if db["responding"]:</code>. The bot will only respond to sad words if <code>db["responding"] = True</code>. The ability to update this value comes after this next section.</p>
<p>Next, after the code to make the bot respond to the "$del" command, there is new code to respond to the "$list" command when sent as a Discord message. </p>
<p>This section starts with creating an empty list called <code>encouragements</code>. Then, if there are already encouragements in the database, those encouragements replace the empty list that was just created. </p>
<p>Finally, the bot sends the list of encouragements as a Discord message.</p>
<p>The final new section comes next. This code makes the bot respond to the "$responding" command. This command takes an argument of either "true" or "false". Here is a usage example: "$responding true". </p>
<p>The code first pulls off the argument with <code>value = msg.split("$responding ",1)[1]</code> (like before, note the space in <code>"$responding "</code>). Then there is an if/else statement that appropriately sets the "responding" key in the database and sends a notification message back to Discord. If the argument is anything but "true", the code assumes "false".</p>
<p>The code for the bot is complete! You can now run the bot and try it out. But there is one more important step that we will discuss next.</p>
<h2 id="heading-how-to-set-up-the-bot-to-run-continuously">How to Set Up the Bot to Run Continuously</h2>
<p>If you run your bot in repl.it and then close the tab it is running in, your bot will stop running.</p>
<p>But there are two ways you can keep your bot running continuously, even after you close your web bowser.</p>
<p>The first way and simplest way is to sign up for paid plan in Repl.it. Their cheapest paid plan is called the Hacker Plan and it includes five always-on repls. </p>
<p>You can get three months free using this link (limited to first 1000 people):  https://repl.it/claim?code=tryalwayson2103</p>
<p>Once you have signed up for that plan, open your Repl and click the name at the top. Then select the "Always On" option.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-35-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>There is another way to keep your code running even on the free tier but it is a little more complicated. Repl.it will continue running a web server even after the tab is closed. But even a web server will only run for up to an hour without any use.</p>
<p>Here is what the repl.it docs say:</p>
<blockquote>
<p>Once deployed, the server will continue to run in the background, even after you close the browser tab. The server will stay awake and active until an hour after its last request, after which it will enter a sleeping stage. Sleeping repls will be woken up as soon as it receives another request; there is no need to re-run the repl. However, if you make changes to your server, you will need to restart the repl in order to see those changes reflected in the live version.</p>
</blockquote>
<p>To keep the bot running continuously, we'll use another free service called Uptime Robot at <a target="_blank" href="https://uptimerobot.com/">https://uptimerobot.com/</a>.</p>
<p>Uptime Robot can be set up to ping the bot's web server on repl.it every 5 minutes. With constant pings, the bot will never enter the sleeping stage and will just keep running.</p>
<p>So we have to do two more things to get our bot to run continuously:</p>
<ol>
<li>create a web server in repl.it and</li>
<li>set up Uptime Robot to continuously ping the web server.</li>
</ol>
<h3 id="heading-how-to-create-a-web-server-in-replit">How to Create a Web Server in repl.it</h3>
<p>Creating a web server is simpler than you may think. </p>
<p>To do it, create a new file in your project called <code>keep_alive.py</code>.</p>
<p>Then add the following code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask
<span class="hljs-keyword">from</span> threading <span class="hljs-keyword">import</span> Thread

app = Flask(<span class="hljs-string">''</span>)

<span class="hljs-meta">@app.route('/')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">home</span>():</span>
    <span class="hljs-keyword">return</span> <span class="hljs-string">"Hello. I am alive!"</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">run</span>():</span>
  app.run(host=<span class="hljs-string">'0.0.0.0'</span>,port=<span class="hljs-number">8080</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">keep_alive</span>():</span>
    t = Thread(target=run)
    t.start()
</code></pre>
<p>In this code, we use Flask to start a web server. The server returns "Hello. I am alive." to anyone who visits it. The server will run on a separate thread from our bot. We won't discuss everything here since the rest is not really relevant to our bot.</p>
<p>Now we just need the bot to run this web server.</p>
<p>Add the following line toward the top of <code>main.py</code>  to import the server.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> keep_alive <span class="hljs-keyword">import</span> keep_alive
</code></pre>
<p>To start the web server when <code>main.py</code> is run, add the following line as the second-to-last line, right before the bot runs.</p>
<p><code>keep_alive()</code></p>
<p>When you run the bot on repl.it after adding this code, a new web server window will open up. There is a URL shown for the web server. Copy the URL so you can use it in the next section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-20-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-set-up-uptime-robot">How to Set Up Uptime Robot</h3>
<p>Now we need to set up Uptime Robot to ping the web server every five minutes. This will cause the bot to run continuously.</p>
<p>Create a free account on <a target="_blank" href="https://uptimerobot.com/">https://uptimerobot.com/</a>.</p>
<p>Once you are logged in to your account, click "Add New Monitor".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-21-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>For the new monitor, select "HTTP(s)" as the Monitor Type and name it whatever you like. Then, paste in the URL of your web server from repl.it. Finally, click "Create Monitor".</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-22-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>We're done! Now the bot will run continuously so people can always interact with it on Repl.it.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You now know how to create a Discord bot with Python, and run it continuously in the cloud. </p>
<p>There are a lot of other things that the discord.py library can do. So if you want to give a Discord bot even more features, your next step is to check out <a target="_blank" href="https://discordpy.readthedocs.io/en/latest/index.html">the docs for discord.py.</a></p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
