<?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[ Naomi Carrigan - 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[ Naomi Carrigan - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 08 May 2026 14:34:29 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/naomilgbt/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Learn React in your Browser – freeCodeCamp Full Stack Curriculum Mid-2025 Update ]]>
                </title>
                <description>
                    <![CDATA[ Hey campers! The freeCodeCamp community is still very hard at work on the rest of the coursework for our full stack curriculum. It’s only been a few months, but already there are many campers going full-force through these new lessons and programming... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-react-in-your-browser-freecodecamp-full-stack-curriculum-mid-2025-update/</link>
                <guid isPermaLink="false">685049cc71faf4a81f93b409</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Mon, 16 Jun 2025 16:43:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1750092183369/0e6a0781-0913-4c28-9289-9af3831ebc67.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Hey campers! The freeCodeCamp community is still very hard at work on the rest of the coursework for our <a target="_blank" href="https://www.freecodecamp.org/learn/full-stack-developer/">full stack curriculum</a>.</p>
<p>It’s only been a few months, but already there are many campers going full-force through these new lessons and programming challenges.</p>
<p>I’m excited to share our next wave of updates with you all.</p>
<h2 id="heading-new-curriculum-coursework">New Curriculum Coursework</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1750152183453/6e973e4a-4b7e-4294-b056-95a5f6155e77.png" alt="6e973e4a-4b7e-4294-b056-95a5f6155e77" class="image--center mx-auto" width="717" height="766" loading="lazy"></p>
<p>We have just released three new sections of the curriculum: The React Hooks and State section, the Performance section, and the Testing section.</p>
<p>This new coursework includes:</p>
<ul>
<li><p>50 lecture videos</p>
</li>
<li><p>a dozen workshops and labs</p>
</li>
<li><p>three new review blocks</p>
</li>
<li><p>and a bunch of coursework to keep you on track with your learning goals (while we work to finish the remainder of the Full Stack curriculum.)</p>
</li>
</ul>
<p>Some of the projects you’ll build include:</p>
<ul>
<li><p>a fully playable Tic Tac Toe game</p>
</li>
<li><p>a color picker</p>
</li>
<li><p>and a superhero application form</p>
</li>
</ul>
<h2 id="heading-exams">Exams</h2>
<p>We know many of you are eagerly awaiting the release of the exams at the end of each module. These are nearly ready.</p>
<p>We’re also building our own custom exam environment that you can use to take these exams. This will strike a balance between respecting privacy and preserving academic integrity.</p>
<h2 id="heading-whats-next">What’s Next?</h2>
<p>The community is now focused on the CSS Libraries and TypeScript modules next. We’re also building Python modules.</p>
<p>We have a lot of stuff coming out in the next few months.</p>
<p>A super early sneak peek of some of the projects coming soon:</p>
<ul>
<li><p>build your own RPG character</p>
</li>
<li><p>a trading card game</p>
</li>
<li><p>a medical data validator</p>
</li>
<li><p>and more 🏕️</p>
</li>
</ul>
<h2 id="heading-get-involved">Get Involved</h2>
<p>Are you interested in helping bring our full stack curriculum to life? We have plenty of opportunities to contribute – you can see all of the <a target="_blank" href="https://github.com/freecodecamp/freecodecamp/issues">open issues on our GitHub repository</a>.</p>
<p>Start by reading our <a target="_blank" href="https://contribute.freecodecamp.org/intro/">contributor onboarding guide</a>. Then <a target="_blank" href="https://discord.gg/KVUmVXA">hop on over to our Discord community</a> with any questions you may have.</p>
<p>We’re stoked to watch you plow through all this new coursework. Keep up the forward momentum with your skills. Happy Coding! 💜</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Help Someone with Their Code Using the Socratic Method ]]>
                </title>
                <description>
                    <![CDATA[ As a programming community, freeCodeCamp helps many people who have questions about their code. It can be quite tempting to simply provide the learner with the answer and move on, but that’s actually detrimental to the learning process. Here’s why: W... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-help-someone-with-their-code-using-the-socratic-method/</link>
                <guid isPermaLink="false">677d816db702998ae550cebf</guid>
                
                    <category>
                        <![CDATA[ coding ]]>
                    </category>
                
                    <category>
                        <![CDATA[ learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Tue, 07 Jan 2025 19:33:01 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/2RRq1BHPq4E/upload/904ec5e77783268f025191b07da29f08.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>As a programming community, freeCodeCamp helps many people who have questions about their code. It can be quite tempting to simply provide the learner with the answer and move on, but that’s actually detrimental to the learning process. Here’s why:</p>
<p>When you give someone the answer, you are depriving them of that “aha” moment. You are removing the opportunity for them to learn how to reach the conclusion through their own thinking, and instead allowing them to progress with minimal effort or thought.</p>
<p>So how can you best help someone with their code?</p>
<h2 id="heading-what-is-the-socratic-method">What is the Socratic Method?</h2>
<p>The Greek philosopher Plato was a student of another philosopher, Socrates. In Plato’s works, he often writes about the debates that Socrates would have with his colleagues and students.</p>
<p>Socrates would begin these discussions by raising common beliefs and scrutinizing them to question their compatibility with other beliefs, and guide people to reach the truth.</p>
<p>But how does that translate to our modern day interactions in a digital age?</p>
<p>Let’s consider a real example from one of our communities:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1736179977371/7e087ee9-4b7e-4321-ade9-18019289320d.png" alt="A Discord message from the user Razzle Dazzle, reading &quot;How do I find the sum of the numbers in an array?&quot;" class="image--center mx-auto" width="600" height="400" loading="lazy"></p>
<p>You might be tempted to give them the direct answer, such as this code example:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> sum = nums.reduce(<span class="hljs-function">(<span class="hljs-params">acc, el</span>) =&gt;</span> acc + el, <span class="hljs-number">0</span>);
</code></pre>
<p>But in doing so, you have performed the logical reasoning on behalf of the learner.</p>
<p>Instead, you should focus on asking pointed questions to <em>guide</em> them to the answer. For example, you might start by asking:</p>
<blockquote>
<p>❓ How would you find the sum of a list of numbers on pen and paper, without code?</p>
</blockquote>
<p>It may seem counter-intuitive to ask the learner a question that requires them to step away from the code, but you are actually pointing them to the underlying logic behind their question.</p>
<p>Let’s assume the learner responds with something like:</p>
<blockquote>
<p>I would go through each number, one by one, and add it to the sum of the previous numbers.</p>
</blockquote>
<p>As the learner begins answering your questions, the dialogue should progress in this call-and-response format. Your questions should become increasingly narrow and pointed with the learner’s progress toward the solution.</p>
<p>For example, a series of questions might look like:</p>
<blockquote>
<p>Instructor: “Great, now what is an array of numbers?”<br>Student: “A list?”<br>Instructor: “Good! How would you iterate through that list?”<br>Student: “With a for loop.”<br>Instructor: “And what does your loop need to do on each iteration?”<br>Student: “Add the current number to the sum.”<br>Instructor: “Where can you find the sum?”<br>Student: “I could put it in a variable outside the loop.”<br>Instructor: “Great, now you’re ready to try writing the code.”<br>— original example written for this article</p>
</blockquote>
<p>At this point, the learner will likely connect the dots and reach the final solution.</p>
<h2 id="heading-socrates-in-modern-culture">Socrates in Modern Culture</h2>
<p>Learning the Socratic method is hard. In fact, many do not encounter it until they reach their university studies. But there are examples found in modern pop culture that can help you understand how the Socratic method works.</p>
<p>The TV show House, M.D. is rife with examples. Take this exchange from the episode titled “Three Stories”:</p>
<blockquote>
<p>House: “Kidney stones would cause what?”<br>Student: “Blood in urine.”<br>House: “What colour is your pee?”<br>Student: “Yellow.”<br>House: “What colour is your blood?”<br>Student: “Red.”<br>House: “What colours did I use?”<br>Student: “Red, yellow, and brown.<br>House: “And brown. What causes brown?”<br>Student: “Waste”.<br>— (Frapier, 2008)¹</p>
</blockquote>
<p>You’ll notice how this exchange took place. House’s goal here was not to <em>give</em> the learner the answer, but to ask deductive questions to <em>guide</em> the learner to reach the answer on their own.</p>
<p>Consider the popular movie The Matrix:</p>
<blockquote>
<p>Morpheus: “Have you ever had a dream, Neo, that you were so sure was real?”<br>Neo: “This can't be…”<br>Morpheus: “Be what? Be real?”<br>Morpheus: “What if you were unable to wake from that dream, Neo? How would you know the difference between the dreamworld and the real world?”<br>— (Wachowski &amp; Wachowski, 1999)²</p>
</blockquote>
<p>In this scene, Morpheus is applying the Socratic method to lead Neo to question his perceptions of reality. This is a rather dramatic example, but the premise remains the same: rather than telling the learner how they should think, you guide them to reach the conclusion through their own volition.</p>
<p>Finally, let’s look at an example from Legally Blonde:</p>
<blockquote>
<p>Elle: “Your father was shot while you were in the shower?”<br>…<br>Chutney: “Yes. I was washing my hair.”<br>Elle: “Miss Windham, can you tell us what you'd been doing earlier in the day?”<br>Chutney: “I got up, went to Starbucks, went to the gym, got a perm, and came home.”<br>Elle: “Where you got in the shower.”<br>Chutney: “Yes.”<br>…<br>Elle: “…Had you ever gotten a perm before, Miss Windham?”<br>Chutney: “Yes.”<br>Elle: “How many, would you say?”<br>Chutney: “Two a year since I was twelve. You do the math.”<br>…<br>Elle: “Chutney, why is it that Tracy Marcinko's curls were ruined when she got hosed down?”<br>Chutney: “Because they got wet.”<br>Elle: “That's right. Because isn't the first cardinal rule of perm maintenance that you are forbidden to wet your hair for at least twenty-four hours after getting a perm at the risk of de-activating the ammonium thiglycolate? And wouldn't someone who's had -- thirty perms ? -- throughout her lifetime, be well aware of this rule? And if you, in fact, were not washing your hair, as I suspect you were not, since your curls are still intact, wouldn't you have heard the gunshot?”<br>—(Luketic, 2001)³</p>
</blockquote>
<p>The Socratic method can often be seen in law, and this serves as an excellent example. Through this exchange, Elle is not trying to guide Chutney to reach a conclusion, but rather the <em>spectators</em> (in this case, the jury). This is an important distinction for a community such as ours: where engaging in a Socratic discussion with one member can actually benefit current and future members who may observe or revisit your conversation.</p>
<h2 id="heading-goal-of-the-socratic-method">Goal of the Socratic Method</h2>
<p>It is important to recognize that the goal of the Socratic method is <em>not</em> to present a quick exchange of information. Instead, the aim is to first get the learner to realize that they know less than they believe they do, then guide them to the answer through questions that elicit certain thought processes.⁴</p>
<p>When applying this to learning programming, then, it’s important to remember the phrase “challenge your assumptions”. We often assume that we know what the code we have written is doing, so our first step when it is not working is to examine those assumptions.</p>
<p>As we guide learners through the process of debugging, we want to ask questions that do the same. A common question in your repertoire should be “What does this line of code do?”. When the learner answers with information that is inaccurate, respond with questions that drill down into smaller components of the code – break the problem into pieces, per se.</p>
<p>Consider this example:</p>
<blockquote>
<p>Learner: “but maybe that wouldnt work because I need to find the book not the id in the book”<br>Instructor: “Correct. Now, the good news is that we have a function that would ostensibly do that. The bad news is that function doesn't do that. What does that function do instead?”<br>Learner: “returns bookId”<br>— sourced from our Discord community⁵</p>
</blockquote>
<p>By asking the question “What does that function do instead?”, the instructor has provided the direction for the learner to reach the correct conclusion without having to provide any specific information. The learner already knew that the function did not perform as expected, and the instructor asked the question to challenge the learner’s assumption that the function <em>did</em> perform.</p>
<p>Here’s another example:</p>
<blockquote>
<p>Instructor: “This is the example from the Chalenge (sic). Did you follow that example they gave?”<br>Learner: “yes but i didnt understand the for-in loop, i didnt understand "(const food in refigerator)", having to state the string property name with const or let was confusing (it isnt anymore)”<br>Instructor: “Ok, so lets start here instead of the full solution to the Challenge”<br>Instructor: “What does that code do when you run it?”<br>…<br>Learner: “each of the object's keys were set to the variable food which was made in the for-in loop, it then iterates through the object and logs the key and value assigned to the key?”<br>— sourced from our Discord community⁶</p>
</blockquote>
<p>Again we can see how the instructor focuses on asking pointed questions that guide the user toward the solutions <em>through their own reasoning</em>.</p>
<blockquote>
<p>❗ That is the ultimate goal of the Socratic method: to guide the learner to reach the logical conclusion solely through their own reasoning.</p>
</blockquote>
<h2 id="heading-long-term-benefits-of-the-socratic-method">Long-Term Benefits of the Socratic Method</h2>
<p>The Socratic method is not solely beneficial for resolving the immediate solution. The deductive reasoning process that is applied in this approach can also serve the learner well over the long-term.</p>
<p>The process of asking a learner questions to challenge their assumptions and knowledge, and guide them to the solution, is something that can be internalized as well. It’s a powerful tool to have in your repertoire for approaching debugging, isolating assumptions, and even learning to articulate an issue.</p>
<p>That is, by walking a learner through this call-and-response conversation, the learner can also walk away with the ability to ask <em>themselves</em> these questions. They can take the same process of asking increasingly narrowing questions as a logical pathway and run through it mentally when they encounter future issues with their code (or even in other aspects of life)!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Providing a solution directly to a learner inhibits their intellectual growth and deprives them of the rewarding experience that comes from reaching their own logical conclusion. By leveraging techniques like the Socratic method, we can foster a stronger and more effective educational environment that allows learners to grow and thrive.</p>
<blockquote>
<p>💡 If you were sent this article in response to a forum post, Reddit comment, Discord message, or other communication where you gave a fellow learner a working solution, please consider the merits of this method instead.</p>
</blockquote>
<p>This approach can seem tedious and lengthy, and at times it is. It is far quicker (and arguably easier) to hand someone the code that resolves their problem.</p>
<p>But that approach, more often than not, harms the learner in the end. And, if you’ll forgive this author for an anecdote, I have seen people get frustrated when they are the subject of a Socratic approach, or lose patience with the back and forth conversation. But I have <em>never</em> seen them upset with or disappointed by that end result – the “aha” moment where they deduce the solution to the problem through their own reasoning.</p>
<h3 id="heading-sources">Sources:</h3>
<ol>
<li><p>¹ Frapier, M. (2008). Being Nice is Overrated: House and Socrates on the Necessity of Conflict. In <em>House and Philosophy</em> (pp. 100–101). John Wiley &amp; Sons, Inc.</p>
</li>
<li><p>² Wachowski, L., &amp; Wachowski, L. (Directors). (1999, March 24). <em>The Matrix</em> (Z. Staenberg, Ed.). Warner Bros.</p>
</li>
<li><p>³ Luketic, R. (Director). (2001, July 13). <em>Legally Blonde</em> (A. Brandt-Burgoyne, Ed.). MGM Distribution Co.</p>
</li>
<li><p>⁴ Frapier, M. (2008). Being Nice is Overrated: House and Socrates on the Necessity of Conflict. In <em>House and Philosophy</em> (p. 103). John Wiley &amp; Sons, Inc.</p>
</li>
<li><p>⁵ lightskingeneral &amp; plamoni (2024, January 4). [Discord conversation]. Discord. Retrieved 2025, January 6, from <a target="_blank" href="https://discord.com/channels/692816967895220344/718214639669870683/1192488621043888198">https://discord.com/channels/692816967895220344/718214639669870683/1192488621043888198</a></p>
</li>
<li><p>⁶ lightskingeneral &amp; jeremylt (2023, November 13). [Discord conversation]. Discord. Retrieved 2025, January 6, from <a target="_blank" href="https://discord.com/channels/692816967895220344/718214639669870683/1173733715109761065">https://discord.com/channels/692816967895220344/718214639669870683/1173733715109761065</a></p>
</li>
</ol>
<p>Special thanks to ArielLeslie and JeremyLT for their help with this article.</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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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="600" height="400" 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[ Hacktoberfest 2023 Contributors ]]>
                </title>
                <description>
                    <![CDATA[ The freeCodeCamp community just finished participating in this year's Hacktoberfest – a month-long celebration of open source. Our core team is excited to recognize all of our wonderful open source contributors. A big thank you to all of these folks ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/hacktoberfest-2023-contributors/</link>
                <guid isPermaLink="false">66ac7ee5ed08c5b0125be187</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hacktoberfest ]]>
                    </category>
                
                    <category>
                        <![CDATA[ open source ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Fri, 03 Nov 2023 00:03:39 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/hf10_banner_sponsors_1032x600.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The freeCodeCamp community just finished participating in this year's Hacktoberfest – a month-long celebration of open source.</p>
<p>Our core team is excited to recognize all of our wonderful open source contributors. A big thank you to all of these folks for the effort they put into helping improve our curriculum and learning resources.</p>
<p>Thank you for your contributions to open source. 🎉</p>
<h2 id="heading-hacktoberfest-contributions-for-freecodecamphttpsgithubcomfreecodecampfreecodecamp">Hacktoberfest Contributions for <a target="_blank" href="https://github.com/freecodecamp/freecodecamp">freeCodeCamp</a></h2>
<ul>
<li><a target="_blank" href="https://github.com/Sboonny">Sboonny</a> made 15 valid contributions</li>
<li><a target="_blank" href="https://github.com/huyenltnguyen">huyenltnguyen</a> made 12 valid contributions</li>
<li><a target="_blank" href="https://github.com/ojeytonwilliams">ojeytonwilliams</a> made 11 valid contributions</li>
<li><a target="_blank" href="https://github.com/raisedadead">raisedadead</a> made 7 valid contributions</li>
<li><a target="_blank" href="https://github.com/gikf">gikf</a> made 7 valid contributions</li>
<li><a target="_blank" href="https://github.com/Riya267">Riya267</a> made 7 valid contributions</li>
<li><a target="_blank" href="https://github.com/dzmdre">dzmdre</a> made 7 valid contributions</li>
<li><a target="_blank" href="https://github.com/anand-harsh">anand-harsh</a> made 6 valid contributions</li>
<li><a target="_blank" href="https://github.com/raditotev">raditotev</a> made 5 valid contributions</li>
<li><a target="_blank" href="https://github.com/shabeebk">shabeebk</a> made 5 valid contributions</li>
<li><a target="_blank" href="https://github.com/ShaunSHamilton">ShaunSHamilton</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/jdwilkin4">jdwilkin4</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/bbsmooth">bbsmooth</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Ann-Cherian">Ann-Cherian</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/rohitrai3">rohitrai3</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/naomi-lgbt">naomi-lgbt</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/sitek94">sitek94</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Yash-Ambekar">Yash-Ambekar</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/moT01">moT01</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/ieahleen">ieahleen</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/weilirs">weilirs</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/AidanLDev">AidanLDev</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/andrianarivo">andrianarivo</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/aotulana">aotulana</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Nirajn2311">Nirajn2311</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/IgorShcherba">IgorShcherba</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Aman1919">Aman1919</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/lasjorg">lasjorg</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/EggSaled">EggSaled</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/stweil">stweil</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Web-Dev-Codi">Web-Dev-Codi</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/tmsagarofficial">tmsagarofficial</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/onukwilip">onukwilip</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Angad-Godara">Angad-Godara</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/hairyputtar">hairyputtar</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/a2937">a2937</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/mdrizwanfk">mdrizwanfk</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/0xLBlaze">0xLBlaze</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/avncharlie">avncharlie</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/joshalling">joshalling</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/shoaib-31">shoaib-31</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lalithasj">lalithasj</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lakshmi930">lakshmi930</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Insidiae">Insidiae</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/RickDeb2004">RickDeb2004</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/gerhynes">gerhynes</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/devuser200">devuser200</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/justynakantyka">justynakantyka</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/ArjunSingh-Rawat">ArjunSingh-Rawat</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/IamAyaanSk">IamAyaanSk</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/MojoBoingo">MojoBoingo</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Vedaant-Rajoo">Vedaant-Rajoo</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/kensiecodes">kensiecodes</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Elya29">Elya29</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/leonfeng">leonfeng</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/BiaDd">BiaDd</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/mribeirodantas">mribeirodantas</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/dipushrestha">dipushrestha</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Ukarus">Ukarus</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/NeetigyaPod">NeetigyaPod</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/arujjval">arujjval</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/pushkar707">pushkar707</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/umangbp">umangbp</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/FarisKarim">FarisKarim</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Tauseef-Hilal">Tauseef-Hilal</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Om-Aditya-Jain">Om-Aditya-Jain</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/shootermv">shootermv</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/KyleGrande">KyleGrande</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/amitabh-sahu">amitabh-sahu</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/MrBrain295">MrBrain295</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/samiralibabic">samiralibabic</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/wahmd">wahmd</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/grbknr1996">grbknr1996</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/pankildoshi">pankildoshi</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Subhrajit-Dutta">Subhrajit-Dutta</a> made 1 valid contribution</li>
</ul>
<h2 id="heading-hacktoberfest-contributions-for-devdocshttpsgithubcomfreecodecampdevdocs">Hacktoberfest Contributions for <a target="_blank" href="https://github.com/freecodecamp/devdocs">DevDocs</a></h2>
<ul>
<li><a target="_blank" href="https://github.com/simon04">simon04</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/thewheat">thewheat</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/icai">icai</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/JuanVqz">JuanVqz</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/ojeytonwilliams">ojeytonwilliams</a> made 1 valid contribution</li>
</ul>
<p>Thank you to all of these contributors! If you are interested in becoming a contributor as well, we recommend starting with our <a target="_blank" href="https://contribute.freecodecamp.org">contributing guidelines</a>. If you have any questions feel free to ask us in our <a target="_blank" href="https://discord.gg/KVUmVXA">Discord server</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Hacktoberfest 2022 Contributors ]]>
                </title>
                <description>
                    <![CDATA[ freeCodeCamp participated in Hacktoberfest (a month-long celebration of open source) again this year. We wanted to take a moment to thank all of our wonderful contributors for the effort they put in to help us continue to improve our curriculum and l... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/hacktoberfest-2022-contributors/</link>
                <guid isPermaLink="false">66ac7ed59e10a480c1303778</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ hacktoberfest ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Fri, 11 Nov 2022 20:25:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/Hacktoberfest_2022.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>freeCodeCamp participated in Hacktoberfest (a month-long celebration of open source) again this year.</p>
<p>We wanted to take a moment to thank all of our wonderful contributors for the effort they put in to help us continue to improve our curriculum and learning resources.</p>
<p>Thank you for your contributions to open source. 🎉</p>
<h2 id="heading-hacktoberfest-contributions-for-freecodecamphttpsgithubcomfreecodecampfreecodecamp">Hacktoberfest Contributions for <a target="_blank" href="https://github.com/freecodecamp/freecodecamp">freeCodeCamp</a></h2>
<ul>
<li><a target="_blank" href="https://github.com/raisedadead">raisedadead</a> made 16 valid contributions</li>
<li><a target="_blank" href="https://github.com/Sboonny">Sboonny</a> made 11 valid contributions</li>
<li><a target="_blank" href="https://github.com/nayabatir1">nayabatir1</a> made 11 valid contributions</li>
<li><a target="_blank" href="https://github.com/naomi-lgbt">naomi-lgbt</a> made 10 valid contributions</li>
<li><a target="_blank" href="https://github.com/ojeytonwilliams">ojeytonwilliams</a> made 10 valid contributions</li>
<li><a target="_blank" href="https://github.com/DerrykBoyd">DerrykBoyd</a> made 5 valid contributions</li>
<li><a target="_blank" href="https://github.com/sidemt">sidemt</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Prince-Mendiratta">Prince-Mendiratta</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/KravMaguy">KravMaguy</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/ahmadabdolsaheb">ahmadabdolsaheb</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/jeremylt">jeremylt</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/scissorsneedfoodtoo">scissorsneedfoodtoo</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/ShaunSHamilton">ShaunSHamilton</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/moT01">moT01</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/k-rajat19">k-rajat19</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/WizardLemon">WizardLemon</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/JustARatherRidiculouslyLongUsername">JustARatherRidiculouslyLongUsername</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/hbar1st">hbar1st</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/AndrewDawes">AndrewDawes</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/saw-jan">saw-jan</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/zairahira">zairahira</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/nouredinateur">nouredinateur</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Yash-Pratapwar">Yash-Pratapwar</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/miyaliu666">miyaliu666</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Rancho2002">Rancho2002</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/DylanDevelops">DylanDevelops</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/jctello">jctello</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/disabledandfab">disabledandfab</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/mmatsumoto1026">mmatsumoto1026</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/RohitPatel1122">RohitPatel1122</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/HatimZ">HatimZ</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/footedroom575">footedroom575</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/dojutsu-user">dojutsu-user</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/denquinlan">denquinlan</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Aman-zishan">Aman-zishan</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/myankpraksh">myankpraksh</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/michpara">michpara</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Kroustille">Kroustille</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/saiharvin">saiharvin</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/KeshariPiyush24">KeshariPiyush24</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Florenciasicre">Florenciasicre</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/mehdibouaziz">mehdibouaziz</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/carrotfarmer">carrotfarmer</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/PakkuDon">PakkuDon</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/aniketsinha2002">aniketsinha2002</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/anubhav823">anubhav823</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Ayush-singla27">Ayush-singla27</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/paulaxisabel">paulaxisabel</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/rffontenelle">rffontenelle</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Anuran12">Anuran12</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/surajrane42">surajrane42</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Nirajn2311">Nirajn2311</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/sadikkuzu">sadikkuzu</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/void-hr">void-hr</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/alekzandriia">alekzandriia</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/webpro">webpro</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lmssr">lmssr</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/JoseDeFreitas">JoseDeFreitas</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Saul-BT">Saul-BT</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Prathamesh010">Prathamesh010</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/krupalitrivedi">krupalitrivedi</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/aryandeelwal">aryandeelwal</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/friendofdog">friendofdog</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/NiharPhansalkar">NiharPhansalkar</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/dannygr664">dannygr664</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/bbsmooth">bbsmooth</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Weredime">Weredime</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/tanmaythakral">tanmaythakral</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/prajwal-144">prajwal-144</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/fzs1994">fzs1994</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/adripo">adripo</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/panoreak">panoreak</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/erkanzileli">erkanzileli</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Insidiae">Insidiae</a> made 1 valid contribution</li>
</ul>
<h2 id="heading-hacktoberfest-contributions-for-devdocshttpsgithubcomfreecodecampdevdocs">Hacktoberfest Contributions for <a target="_blank" href="https://github.com/freecodecamp/devdocs">DevDocs</a></h2>
<ul>
<li><a target="_blank" href="https://github.com/simon04">simon04</a> made 7 valid contributions</li>
<li><a target="_blank" href="https://github.com/thewheat">thewheat</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/fabianchoxD">fabianchoxD</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/ClasherKasten">ClasherKasten</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/JuanVqz">JuanVqz</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/gonglexin">gonglexin</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/axelf4">axelf4</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/TomerPacific">TomerPacific</a> made 1 valid contribution</li>
</ul>
<h2 id="heading-hacktoberfest-contributions-for-developer-quiz-sitehttpsgithubcomfreecodecampdeveloperquizsite">Hacktoberfest Contributions for <a target="_blank" href="https://github.com/freecodecamp/Developer_Quiz_Site">Developer Quiz Site</a></h2>
<ul>
<li><a target="_blank" href="https://github.com/Shaik-mohd-huzaifa">Shaik-mohd-huzaifa</a> made 13 valid contributions</li>
<li><a target="_blank" href="https://github.com/frankiefab100">frankiefab100</a> made 8 valid contributions</li>
<li><a target="_blank" href="https://github.com/Chirag8023">Chirag8023</a> made 6 valid contributions</li>
<li><a target="_blank" href="https://github.com/a2937">a2937</a> made 6 valid contributions</li>
<li><a target="_blank" href="https://github.com/ddoyediran">ddoyediran</a> made 5 valid contributions</li>
<li><a target="_blank" href="https://github.com/adwaithks">adwaithks</a> made 5 valid contributions</li>
<li><a target="_blank" href="https://github.com/shubhshish">shubhshish</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/advats2">advats2</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/moshigintama">moshigintama</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/bishwassagar">bishwassagar</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/devsapariya94">devsapariya94</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/arpit-srivastava33">arpit-srivastava33</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/836hardik-agrawal">836hardik-agrawal</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Sabretooth1405">Sabretooth1405</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/RK-Nanda01">RK-Nanda01</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/stupidcoder53">stupidcoder53</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/PranjalSK03">PranjalSK03</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/kingpinzs">kingpinzs</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/sinhashubhashish">sinhashubhashish</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/erickleon88">erickleon88</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/shubhsinha">shubhsinha</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/gerhynes">gerhynes</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/YuriDevAT">YuriDevAT</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/astitva0011">astitva0011</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/ShyamPraveenSingh">ShyamPraveenSingh</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/adarsh-gupta101">adarsh-gupta101</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/preeformance">preeformance</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/anp-scp">anp-scp</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/himanishu-2512">himanishu-2512</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Sin-Sumit">Sin-Sumit</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Utpalkant6204">Utpalkant6204</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/robinathon">robinathon</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/anupriyalathey">anupriyalathey</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/GulabSinghSikarwar">GulabSinghSikarwar</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/Gautam-Hegde">Gautam-Hegde</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/toukirkhan">toukirkhan</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/TauqeerAhmad5201">TauqeerAhmad5201</a> made 4 valid contributions</li>
<li><a target="_blank" href="https://github.com/dhruvjain896">dhruvjain896</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/YashasviChaurasia">YashasviChaurasia</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/tripura-kant">tripura-kant</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/Atharva-Morankar-09">Atharva-Morankar-09</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/J-ell">J-ell</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/Akshu-on-github">Akshu-on-github</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/shikharcodes">shikharcodes</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/Sneha-Mittal88293">Sneha-Mittal88293</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/Garv-it2002">Garv-it2002</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/prashalruchiranga">prashalruchiranga</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/MusfiqDehan">MusfiqDehan</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/Shubham1450">Shubham1450</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/SaulMiquilena">SaulMiquilena</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/thegeekyb0y">thegeekyb0y</a> made 3 valid contributions</li>
<li><a target="_blank" href="https://github.com/jayantpranjal0">jayantpranjal0</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/ispandey81">ispandey81</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/dhatGuy">dhatGuy</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/not-a-ethan">not-a-ethan</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Suhas2002">Suhas2002</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/MadhulikaSingh2304">MadhulikaSingh2304</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Manmeet2607">Manmeet2607</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/chefgs">chefgs</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/johansantana">johansantana</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/wizardx5">wizardx5</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/richardoey">richardoey</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/deedeecee">deedeecee</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/CamiloCortesM">CamiloCortesM</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/DANIELSSF">DANIELSSF</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/KmKalpana">KmKalpana</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Bhawna-3001">Bhawna-3001</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/anish853">anish853</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Athi223">Athi223</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/samarjaffal">samarjaffal</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/horizon3902">horizon3902</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Evavic44">Evavic44</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/humsarika">humsarika</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/mukul-kr">mukul-kr</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/lakshyabhasin509">lakshyabhasin509</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/kalashjain23">kalashjain23</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/atharvagarwal">atharvagarwal</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/GJS2162">GJS2162</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/suryakapurothu">suryakapurothu</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Moksh45">Moksh45</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/ariyonaty">ariyonaty</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/raykotab">raykotab</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/Preethi-Dev">Preethi-Dev</a> made 2 valid contributions</li>
<li><a target="_blank" href="https://github.com/christophszcz">christophszcz</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/CamilaNieto-Centennial">CamilaNieto-Centennial</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Aman-Singh-Kushwaha">Aman-Singh-Kushwaha</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lakhanjindam">lakhanjindam</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/ARYAN-CODES-STAR">ARYAN-CODES-STAR</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/ShubhanshuJha">ShubhanshuJha</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/nicholeboaz">nicholeboaz</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Amit-TheOne">Amit-TheOne</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Vishal1297">Vishal1297</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/prasad-333">prasad-333</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/manojku1803">manojku1803</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Ankit-Aslekar">Ankit-Aslekar</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/SharoonRafeek">SharoonRafeek</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/stmapman7">stmapman7</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/KristenHarman">KristenHarman</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/rachelschipull">rachelschipull</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/sysoutayush">sysoutayush</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Mylena-angelica">Mylena-angelica</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/itsNue">itsNue</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/NimishAgarwal108">NimishAgarwal108</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lishakothari">lishakothari</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Atharva-Nimbalkar">Atharva-Nimbalkar</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Aravind22">Aravind22</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Shivankur25">Shivankur25</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/kristlyuen">kristlyuen</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Priyanshu236">Priyanshu236</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Harshit-Raj-14">Harshit-Raj-14</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lshaoqin">lshaoqin</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/carmenkolohe">carmenkolohe</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/LeGrandMAG">LeGrandMAG</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/OmarCardoze">OmarCardoze</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/XRegiGigaSX">XRegiGigaSX</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/lizwe-mac">lizwe-mac</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/ericdain">ericdain</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/satnaing">satnaing</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/dazzle-sah">dazzle-sah</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/abhishekiiitr">abhishekiiitr</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/paco9595">paco9595</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/TannisthaBiswas">TannisthaBiswas</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/jnadroj">jnadroj</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/sonali-rajput">sonali-rajput</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/makdoom">makdoom</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/tejeswar68">tejeswar68</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/FranciscoHeronildo">FranciscoHeronildo</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/manan-dude">manan-dude</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/piyushkdas0611">piyushkdas0611</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/silver919">silver919</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/PhilipeSousa">PhilipeSousa</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Virajj28">Virajj28</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/P766-spec">P766-spec</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/AHTHneeuhl">AHTHneeuhl</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/atulnarayan16">atulnarayan16</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/alondramora">alondramora</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/aishwarya-mali">aishwarya-mali</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Mannuel25">Mannuel25</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/mfonPeeter">mfonPeeter</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/amelia2802">amelia2802</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/srinidhigopalan">srinidhigopalan</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Bobliuuu">Bobliuuu</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/anika-kamath">anika-kamath</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/J-ATJ">J-ATJ</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/YanehCheck">YanehCheck</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/vivian-dai">vivian-dai</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Ksound22">Ksound22</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/DeepankRx">DeepankRx</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Ryukemeister">Ryukemeister</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/YashNandwana">YashNandwana</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/rajeev033">rajeev033</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/isurumaldeniya">isurumaldeniya</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/h-yung">h-yung</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/vishnus17">vishnus17</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/johnpatrickanders">johnpatrickanders</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Kunal-Garg-12">Kunal-Garg-12</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/HaimHamiel">HaimHamiel</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Insidiae">Insidiae</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/dhruviochani">dhruviochani</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Srisuma13">Srisuma13</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/mstomar698">mstomar698</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Sarang-Goutam">Sarang-Goutam</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Pratikxya">Pratikxya</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Preet-Sojitra">Preet-Sojitra</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Parajulibkrm">Parajulibkrm</a> made 1 valid contribution</li>
<li><a target="_blank" href="https://github.com/Tathagat27">Tathagat27</a> made 1 valid contribution</li>
</ul>
<p>Thank you to all of these contributors! If you are interested in becoming a contributor as well, we recommend starting with our <a target="_blank" href="https://contribute.freecodecamp.org">contributing guidelines</a>, and if you have any questions feel free to ask us in our <a target="_blank" href="https://discord.gg/KVUmVXA">Discord server</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up Your Own Mastodon Instance ]]>
                </title>
                <description>
                    <![CDATA[ Mastodon is a decentralized, federated social media platform based on the ActivityPub protocol. It allows you to follow and interact with friends across multiple instances.  In this article, you will learn how freeCodeCamp set up our own Mastodon ins... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-your-own-mastodon-instance/</link>
                <guid isPermaLink="false">66ac7f467d22919a412de7fd</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ mastadon ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Fri, 11 Nov 2022 16:29:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/11/Screen-Shot-2022-11-14-at-4.49.55-PM.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Mastodon is a decentralized, federated social media platform based on the <a target="_blank" href="https://www.w3.org/TR/activitypub/">ActivityPub</a> protocol. It allows you to follow and interact with friends across multiple instances. </p>
<p>In this article, you will learn how freeCodeCamp set up our own Mastodon instance - and how you can too.</p>
<h2 id="heading-what-is-mastodon">What is Mastodon?</h2>
<p>Imagine if there were multiple different websites for Twitter. On each of those websites, you could create an account (create one on all of them, if you were feeling ambitious). </p>
<p>You could then use your account to follow any of your friends on any of the other websites. You could repost their content to your account, and see activity from all of your followed accounts on a single timeline.</p>
<p>While Mastodon is by far the most popular platform to use, there are also other options such as <a target="_blank" href="https://github.com/misskey-dev/misskey">Misskey</a>, <a target="_blank" href="https://git.pleroma.social/pleroma/pleroma">Pleroma</a>, and their various forks. </p>
<p>Some of these platforms will be able to federate with each other, with cross-platform capabilities, while others will not.</p>
<h2 id="heading-getting-started-with-mastadon">Getting Started with Mastadon</h2>
<p>There are a couple of things you will need to get started with the process we followed for freeCodeCamp:</p>
<ul>
<li>A <a target="_blank" href="https://digitalocean.com">DigitalOcean</a> account</li>
<li>A DNS provider (we use <a target="_blank" href="https://cloudflare.com">Cloudflare</a>).</li>
</ul>
<p>Begin by logging in to DigitalOcean and creating a new droplet. In the image options, select the marketplace tab. Then, search for the Mastodon image - this will handle a good portion of the setup for you.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-48.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Configure the rest of the droplet settings to your own needs - for the size, we began with the $12 option with the plan to scale up as necessary.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-49.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once the droplet has finished provisioning, copy the IP address. Set up an A record for your domain or subdomain to point to your droplet - <strong>do this BEFORE you SSH into the droplet</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-50.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-52.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once your DNS is ready, SSH into the droplet as the <code>root</code> user.</p>
<h2 id="heading-how-to-set-up-your-mastadon-instance">How to Set Up Your Mastadon Instance</h2>
<p>When you first SSH into the server, the automatic setup tool will run.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-54.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Follow the prompts:</p>
<ul>
<li>For <code>Domain name</code>, enter the new domain/subdomain record you just set up.</li>
<li>The tool will ask if you want to store user-uploaded files on the cloud. If you do, you'll need to provide credentials for a storage provider such as Amazon S3.</li>
<li>Mastodon requires an SMTP server for email notifications and the registration flow. You can either spin up your own, or provide credentials for a service like <a target="_blank" href="https://sendgrid.com">SendGrid</a>.</li>
<li>The SMTP flow will prompt you to send a test email. This is <em>highly recommended</em>, as this will confirm your SMTP settings are correct. If they are not, the setup wizard will prompt you to re-enter them.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-55.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-67.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You will next create a Mastodon account to serve as the administrator. This can be your personal account, or it can be a shared account among your team.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-68.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Finally, you'll be prompted to enter your email for LetsEncrypt certificate renewal notifications.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>After a few minutes, your instance should be up and running.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-61.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-configure-your-mastadon-instance">How to Configure Your Mastadon Instance</h2>
<p>Visiting your new domain/subdomain should display the Mastodon landing page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-58.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Sign in using the admin credentials you generated earlier. Then, select <code>Preferences</code> on the right sidebar, followed by <code>Administration</code> -&gt; <code>Site Settings</code> on the left.</p>
<p>Here you can configure the basic information related to your instance, and upload your branding assets.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>These will be displayed on your instance's <code>/about</code> page, shown to users when they register/login (and available in the footer).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-66.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>When you are ready to start accepting user sign-ups, change the <code>Registration mode</code> setting to either <code>Anyone can sign up</code> or <code>Approval required for sign up</code>.</p>
<h2 id="heading-how-to-manage-users">How to Manage Users</h2>
<p>Under the <code>Moderation</code> -&gt; <code>Accounts</code> tab in the settings, you can see registered and pending users.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-63.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>If you click on a username, you will be taken to the user management view.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/11/image-65.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>From this screen, you can manage their permission level (that is, grant moderation or admin status), check IP information, and block email domains. </p>
<p>Your moderation team can also leave private notes (only visible to the team) on a user account, to help keep a history of any moderation concerns.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Now that your instance is up and running, feel free to poke around the settings and interaction options. For more information on the various options, check out the <a target="_blank" href="https://docs.joinmastodon.org/">official documentation</a>.</p>
<p>You can find the freeCodeCamp core team and volunteer moderators on <a target="_blank" href="https://social.freecodecamp.org">our private instance</a>, where you can follow us from the instance you just created.</p>
<p>Enjoy your new platform, and happy coding!</p>
<p><em>Cover image from Mastodon's <a target="_blank" href="https://blog.joinmastodon.org/2022/06/mastodon-branding-updates/">branding update page</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Set Up a GitHub OAuth Application ]]>
                </title>
                <description>
                    <![CDATA[ GitHub is an incredibly useful OAuth provider, especially if you are building an application targeted toward developers.  In this article, we will give you a quick rundown of how to set up a GitHub OAuth application. Create Your Application Begin by ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-set-up-a-github-oauth-application/</link>
                <guid isPermaLink="false">66ac7f3d23cc28a03a55e088</guid>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                    <category>
                        <![CDATA[ GitHub ]]>
                    </category>
                
                    <category>
                        <![CDATA[ oauth ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Thu, 27 Oct 2022 21:31:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/10/pexels-george-becker-333837--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>GitHub is an incredibly useful OAuth provider, especially if you are building an application targeted toward developers. </p>
<p>In this article, we will give you a quick rundown of how to set up a GitHub OAuth application.</p>
<h2 id="heading-create-your-application">Create Your Application</h2>
<p>Begin by navigating to your GitHub settings (make sure you are logged in!). Scroll down to the bottom of the sidebar and click "Developer Settings".</p>
<p>This will take you to the application page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-230.png" alt="GitHub OAuth Apps view, showing a hacktoberfest and mattermost application that have been previously authorised." width="600" height="400" loading="lazy">
<em>You may see some applications you've previously authorised.</em></p>
<p>Click the "New OAuth App" button to create a new application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-232.png" alt="The new OAuth application page, showing form fields for Application name, homepage URL, application description, and authorisation callback URL." width="600" height="400" loading="lazy"></p>
<p>Fill in the form and click "Register application". This will create your application and take you to the settings page.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-233.png" alt="The Application settings page, which shows the same form fields as the previous form, with additional options to transfer ownership, revoke user tokens, generate client secrets, and upload a logo." width="600" height="400" loading="lazy"></p>
<p>For OAuth applications, you will need the Client ID. You will also need to generate a client secret. Click the "Generate a new client secret" to do so.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/10/image-234.png" alt="The new client secret (obfuscated for security in this image)" width="600" height="400" loading="lazy"></p>
<p>Make sure to save this secret in a secure location as you will not be able to view it again.</p>
<h2 id="heading-using-your-new-application">Using Your New Application</h2>
<p>Now that you have a client ID and secret, you can use your OAuth application in your project. </p>
<p>If you want to learn how to do so, <a target="_blank" href="https://www.freecodecamp.org/learn/quality-assurance/#advanced-node-and-express">freeCodeCamp's curriculum can teach you</a>. </p>
<p>Happy coding!</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[ How to Add Sentry to Your Node.js Project with TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ Sentry.io is an external monitoring and logging service which can help you identify and triage errors in your code.  These logs provide information such as a trace stack, breadcrumbs, and (assuming this is a web application) browser data. This can he... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-add-sentry-to-your-node-js-project-with-typescript/</link>
                <guid isPermaLink="false">66ac7ef210d8e430980ae9dc</guid>
                
                    <category>
                        <![CDATA[ error handling ]]>
                    </category>
                
                    <category>
                        <![CDATA[ logging ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Tue, 28 Sep 2021 16:24:26 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/09/pexels-pixabay-366283.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sentry.io is an external monitoring and logging service which can help you identify and triage errors in your code. </p>
<p>These logs provide information such as a trace stack, breadcrumbs, and (assuming this is a web application) browser data. This can help you triage issues and resolve bugs faster, with less investigative overhead.</p>
<h2 id="heading-how-to-prepare-your-sentry-account">How to Prepare Your Sentry Account</h2>
<p>Begin by navigating to <a target="_blank" href="https://sentry.io">Sentry</a> and clicking "Get Started". You will be taken to the account creation screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-83.png" alt="Sentry's account creation screen." width="600" height="400" loading="lazy"></p>
<p>You can either sign up with OAuth or create separate credentials for Sentry. If you choose to create separate credentials, you'll need to enter an organization name now (this can be changed later). I used my username as my organization name.  </p>
<p>Once you create your account, Sentry will take you through a tutorial to set up your first project. Click "I'm Ready" to be taken to the first step.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-84.png" alt="Sentry's &quot;Choose your project's platform&quot; screen" width="600" height="400" loading="lazy"></p>
<p>For our purposes, the <code>NODE.JS</code> option is the platform you should select. Then click "Create Project".</p>
<p>This takes you to the instructions for preparing the SDK to integrate with your codebase. Leave that page open as you will need your <code>dsn</code> value.</p>
<h2 id="heading-how-to-use-sentry-in-your-code">How to Use Sentry in Your Code</h2>
<p>Your next step is to install the necessary Sentry packages:</p>
<pre><code class="lang-bash">npm install @sentry/node @sentry/integrations
</code></pre>
<p>The <code>@sentry/node</code> package is the core SDK for your Node.js project, and the <code>@sentry/integrations</code> package contains a tool you will use for mapping the file path.</p>
<p>Your Sentry tooling should be loaded as early as possible in your code flow. Ideally, this means you should initialize it within the entry point for your application (that is, <code>index.ts</code>). </p>
<p>Start by importing the packages:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> * <span class="hljs-keyword">as</span> Sentry <span class="hljs-keyword">from</span> <span class="hljs-string">"@sentry/node"</span>;
<span class="hljs-keyword">import</span> { RewriteFrames } <span class="hljs-keyword">from</span> <span class="hljs-string">"@sentry/integrations"</span>;
</code></pre>
<p>The first import pulls in the Sentry-Node tooling, and the second gives you access to the <code>RewriteFrames</code> integration. This integration allows you to adjust the pathing of the stack trace, which is necessary for properly pointing to your compiled JavaScript files.</p>
<p> Now you need to instantiate the Sentry monitor and provide the configuration:</p>
<pre><code class="lang-ts">Sentry.init({
  dsn: process.env.SENTRY_DSN,
  tracesSampleRate: <span class="hljs-number">1.0</span>,
  integrations: [
    <span class="hljs-keyword">new</span> RewriteFrames({
      root: <span class="hljs-built_in">global</span>.__dirname,
    }),
  ],
});
</code></pre>
<p>Here you have passed a configuration object to the <code>Sentry.init()</code> method (which is used to instantiate and initialise the Sentry process). To break these options down:</p>
<ul>
<li><code>dsn</code> is a unique URL used to connect your Sentry instance to your dashboard. We will explore this a bit later.</li>
<li><code>tracesSampleRate</code> determines the percent of events the monitor should send to the dashboard. A value of <code>1.0</code> sends 100% of the captured events – but if you find this to be too noisy you can reduce this number.</li>
<li><code>integrations</code> loads the integrations you want to use. In this case, you are loading the <code>RewriteFrames</code> option and setting the <code>root</code> path for your stack traces to <code>global.__dirname</code> (which resolves to the directory you run your application from).</li>
</ul>
<p>Then, anywhere in your code base where you are logging errors (such as a <code>try / catch</code> block or a <code>.catch()</code> chain), add <code>Sentry.captureException(error)</code> (replacing <code>error</code> with the variable that represents your error object) to pass that error off to your Sentry monitor. </p>
<h2 id="heading-how-to-connect-your-code-to-your-dashboard">How to Connect Your Code to Your Dashboard</h2>
<p>Back on that project setup page, you'll see a URL value for the <code>dsn</code> option in the configuration.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-85.png" alt="Example sentry option, showing a valid dsn URL." width="600" height="400" loading="lazy"></p>
<p><strong>Your <code>dsn</code> should be treated as a secret and not shared with anyone.</strong> You can achieve this by adding it to your <code>.env</code> file (assign it to <code>SENTRY_DSN</code> to match with our configuration from the previous step).</p>
<p>The <code>dsn</code> tells Sentry where to send the captured errors, and the dashboard uses it to link those errors to your new project.</p>
<blockquote>
<p>A note for front end projects:<br>Because you do not have access to a <code>.env</code> on the front end, you will need to expose your <code>dsn</code> publicly. We will cover how to handle this in the next step.</p>
</blockquote>
<p>Once this is set up, you can click "View a sample event for this SDK" in the small print at the bottom of the Sentry page. This will generate a fake error event and take you to the dashboard.</p>
<h2 id="heading-how-to-configure-your-sentry-dashboard">How to Configure Your Sentry Dashboard</h2>
<p>The Sentry website will offer you a quick tour of the dashboard, which you can follow if you would like, or you can skip it and continue with this article.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-86.png" alt="The top half of the Sentry Dashboard" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-87.png" alt="The bottom half of the Sentry dashboard" width="600" height="400" loading="lazy"></p>
<p>This view shows you the specific details for a captured error event. In this case, it is the sample event generated by Sentry from the previous step.</p>
<p>The top half offers information such as the browser data from the user that triggered the error, the error message, and the error type. The bottom half provides the stack trace and breadcrumbs (actions that took place to trigger this error) – both helpful for reproducing this error in triage.</p>
<p>At the very top you should see your project's name (which defaults to your organization name) and a gear. Click that gear to be taken to your project's settings.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-89.png" alt="The Sentry project settings" width="600" height="400" loading="lazy"></p>
<p>Here you see some configurations for your project. The "Name" determines the name of your project. Changing the "Platform" affects how stack traces are rendered. You are welcome to experiment with the other settings as desired.</p>
<blockquote>
<p>For front-end projects:<br>As mentioned earlier, you will need to expose your DSN publicly. You can set your webpage's URL in the "Allowed Domains" to prevent data being sent from any other source.</p>
</blockquote>
<p>On the side bar are a few additional options. Selecting "Client Keys(DSN)" will take you to a page where you can copy your DSN again, if needed. You can also delete and regenerate it if you accidentally exposed it.</p>
<p>Selecting "Alerts" will allow you to configure how you receive notifications for error events. I have mine set to send to a <a target="_blank" href="https://github.com/nhcarrigan/discord-integrations">Discord Webhook</a>, but you can configure a number of integration options for receiving your alerts.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/09/image-91.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Finally you have the main side bar. Here you can configure your organization settings, including renaming your organization or creating additional organizations and projects.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>You have now successfully integrated Sentry with your Node.js-Typescript project. You are now ready to start receiving error information, triaging issues, and improving your project's stability. </p>
<p>Feel free to experiment with Sentry's settings and features to personalize your experience to meet your needs. Happy Coding!</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[ What is SendGrid? SMTP Email Newsletter Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ You may have heard of the term SMTP before, and wondered what it is. SMTP is a common method for handling email messages. Today I am going to explain what SMTP is, and how to use an SMTP provider such as SendGrid to send emails from your address. Wha... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/what-is-sendgrid-smpt-email-newsletter-tutorial/</link>
                <guid isPermaLink="false">66ac7f74ed08c5b0125be191</guid>
                
                    <category>
                        <![CDATA[ communication ]]>
                    </category>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ newsletters ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Fri, 14 May 2021 20:31:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/05/pexels-rakicevic-nenad-1262304.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>You may have heard of the term SMTP before, and wondered what it is. SMTP is a common method for handling email messages.</p>
<p>Today I am going to explain what SMTP is, and how to use an SMTP provider such as <a target="_blank" href="https://sendgrid.com">SendGrid</a> to send emails from your address.</p>
<h2 id="heading-what-is-smtp">What Is SMTP?</h2>
<p>SMTP, or Simple Mail Transfer Protocol, is the method through which internet servers send email messages. When you send an email through your Gmail account, for example, your mail client uses SMTP to send that message to the server. The server then also uses SMTP to send it to the receiving server.</p>
<p>Without diving too much into the technical details, the easiest way to think of it is that SMTP is an email server. </p>
<h2 id="heading-what-is-sendgrid">What is SendGrid?</h2>
<p>SendGrid is an SMTP service provider – in fact, it's the provider freeCodeCamp uses to send out Quincy's weekly newsletter. </p>
<p>Like many SMTP providers, SendGrid offers the use of their mail servers to send your emails. This is an excellent option for sending large volumes of emails, where having to do so manually would take a significant amount of time and effort.</p>
<h3 id="heading-how-to-create-a-sendgrid-account">How to Create a SendGrid Account</h3>
<p>The first step in using SendGrid's services is to create your account. Head on over to the <a target="_blank" href="https://sendgrid.com">SendGrid website</a> to get signed up. They offer multiple pricing models, but the free level will be sufficient at least for this tutorial. </p>
<p>As you scale up your email list, however, you may need additional features from a higher subscription tier.</p>
<p>Once you are signed in, you should see a default dashboard view:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/image-4.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the default SendGrid dashboard view.</em></p>
<h3 id="heading-how-to-set-up-your-domain-or-email-with-sendgrid">How to Set up Your Domain or Email with SendGrid</h3>
<p>From that dashboard view, select "Settings", then choose "Sender Authentication" from the dropdown menu. The Sender Authentication settings are where you tell SendGrid which email accounts to allow emails to be sent from.</p>
<p>There are two approaches here – if you have a custom domain for your emails, you can set up Domain Authentication. If you are using a personal email address, such as a Gmail address, then you will need to set up Single Sender Authentication.</p>
<p>Choose whichever option works best for you, and follow SendGrid's prompts to set it up. Your end result should look similar to this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/image-5.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting Sender Authentication settings.</em></p>
<h3 id="heading-how-to-send-emails-via-sendgrids-api">How to Send Emails Via SendGrid's API</h3>
<p>The actual process of sending the emails is done through <a target="_blank" href="https://sendgrid.com/docs/api-reference/">SendGrid's API</a>. But before you can use the API, you'll need to set up an API key. </p>
<p>From your dashboard view, select "Settings", then select "API Keys". Choose "Create API Key" and select the permissions you want the key to have (I gave mine full permissions, just to avoid issues). </p>
<p>Once you have the key, save it somewhere safe as you will not be able to access it again.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/image-6.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the API Keys settings page.</em></p>
<p>Now that you have the API key, you'll need to set up the code to use the <code>/mail/send</code> endpoint. You can write the code manually, or use one of the helper libraries such as SendGrid's <a target="_blank" href="https://github.com/sendgrid/sendgrid-nodejs">Node.js package</a>. </p>
<p>When using the Node.js package, you set the values for your email as follows:</p>
<ul>
<li><code>to</code>: The address to send the email to.</li>
<li><code>from</code>: The address to send the email from. This should match your settings in the Sender Authentication.</li>
<li><code>subject</code>: The subject of your email.</li>
<li><code>text</code>: The content of your email, if you are sending a plain text email.</li>
<li><code>html</code>: The content of your email, if you are sending an HTML email.</li>
</ul>
<p>The properties in a raw API call are different, as are the properties in the other helper libraries. Be sure to refer to the documentation for your specific approach.</p>
<h3 id="heading-how-to-use-dynamic-templates-in-sendgrid">How to Use Dynamic Templates in SendGrid</h3>
<p>As an alternative option, instead of sending the email content in your API call, you can use a Dynamic Template to generate the content. </p>
<p>A Dynamic Template lets you set the content for emails to send out, and offers Handlebars functionality to replace specific data fields.</p>
<p>To create a Dynamic Template, from your dashboard select "Email API" and then "Dynamic Templates". Then click "Create a Dynamic Template" – you should see your template appear below. </p>
<p>Click on it, then select "Add Version" to open the template selection. Choose the blank template, then select the type of editor you would like to use (I use the code editor).</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/05/image-7.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the editor.</em></p>
<p>You can write the content of your email, and use placeholders such as <code>{{name}}</code> for the dynamic data. These placeholders would be given values through your API calls when you send the emails. </p>
<p>If you want to see how it would render, you can use the "Test Data" tab to add sample data for the placeholders.</p>
<h3 id="heading-how-to-get-blocksbouncesspams-via-sendgrids-api">How to Get Blocks/Bounces/Spams via SendGrid's API</h3>
<p>It's important to track undeliverable emails. SendGrid offers tooling to help track this for you, and that data is available through three different dashboard views (or API endpoints, if you want to parse the data programmatically).</p>
<ul>
<li><code>Blocked</code> emails are emails that were rejected by the receiving email provider's policies, such as university emails that don't accept external traffic, or emails that could not be resolved (the mail server was not found).</li>
<li><code>Bounced</code> emails are emails that were received by the server but returned. This occurs in cases where the email server exists, but the specific user does not, or the email inbox is at capacity.</li>
<li><code>Spam</code> emails are arguably the most important to monitor, as these are generated when a user receives your email and reports to their provider that your email is spam. These directly impact your reputation as a sender, so it is imperative that you do not send an email to someone who has marked your previous emails as spam.</li>
</ul>
<h3 id="heading-other-concerns">Other Concerns</h3>
<p>Speaking of your reputation as a sender, SendGrid offers a top-level metric called "Sender Reputation". This metric is an aggregation of your activity through their platform, and helps give a general idea of the way email providers are likely to handle your emails. </p>
<p>A lower reputation will result in your emails being marked as spam automatically, or even your IP addresses being blocked.</p>
<p>If you are on the free tier for SendGrid, you will be using shared IP addresses. This means that other customers will also be sending emails through that same IP, and their actions can negatively impact your reputation. </p>
<p>If you intend to send large volumes of emails, I recommend that you purchase dedicated IP addresses to ensure that your reputation is protected.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope this article has helped you become more familiar with SendGrid and the services they offer. You should now be ready to start sending your own emails. </p>
<p>If you are planning to start an email newsletter, I wrote an article about <a target="_blank" href="https://www.freecodecamp.org/news/how-to-create-an-email-newsletter-design-layout-send/">creating effective email newsletters</a> that might help.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create an Email Newsletter – Design, Layout, Send ]]>
                </title>
                <description>
                    <![CDATA[ If you manage a large community, chances are you need a way to communicate updates to your members quickly and efficiently. An email newsletter can be a very effective way to do so. In this article, I am going to provide some tips and suggestions for... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-an-email-newsletter-design-layout-send/</link>
                <guid isPermaLink="false">66ac7f3423cc28a03a55e084</guid>
                
                    <category>
                        <![CDATA[ email ]]>
                    </category>
                
                    <category>
                        <![CDATA[ email marketing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ newsletters ]]>
                    </category>
                
                    <category>
                        <![CDATA[ writing tips ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Thu, 13 May 2021 21:19:40 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/05/pexels-anthony-shkraba-5206271.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>If you manage a large community, chances are you need a way to communicate updates to your members quickly and efficiently. An email newsletter can be a very effective way to do so.</p>
<p>In this article, I am going to provide some tips and suggestions for maximizing the impact of your email newsletter: open rates, click-through rate, and not irritating your recipients.</p>
<p>I will also share some technical details and configurations you can use to maximize the deliverability of your email newsletter.</p>
<h2 id="heading-how-to-write-an-impactful-subject-line">How to Write an Impactful Subject Line</h2>
<p>Your subject line is the first thing your readers will see when the email appears in their inbox. It's important, then, that you use something eye catching that encourages them to read the message.</p>
<h3 id="heading-what-to-include-in-the-subject-line">What to Include in the Subject Line</h3>
<p>Your subject line should provide a brief summary of the email content or highlight the key information. Some examples would be:</p>
<blockquote>
<p>Learn Data Structures and Algorithms [free 6-hour coding course]</p>
</blockquote>
<p>This headline comes from one of Quincy's email newsletters, and calls out the first article out of a weekly curated list of five articles.</p>
<blockquote>
<p>Our Community Reached 3,000 Members - Here's What's Next</p>
</blockquote>
<p>Here the headline provides a clear summary of the email's content – an update on the community's success and future plans.</p>
<blockquote>
<p>The Observatory #4: Gravity is the key to community growth</p>
</blockquote>
<p>Pulled from an <a target="_blank" href="https://orbit.love">Orbit</a> newsletter, this subject line has two benefits: Like the others, it summarizes the content of the email. It also helps readers recognize this email as a serial newsletter, and track the number to ensure they have not missed one.</p>
<h3 id="heading-what-to-exclude-from-the-subject-line">What to Exclude from the Subject Line</h3>
<p>You want to avoid headlines which are vague, uninformative, or may seem like spam.</p>
<blockquote>
<p>Click Here! Important Information Within!</p>
</blockquote>
<p>Avoid words and phrases such as <code>click here</code>, <code>urgent</code>, <code>important</code>, and <code>priority</code>. These types of subject lines are often used in spam emails, and can cause your readers to disengage.</p>
<blockquote>
<p>April 2020 Community Update</p>
</blockquote>
<p>While this subject is not going to seem like spam, it is also vague and uninformative. You want your subject to catch the reader's attention and be unique.</p>
<blockquote>
<p>Top Picks Just For You</p>
</blockquote>
<p>Another vague subject line, this one also gives off a "click-bait" vibe. This type of subject line doesn't hook your readers or entice them in to reading your full email.</p>
<h2 id="heading-how-to-craft-an-effective-email-body">How to Craft an Effective Email Body</h2>
<p>The body of your email is where your core content will go. Having a well written and effective email body is essential.</p>
<h3 id="heading-html-vs-plain-text-in-emails">HTML vs. Plain Text in Emails</h3>
<p>Using HTML in your email can make it look nice, certainly. However, HTML emails are more likely to be flagged as spam by the receiving providers.</p>
<p>Plain text emails are generally safer, though they offer less functionality. Additionally, a plain text email will ensure maximum compatibility with all devices, and ensure that your email can be viewed even over slower internet connections.</p>
<p>HTML, on the other hand, offers a much higher level of customization in terms of layout, content, and functionality. You can even implement things like interaction tracking to see who is opening (open rate), reading, and clicking on links in your email (click-through rate). Whether these features are worth the potential impact to delivery is or not is up to you.</p>
<h3 id="heading-how-to-handle-links-in-emails">How to Handle Links in Emails</h3>
<p>Speaking of links, there are some special considerations when it comes to handling links in your email. The key idea is to keep your links easily readable and verifiable.</p>
<p>One important aspect is the transparency of your links. You want to avoid using things like URL shorteners that hide the domain the link points to. Keeping your link's target clear ensures that your readers can trust the safety of the link.</p>
<p>Many sending providers offer click-tracking features, where they will provide metrics on how many of your readers click the links within your email.</p>
<p>While these metrics may seem useful, they do come at a cost – usually the links are obfuscated through a redirect via the provider's domain. This decreases the chance that your readers will click the link.</p>
<h3 id="heading-unsubscribe-links">Unsubscribe Links</h3>
<p>If your email is considered commercial material (marketing, advertisements, and so on.), having an unsubscribe link is <em>required</em>. If it is a transactional (receipt, account updates) or relationship (communicating with existing contacts) email, then an unsubscribe link is not necessarily required but is still strongly recommended.</p>
<p>Having an unsubscribe link allows your recipients to quickly and easily opt-out of receiving future communications. While it may not seem ideal to lose your reader base, it is much better than having your emails flagged as spam (which can impact your sender reputation and cause future emails to anyone with that email provider to be blocked).</p>
<p>Your unsubscribe link does not need to be prominent or featured – a simple link with the text "Unsubscribe" at the bottom of your email is often sufficient.</p>
<h3 id="heading-content-and-layout-of-your-emails">Content and Layout of Your Emails</h3>
<p>The content and layout of your email body are also very important. If you are using plain text to send your email, then your biggest consideration will be the length of your paragraphs. In general, shorter paragraphs are easier to read than a "wall of text", and will help your readers stay engaged with the content.</p>
<p>If you are using HTML, then you have some additional obstacles to consider.</p>
<p>For example, including images may seem very tempting as they can make the email look more visually appealing. But each image increases the time it takes for the email to render on the reader's device, and images aren't as accessible for those who use screen readers as plain text is.</p>
<p>Remember that images should add to the content, not detract from the information.</p>
<p>Another aspect to consider when using HTML is the semantic structure. To avoid any rendering errors or accessibility concerns, you will want to keep your HTML semantically correct. I recommend using an <a target="_blank" href="https://validator.w3.org/">HTML Validator</a> before sending the email to ensure you don't have any errors.</p>
<p>Content length itself is also important. You don't want to overwhelm your readers with too much information in a single email. If you have a lot of content you want to share, consider making a blog post or news article – then, link that article in your email with a brief summary of the content.</p>
<h3 id="heading-how-to-sign-off-in-an-email">How to Sign Off in an Email</h3>
<p>The signature of your email is just as important as the main content. Signing off your email lets your readers know who sent it, and gives you the opportunity to close out the content.</p>
<p>You should include your name and your role within your organization, so that your readers see that the email came from a person. This creates the impression that it is a more personal communication and not an automatically generated email.</p>
<p>Finally, the signature and conclusion also give you the chance to thank your readers for their time. It is good to recognize that your readers took time out of their day to read your content. Time is valuable, and your readers have given you theirs.</p>
<h2 id="heading-technical-aspects-of-email-newsletters">Technical Aspects of Email Newsletters</h2>
<p>Now that you've drafted your first newsletter, you need to address the technical concerns. Namely, you'll need to figure out how you'll send this newsletter.</p>
<h3 id="heading-choose-a-sending-provider">Choose a Sending Provider</h3>
<p>The first question to answer is what will you use to send this email? It is entirely possible to set up and run your own Secure Mail Transfer Protocol (SMTP) server, but that can be a significant investment of time and resources.</p>
<p>Instead, there are a number of providers that will handle sending the emails for you. Here at freeCodeCamp we use <a target="_blank" href="https://www.sendgrid.com">SendGrid</a>. Most providers will offer an API to request that emails be sent, or a UI to prepare your emails. Then they will handle the actual send process for you.</p>
<p>While these providers can get expensive at higher volumes, often times the cost is worth the access to additional features such as email validation and bounce report tracking.</p>
<h3 id="heading-set-up-email-authentication">Set up Email Authentication</h3>
<p>Another important technical aspect is email verification and authentication, especially if you are using a custom domain.</p>
<p>How does a provider such as Gmail confirm that the email has legitimately come from your organization and not someone pretending to be you?</p>
<p>There are a couple of security steps you can take to ensure that your emails are delivered while spoofed emails are not.</p>
<ul>
<li><p><strong>Sender Policy Framework (SPF):</strong> You can use an SPF setting to validate that the mail server is authorized to send emails from your email domain. SPF is enabled via a <code>TXT</code> record in your domain settings. The content of that record will depend on the system you are using to send those emails.</p>
</li>
<li><p><strong>DomainKeys Identified Mail (DKIM):</strong> You can use DKIM to assert that the email message has not been modified in transit from the sending server to the receiving server. DKIM records are set up in your domain settings either as a <code>TXT</code> record or a <code>CNAME</code> record, depending on your provider. These records will indicate the public encryption/decryption keys.</p>
</li>
<li><p><strong>Domain Message Authentication, Reporting, and Conformance (DMARC):</strong> The final step, after enabling SPF and DKIM, is to set up a DMARC record. The DMARC record tells email providers what to do if an email from your domain fails SPF or DKIM, where to send delivery reports, and how often to apply the DMARC policy. A DMARC record is added as a <code>TXT</code> record to your domain settings, as <code>_dmarc.yourdomain.tld</code>.</p>
</li>
</ul>
<p>Congratulations. Now that you have read this article, you should have a basic understanding of how to create and send your first email newsletter. I hope you found this article helpful.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ PowerShell Themes and Windows Terminal Color Schemes – How to Customize Your Command Line ]]>
                </title>
                <description>
                    <![CDATA[ I recently set up and configured Windows Terminal for my local development environment. In this article, I will walk you through the steps to configure your own Terminal.   If you have not done so already, you can download Windows Terminal from the M... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/windows-terminal-themes-color-schemes-powershell-customize/</link>
                <guid isPermaLink="false">66ac7f853da5d5647182a66b</guid>
                
                    <category>
                        <![CDATA[ Powershell ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Windows ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Sat, 06 Mar 2021 01:39:57 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/03/header-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>I recently set up and configured Windows Terminal for my local development environment. In this article, I will walk you through the steps to configure your own Terminal.  </p>
<p>If you have not done so already, you can download Windows Terminal <a target="_blank" href="https://aka.ms/terminal">from the Microsoft Store</a> if you are on Windows 10. Windows Terminal is not available on earlier versions of Windows.</p>
<h2 id="heading-how-to-configure-your-powershell-selections">How to Configure your PowerShell Selections</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-32.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image demonstrating the multi-tab functionality offered by Windows Terminal</em></p>
<p>One of the greatest benefits of Windows Terminal is the ability to use multiple shells in the same screen, switching between tabs to access different shells.</p>
<p>Once you have installed the application, open the terminal and select the <code>v</code> symbol at the top (next to the open tab). You should see a list of available terminals, but we will ignore those for now.</p>
<p>From the dropdown, select the "Settings" option and the <code>settings.json</code> file should open in your default text editor.</p>
<p>There are quite a few properties in here. The first one you will need to look at is the <code>profiles</code> property. The <code>profiles</code> property contains all of your terminal selection options - the nested <code>defaults</code> property contains default settings for <strong>all</strong> profiles, and the <code>list</code> property contains your terminal profiles.</p>
<p>We are going to focus on the <code>list</code> property, which should currently contain values similar to this:</p>
<pre><code class="lang-json">    [
        {
            <span class="hljs-attr">"guid"</span>: <span class="hljs-string">"{61c54bbd-c2c6-5271-96e7-009a87ff44bf}"</span>,
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Windows PowerShell"</span>,
            <span class="hljs-attr">"commandline"</span>: <span class="hljs-string">"powershell.exe"</span>,
            <span class="hljs-attr">"hidden"</span>: <span class="hljs-literal">false</span>
        },
        {
            <span class="hljs-attr">"guid"</span>: <span class="hljs-string">"{0caa0dad-35be-5f56-a8ff-afceeeaa6101}"</span>,
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"cmd"</span>,
            <span class="hljs-attr">"commandline"</span>: <span class="hljs-string">"cmd.exe"</span>,
            <span class="hljs-attr">"hidden"</span>: <span class="hljs-literal">false</span>
        }
    ],
</code></pre>
<p>The <code>list</code> property is an array of objects and determines which terminal executables can be loaded via Windows Terminal. In this example, the options available are Windows PowerShell and the CMD prompt.</p>
<p>Here is a breakdown of the properties in these objects:</p>
<ul>
<li><code>guid</code>: This is a <strong>G</strong>lobally <strong>U</strong>nique <strong>Id</strong>entifier and is used exclusively for the <code>defaultProfile</code> setting (which we will cover later).</li>
<li><code>name</code>: This is the name that displays in the dropdown when you open a new tab in the Windows Terminal.</li>
<li><code>commandline</code>: This is the executable that loads when you open a tab with this profile.</li>
<li><code>hidden</code>: This option is a Boolean and determines whether the profile appears in the new tab dropdown. If you are not using a terminal often, set this to <code>true</code> to keep it from displaying in the dropdown. This allows you to preserve the profile settings while keeping your dropdown list to just the terminals you need.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-29.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the dropdown menu, showing two terminals enabled: Ubuntu-20.04 and Git Bash</em></p>
<h2 id="heading-custom-powershell-window-terminal-configuration-example">Custom PowerShell Window Terminal Configuration Example</h2>
<p>These default options may be all you need, depending on your development environment. I do most of my work in Windows Subsystems for Linux (WSL 2) and occasionally use Git Bash, so I have a couple of extra options.</p>
<pre><code class="lang-json">      {
        <span class="hljs-attr">"guid"</span>: <span class="hljs-string">"{07b52e3e-de2c-5db4-bd2d-ba144ed6c273}"</span>,
        <span class="hljs-attr">"hidden"</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Ubuntu-20.04"</span>,
        <span class="hljs-attr">"source"</span>: <span class="hljs-string">"Windows.Terminal.Wsl"</span>,
        <span class="hljs-attr">"startingDirectory"</span>: <span class="hljs-string">"//wsl$/Ubuntu-20.04/home/nhcarrigan"</span>,
      },
      {
        <span class="hljs-attr">"guid"</span>: <span class="hljs-string">"{00000000-0000-0000-ba54-000000000002}"</span>,
        <span class="hljs-attr">"commandline"</span>: <span class="hljs-string">"%PROGRAMFILES%/git/usr/bin/bash.exe -i -l"</span>,
        <span class="hljs-attr">"icon"</span>: <span class="hljs-string">"%PROGRAMFILES%/Git/mingw64/share/git/git-for-windows.ico"</span>,
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Git Bash"</span>,
        <span class="hljs-attr">"startingDirectory"</span>: <span class="hljs-string">"%USERPROFILE%"</span>,
      },
</code></pre>
<p>You might see some new properties in here.</p>
<ul>
<li><code>source</code>: This property is generated automatically when Windows Terminal detects and generates a profile for a new terminal executable. You should not add this when you manually construct a profile.</li>
<li><code>icon</code>: This property is used to select which <code>.ico</code> icon file is used next to the name in the new tab dropdown.</li>
<li><code>startingDirectory</code>: This is the file path Windows Terminal will point to when you load a new tab with this profile.</li>
</ul>
<h2 id="heading-how-to-construct-a-custom-profile-in-powershell">How to Construct a Custom Profile in PowerShell</h2>
<p>The profile settings for Git Bash were not automatically generated by Windows Terminal, and I had to construct them manually. If you need to do the same, here is how you can do this.</p>
<p>First, you will need to generate a <code>guid</code> value. These take the format of <code>{00000000-0000-0000-0000-000000000000}</code>. You can generate one in Windows PowerShell by running <code>[guid]::NewGuid()</code>, or in WSL by running <code>uuidgen</code>.</p>
<p>Next, define the path to the executable in the <code>commandline</code> property. The <code>%PROGRAMFILES%</code> value points to your "Program Files" directory and will account for the difference between the path for 32-bit and 64-bit applications.</p>
<p>If your installation is located in your user directory instead, you can use the <code>%USERPROFILE%</code> value. The <code>-i -l</code> flags are used to ensure Windows Terminal will load your <code>.bashrc</code> file correctly.</p>
<p>The <code>icon</code> property can be omitted, but if you want the icon to appear next to the terminal name you will need to add the file path to that icon file here.</p>
<p>The <code>name</code> property is required, and determines the display name in the dropdown selector. Here I use "Git Bash", so I know which terminal this option opens.</p>
<p>Finally, the <code>startingDirectory</code> should be set to the default filepath location you would like the terminal to target when it opens. I set mine to <code>%USERPROFILE%</code>, which points the terminal to my Windows user directory on load. This way I can quickly access my "documents" folder or other folders.</p>
<h2 id="heading-how-to-set-your-default-profile-in-powershell-windows-terminal">How to Set your Default Profile in PowerShell Windows Terminal</h2>
<p>Now if you scroll back to the top of your <code>settings.json</code> file you should see a <code>defaultProfile</code> property. This option accepts a <code>guid</code> value, which should match one of the <code>guid</code> values in your <code>list</code> array. Windows Terminal will load a tab with this profile when you start it.</p>
<p>In my case, the bulk of my work is done in WSL so I've set my <code>defaultProfile</code> to that <code>guid</code>:</p>
<pre><code class="lang-json">  <span class="hljs-string">"defaultProfile"</span>: <span class="hljs-string">"{07b52e3e-de2c-5db4-bd2d-ba144ed6c273}"</span>,
</code></pre>
<p>Now when I open my Windows Terminal application, a WSL instance is spawned.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-18.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot depicting a Windows Terminal instance, with a single tab titled "Ubuntu-20.04". "nhcarrigan @ DESKTOP-049HSUK ~" is displayed in the terminal screen.</em></p>
<h2 id="heading-how-to-design-your-color-scheme-in-powershell-windows-terminal">How to Design your Color Scheme in PowerShell Windows Terminal</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-30.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that you have set up your terminal applications, we can focus on styling the text to make it pretty.</p>
<p>Below your <code>profiles</code> property in the <code>settings.json</code>, you should see a <code>schemes</code> property. <code>schemes</code> contains an array of color scheme objects, which look something like this:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"name"</span> : <span class="hljs-string">"Campbell"</span>,

    <span class="hljs-attr">"cursorColor"</span>: <span class="hljs-string">"#FFFFFF"</span>,
    <span class="hljs-attr">"selectionBackground"</span>: <span class="hljs-string">"#FFFFFF"</span>,

    <span class="hljs-attr">"background"</span> : <span class="hljs-string">"#0C0C0C"</span>,
    <span class="hljs-attr">"foreground"</span> : <span class="hljs-string">"#CCCCCC"</span>,

    <span class="hljs-attr">"black"</span> : <span class="hljs-string">"#0C0C0C"</span>,
    <span class="hljs-attr">"blue"</span> : <span class="hljs-string">"#0037DA"</span>,
    <span class="hljs-attr">"cyan"</span> : <span class="hljs-string">"#3A96DD"</span>,
    <span class="hljs-attr">"green"</span> : <span class="hljs-string">"#13A10E"</span>,
    <span class="hljs-attr">"purple"</span> : <span class="hljs-string">"#881798"</span>,
    <span class="hljs-attr">"red"</span> : <span class="hljs-string">"#C50F1F"</span>,
    <span class="hljs-attr">"white"</span> : <span class="hljs-string">"#CCCCCC"</span>,
    <span class="hljs-attr">"yellow"</span> : <span class="hljs-string">"#C19C00"</span>,
    <span class="hljs-attr">"brightBlack"</span> : <span class="hljs-string">"#767676"</span>,
    <span class="hljs-attr">"brightBlue"</span> : <span class="hljs-string">"#3B78FF"</span>,
    <span class="hljs-attr">"brightCyan"</span> : <span class="hljs-string">"#61D6D6"</span>,
    <span class="hljs-attr">"brightGreen"</span> : <span class="hljs-string">"#16C60C"</span>,
    <span class="hljs-attr">"brightPurple"</span> : <span class="hljs-string">"#B4009E"</span>,
    <span class="hljs-attr">"brightRed"</span> : <span class="hljs-string">"#E74856"</span>,
    <span class="hljs-attr">"brightWhite"</span> : <span class="hljs-string">"#F2F2F2"</span>,
    <span class="hljs-attr">"brightYellow"</span> : <span class="hljs-string">"#F9F1A5"</span>
},
</code></pre>
<p>If you have used tools like the <code>chalk</code> package on <code>npm</code>, you may recognize some of these color values (<code>purple</code> here is <code>magenta</code> in chalk.) The other keys do the following:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-34.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the resulting colors from the default theme above.</em></p>
<ul>
<li><code>name</code>: This is used to link the color scheme to one of the profiles we created earlier.</li>
<li><code>cursorColor</code>: This determines the color of your text cursor.</li>
<li><code>selectionBackground</code>: This determines the background color of highlighted text.</li>
<li><code>background</code>: This determines the background color for your terminal.</li>
<li><code>foreground</code>: This determines the foreground color for your terminal. With my current configuration, I haven't seen any distinguishable difference when modifying this value.</li>
</ul>
<p>The color properties determine how each color value sent by a terminal command (such as <code>console.log</code>) are displayed.</p>
<p>The settings I use for my color profile are:</p>
<pre><code class="lang-json">    {
      <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Duotone Dark"</span>,
      <span class="hljs-attr">"black"</span>: <span class="hljs-string">"#1f1d27"</span>,
      <span class="hljs-attr">"red"</span>: <span class="hljs-string">"#d9393e"</span>,
      <span class="hljs-attr">"green"</span>: <span class="hljs-string">"#2dcd73"</span>,
      <span class="hljs-attr">"yellow"</span>: <span class="hljs-string">"#d9b76e"</span>,
      <span class="hljs-attr">"blue"</span>: <span class="hljs-string">"#2488ff"</span>,
      <span class="hljs-attr">"purple"</span>: <span class="hljs-string">"#de8d40"</span>,
      <span class="hljs-attr">"cyan"</span>: <span class="hljs-string">"#6ad7d9"</span>,
      <span class="hljs-attr">"white"</span>: <span class="hljs-string">"#b7a1ff"</span>,
      <span class="hljs-attr">"brightBlack"</span>: <span class="hljs-string">"#353147"</span>,
      <span class="hljs-attr">"brightRed"</span>: <span class="hljs-string">"#d9393e"</span>,
      <span class="hljs-attr">"brightGreen"</span>: <span class="hljs-string">"#2dcd73"</span>,
      <span class="hljs-attr">"brightYellow"</span>: <span class="hljs-string">"#d9b76e"</span>,
      <span class="hljs-attr">"brightBlue"</span>: <span class="hljs-string">"#2488ff"</span>,
      <span class="hljs-attr">"brightPurple"</span>: <span class="hljs-string">"#de8d40"</span>,
      <span class="hljs-attr">"brightCyan"</span>: <span class="hljs-string">"#6ad7d9"</span>,
      <span class="hljs-attr">"brightWhite"</span>: <span class="hljs-string">"#dfd1ed"</span>,
      <span class="hljs-attr">"background"</span>: <span class="hljs-string">"#1f1d27"</span>,
      <span class="hljs-attr">"foreground"</span>: <span class="hljs-string">"#b7a1ff"</span>
    },
</code></pre>
<p>I encourage you to play with these values until you find a color set that fits your preferences.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-20.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the resulting colors from my theme settings.</em></p>
<h3 id="heading-how-to-link-your-color-scheme-to-a-profile">How to Link Your Color Scheme to a Profile</h3>
<p>Now that you have defined your color settings, you need to link those settings to a terminal profile. You could apply the settings to the <code>defaults</code> object within the <code>profiles</code> property, which will apply them to all of your terminals. I prefer to configure different color settings for different terminals, so I can quickly identify when I am in the correct window.</p>
<p>Let's apply this to our WSL profile. Add a <code>colorScheme</code> key to your profile object, and give it a value that matches your scheme's <code>name</code>. You should now have something like this:</p>
<pre><code class="lang-json">      {
        <span class="hljs-attr">"guid"</span>: <span class="hljs-string">"{07b52e3e-de2c-5db4-bd2d-ba144ed6c273}"</span>,
        <span class="hljs-attr">"hidden"</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Ubuntu-20.04"</span>,
        <span class="hljs-attr">"source"</span>: <span class="hljs-string">"Windows.Terminal.Wsl"</span>,
        <span class="hljs-attr">"startingDirectory"</span>: <span class="hljs-string">"//wsl$/Ubuntu-20.04/home/nhcarrigan"</span>,
        <span class="hljs-attr">"colorScheme"</span>: <span class="hljs-string">"Duotone Dark"</span>,
      }
</code></pre>
<p>If you reload your Windows Terminal, you should see your new colors take effect.</p>
<h3 id="heading-how-to-configure-additional-appearance-settings">How to Configure Additional Appearance Settings</h3>
<p>My full WSL profile object has a few extra settings:</p>
<pre><code class="lang-json">      {
        <span class="hljs-attr">"guid"</span>: <span class="hljs-string">"{07b52e3e-de2c-5db4-bd2d-ba144ed6c273}"</span>,
        <span class="hljs-attr">"hidden"</span>: <span class="hljs-literal">false</span>,
        <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Ubuntu-20.04"</span>,
        <span class="hljs-attr">"source"</span>: <span class="hljs-string">"Windows.Terminal.Wsl"</span>,
        <span class="hljs-attr">"startingDirectory"</span>: <span class="hljs-string">"//wsl$/Ubuntu-20.04/home/nhcarrigan"</span>,
        <span class="hljs-attr">"colorScheme"</span>: <span class="hljs-string">"Duotone Dark"</span>,
        <span class="hljs-attr">"useAcrylic"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">"acrylicOpacity"</span>: <span class="hljs-number">0.5</span>,
        <span class="hljs-attr">"fontFace"</span>: <span class="hljs-string">"PxPlus IBM VGA8"</span>,
        <span class="hljs-attr">"fontSize"</span>: <span class="hljs-number">16</span>,
        <span class="hljs-attr">"experimental.retroTerminalEffect"</span>: <span class="hljs-literal">true</span>
      },
</code></pre>
<p>You can tweak these settings to your preference as well.</p>
<ul>
<li><code>useAcrylic</code> will enable the Windows 10 transparency effect on the terminal background</li>
<li>If the transparency effect is enabled, <code>acrylicOpacity</code> will determine how strong the transparency effect is. The lower the number, the higher the transparency.</li>
<li><code>fontFace</code> will select which font is used for the terminal. Note that you need to have the font installed on your computer. I use the <a target="_blank" href="https://github.com/pocketfood/Fontpkg-PxPlus_IBM_VGA8">PxPlus IBM VGA8</a> font, and downloaded the <code>.ttf</code> file which Windows can install.</li>
<li><code>fontSize</code> will determine the size of the font (in <code>pt</code>).</li>
<li>The <code>experimental.retroTerminalEffect</code> is my favorite setting. This simulates "scan lines" on your terminal, much like old CRT monitors.</li>
</ul>
<p>Here's what my final setup looks like:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-21.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting the final result of the theme settings.</em></p>
<p>Any of these appearance settings can be passed in to the <code>defaultSettings</code> option instead to apply them to all of your profiles globally.</p>
<pre><code class="lang-json">    <span class="hljs-string">"defaultSettings"</span>:
    {
        <span class="hljs-attr">"useAcrylic"</span>: <span class="hljs-literal">true</span>,
        <span class="hljs-attr">"acrylicOpacity"</span>: <span class="hljs-number">0.1</span>,
        <span class="hljs-attr">"fontFace"</span>: <span class="hljs-string">"Cascadia Code"</span>,
        <span class="hljs-attr">"fontSize"</span>: <span class="hljs-number">10</span>
    },
</code></pre>
<h2 id="heading-how-to-configure-additional-settings-in-powershell-windows-terminal">How to Configure Additional Settings in PowerShell Windows Terminal</h2>
<p>There are a few additional settings that are worth your time to consider.</p>
<p>After the <code>schemes</code> property, you should see an <code>actions</code> property. This contains an array of keyboard shortcut settings.</p>
<p>By default, Windows Terminal assigns the copy and paste commands to <code>Ctrl+Shift+C</code> and <code>Ctrl+Shift+V</code> respectively. You can bind these to the default <code>Ctrl+C</code> and <code>Ctrl+V</code> with these settings:</p>
<pre><code class="lang-json">    { <span class="hljs-attr">"command"</span>: { <span class="hljs-attr">"action"</span>: <span class="hljs-string">"copy"</span>, <span class="hljs-attr">"singleLine"</span>: <span class="hljs-literal">false</span> }, <span class="hljs-attr">"keys"</span>: <span class="hljs-string">"ctrl+c"</span> },
    { <span class="hljs-attr">"command"</span>: <span class="hljs-string">"paste"</span>, <span class="hljs-attr">"keys"</span>: <span class="hljs-string">"ctrl+v"</span> },
</code></pre>
<p>The <code>singleLine</code> option set to <code>false</code> preserves line breaks in the copied text.</p>
<p>Likely near the top of your <code>settings.json</code> file, there are two properties which also affect the behavior of copying text from your terminal:</p>
<ul>
<li><code>copyOnSelect</code> defaults to <code>false</code>. When set to true, highlighting text in the terminal with your mouse will copy it to your clipboard.</li>
<li><code>copyFormatting</code> defaults to <code>false</code>. When set to true, text formatting will also be copied (otherwise, the content is copied as plain text).</li>
</ul>
<p>Copying text from your terminal is generally safe, but <em>pasting</em> text into your terminal <a target="_blank" href="https://www.nhcarrigan.com/dont-paste-to-terminal/">can be dangerous</a>. Windows Terminal comes with a couple of protections to help avoid the risks:</p>
<ul>
<li><code>largePasteWarning</code> defaults to <code>true</code> and may not be present in your <code>settings.json</code> file. This setting triggers a dialogue box when you attempt to paste more than 5KB of content.</li>
<li><code>multiLinePasteWarning</code> defaults to true and may not be present in your <code>settings.json</code> file. This setting triggers a dialogue box when you attempt to paste text content that contains line breaks (a common tactic for clipboard hijacking attacks is to force commands to run on paste with newline characters).</li>
</ul>
<p>I strongly recommend leaving these protections in place.</p>
<h2 id="heading-additional-resources">Additional Resources</h2>
<p>Congratulations! You have now set up and configured your own Windows Terminal setup. </p>
<p>For additional customization options that you did not read about in this article, visit the <a target="_blank" href="https://docs.microsoft.com/en-us/windows/terminal/customize-settings/startup">Windows Terminal Customization Documentation</a>.</p>
<p>For a large selection of color schemes, available as downloadable JSON files, check out <a target="_blank" href="https://windowsterminalthemes.dev/">Windows Terminal Themes</a>. This is where I got my settings from, which I tweaked a little to my personal taste.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/03/image-28.png" alt="Image" width="600" height="400" loading="lazy">
<em>The image which started it all - a demonstration screenshot of my <a target="_blank" href="https://www.freecodecamp.org/news/send-email-newsletter-with-the-sendgrid-api/">email blast tool</a>.</em></p>
<p>Happy coding.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a RocketChat Chatbot with TypeScript ]]>
                </title>
                <description>
                    <![CDATA[ Today I will show you how to build your own Rocket.Chat bot and test it locally. This is the same process I used to build freeCodeCamp's moderation chat bot for our community's self-hosted chat server. This code is now running in production, and lots... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-rocketchat-bot-with-typescript/</link>
                <guid isPermaLink="false">66ac7f2534f459ab1f112a76</guid>
                
                    <category>
                        <![CDATA[ Chat ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Thu, 07 Jan 2021 23:30:51 +0000</pubDate>
                <media:content url="https://cdn-media-2.freecodecamp.org/w1280/5fecb6ff7af2371468bb4b4c.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Today I will show you how to build your own Rocket.Chat bot and test it locally.</p>
<p>This is the same process I used to build freeCodeCamp's <a target="_blank" href="https://github.com/freeCodeCamp/rocketchat-bot">moderation chat bot</a> for our community's self-hosted chat server. This code is now running in production, and lots of people are using it.</p>
<h2 id="heading-how-to-set-up-a-rocketchat-server">How to Set up a Rocket.Chat Server</h2>
<p>Your first step is to get an instance of Rocket.Chat running locally – you will need this to test the bot's functionality. </p>
<p>You can use freeCodeCamp's <a target="_blank" href="https://github.com/freeCodeCamp/chat-config/blob/main/docker-compose.dev.yml">docker file</a>, which will spin up both Rocket.Chat and MongoDB automatically for a development environment. This will save you a lot of time.</p>
<p>You can either clone <a target="_blank" href="https://github.com/freeCodeCamp/chat-config/blob/main/docker-compose.dev.yml">this repository</a>, or manually create your own docker file based on our configuration. This tutorial will assume that you are using our existing docker file.</p>
<blockquote>
<p>Note: If you do not have docker installed, you will need to install it. The installation process is different for each operating system. I personally use Windows 10, so I installed <a target="_blank" href="https://www.docker.com/products/docker-desktop">the Docker desktop client</a> and had to enable <code>hardware virtualisation</code> in my BIOS.</p>
</blockquote>
<p>Within your Rocket.Chat directory, create a <code>.env</code> file and insert the following contents:</p>
<pre><code class="lang-env">COMPOSE_FILE=docker-compose.dev.yml
PORT=3000
ROOT_URL=http://localhost:3000
ROCKETCHAT_VERSION=latest
</code></pre>
<p>Then open your terminal pointed at that same directory and run:</p>
<pre><code class="lang-bash">docker-compose up -d
</code></pre>
<p>You should see three success messages in your terminal:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-180.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing the console output for the <code>docker-compose up -d</code> command. Three docker images were created, and each shows <code>done</code>.</em></p>
<p>Now if you open your browser and navigate to <code>localhost:3000</code> you should see your local Rocket.Chat instance. The first screen you see will be the Setup Wizard, which will walk you through creating your Admin account. </p>
<p>Most developers use the Admin account for root-level access to configure their chat. Because this is a local instance, your credentials' security is less important than in a live instance. </p>
<p>Fill in your information to create the admin account:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-181.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing the Admin Info modal, with inputs for <code>Name</code> set to Nicholas Carrigan, <code>Username</code> set to nhcarrigan, <code>Organization Email</code> set to nick@freecodecamp.org, and <code>Password</code> which is obfuscated. Below the input fields is a button labelled <code>Continue</code>.</em></p>
<p>The next screen is the Organization Info screen. This information is optional. For this tutorial, we will leave this information blank. </p>
<p>Clicking <code>Continue</code> will take you to the Server Info page. Here you set the name for your chat server (which will appear in the <code>title</code> metadata), your default language, the server type, and the 2FA setting.</p>
<p><strong>Be sure to turn off the automatic 2FA setup for your local instance or you could be locked out of your own server.</strong></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-182.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing Server Info modal, with inputs for <code>Site Name</code> set to fCC ChatBot tutorial, <code>Language</code> set to Default, <code>Server Type</code> with no selection made, and <code>Auto opt in new users for Two Factor via Email</code> set to No. Below the input fields are buttons labelled "Back" and "Continue".</em></p>
<p>The final step is to optionally register your server and gain access to Rocket.Chat's services such as push notifications. Note that these are paid services.</p>
<p>For the purpose of this tutorial, you can select the <code>Keep standalone</code> option. Then you can decide whether you want any paid services later.</p>
<p>After clicking <code>Continue</code>, you'll see a modal indicating that your workspace is ready to use. Then you should see your new chat room. The default channel created by the Setup Wizard is <code>general</code>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-183.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing Rocket.Chat after completing the setup wizard. Sidebar on the left shows a <code>general</code> channel, and primary windows shows a system message that "nhcarrigan has joined the channel".</em></p>
<p>If you see this, congratulations. You are half way there and now have a functional chat server.</p>
<h2 id="heading-how-to-set-up-a-bot-account-in-rocketchat">How to Set up a Bot Account in Rocket.Chat</h2>
<p>Now we need to create a bot user in our local chat server for our code to connect to.</p>
<p>Select the three dots at the top of the sidebar and choose <code>Administration</code>. Then select <code>Users</code> from the new sidebar that appears, and click the <code>+New</code> button in the top right. This opens a pane for creating a new user account.</p>
<p>Fill in the information and credentials for your bot account. </p>
<p>A few key things to note:</p>
<ul>
<li>Leave  <code>Require password change</code> and <code>Set random password and send by email</code> set to off.</li>
<li>Leave <code>Send welcome email</code> set to off.</li>
<li>Select <code>bot</code> from the <code>Roles</code> dropdown menu.</li>
</ul>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/12/image-185.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image showing the Rocket.Chat settings screen. The left sidebar shows the list of settings - <code>Users</code> has been selected. The center screen shows a list of user accounts - nhcarrigan and Rocket.Cat. The right sidebar shows the Add User interface, with inputs for <code>Name</code> set to Tutorial Bot, <code>Username</code> set to tutorial-bot, <code>Email</code> set to nhcarrigan@gmail.com, <code>Verified</code> toggled off, <code>Status Message</code> with no value, <code>Bio</code> with no value, <code>Nickname</code> with no value, <code>Password</code> which is obfuscated, <code>Require Password Change</code> which is toggled off, <code>Set random password and send by email</code> which is toggled off, <code>Roles</code> with "bot" selected, <code>Join default channels</code> which is toggled on, and <code>Send Welcome Email</code> which is toggled off.</em></p>
<blockquote>
<p>Rocket.cat is a built-in account used for system notifications (i.e. Rocket.Chat updates).</p>
</blockquote>
<p>Save the changes, and your bot account should now be created! Keep a note of the username and password, as we will need these for the code.</p>
<h2 id="heading-how-to-code-your-rocketchat-chatbot">How to Code your Rocket.Chat Chatbot</h2>
<p>Now it's time to create the code. Start with a new, empty folder for your project.</p>
<h3 id="heading-initial-rocketchat-chatbot-project-setup">Initial Rocket.Chat Chatbot Project Setup</h3>
<p>We will begin with initializing a <code>node.js</code> project. You are welcome to use <code>npm init</code> to generate a <code>package.json</code>, or you may create one manually. </p>
<p>Either way, you will need to add some specific values to the <code>scripts</code> section:</p>
<pre><code class="lang-json">  <span class="hljs-string">"scripts"</span>: {
    <span class="hljs-attr">"prebuild"</span>: <span class="hljs-string">"rm -rf ./prod"</span>,
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node ./prod/bot.js"</span>
  },
</code></pre>
<p>Next you will install your necessary dependencies. First, install the development dependencies:</p>
<pre><code class="lang-bash">npm install --save-dev typescript @types/node
</code></pre>
<p>Then, install your primary dependencies:</p>
<pre><code class="lang-bash">npm install @rocket.chat/sdk dotenv
</code></pre>
<p>Your next step is to set up the TypeScript configuration.</p>
<p>If you have installed TypeScript globally, you'll be able to call <code>tsc --init</code> and automatically generate a configuration file. Otherwise, you'll need to manually create a <code>tsconfig.json</code> file in your project's root directory.</p>
<p>Either way, these are the settings you will need for this project:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES5"</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>,
    <span class="hljs-attr">"noImplicitAny"</span>: <span class="hljs-literal">false</span>,
  }
}
</code></pre>
<p>If you are using <code>git</code> for version control, you will need to create a <code>.gitignore</code> file. This file tells <code>git</code> which files/folders to ignore. In this case, you want to ignore: </p>
<ul>
<li>the compiled JavaScript in <code>prod</code></li>
<li>your Node modules</li>
<li>your <code>.env</code> secrets.</li>
</ul>
<p>Add these to your <code>.gitignore</code>:</p>
<pre><code class="lang-txt">/node_modules/
/prod/
.env
</code></pre>
<p>Speaking of secrets, you should set those up now. Create a <code>.env</code> file, and add the following values:</p>
<pre><code class="lang-txt">ROCKETCHAT_URL="localhost:3000"
ROCKETCHAT_USER="tutorial-bot"
ROCKETCHAT_PASSWORD="********"
ROCKETCHAT_USE_SSL=""
</code></pre>
<p><a target="_blank" href="https://github.com/naomis-archive/fcc-rocketchat-tutorial/tree/9cd28ab2adea2c4ce9294c0c35682031cf343b5f">View the code at this point</a>.</p>
<h3 id="heading-how-to-write-the-primary-rocketchat-chatbot-code">How to Write the Primary Rocket.Chat ChatBot Code</h3>
<p>Now it is time to write the initial bot code. Create a <code>src</code> folder within your project directory, and inside that <code>src</code> folder create a <code>bot.ts</code> file. Your file structure should now look like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-9.png" alt="Image" width="600" height="400" loading="lazy">
_Image showing a file tree. From top to bottom: A <code>node_modules</code> folder, which is collapsed, a <code>src</code> folder which contains a <code>bot.ts</code> file, a <code>.env</code> file, a <code>.gitignore</code> file, a <code>.package-lock.json</code> file, a <code>package.json</code> file, and a <code>tsconfig.json</code> file. The files show they are being tracked by <code>git</code>, except the <code>node_modules</code> folder and <code>.env</code> file._</p>
<blockquote>
<p>The <code>package-lock.json</code> file is created/updated by <code>npm</code> whenever you run <code>install</code>. This should be committed to your repository too, as it is required for the <code>npm ci</code> command.</p>
</blockquote>
<p>Within your <code>bot.ts</code> file you will write the basic code that powers your bot. Start with your necessary imports:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { api, driver } <span class="hljs-keyword">from</span> <span class="hljs-string">"@rocket.chat/sdk"</span>;
<span class="hljs-keyword">import</span> dotenv <span class="hljs-keyword">from</span> <span class="hljs-string">"dotenv"</span>;
</code></pre>
<p>Because <code>node</code> doesn't load environment variables automatically, you need to call <code>dotenv</code>'s <code>config()</code> method to bring your <code>.env</code> values into the node process:</p>
<pre><code class="lang-ts">dotenv.config();
</code></pre>
<p>Now you can extract those variables from the node environment. Use destructuring to grab the values:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> {
  ROCKETCHAT_URL,
  ROCKETCHAT_USER,
  ROCKETCHAT_PASSWORD,
  ROCKETCHAT_USE_SSL,
} = process.env;
</code></pre>
<p>Aside from <code>ROCKETCHAT_USE_SSH</code>, these environment values are <em>required.</em> Missing one will cause the code you write to error out, so you need to add a step to verify that all of these values are present.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">if</span> (!ROCKETCHAT_URL || !ROCKETCHAT_USER || ROCKETCHAT_PASSWORD) {
  <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Missing required environment variables."</span>);
  process.exit(<span class="hljs-number">1</span>);
}
</code></pre>
<p>Now you can use the Rocket.Chat SDK to connect your bot to the account you created.</p>
<p>Because the methods in the SDK are asynchronous, you will use an anonymous immediately-invoked function expression (IIFE) to enable <code>async/await</code> features.</p>
<pre><code class="lang-ts">(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-comment">// Nothing here yet</span>
})();
</code></pre>
<blockquote>
<p>These next steps will be written inside this function.</p>
</blockquote>
<p>First, determine if your bot should use SSL to connect to the chat server. If your chat server uses <code>HTTPS://</code>, this should be set to <code>true</code>. Because you are developing locally, this is set to <code>false</code> as <code>localhost</code> does not have an HTTPS protocol.</p>
<p>To ensure your code would work in a production environment as well, you can dynamically set this value based on your environment variables:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">const</span> ssl = !!ROCKETCHAT_USE_SSL;
</code></pre>
<p>Next, use the SDK <code>driver</code> to interface with your chat server. Connect the <code>driver</code> to your server:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">await</span> driver.connect({ host: ROCKETCHAT_URL, useSsl: ssl });
</code></pre>
<p>Login as the bot account:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">await</span> driver.login({
    username: ROCKETCHAT_USER,
    password: ROCKETCHAT_PASSWORD,
  });
<span class="hljs-keyword">await</span> api.login({ username: ROCKETCHAT_USER, password: ROCKETCHAT_PASSWORD });
</code></pre>
<p>Have the bot join your <code>general</code> room, to handle instances where the bot isn't already in the room. Also tell the bot to listen for messages with the <code>subscribeToMessages()</code> method.</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">await</span> driver.joinRooms([<span class="hljs-string">"general"</span>]);
  <span class="hljs-keyword">await</span> driver.subscribeToMessages();
</code></pre>
<p>Finally, have the bot send a message when it comes online (so you can confirm connection status).</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">await</span> driver.sendToRoom(<span class="hljs-string">"I am alive!"</span>, <span class="hljs-string">"general"</span>);
</code></pre>
<p>Now, build and run the code. Call these necessary scripts in your terminal:</p>
<pre><code class="lang-bash">npm run build
npm run start
</code></pre>
<p>After some built-in logging from the SDK, you should see the bot send its online message in your chat server.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-10.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting a Rocket.Chat message. The message was sent by <code>tutorial-bot</code>, which has the <code>bot</code> role by its name. The message text reads <code>I am alive!</code>.</em></p>
<p><a target="_blank" href="https://github.com/naomis-archive/fcc-rocketchat-tutorial/tree/216a9a20a4872670d838a475c0140fa1110638d3">View the code at this point</a>.</p>
<h3 id="heading-how-to-write-the-command-handler">How to Write the Command Handler</h3>
<p>Your bot will now connect to the chat server and listen for messages, but it does not have any functions.</p>
<p>Before you can add commands, you need to build the infrastructure to handle those commands.</p>
<p>First, tell the bot to handle messages. Just after your <code>subscribeToMessages()</code> call, add a line to handle responding to messages:</p>
<pre><code class="lang-ts">driver.reactToMessages();
</code></pre>
<p>You'll see an error in your Intellisense, because the <code>reactToMessages()</code> method expects a callback function.</p>
<p>You could write the callback function within this method directly, but instead you will modularise your code and create an exported handler. This keeps your code cleaner and more maintainable.</p>
<p>Create a folder called <code>commands</code> within your <code>src</code> folder, and add two files: <code>CommandHandler.ts</code> and <code>CommandList.ts</code>. Within the <code>CommandList.ts</code> file, we are going to add a single line for now:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList = [];
</code></pre>
<p>As you build commands, you will add them to this array to be able to iterate through them in our handler.</p>
<p>Now you need to write your handler's logic in the <code>CommandHandler.ts</code> file.</p>
<p>Start with your required imports:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { driver } <span class="hljs-keyword">from</span> <span class="hljs-string">"@rocket.chat/sdk"</span>;
<span class="hljs-keyword">import</span> { IMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@rocket.chat/sdk/dist/config/messageInterfaces"</span>;
<span class="hljs-keyword">import</span> { CommandList } <span class="hljs-keyword">from</span> <span class="hljs-string">"./CommandList"</span>;
</code></pre>
<p>Define the command handler function:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandHandler = <span class="hljs-keyword">async</span> (
    err: unknown,
    messages: IMessage[]
): <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt; =&gt; {
    <span class="hljs-comment">// Code will go here.</span>
}
</code></pre>
<p>Add some error handling.</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">if</span> (err) {
    <span class="hljs-built_in">console</span>.error(err);
    <span class="hljs-keyword">return</span>;
  }
  <span class="hljs-keyword">const</span> message = messages[<span class="hljs-number">0</span>];
  <span class="hljs-keyword">if</span> (!message.msg || !message.rid) {
    <span class="hljs-keyword">return</span>;
  }
</code></pre>
<p>If you see an error in the <code>err</code> parameter, you need to <code>return</code> early.</p>
<p>The <code>messages</code> parameter takes an array of messages, but you want to react to the <em>first</em> message, so we extract it from that array as <code>message</code>.</p>
<p>Then, for TypeScript's assertion handling, you need to exit early if certain properties are missing or undefined. In this case, <code>message.msg</code> is the text content of the message, and <code>message.rid</code> is the ID of the room the message was received in.</p>
<p>For cleaner/more readable code, you can destructure some values out of the message object. Get the room's name from the <code>rid</code> value - the SDK includes a method for doing just this. Also get the prefix and the command that is called.</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">const</span> roomName = <span class="hljs-keyword">await</span> driver.getRoomName(message.rid);
  <span class="hljs-keyword">const</span> [prefix, commandName] = message.msg.split(<span class="hljs-string">" "</span>);
</code></pre>
<p>Add the logic to iterate through our array of commands. TypeScript will identify some errors due to missing structures, but you can ignore those for now as you have not written the commands yet.</p>
<pre><code class="lang-ts">  <span class="hljs-keyword">if</span> (prefix === <span class="hljs-string">"!fCC"</span>) {
    <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> (commandName === Command.name) {
        <span class="hljs-keyword">await</span> Command.command(message, roomName);
        <span class="hljs-keyword">return</span>;
      }
    }
    <span class="hljs-keyword">await</span> driver.sendToRoom(
      <span class="hljs-string">`I am sorry, but \`<span class="hljs-subst">${commandName}</span>\` is not a valid command.`</span>,
      roomName
    );
  }
</code></pre>
<p>This block of code might be a bit confusing as we have not established how commands work yet.</p>
<p>First, the bot determines if the message begins with the correct prefix. If it does not, the bot will ignore the message.</p>
<p>Then the bot iterates through the list of commands, and if it finds a command for which the <code>name</code> value matches the command name sent in the message, it will run that command.</p>
<p>If it does not find <em>any</em> matching commands, it will send a response in the room that the command was not valid.</p>
<p>Before you move on to a command, head back to the <code>reactToMessages()</code> call in the <code>bot.ts</code> file and pass your new handler as the callback:</p>
<pre><code class="lang-ts">  driver.reactToMessages(CommandHandler);
</code></pre>
<p>You may need to manually import it:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { CommandHandler } <span class="hljs-keyword">from</span> <span class="hljs-string">"./commands/CommandHandler"</span>;
</code></pre>
<p><a target="_blank" href="https://github.com/naomis-archive/fcc-rocketchat-tutorial/tree/b3e62ec3ad4215f4081293077774b3e81f67a52c">View the code at this point</a>.</p>
<h3 id="heading-how-to-write-a-command">How to Write a Command</h3>
<p>TypeScript offers an <code>interface</code> feature which can be used to define an object structure. </p>
<p>In your <code>src</code> folder, create an <code>interfaces</code> folder, and create a <code>CommandInt.ts</code> file. </p>
<p>Inside that file, you will define your command type. First, import the message type again.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { IMessage } <span class="hljs-keyword">from</span> <span class="hljs-string">"@rocket.chat/sdk/dist/config/messageInterfaces"</span>;
</code></pre>
<p>Now build the exported interface for the command definitions.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">interface</span> CommandInt {
    name: <span class="hljs-built_in">string</span>;
    description: <span class="hljs-built_in">string</span>;
    command: <span class="hljs-function">(<span class="hljs-params">message: IMessage, room: <span class="hljs-built_in">string</span></span>) =&gt;</span> <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">void</span>&gt;
}
</code></pre>
<p>Congratulations! You are now ready to build the <code>ping</code> command.</p>
<p>Within your <code>src/commands</code> folder, create a <code>ping.ts</code> file. Start with your necessary imports: the Rocket.Chat driver and your new command interface.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { driver } <span class="hljs-keyword">from</span> <span class="hljs-string">"@rocket.chat/sdk"</span>;
<span class="hljs-keyword">import</span> { CommandInt } <span class="hljs-keyword">from</span> <span class="hljs-string">"../interfaces/CommandInt"</span>;
</code></pre>
<p>Define and export the command:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> ping: CommandInt = {
    name: <span class="hljs-string">"ping"</span>,
    description: <span class="hljs-string">"Pings the bot."</span>,
    command: <span class="hljs-keyword">async</span> (message, room) =&gt; {
        <span class="hljs-comment">// Code will go here.</span>
    }
}
</code></pre>
<p>Let's have the bot respond with "Pong!" when this command is called. Inside the function, replace the comment with:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">await</span> driver.sendToRoom(<span class="hljs-string">"Pong!"</span>, room);
</code></pre>
<p>Now, load this command in your list of commands. Open the <code>CommandList.ts</code> file, where you will import our new command and include it in the array.</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { ping } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ping"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList = [ping];
</code></pre>
<p>With this, you should see the errors in the <code>CommandHandler.ts</code> file disappear as well, because TypeScript is inferring that the <code>CommandList</code> array contains <code>CommandInt</code> types.</p>
<p>For extra type safety, and to ensure you do not accidentally add values to your <code>CommandList</code> that aren't proper <code>CommandInt</code> objects, explicitly type this variable.</p>
<pre><code><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> { ping } <span class="hljs-keyword">from</span> <span class="hljs-string">"./ping"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> CommandList: CommandInt[] = [ping];
</code></pre><p>Run your <code>build</code> and <code>start</code> scripts again to test this new feature.</p>
<p>Call your <code>ping</code> command in the chat room. You should see a successful response:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-11.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting a Rocket.Chat conversation. The first message was sent by <code>nhcarrigan</code>, who has the <code>Admin</code> role by his name. The first message content reads "!fCC ping". The second message was sent by <code>tutorial-bot</code>, which has the <code>Bot</code> role by its name. The second message content reads "Pong!".</em></p>
<p>Call a <code>pong</code> command. You should see that the bot identifies it is not a valid command:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/01/image-12.png" alt="Image" width="600" height="400" loading="lazy">
<em>Image depicting a Rocket.Chat conversation. The first message was sent by <code>nhcarrigan</code>, who has the <code>Admin</code> role by his name. The first message content reads "!fCC pong". The second message was sent by <code>tutorial-bot</code>, which has the <code>Bot</code> role by its name. The second message content reads "I am sorry, but <code>pong</code> is not a valid command".</em></p>
<p><a target="_blank" href="https://github.com/naomis-archive/fcc-rocketchat-tutorial/tree/df645aa39fbb9f18513128cfb5b55b804719ee78">View our final code</a>.</p>
<h2 id="heading-further-exploration">Further Exploration</h2>
<p>Congratulations! You have now successfully built a basic Rocket.Chat chatbot.</p>
<p>If you would like to explore further features and command implementations, feel free to browse <a target="_blank" href="https://github.com/nhcarrigan/rocketchat-bot">our live bot's codebase</a>. </p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
