<?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[ #chatbots - 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[ #chatbots - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Mon, 25 May 2026 22:38:18 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/chatbots/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a RAG Chatbot with Agent Cloud and Google Sheets ]]>
                </title>
                <description>
                    <![CDATA[ Today's companies are data factories. Every interaction, transaction, and internal process generates a constant stream of information. This data holds immense value, promising to improve decision-making, streamline operations, and unlock deep custome... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-rag-chatbot-agent-cloud-google-sheets/</link>
                <guid isPermaLink="false">66c3754b1e62f88108dcb76b</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ LLM&#39;s  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ankur Tyagi ]]>
                </dc:creator>
                <pubDate>Wed, 26 Jun 2024 14:43:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/Orange---Yellow-Gradient-Make-Design-Blog-Banner--73-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Today's companies are data factories. Every interaction, transaction, and internal process generates a constant stream of information.</p>
<p>This data holds immense value, promising to improve decision-making, streamline operations, and unlock deep customer insights. </p>
<p>But data often remains siloed and inaccessible. It may be spread across different departments and systems, and it can be challenging to understand and utilize effectively.</p>
<p>This is where the concept of Retrieval-Augmented Generation (<a target="_blank" href="https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/">RAG</a>) technology comes in. By combining the power of retrieval-based techniques and modern generative AI tools, you can build Retrieval-Augmented Generation (RAG) chat applications that allow you to interact with your data using a simple chat interface. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image-57.png" alt="Conceptual flow of using RAG with LLMs." width="600" height="400" loading="lazy">
<em>What is Retrieval-Augmented Generation (RAG)?</em></p>
<p>But before you can chat about your data, a lot of “legwork” is involved. Setting up the infrastructure – the pipeline, vector database, message broker, and knowledge retrieval – is a complex and time-consuming process. This is where the open source tool <a target="_blank" href="https://theankurtyagi.com/what-is-agent-cloud/">Agent Cloud</a> comes in.</p>
<p>In this guide, you'll learn all about Agent Cloud and what it can do. We'll start by looking at some background info and the current problems we're dealing with. Then, we'll see how Agent Cloud can help solve them.</p>
<h2 id="heading-how-i-started-working-with-agent-cloud">How I Started Working with Agent Cloud</h2>
<p>I'm passionate about new technology and <a target="_blank" href="https://theankurtyagi.com/blog/">developer tools</a>, and I sit somewhere between Product Marketing, Growth, and Developer Advocacy. I specialize in the creation of high-quality, technical written content for educational purposes. </p>
<p>I've been involved with the web for ~14 years, the last 4 of which have been documented in punishing detail on my <a target="_blank" href="https://theankurtyagi.com/">website</a>.</p>
<p>I liked being a Software Engineer but, what I really <strong>love</strong> to do is code, design, develop, and then write.</p>
<p>Earlier this year I met <a target="_blank" href="https://www.linkedin.com/in/andrewnada/">Andrew</a> (founder of Agent cloud) in a private Slack group. He was seeking someone who could not only write about the product but also discuss and teach people about what they're building. I reached out to him, and after two rounds of discussions, we began working together.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>I started with building some cool RAG chatbots in my local and later wrote a couple of comprehensive guides on "<a target="_blank" href="https://www.agentcloud.dev/blog">How to Build a RAG Chatbot with Agent Cloud</a>".</p>
<p>In this article, I'll teach you how to build a RAG chat app using Agent Cloud to privately and securely talk with your Google Sheets data. I'll also talk about why I think Agent Cloud is good open source developer tool.</p>
<h2 id="heading-table-of-contents">Table of Contents:</h2>
<ul>
<li><a class="post-section-overview" href="#heading-what-is-agent-cloud">What is Agent Cloud</a>?</li>
<li><a class="post-section-overview" href="#heading-what-is-retrieval-augmented-generation">What is RAG</a>?</li>
<li><a class="post-section-overview" href="#heading-challenges-of-building-a-rag-chatbot-without-agent-cloud">Challenges of Building a RAG Chatbot Without Agent Cloud</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-agent-cloud-via-docker">How to Set Up Agent Cloud via Docker</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-models-in-agent-cloud">How to Add Models in Agent Cloud</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-your-gcp-service-account-key">How to Create Your GCP Service Account Key</a></li>
<li><a class="post-section-overview" href="#heading-how-to-enable-google-sheets-api">How to Enable Google Sheets API</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-data-sources">How to Set Up your Data Sources</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-tools">How to Set Up tools</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-an-agent">How to Set Up an Agent</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-a-task">How to Create a Task</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-your-app">How to Set Up your App</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-what-is-agent-cloud">What is Agent Cloud?</h2>
<p><a target="_blank" href="https://www.agentcloud.dev">Agent Cloud</a> is an open-source platform that lets you build private, secure chat applications powered by large language models (think ChatGPT). </p>
<p>It streamlines this process by providing a "RAG as a service" offering and a built-in pipeline that allows you to split, chunk, and embed data from over 300 sources (including <a target="_blank" href="https://www.agentcloud.dev/integrations/google-sheets">Google Sheets</a>, Salesforce, Atlassian Confluence, <a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-rag-chat-app-with-agent-cloud-and-bigquery-15b">BigQuery</a>, <a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-rag-chatbot-with-agentcloud-and-mongodb-4la7">MongoDB</a>, <a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-chat-app-with-your-postgres-data-using-agent-cloud-33hk">Postgres Data</a>, SharePoint, and OneDrive).</p>
<p><img src="https://lh7-us.googleusercontent.com/mCVaA9lJyTTTLY7YNebA8AyR5Tj_iQ3werHlAERD9-NgHPQ6BXUo42NMIm9HwnIXni-iWaTrjVtROtmx8XhY7RXF_wh2LnYAifRDnP7GYFl9EAvP83EuEtoHa7BM4OZBjCokVzYwBF-4Nrd8TlG-JvQ" alt="List of data sources agentcloud supports" width="1600" height="809" loading="lazy">
<em>Data sources</em></p>
<h2 id="heading-what-is-retrieval-augmented-generation">What is Retrieval-Augmented Generation?</h2>
<p>RAG is a process for enhancing the accuracy of large language models. It does this through the on-demand retrieval of external data and by injecting context into the prompt, at runtime. </p>
<p>This data can come from various sources, such as your customers' documentation/web pages (through scraping), and data or documents from dozens (if not hundreds) of 3rd party applications like their Databases, Google BigQuery, HubSpot, Google Ads, Google Analytics 4 (GA4) and so on.</p>
<p>For those who want to dive deeper into Retrieval-Augmented Generation and understand its broader applications and significance, I highly recommend reading this comprehensive <a target="_blank" href="https://blogs.nvidia.com/blog/what-is-retrieval-augmented-generation/">blog by NVIDIA</a>. It offers valuable insights and context that complement the practical aspects covered in this article.</p>
<h2 id="heading-challenges-of-building-a-rag-chatbot-without-agent-cloud">Challenges of Building a RAG Chatbot Without Agent Cloud</h2>
<p>If you're working with these AI tools on a daily basis, it becomes easy to understand the value they bring and realize the significance of Agent Cloud in simplifying the chatbot development process. </p>
<p>But to fully appreciate its benefits, you should understand how chatbot development was handled before such tools were available.</p>
<p>Before tools like Agent Cloud, creating a RAG (Retrieval-Augmented Generation) chatbot was a daunting and resource-intensive task. You had to manually integrate various components, which required significant expertise in multiple areas. </p>
<p>Here are some challenges faced and the solutions the Agent Cloud team had to devise:</p>
<h3 id="heading-data-retrieval-and-management">Data Retrieval and Management:</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-36.png" alt="Image" width="600" height="400" loading="lazy">
<em>List of data sources</em></p>
<ul>
<li><strong>Problem:</strong> Ensuring that the chatbot could efficiently retrieve and manage data from sources like Google Sheets, Databases, and so on. </li>
<li><strong>Without Agent Cloud:</strong> Developers had to write custom scripts for data retrieval, often using APIs to fetch data from Google Sheets. This involved handling data formatting, error checking, and real-time updates manually. It was a time-consuming process prone to errors.</li>
<li><strong>Agent Cloud's Solution:</strong> Automates data retrieval and management, ensuring seamless and accurate integration with minimal manual intervention.</li>
</ul>
<h3 id="heading-natural-language-processing-nlp">Natural Language Processing (NLP):</h3>
<ul>
<li><strong>Problem:</strong> Implementing NLP to understand user queries and generate human-like responses.</li>
<li><strong>Without Agent Cloud:</strong> Developers needed to integrate standalone NLP engines such as TensorFlow. This required training models on vast datasets, fine-tuning them for accuracy, and constantly updating them to handle new queries effectively.</li>
<li><strong>Agent Cloud's Solution:</strong> Offers built-in NLP capabilities, significantly reducing setup time and providing high-quality language understanding out of the box.</li>
</ul>
<h3 id="heading-scalability-and-maintenance">Scalability and Maintenance:</h3>
<ul>
<li><strong>Problem:</strong> Ensuring the chatbot could handle increasing data loads and user interactions.</li>
<li><strong>Without Agent Cloud:</strong> Building a scalable architecture meant investing in robust server infrastructure, writing efficient code, and continuously monitoring and maintaining the system to handle growth.</li>
<li><strong>Agent Cloud's Solution:</strong> Designed to scale effortlessly, allowing developers to focus on improving functionality rather than managing infrastructure.</li>
</ul>
<h3 id="heading-user-interaction-and-experience">User Interaction and Experience:</h3>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/06/image-37.png" alt="Image" width="600" height="400" loading="lazy">
<em>Agent cloud app UI</em></p>
<ul>
<li><strong>Problem:</strong> Creating an engaging and user-friendly interface.</li>
<li><strong>Without Agent Cloud:</strong> Developers had to build custom interfaces, often from scratch, which required additional design and development resources. Ensuring smooth interactions and responsiveness was a major challenge.</li>
<li><strong>Agent Cloud's Solution:</strong> Provides pre-built templates and easy integration options, enhancing the user experience with minimal effort.</li>
</ul>
<p>By understanding these challenges, you can see how a tool like Agent Cloud helps the process of building RAG chatbots. It addresses the pain points of manual data handling, complex NLP integration, scalability issues, and user interface design, providing an all-in-one solution that saves time and resources.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>You don't need any prior knowledge of RAG chat apps or Google Sheets to follow along because Agent Cloud handles the data splitting, encoding, storage, and synchronization. This allows you to focus on talking to your data and interpreting the results.</p>
<h2 id="heading-how-to-set-up-agent-cloud-via-docker">How to Set Up Agent Cloud via Docker</h2>
<p>First, you'll need to install Docker on your system if you don't have it already. <a target="_blank" href="https://docs.docker.com/get-docker/">Docker</a> is a platform for running containerized applications.</p>
<p>Open your terminal and run the following command to clone the Agent Cloud repository from GitHub:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>git</span><span> clonehttps://github.com/rnadigital/agentcloud.git</span></p></td></tr></tbody></table>

<p>Use the following command to move into the newly cloned <code>agentcloud</code> directory:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>cd</span><span> agentcloud</span></p></td></tr></tbody></table>

<p>To run Agent Cloud locally, execute this command:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>chmod</span><span> +x install.sh &amp;&amp; ./install.sh</span></p></td></tr></tbody></table>

<p>This command will grant executable permissions to the install.sh script and then run it. The script will download the necessary Docker images and start the Agent Cloud containers within your Docker environment.</p>
<p><img src="https://lh7-us.googleusercontent.com/IJP_WeswIONKA5EsL87jVisv0mZRsk__P5BajAlXZU3fQW8Fif6mdjqW0t-NTCkU_ZNHAk6PJ4U5UthUmDFOsOQhnmQyY6HwMxHEDIxfqy-VfZODKOq7jFv9OpAlXkR1AszdYK0gkn0RDEut3Y7U7K4" alt="Image" width="1587" height="1600" loading="lazy">
<em>local dev setup- agent cloud</em></p>
<p>Once the installation script finishes successfully, you can view the running Agent Cloud containers in the Docker application.</p>
<p><img src="https://lh7-us.googleusercontent.com/oZ4mwbfNiCtFcv4scaILguo5QYVR_cwU5mpJqEzDzq-2gMHtyrD-XbZJnMiloPDFmVcFaUc6KQLyBWw6SnnSlVrTU-IcBIspkIELSZaGJ3M-9bRaq7H9NX94tdug39a98p0XQfa3RNmCYsxiSlTQDUA" alt="Image" width="1600" height="536" loading="lazy">
<em>local docker services</em></p>
<p>Once everything is up and running, you can access the Agent Cloud user interface in your web browser. </p>
<p>Navigate to the URL:</p>
<table><colgroup></colgroup><tbody><tr><td><p><span>http://localhost:3000/register</span></p></td></tr></tbody></table>

<p>This will typically open the registration page where you can create an account to use Agent Cloud. </p>
<p><img src="https://lh7-us.googleusercontent.com/-o54n5I5Z_6RByvP6IaDXyDC5hlLgkRMFCEHlvJukZ5RWMV31G0ty2NZC09xA-O2-wslq_BUWCxGMcWRX1RT-ed5D75MFqOvNZR5-qA1Dg_lDChV-BGnrdNgq4epGxGGWmgSfT0qLvxq8J80tgAG64Q" alt="Image" width="1049" height="1600" loading="lazy">
<em>signup- page- agent cloud</em></p>
<p>You can now log in using the credentials you created during signup.</p>
<p>Once you've successfully registered and logged in, you'll be greeted by the Agent Cloud user interface. This interface provides a central hub for managing your data sources, agents, tasks, models, applications, credentials, and so on.</p>
<p><img src="https://lh7-us.googleusercontent.com/zEAo52ay_80MFLRDZPeRUgMCgx0VtPhOzX_68BSkO0Bkh9-66sAtrVYRTig15imqVFTAHs6OZ0fijXYrZxUMgeExMkRFyTEI9OvKijZWBZKzaQcYrBl0dfJ-MH5E5_5G-IpcTs-312lIdq77INYDk00" alt="Image" width="1600" height="780" loading="lazy">
<em>Home screen - agent cloud</em></p>
<p>Congratulations! You've successfully set up Agent Cloud. Now let's move on to the next step and build our RAG chat application using Google Sheets as the data source.</p>
<h2 id="heading-how-to-add-models-in-agent-cloud">How to Add Models in Agent Cloud</h2>
<p>Go to the Models tab in Agent Cloud. Click the Add Models button to add two types of models. </p>
<ul>
<li>A fast embed model is a lightweight model that runs locally on your machine. It splits and chunks your data before embedding it.</li>
<li>OpenAI is a popular cloud-based LLM provider.</li>
</ul>
<p><img src="https://lh7-us.googleusercontent.com/SlXyhi9xFjz8o1dsMnNApxNDJ8G-NEppj6jfP1TkyaNjU3X6Ewt5NuKQ4mj9SKYxsQOMJ650ErJVvJWR9w4WbNfPRtb26pXnjzoUEsOX6jN7foDbiaM_U6jUJE9HSKqQpSDK54QKjLyI_T9yr2xTGDs" alt="Image" width="1600" height="348" loading="lazy">
<em>Models screen- agent cloud</em></p>
<h2 id="heading-how-to-create-your-gcp-service-account-key">How to Create Your GCP Service Account Key</h2>
<p>Agent Cloud offers two authentication methods for accessing your Google Sheets data:</p>
<ul>
<li>Google (Auth) – This method involves directly authorizing Agent Cloud through your Google account.</li>
<li>Service Key Account – This approach utilizes a service key, a credential specifically created for application access to Google Cloud Platform (GCP) services, including Google Sheets.</li>
</ul>
<p>For this guide, I'll focus on the service key account method, which is generally considered a more secure and streamlined approach for application-to-application communication.</p>
<p><strong>Here is what you’ll need:</strong></p>
<ul>
<li>A Google Cloud Platform (GCP) project with the Google Sheets API enabled.</li>
<li>A service account key is created within your GCP project. This key will be used to authenticate Agent Cloud.</li>
</ul>
<p>I'll walk you through creating a service account key in your GCP project and configuring Agent Cloud to use it to access your Google Sheets data.</p>
<p>First, create a Google Cloud Platform account. Then create a new project. You can give your project any name that you prefer.</p>
<p><img src="https://lh7-us.googleusercontent.com/s1m6tovJn9Hv7NsWNat4-0AKU_PzwiO6oujqSFwG0Yj-lEyVFbwBMrNIWd-h6ill46ZbHqmdrBH8_xTxXWRP-I6G33n2qB9jhYqCNvtQiYqmc15rSJ7jgP9Qw4y4CfaWDkHfNVl_cb4qHa5HhfyTnns" alt="Image" width="1600" height="892" loading="lazy">
<em>How to Create a GCP Account</em></p>
<p>Navigate to the "IAM &amp; Admin" section and select "Service Accounts."</p>
<p><img src="https://lh7-us.googleusercontent.com/jObPCuwTVS1B9-z5yy4rX4Xi775Ur2AGz8B8k-dISs92F-0Ww5Nk4i3m77VzwvKT5w8pjtpHvEBqfvPlKQf6HC_hF4ghh6mQmeAj_BQm7qH3DJeF_tUFZU2lrDvZ5jB3taEpmQu5kn4cWM_W8mWbA_M" alt="Image" width="1600" height="687" loading="lazy">
<em>IAM and Admin Screen</em></p>
<p>Click "Create Service Account" and provide a name and description.</p>
<p><img src="https://lh7-us.googleusercontent.com/un89_I_sIaEsLrROHvoZF10CYb0KeOrRjhzcm_kregQD-4v7-7Tg0xkhVOqqTNwPcaE0xvio_SL9OD4JFxxwql_T_YIbazfAUADcwh-tkM8FZ8YNwiDiEgTqA-zELb4kIILxFYGDw6t4Vc8qYD7UFPI" alt="Image" width="1600" height="656" loading="lazy">
<em>How to add service account</em></p>
<p>Enter your service account name and unique service ID, then click <strong>Done</strong>.</p>
<p><img src="https://lh7-us.googleusercontent.com/pKIZ1n1-dTCXB0Lzqb5unx-ChrghpEVp8z_zC0Cv6N5PhN2oaHKtgBjsutM_YvynvtSO17cq89uIB6koyktx0W-vxGy_xIyr_nOlwDe_jcvU4ZtU1fGKDG7lMxPvgMZgjkgMO6odWV56gR3BAKWq53I" alt="Image" width="1464" height="1286" loading="lazy">
<em>How to create a service account in GCP</em></p>
<p>Under the actions tab, click the manage keys option.</p>
<p><img src="https://lh7-us.googleusercontent.com/ju2mGTR2qMExBbW1vt8budeR1MeA9uNZddtx-IJhFAUaV-bw0GlVJUny9kdXJWndgWA4VXpl70DtYMpXCKQj7-zLus_3iJkl430EoIIcNtBfe2FThCFwQirwlCb0YJwJHb5z54HVKbuw4WlC8PY-HyE" alt="Image" width="1600" height="598" loading="lazy">
<em>Project</em></p>
<p>Click the <strong>ADD KEY</strong> button and select the Create Key option.</p>
<p>Select JSON as the key type, then click on Create New Key. </p>
<p><img src="https://lh7-us.googleusercontent.com/cynXfWX4n8ngRB-mB_CTLvUC1xA4ZqmSnaYtv6K3haSgJyAGlTk-l__J2LXZhupGJpJcZ9LW6NDlw6l05YlKev6sYVsXC9vMjCD3LgPGcCX9O405L-Ixn-LV8jmJbiRcd97y2TfL3zvezgZMi4eNYKw" alt="Image" width="1600" height="855" loading="lazy">
<em>Add keys in GCP</em></p>
<p>Select JSON as the key type, then click on Create. Your service account JSON or key JSON will automatically download.</p>
<p><img src="https://lh7-us.googleusercontent.com/RmAJh50XGOSlSbK-hqWveHc4GozeFZSTsB3oU7y7d4nJ-tiEbfdcyQDdrUOfJonS-8w2GAw30vF1AlII8SOEPHSGz7Ip2Xdc60ypSi_gfljQJa_w84UoyfakUM_U-DcbcOlujk4uN0rrFDXG19aW0Rc" alt="Image" width="1600" height="820" loading="lazy">
<em>Create private key in GCP</em></p>
<p>Keep this file safe we will use it later for authentication. </p>
<h2 id="heading-how-to-enable-google-sheets-api">How to Enable Google Sheets API</h2>
<p>You'll also need to enable the Google Sheets API in your Google Cloud project.</p>
<p>Open the left navigation menu and navigate to "APIs &amp; Services" and then "Library."</p>
<p><img src="https://lh7-us.googleusercontent.com/1wVRoIvsYl6cYlaxeck2Ob2EviXaktCRdI68xVMjwSbXfABsYWTAkCHNEW7kwc2Ww2MoBop8-3-vS9s_1FIGuM7lq1E5cmp02dZ4ApPdbasZ6SneopXV5PGaiGF5AnVUVV9LVTdMQW8qSpOCzzfMs40" alt="Image" width="1600" height="1022" loading="lazy">
<em>APIs and Services in GCP</em></p>
<p>Type "Google Sheets API" in the search bar and press Enter.</p>
<p>The Google Sheets API will appear in the search results. Click on it.</p>
<p><img src="https://lh7-us.googleusercontent.com/TUq6HeWJfhZkZaAHQRst9cZKKlG3zbQkU8NBfl5tEZ43pgBbyYPDiLpHUt9yu1xF1-e8XWUp2isXS7zRcYonVlOwoEj2KJn2PZbK65UmA1KWv2y9AcoCxIaTuCaq5pJ2rrCo1wiZbwL1nDnBhsjPzec" alt="Image" width="1600" height="642" loading="lazy">
<em>Google Sheets API</em></p>
<p>On the API details page, click the "<strong>ENABLE</strong>" button.</p>
<p><img src="https://lh7-us.googleusercontent.com/Rh0qrd79Yqlw1YF5JZ_CSJwvO8ZpnytUs0392Y3OBEmmpLar1JyuOggQo-qms4qWtv9ZPLS69uiNmn4Hi4fSneAQZRIR-eRxWtbagqwNrf0q5qNGIxKJNAnjNk1uDziRLlzny4ZWZL0zeljWkmFT-7E" alt="Image" width="1600" height="879" loading="lazy">
<em>Enable- Google Sheets API</em></p>
<h2 id="heading-how-to-set-up-your-data-sources">How to Set Up your Data Sources</h2>
<p>Agent Cloud empowers you to process data from a wide range of sources. </p>
<p>In this guide, I'll use Google Sheets as the data source. Sheets is a popular web-based spreadsheet application included in Google Workspace. Google Sheets allows you to create, edit, and collaborate on spreadsheets in real-time. </p>
<p>For this example, I'll be working with a financial consumer complaints dataset stored in a Google Sheet. </p>
<p>This dataset contains several columns representing key sales stages and details, potentially including:</p>
<ul>
<li>Complaint ID</li>
<li>Submitted Via</li>
<li>Date Submitted</li>
<li>Date Received</li>
<li>State</li>
<li>Product</li>
<li>Sub-product</li>
<li>Issue</li>
<li>Sub-issue</li>
<li>Company</li>
<li>Public Response</li>
<li>Company Response to Consumer</li>
<li>Timely Response?</li>
</ul>
<p>Here's how to connect your Google Sheets data:</p>
<p>Navigate to the Data Sources tab within the Agent Cloud interface.</p>
<p>Click the button labeled <strong>New Connection</strong>. This will initiate the process of adding a new data source.</p>
<p><img src="https://lh7-us.googleusercontent.com/Tr8WlGVlJTmef0xrTvAxjLeTHCpLua_VDxZ9jamnlXQDY8wKsPf0skYQ_b1PFE0d9K13ndHS-piLpDKu16ikxLL9-AUb4lpgFw2pA6-TOI0lRwLQR5KBPbus5FDqJNkbKQYXdf2s4daLRHP4gPci0Ec" alt="Image" width="1600" height="756" loading="lazy">
<em>Data Sources- Screen- Agent Cloud</em></p>
<p>Search and select “Google sheets” as the data source.</p>
<p><img src="https://lh7-us.googleusercontent.com/aNI6G2gH3HNF4XqibSxNOHjXWwW7X_jgtyK79B0k46LJUL1j_1G9vgYOtUdgT__6mjet3AMXEosqRLsrXZHZsf2rW3UhKPKdpAO2v6RFl8acpdINe6l6-1Y4ZGP52oP4xQVrm1XBlpHVuZVYLfx6-M0" alt="Image" width="1600" height="732" loading="lazy">
<em>Create a Datasource</em></p>
<p>I've named our data source <strong>Financial Consumer Complaints</strong>, which you can name however you want. Add a clear and concise description. The data sync will be manual. This means you'll need to initiate the data refresh process whenever you want the latest information from your Google Sheets to be reflected in Agent Cloud.</p>
<p>Enter the appropriate row batch size. Row batch size means how many rows are processed from the Google sheet. For example, the default value 200 would process rows 1-201, then 201-401, and so on. </p>
<p>Choose the authentication method as “<strong>Service Account Key Authentication</strong>”.</p>
<p>Enter the JSON key <strong>we downloaded earlier above</strong> under the Service Account Information field.</p>
<p>Enter the link to the Google spreadsheet you want to sync. To copy the link, click the 'Share' button in the top-right corner of the spreadsheet, then click 'Copy link'.</p>
<p><img src="https://lh7-us.googleusercontent.com/L9rtuBctsZISgx997WPk-zW25t46yJEvTMg7-wOCE7yBYPrd6l58BkPRFSWIErEf-1QR8v_6QHWQMtOlMyjOVfPPUUiE6yTOSg5BV5DexE3Jw_fANfmPSQRQqueAYJ3ODS3HRFzmA19PN8JYtOTXbi4" alt="Image" width="1600" height="1576" loading="lazy">
<em>Adding details under datasource</em></p>
<p>Click the submit button.</p>
<p>Select the collection and fields you want to sync to the vector database and enter their descriptions.</p>
<p><img src="https://lh7-us.googleusercontent.com/oPDKJbdi5uWh4q0staVJA4gTUytt_EVxCPBeIhV8VUGBsShtYeH9OJ_1R1uvN6HJYcgBvX64DHKt4qxNggdV6bx0filBtMdsuDo_xyhJjmipgIO52dzNmy-ABAtOwi-x-l7hCJoo6lFITMOpSDz83eo" alt="Image" width="1600" height="607" loading="lazy">
<em>Model pop-up</em></p>
<p>Click continue and Choose the field you want to embed then choose the model. </p>
<p><img src="https://lh7-us.googleusercontent.com/fa8oHY4rDm9SHQ4La-FM5c8BuWq1eWOsTzAhyq-IgMBaGIXR5crLog7gQ3Ziq0X_cngVy5J9yCF6Ld8u-Py6ByQI_S72jB4a5An8BHES6lng2675hjQeyP-ai0_zBY5pmTmX1LgRDI1qLPGsQ3Ws0QA" alt="Image" width="1600" height="649" loading="lazy">
<em>Data sources</em></p>
<p>The connection to your Google Sheet is now established. </p>
<p>During the initial run, Agent Cloud will process your spreadsheet data and convert it into a format suitable for efficient retrieval. This processed data will then be stored within Agent Cloud's vector store.</p>
<p><img src="https://lh7-us.googleusercontent.com/DWPrunB5TnUXQg7E0gV_XwoETIGO-Qo7cBpN8oYUl9Up--j3_roKiNS6--3CZZADnSeQWfjtO-j3r9RfQsVxQBsZ_NZvhq5Ahnkik2fGPaz0B6bDDVUJRq5rVXDxEkZ0fIIxMxRjqRdxZOIAM3SXiug" alt="Image" width="1600" height="353" loading="lazy">
<em>New- datasource- screen</em></p>
<p>If you're comfortable with technical details, you can verify the data's presence in the Qdrant vector store running locally on port 6333. </p>
<p>This can be accessed through: http://localhost:6333/dashboard#/collections.</p>
<p>Look for a new collection corresponding to your Google Sheet data.</p>
<p><img src="https://lh7-us.googleusercontent.com/gEdkROWowqm1Xr8Q0ixb65CVZB-UNrzLnNh6KPXkPhAZaP5vHIOSJMeK28nyXye2I846SbMhfo9qG9I2qp67r6BNJLHJDM_z9kYc-KSoN0bUpfUS0CIoQBV_qQcVeXm5mqbIxclnM4VCN4pmc_w1u1k" alt="Image" width="1600" height="817" loading="lazy">
<em>collection-screen</em></p>
<p>You can click the collection to view the payloads and the fields populated in the collection.</p>
<p><img src="https://lh7-us.googleusercontent.com/kQtXRqdF1VR_4UmJUqYGcNMcoVrx9kj1Ncrkgvgb3nzEX7s8UvHscOkYEx18P1qxYy6U04UjLDz-SqjVtTsvjHjxajx5qwsAsVkRPXy1nFBAtjELcht8cSL1vE4jVl49J39KHZuIohiNkAFscO9H4m0" alt="Image" width="1600" height="1070" loading="lazy">
<em>Payload</em></p>
<h2 id="heading-how-to-set-up-tools">How to Set Up Tools</h2>
<p>Tools play a crucial role in facilitating effective interaction between the AI agent and its environment, enabling seamless information processing and action execution aligned with its objectives. </p>
<p>These tools can include functions, APIs, data sources, and resources tailored to empower the agent to autonomously and efficiently undertake diverse tasks. While you have the flexibility to craft your own tools as per their requirements, Agent Cloud also streamlines the process by automatically generating a tool upon the addition of a new data source, </p>
<p>Click the Tools tab tools to switch to the Tools page and click the <strong>New</strong> button.</p>
<p>Enter the tools' names and add a description. A verbose and detailed description helps agents better understand when to use each tool.</p>
<p>Choose the data source and click the Save button.</p>
<p><img src="https://lh7-us.googleusercontent.com/URmNamQ6ccOn5FbABAaBo7tfSjAmxZZgT_sN5W8aNrzdfFPbMCMcRwUk6X5YNL_CKTctP-cQxUURBwCADnGQyALwEUmmoQgBBUAsdtEDUWkWxH9oRp15pKHFcnObNpCkjw_eEdSiGnZbdVZodNraQ5Y" alt="Image" width="1600" height="543" loading="lazy">
<em>Edit tool screen</em></p>
<h2 id="heading-how-to-set-up-an-agent">How to Set Up an Agent</h2>
<p>AI Agents are intelligent systems that excel at handling routine or repetitive tasks by perceiving their environment, collecting data, and using that data to make decisions. </p>
<p>Click the Agents tab and then click on the New Agent button.</p>
<p><img src="https://lh7-us.googleusercontent.com/5JrsomIfl-WKZ2hOLW-MZ34xhRpJdhx9hzKwAz2Ab0kDtCVTnZy_rsgjoreGuS533ZCgq125n11M9siNy_AlFHTuMXOHhCSkFGzbE6gNSCQ7YMxw4-Sut1f_-ydDeKchOJjeAtcKxUVoSBmattKAiIg" alt="Image" width="1600" height="691" loading="lazy">
<em>New Agent Screen</em></p>
<p>Define an Agent's <strong>Name</strong>, <strong>Role</strong>, <strong>Goal</strong>, and <strong>Backstory</strong> as shown below.</p>
<p>In the "Model" section, choose the AI engine to power your agent's reasoning capabilities.  For this example, we've selected  "OpenAI GPT-4" as both the Model and Function Calling Model.</p>
<p>Choose the tool we set up earlier under Tools (Optional).</p>
<p><img src="https://lh7-us.googleusercontent.com/d_NajepTkSjuk-tkpiuWcdupeY02HU5nYatqyfjMgj6WXXvBJTgYuStRoSgCFAqNpU4WK8MIarqLJY5MvytjkuQWI4CFyBvTvDsylYEAWgKS8p9f-EbO6LvHYnRFwBd5yQjDvt6XGlriuEfGkicVi-g" alt="Image" width="1119" height="1600" loading="lazy">
<em>Adding Details in Agent Screen</em></p>
<p>If "OpenAI GPT-4" isn't already configured within Agent Cloud, you can easily add it. Click on the "Model" field and select "Add new model." A new window will appear, allowing you to define the model's name, type, credentials (your OpenAI API key), and the specific LLM model.  </p>
<p><img src="https://lh7-us.googleusercontent.com/0M9gkgwNfR-2VPGdgVN75Aqh-_aZuotKjasIVbiuOEaV6Wf8O-rQZo3bwk8-ZdHv8tfgRywXnlSy57An1rndCfzmfExx4K7nZ_UXpqDOJ_x1q0IUEnYaMRWqI6EzoEWcDzwx4CsFeWncc2bIiI82uOA" alt="Image" width="1600" height="1114" loading="lazy">
<em>Model- popup</em></p>
<p>Click the Save button. Once you save this information, "GPT-4" will be available for future agent creation.</p>
<p><img src="https://lh7-us.googleusercontent.com/3qE96774xn2RHAecevtTlCQ4GUv_WcC6r3sFZFdPNGUNCafENkdQ5SaONMSLzwwIsFyjIgLyp-XlVU-ZijHBybv_ay2KCICrxcR71w7GgqCuklvXK5znfIoRtM03Wd9pSdXFUEWVKnrYnjaGv__l8kk" alt="Image" width="1600" height="846" loading="lazy">
<em>Agents-Screen</em></p>
<h2 id="heading-how-to-create-a-task">How to Create a Task</h2>
<p>Tasks are specific actions assigned to agents for completion. To create a new task, navigate to the Tasks tab and click the "Add Task" button.</p>
<p><img src="https://lh7-us.googleusercontent.com/wN1Wy93IPaxiU6NdElwizuJ4e6meqz4-jMiibnKN02WZqNikxMPWOZKsWDNVSlGU-X7IpY1cqPPAlF13aVKSVBGyw-ImPGKYXH2XH-1Yjg3FjI66mBrU-_hrPfbTntKJ1JZ4keZ5X06jnK06-ikwNdE" alt="Image" width="1600" height="622" loading="lazy">
<em>Create Tasks Screen</em></p>
<p>Enter the following details in the below pop-up window. </p>
<ul>
<li><strong>Task name</strong>: Give your task a clear and concise title that reflects its purpose.</li>
<li><strong>Detailed description</strong> of what the task entails.</li>
<li>Choose the tool we created earlier, which is “<strong>Financial Consumer Complaint</strong>” in my case.</li>
<li>Select the preferred Agent we created earlier we named it “<strong>Customer Complaints Agent</strong>”</li>
</ul>
<p><img src="https://lh7-us.googleusercontent.com/RujrqL0gjUYk31aJSYKQYyY55T_nIy-PdSdm8uhcodHGcs8lsHyctuJLnkI4COAL34tXHFjnsjRvypzRaThbIkVEhlTU2quvmaA5cxihN_MHGfg6QaB1i7oBgtXx2Bwa5hEQLbdtEM8Za70JgLRuONI" alt="Image" width="1298" height="1298" loading="lazy">
<em>Adding Details in Tasks Screen</em></p>
<p>Click the <strong>Save</strong> button to save the task.</p>
<h2 id="heading-how-to-set-up-your-app">How to Set Up Your App</h2>
<p>Now, buckle up because we're about to embark on the exciting part: creating a conversational app. This app will transform our data source into a chat partner, allowing you to have interactive conversations and unlock insights through natural language.</p>
<p>So far, we've laid the foundation for our app. We've created:</p>
<ul>
<li>A data source.</li>
<li>A data retrieval tool.</li>
<li>An Agent.</li>
<li>Tasks.</li>
</ul>
<p>Click the Apps tab and then click the <strong>New App</strong> Button, and then enter the details below:</p>
<p><img src="https://lh7-us.googleusercontent.com/jBOcimMYi1-HQGbpUXJYFLho-5LGKGKVJJ5E9OnNpy83PzSX0RINP0DN6oK_9p9LztvSm5yQdMSDqmLqY_fvmC_3pREpO2f9h_zRmPGwaYdr9TwLmcWzWhZSRPMlWDK15x9cEdMz6gNwyL4uYnB0Pyc" alt="Image" width="1600" height="1056" loading="lazy">
<em>New App Screen</em></p>
<ul>
<li>App name – Enter the name you’d like to give to your app.</li>
<li>Enter a description of what our App.</li>
<li>Select a Tasks</li>
<li>Select an Agent</li>
<li>Choose a Chat Manager Model – Pick the Open AI model we configured earlier.</li>
</ul>
<p><img src="https://lh7-us.googleusercontent.com/1SMFU6auHKUZgxZGUQ9yJQzKuqDwtU4KbZBJGweDPaJBW_Oc6plMrcror2o2dWindixOWu6bGyYNGg6eUB5TS2iYYmOzP9FHFIvacZTle32ZqL7Gylxxy1XlZLR9YivE3KDHBXxJ1_dvO-aVHWbc2Mo" alt="Image" width="1600" height="745" loading="lazy">
<em>App Screen</em></p>
<p>Now, let's test our app. </p>
<p>Clicking the play button will open a chat window for us where we can have a conversation.</p>
<p><img src="https://lh7-us.googleusercontent.com/ju-0a5CRhaHgVEqFZkDhdlPkCBqtwOM0Mjnsaz2D7ftl5Hsfku49pkBFYyY9DBr41Rzbwqm90vf8pirBzz2hsUBnsM6YOwjPoCmGhzkm6OQefP_jNNIdOGKx9geEibQLQv_ZzGsaN8AhuXIBl-F9P64" alt="Image" width="1600" height="785" loading="lazy">
<em>Play Button in App Screen</em></p>
<p>A chat interface window should be opened for us where we can chat with our data. For instance, with the data I have used, I can prompt the app to summarize some of the issues raised by customers related to the Mortgage product.</p>
<p><img src="https://lh7-us.googleusercontent.com/tfEGreNRfDRwyiJs0ZomrjfRmSQajuMSYzqL-uxa8tULoso0d56mwE-JiNpLEiv0-x0dnN9XgpajdiwW9aa-dvfnF47dmhW4daQkJ21JYYmC25svAfd3PBXdm9HrmC6tVFAlZ04-H0PLf1B5GfOsqHc" alt="Image" width="1600" height="777" loading="lazy">
<em>Live Chat with Data</em></p>
<p>Or I can have it summarize company responses related to different issues.</p>
<p><img src="https://lh7-us.googleusercontent.com/KpGoDKLffAOcUzFdN7_tJwMved1uDklnP5XQAkaLbwhn1hTlnOgI7Br8QmaPfGUgaK8hLvW958KIyZfDI577PB2aTHgSEHQjBFRN7TeOAgQA0cYjds4R5evGbuMfNXBJOl5x-7Uur0akvOsWgNjI4V4" alt="Image" width="1600" height="802" loading="lazy">
<em>Answer Screen</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, we explored building a RAG chat application using Agent Cloud and Google Sheets. </p>
<p>We covered setting up Google Sheets as a data source, embedding the data for efficient retrieval, and storing it within a vector store like Qdrant. We also learned how to create tools for Agents (chatbot components) and build an app chat interface where users can interact with the data without writing a single line of code.</p>
<p>If you want to read more interesting articles about developer tools, React, Next.js, AI and more, then I'll encourage you to checkout my <a target="_blank" href="https://theankurtyagi.com/">blog</a>. </p>
<p>Some of the fresh articles I've written this year. </p>
<ul>
<li><a target="_blank" href="https://theankurtyagi.com/how-to-create-blog-with-nextjs-and-firebase/">How I Build a Blog with Next.js and Firebase</a></li>
<li><a target="_blank" href="https://theankurtyagi.com/appwrite/">How I Build a Task Management App with React and Appwrite</a></li>
<li><a target="_blank" href="https://theankurtyagi.com/notes-app-react-supabase/">How I Build a Notes App Using React and Supabase – The Complete Guide</a></li>
<li><a target="_blank" href="https://dev.to/agentcloud/how-to-build-a-rag-chat-app-with-agent-cloud-and-bigquery-15b">How I Build a RAG Chat App With Agent Cloud and BigQuery</a></li>
</ul>
<p>You can get in touch if you have any questions or corrections. I’m expecting them.</p>
<p>And if you found this tutorial useful, please share it with your friends and colleagues who might benefit from it as well. Your support enables me to continue producing useful content for the tech community.</p>
<p>Now it’s time to take the next step by subscribing to my <strong><a target="_blank" href="https://bytesizedbets.com/">newsletter</a></strong> and following me on <a target="_blank" href="https://twitter.com/theankurtyagi"><strong>Twitter</strong></a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a React Chatbot – a Step by Step Guide ]]>
                </title>
                <description>
                    <![CDATA[ In the ever-evolving realm of web technologies, the integration of AI-powered chatbots has become a defining trend in 2024.  With rapid advancements in Large Language Models (LLMs), chatbots have grown to become pivotal tools adopted across many webs... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-react-chatbot/</link>
                <guid isPermaLink="false">66c770a059843c48e8082e5b</guid>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Tan Jin ]]>
                </dc:creator>
                <pubDate>Fri, 10 May 2024 22:27:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/rcb-logo-large---Copy.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In the ever-evolving realm of web technologies, the integration of AI-powered chatbots has become a defining trend in 2024. </p>
<p>With rapid advancements in Large Language Models (LLMs), chatbots have grown to become pivotal tools adopted across many websites and services. From FAQ bots to live chat support, they can provide users with information and assistance.</p>
<p>If you have a website, a sleek chatbot interface can offer support to your users. And you'll want to present a modern chatbot that can captivate your users and leave an impression. </p>
<p>React is one of the most popular tools for developing websites, and React-powered sites and apps are great candidates for chatbots. In this short guide, you'll see how easy it can be to integrate a chatbot into your React website.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Before we start setting up our chatbot, note that this guide assumes knowledge of the following:</p>
<ul>
<li><a target="_blank" href="https://www.freecodecamp.org/news/react-for-beginners-handbook/">React</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/free-8-hour-node-express-course/">Node.js/npm</a></li>
<li><a target="_blank" href="https://www.freecodecamp.org/news/helpful-linux-commands-you-should-know/">Linux Commands</a></li>
</ul>
<p>A basic understanding of the above is sufficient, and you should be able to set up your own React project. If you're completely unfamiliar with the above, you can check out the linked tutorials (as well as any other helpful resources you find) for them first. Otherwise, let's begin!</p>
<h2 id="heading-step-1-create-a-project">Step 1: Create a Project</h2>
<p>Before we can setup our chatbot, let's create a new blank React project. Head over to a project folder of your choice and run the following commands in your terminal:</p>
<pre><code class="lang-bash">npm create vite@latest
</code></pre>
<p>You will be prompted to enter a project name, framework, and variant. For the purpose of this tutorial, we will go with the following selections:</p>
<ul>
<li>Project Name: MyChatBot</li>
<li>Framework: React</li>
<li>Variant: JavaScript</li>
</ul>
<p>Once your setup is complete, head into your project folder and run the following commands:</p>
<pre><code class="lang-bash">npm install &amp;&amp; npm run dev
</code></pre>
<p>If you visit <em>http://localhost:5173</em> on your browser, you should be greeted with the following screen:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image-16.png" alt="Image" width="600" height="400" loading="lazy">
<em>React app set up</em></p>
<h2 id="heading-step-2-install-react-chatbotify">Step 2: Install React ChatBotify</h2>
<p>Now that we have our project setup, it's time to install <a target="_blank" href="https://react-chatbotify.tjtanjin.com/">React ChatBotify</a>. React ChatBotify is a highly customizable React library that simplifies the process of integrating a chatbot into your application. We'll be using it to include a chatbot so install it with the command below:</p>
<pre><code class="lang-bash">npm install react-chatbotify
</code></pre>
<p>Once the installation is complete, we can proceed to import the library and have it rendered. Within your <code>src</code> folder, open up your <code>App.jsx</code> file with an editor of your choice. The default file should look similar to this:</p>
<pre><code class="lang-app.jsx">import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    &lt;&gt;
      &lt;div&gt;
        &lt;a href="https://vitejs.dev" target="_blank"&gt;
          &lt;img src={viteLogo} className="logo" alt="Vite logo" /&gt;
        &lt;/a&gt;
        &lt;a href="https://react.dev" target="_blank"&gt;
          &lt;img src={reactLogo} className="logo react" alt="React logo" /&gt;
        &lt;/a&gt;
      &lt;/div&gt;
      &lt;h1&gt;Vite + React&lt;/h1&gt;
      &lt;div className="card"&gt;
        &lt;button onClick={() =&gt; setCount((count) =&gt; count + 1)}&gt;
          count is {count}
        &lt;/button&gt;
        &lt;p&gt;
          Edit &lt;code&gt;src/App.jsx&lt;/code&gt; and save to test HMR
        &lt;/p&gt;
      &lt;/div&gt;
      &lt;p className="read-the-docs"&gt;
        Click on the Vite and React logos to learn more
      &lt;/p&gt;
    &lt;/&gt;
  )
}

export default App
</code></pre>
<p>Next, with just adding 2 lines of code, we will see the chatbot rendered in our application. At the top of your file, add the line:</p>
<ul>
<li><code>import ChatBot from 'react-chatbotify'</code></li>
</ul>
<p>Above your <code>&lt;div&gt;</code> element in the <code>return</code> statement, add the following line:</p>
<ul>
<li><code>&lt;ChatBot/&gt;</code></li>
</ul>
<p>Your edited file should look like this:</p>
<pre><code class="lang-app.jsx">import ChatBot from 'react-chatbotify'
import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [count, setCount] = useState(0)

  return (
    &lt;&gt;
      &lt;ChatBot/&gt;
      &lt;div&gt;
        &lt;a href="https://vitejs.dev" target="_blank"&gt;
          &lt;img src={viteLogo} className="logo" alt="Vite logo" /&gt;
        &lt;/a&gt;
        &lt;a href="https://react.dev" target="_blank"&gt;
          &lt;img src={reactLogo} className="logo react" alt="React logo" /&gt;
        &lt;/a&gt;
      &lt;/div&gt;
      &lt;h1&gt;Vite + React&lt;/h1&gt;
      &lt;div className="card"&gt;
        &lt;button onClick={() =&gt; setCount((count) =&gt; count + 1)}&gt;
          count is {count}
        &lt;/button&gt;
        &lt;p&gt;
          Edit &lt;code&gt;src/App.jsx&lt;/code&gt; and save to test HMR
        &lt;/p&gt;
      &lt;/div&gt;
      &lt;p className="read-the-docs"&gt;
        Click on the Vite and React logos to learn more
      &lt;/p&gt;
    &lt;/&gt;
  )
}

export default App
</code></pre>
<p>Try running your application again and you'll be greeted with a chatbot on the bottom right corner as shown in the screenshot below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/05/image-17.png" alt="Image" width="600" height="400" loading="lazy">
<em>Chatbot in the React app</em></p>
<p>Pretty neat, isn't it?</p>
<h2 id="heading-step-3-craft-your-hello-world-message">Step 3: Craft Your "Hello World" Message</h2>
<p>It's nice to have the chatbot setup easily, but it's not great to only have the default welcome message. Let's quickly add a <strong>hello world</strong> message of our own.</p>
<p>The <code>&lt;ChatBot/&gt;</code> component takes in a <code>flow</code> prop to define conversations. By default, the entry point of a conversation is always named the <code>start</code> block as shown in this example below:</p>
<pre><code><span class="hljs-keyword">const</span> flow = {
  <span class="hljs-string">"start"</span>: {
    <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world!"</span>
  }
}
</code></pre><p>We will go ahead and pass the above flow into our chatbot:</p>
<pre><code><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>
<span class="hljs-keyword">import</span> reactLogo <span class="hljs-keyword">from</span> <span class="hljs-string">'./assets/react.svg'</span>
<span class="hljs-keyword">import</span> viteLogo <span class="hljs-keyword">from</span> <span class="hljs-string">'/vite.svg'</span>
<span class="hljs-keyword">import</span> ChatBot <span class="hljs-keyword">from</span> <span class="hljs-string">"react-chatbotify"</span>
<span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [count, setCount] = useState(<span class="hljs-number">0</span>)

  <span class="hljs-keyword">const</span> flow = {
    <span class="hljs-string">"start"</span>: {
      <span class="hljs-string">"message"</span>: <span class="hljs-string">"Hello world!"</span>
    }
  }
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://vitejs.dev"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{viteLogo}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"logo"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"Vite logo"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"https://react.dev"</span> <span class="hljs-attr">target</span>=<span class="hljs-string">"_blank"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">img</span> <span class="hljs-attr">src</span>=<span class="hljs-string">{reactLogo}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"logo react"</span> <span class="hljs-attr">alt</span>=<span class="hljs-string">"React logo"</span> /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Vite + React<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"card"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setCount((count) =&gt; count + 1)}&gt;
          count is {count}
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          Edit <span class="hljs-tag">&lt;<span class="hljs-name">code</span>&gt;</span>src/App.jsx<span class="hljs-tag">&lt;/<span class="hljs-name">code</span>&gt;</span> and save to test HMR
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"read-the-docs"</span>&gt;</span>
        Click on the Vite and React logos to learn more
      <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">ChatBot</span> <span class="hljs-attr">flow</span>=<span class="hljs-string">{flow}/</span>&gt;</span>
    <span class="hljs-tag">&lt;/&gt;</span></span>
  )
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App
</code></pre><p>When you look at your application again, you will notice that the default message has disappeared and is replaced with <code>Hello world!</code> instead. Not too hard, isn't it? </p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this guide, you've seen how easy it can be to setup a modern React chatbot. </p>
<p>While the example above is simple, there are plenty of other properties within a <code>flow</code> that can help you build your conversations. These are documented on the <a target="_blank" href="https://react-chatbotify.tjtanjin.com/docs/introduction/quickstart">library website</a> which also comes with <a target="_blank" href="https://react-chatbotify.tjtanjin.com/docs/examples/basic_form">live playground examples</a> for you to explore and find out more. </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use Claude AI – Introduction to Claude AI + Code Example ]]>
                </title>
                <description>
                    <![CDATA[ By Firas Ahmed Claude is one of the leading Large Language Models (LLMs) in the market, developed by Anthropic — an AI startup that was co-founded by ex OpenAI employees. The company is known for its stringent set of AI ethics and is currently backed... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/introduction-to-claude-ai/</link>
                <guid isPermaLink="false">66d45edb3a8352b6c5a2aa4f</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ LLM&#39;s  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 15 Mar 2024 00:54:58 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/03/claude-2.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Firas Ahmed</p>
<p>Claude is one of the leading Large Language Models (LLMs) in the market, developed by <em>Anthropic —</em> an AI startup that was co-founded by ex OpenAI employees. The company is known for its stringent set of AI ethics and is currently backed by tech giants such as Google and Amazon.</p>
<p>In this article, we're going to delve into Claude AI, compare it to ChatGPT, and provide a quick example of how to work with it via the API.</p>
<h2 id="heading-what-is-claude">What is Claude?</h2>
<p>Similar to AI chatbots such as ChatGPT and Gemini, Claude is a chatbot powered by Claude 3  —  Anthropic's latest large language model. It is capable of taking user input and generating human-like output. Besides conversations, you can upload images and documents to Claude and have it summarize them or answer questions about specific points.</p>
<p>What distinguishes Claude from other competitors is Anthropic's claim that it is safer and less likely to produce harmful and offensive output due to "Constitutional AI" – a unique training approach pioneered by Anthropic aimed at developing AI systems that adhere to a set of ethical principles.</p>
<p>The first model was released on March 2023, which was later followed by updated versions with enhanced capabilities, more advanced training techniques, and more focus on safety.</p>
<p>In March 2024, Anthropic launched Claude 3, its most advanced, state-of-the-art suite of models: <em>Haiku</em>, <em>Sonnet</em> and <em>Opus.</em> Each having their own unique capabilities, with Opus being the most powerful.</p>
<p>Claude 3 offers image processing, lower rates of hallucination and a significantly larger context window. Claude chatbot, currently powered by Claude 3, demonstrated superior performance over ChatGPT in standardized benchmarks.</p>
<p>Apart from the chatbot, Claude is also available through API where developers can build applications on top of it.</p>
<h2 id="heading-claude-ai-capabilities">Claude AI Capabilities</h2>
<p>Here are the key areas where Claude excels:</p>
<ul>
<li><strong>Conversation:</strong> Claude is highly capable of engaging in natural conversations, understanding user's context and providing thoughtful responses.</li>
<li><strong>Content Creation:</strong> Claude can generate high quality content tailored to requirements set by the user.</li>
<li><strong>Language Translation:</strong> In this day and age, global communication is crucial. Claude has multilingual capabilities, allowing translation between different languages in real-time and multi-lingual content creation.</li>
<li><strong>Visual processing:</strong> Claude can analyze and transcribe images, including photographs and handwritten notes.</li>
<li><strong>Code Generation:</strong> Code generation has become an attractive feature and a key competitive advantage with every new AI model release. Claude can generate code snippets, understand different programming languages, explain code functionality, and assist in debugging.</li>
</ul>
<h2 id="heading-claude-vs-chatgpt">Claude vs ChatGPT</h2>
<p>Let's compare the differences between ChatGPT and Claude:</p>
<h3 id="heading-llm">LLM</h3>
<ul>
<li>Claude 3 is the latest model lineup released on March 2024.</li>
<li>GPT-4 is the current model since March 2023.</li>
</ul>
<h3 id="heading-performance">Performance</h3>
<p>At the time of writing, Claude excels in terms of factual accuracy and can maintain context over longer conversations. Claude Opus is shown to outperform GPT-4 in all evaluation benchmarks of AI systems, especially in terms of knowledge and language understanding.</p>
<h3 id="heading-context-window">Context Window</h3>
<p>Context window represents the maximum number of tokens an AI system can process in a single input or output. Larger context means the LLM can handle longer text and maintain the context while processing that text.</p>
<ul>
<li>GPT-4 has <strong>8,192 tokens</strong>, while a newer version (GPT-4-32k) has <strong>32,768 tokens</strong>.</li>
<li>All three of Claude 3 versions have a context window of <strong>200k tokens</strong> which is significantly larger than GPT-4.</li>
</ul>
<h3 id="heading-safety">Safety</h3>
<p><strong>GPT-4:</strong></p>
<ul>
<li>While safety is a critical aspect of ChatGPT and efforts have been made to mitigate misinformation, the chatbot tends to generate some incorrect output.</li>
<li>ChatGPT saves conversations with the user to further train and improve the model.</li>
</ul>
<p><strong>Claude 3:</strong></p>
<ul>
<li>Claude was developed with safety in mind. Anthropic emphasizes on ethical use of AI in both the training and how the chatbot processes input and generates output. The model strictly follows Constitutional AI.</li>
<li>Claude does not retain user data.</li>
</ul>
<h3 id="heading-accessibility">Accessibility</h3>
<p>Both Claude 3 and GPT-4 models are available directly through the chatbots as well as through API. Additionally, they're accessible in other platforms:</p>
<ul>
<li><strong>GPT-4:</strong> Microsoft invested heavily on OpenAI to integrate their latest LLMs into Microsoft platforms. As of today, GPT-4 is available via Microsoft's Copilot for free.</li>
<li><strong>Claude 3:</strong> Anthropic has partnered with companies like Notion, Amazon, and DuckDuckGo to integrate Claude 3 into their products.</li>
</ul>
<h2 id="heading-how-to-interact-with-claude-ai">How to Interact with Claude AI</h2>
<p>If you're familiar with ChatGPT (by now, that is most likely the case), you should have a similar experience with the Claude chatbot. Once you create an account <a target="_blank" href="https://claude.ai/chats">here</a>, it'll be as simple as typing your queries. </p>
<p>In this section, we'll focus on interacting with the AI model through the provided API using Python to ask it to explain the concept of neural networks.</p>
<p>To get started, sign up <a target="_blank" href="https://console.anthropic.com/login">here</a>, then navigate <a target="_blank" href="https://console.anthropic.com/settings/keys">here</a> to create your first API key. Make sure you copy and store the API key in a secure file.</p>
<p>Here's an example of a Python script that instructs Claude to explain the concept of neural networks:</p>
<pre><code class="lang-python">  <span class="hljs-comment"># import anthropic library</span>
  <span class="hljs-keyword">import</span> anthropic

  <span class="hljs-comment"># create a client instance</span>
  client = anthropic.Anthropic(
      api_key=<span class="hljs-string">"your_api_key"</span>,
  )

  <span class="hljs-comment"># create the prompt and call the API</span>
  message = client.messages.create(
      model=<span class="hljs-string">"claude-3-opus-20240229"</span>,
      max_tokens=<span class="hljs-number">1000</span>,
      temperature=<span class="hljs-number">0.0</span>,
      system=<span class="hljs-string">"Respond in short and clear sentences."</span>,
      messages=[
          {
            <span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>,
            <span class="hljs-string">"content"</span>: <span class="hljs-string">"Can you explain the concept of neural networks?"</span>
          }
      ]
  )

  print(message.content)
</code></pre>
<p>Make sure you replace <code>your_api_key</code> with the actual API key that you created. </p>
<p>Let's quickly discuss the parameters defined above:</p>
<ul>
<li><code>model="claude-3-opus-20240229"</code> specifies the model to be used.</li>
<li><code>max_tokens=1000</code> sets the maximum number of tokens that the generated response can have.</li>
<li><code>temperature=0.0</code> the temperature controls the level of randomness of the generated response. <code>0.0</code> means the response will be more consistent and less varied.</li>
<li><code>system="Provide short and clear responses."</code> specifies how the system should generate the response.</li>
<li><code>messages=[{"role": "user", "content": Can you explain the concept of neural networks?"}]</code> defines the role and input message based on which the output will be generated.</li>
</ul>
<p>Here's a sample response in JSON:</p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"id"</span>: <span class="hljs-string">"msg_01H4xwvAZnb6XTz8cHPoerBS"</span>,
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"message"</span>,
    <span class="hljs-attr">"role"</span>: <span class="hljs-string">"assistant"</span>,
    <span class="hljs-attr">"content"</span>: [
        {
            <span class="hljs-attr">"type"</span>: <span class="hljs-string">"text"</span>,
            <span class="hljs-attr">"text"</span>: <span class="hljs-string">"Neural networks are a type of machine learning algorithm inspired by the structure and function of the human brain. They consist of interconnected nodes, or \"neurons,\" organized in layers. Each neuron receives input, processes it, and passes the output to neurons in the next layer. Through training on large datasets, neural networks learn to recognize patterns and make predictions or decisions.\n\nKey points about neural networks:\n\n1. Structure: Input layer, hidden layer(s), and output layer of neurons\n2. Weights and biases: Each connection has a weight that determines the importance of the input\n3. Activation functions: Determine the output of a neuron based on its input\n4. Training: Networks learn by adjusting weights through backpropagation and optimization algorithms\n5. Applications: Used for tasks like image recognition, natural language processing, and prediction\n\nNeural networks excel at learning complex, non-linear relationships in data and have revolutionized fields like computer vision and speech recognition. However, they require large amounts of training data and computational resources."</span>
        }
    ],
    <span class="hljs-attr">"model"</span>: <span class="hljs-string">"claude-3-opus-20240229"</span>,
    <span class="hljs-attr">"stop_reason"</span>: <span class="hljs-string">"end_turn"</span>,
    <span class="hljs-attr">"stop_sequence"</span>: <span class="hljs-literal">null</span>,
    <span class="hljs-attr">"usage"</span>: {
        <span class="hljs-attr">"input_tokens"</span>: <span class="hljs-number">23</span>,
        <span class="hljs-attr">"output_tokens"</span>: <span class="hljs-number">219</span>
    }
}
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Claude represents a significant leap in the realm of artificial intelligence that outmatches various competitors in the market.</p>
<p>Claude provides a unique view, a set of standards regarding safety and security, and offers a diverse range of applications, from content creation to code generation.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a WhatsApp Dictionary Chatbot using Twilio, FastAPI, and MongoDB ]]>
                </title>
                <description>
                    <![CDATA[ Sometimes you want to check what a word means while chatting with someone in WhatsApp. But you don't want to exit or minimize the app. Well, how about building a WhatsApp bot that can give you the meaning of words you want to know? In this tutorial, ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-whatsapp-dictionary-chatbot-using-twilio-fast-api-and-mongodb/</link>
                <guid isPermaLink="false">66d45d5a3a8352b6c5a2a9ed</guid>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ MongoDB ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Adejumo Ridwan Suleiman ]]>
                </dc:creator>
                <pubDate>Wed, 02 Aug 2023 23:47:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/08/WHATSAPP-DICTIONARY-CHATBOT.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Sometimes you want to check what a word means while chatting with someone in WhatsApp. But you don't want to exit or minimize the app.</p>
<p>Well, how about building a WhatsApp bot that can give you the meaning of words you want to know?</p>
<p>In this tutorial, you will learn how to build a chatbot that can serve as your dictionary. It'll be easily accessible on WhatsApp using the Twilio MessagingX WhatsApp API to send and receive messages. We'll use Fast API to create the web server and interact with the database, and MongoDB to store the words and their meanings into a database.</p>
<p>By the end of this tutorial, you will have developed a functional chatbot that can define words in real-time while you're conversing on WhatsApp.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Python 3.9+ installed on your machine.</p>
</li>
<li><p>MongoDB free account – if you don’t have one, you can set it up <a target="_blank" href="https://www.mongodb.com/cloud/atlas/lp/try4?utm_ad_campaign_id=12212624539&amp;adgroup=115749718303&amp;cq_cmp=12212624539&amp;gad=1">here</a>.</p>
</li>
<li><p>Twilio free account – you can set up one <a target="_blank" href="https://www.twilio.com/en-us">here</a>.</p>
</li>
<li><p>Merriam-Webster Developer’s account – you can set up one <a target="_blank" href="https://www.dictionaryapi.com/register/index">here</a>.</p>
</li>
<li><p>An IDE or text editor, such as <a target="_blank" href="https://code.visualstudio.com/">VS code</a>.</p>
</li>
</ul>
<h2 id="heading-set-up-your-development-environment">Set Up Your Development Environment</h2>
<p>Before you start, you need to set up your development environment by creating the required directory and files. Here's the commands for that:</p>
<pre><code class="lang-shell">mkdir whatsappDictionary
cd whatsappDictionary
touch requirements.txt models.py utils.py main.py .env
</code></pre>
<ul>
<li><p><code>requirements.txt</code> contains the required libraries to get the chatbot up and running.</p>
</li>
<li><p><code>model.py</code> contains the code connecting your chatbot to the MongoDB server</p>
</li>
<li><p><code>utils.py</code> includes the code to connect to Twilio MessagingX WhatsApp API</p>
</li>
<li><p><code>main.py</code> contains the code to build the Fast API server and connect to the Merriam-Webster API.</p>
</li>
<li><p><code>whatsappDictionary</code> is the directory for all the files.</p>
</li>
</ul>
<p>Next, you'll create and activate a virtual environment and update the Python package manager <code>pip</code> to the most recent version using the following command:</p>
<pre><code class="lang-shell">python -m venv venv; ./venv/Scripts/Activate; pip --upgrade pip
</code></pre>
<p>If you are on a Linux machine, use this command:</p>
<pre><code class="lang-shell">pyton -m venv venv; venv\\Scripts\\activate.bat; pip --upgrade pip
</code></pre>
<p>To learn more about virtual environments and their benefits, you can read this <a target="_blank" href="https://linuxhostsupport.com/blog/why-using-a-python-virtual-environment-is-a-good-choice/#:~:text=Virtual%20environments%20are%20of%20great,the%20help%20of%20virtual%20environments.">tutorial</a>.</p>
<p>Next you'll need to populate the <code>requirements.txt</code> file with the following dependencies:</p>
<pre><code class="lang-text">fastapi
uvicorn
twilio
pymongo
pyngrok
requests
dotenv
</code></pre>
<ul>
<li><p><code>fastapi</code> is a Python framework for building APIs quickly and easily</p>
</li>
<li><p><code>uvicorn</code> is a lightning-fast server implementation for Python</p>
</li>
<li><p><code>twilio</code> allows you to interact with the Twilio MessagingX WhatsApp API</p>
</li>
<li><p><code>pymongo</code> is the driver you will use to connect to the MongoDB server</p>
</li>
<li><p><code>pyngrok</code> enables you to tunnel a local server to a public URL</p>
</li>
<li><p><code>requests</code> allow you to send HTTP requests using Python</p>
</li>
<li><p><code>dotenv</code> loads the environment variables from the <em>.env</em> file.</p>
</li>
</ul>
<p>Install these dependencies by running the following command on your terminal:</p>
<pre><code class="lang-shell">pip install -r requirements.txt
</code></pre>
<h2 id="heading-configure-the-database">Configure the Database</h2>
<p>You now want to set up a database to store words and their definitions. You are going to use MongoDB, which is a NoSQL language and is easy to set up.</p>
<p>You will need to create a free account on the MongoDB website (if you don't have one already). Once you have an account, log in to create a new <strong>Shared</strong> cluster and database.</p>
<p><img src="https://lh5.googleusercontent.com/Oz_BdWfzS5wphrYXi_WzAX_2e-2rzHPp3wqlTeH4BXP5HSU73Scnt39qO85Ao9TstyzYKjHYnjXXO1qabp43jJF0W6vcGkhQ3mkt6ZHn-OvurIJKAv1WYOYBxmklS0_zw775g51X5xhc3js92qNe7mUErBpnKooWtvzl7AMK6TzAO8X6qwSbbK2lRWeBNQ" alt="This image shows you how to create a shared cluster on MongoDB" width="600" height="400" loading="lazy"></p>
<p><em>Image showing how to create a shared cluster on Mongo DB</em></p>
<p>Afterwards, go to <strong>Security_,_</strong> then <strong>ADD NEW DATABASE USER</strong> to add a new user with read/write access to the database.</p>
<p><img src="https://lh3.googleusercontent.com/r0HxaAqOZZn8yIV3ObgfuAojNdkGefNzjPJM4DVRRlPCTIpl9NmvGr-lY0hjLGWlNnyRuHQF34ujKGV2H_F4BjS746TLOfljbMax24kFo9haf0gxa9f2ZQzG-DVxI7qqzQx4W_YuZhh7y7ENSqCgOH60EWAi6hiDYO9_GilZut2uJPgy7aYMaTOd-uQTRg" alt="This image shows the tab where you can add a new database" width="600" height="400" loading="lazy"></p>
<p><em>This image shows the tab where you can add a new database</em></p>
<p><img src="https://lh6.googleusercontent.com/iOOmBGx8NfOkCeZ_ioNqLN0aRKoJMs9RWKaRgd1xiex6WnzekwpuKLlSpj963PvfKTRTI7ZZ9pzGIFn_rJ5mFm7rrIodPTTZ1jpdnUEqj4drCOpcQ6ZWb5k6R2azZ5BCBA_zsY_ee5OtJ8m4roU0SHNRFoeXYKMu2yKKLOxpnPoEYBJKpfIavRP5-KBA3A" alt="This image shows the window of adding a new database user" width="600" height="400" loading="lazy"></p>
<p><em>This image shows the window of adding a new database user</em></p>
<p>Go back to the cluster dashboard, click on <strong>Connect</strong> and then on <strong>Drivers</strong>.</p>
<p><img src="https://lh4.googleusercontent.com/EOqNVq2nqObKtABzLQphGDWRFgXPFybQhm11R_XPWdlqgigbzVsMA1zaj2zVjjMjlHpdzauXpfjofw1cyE_RUc2ijI8h_qDLN10sSoMeCGUuGU1DZ3tGB4IqRRkJmgUhNGxDhUX3Zljgo1SSgdLZwWreqFHXpEJ2WuPE62QamlRAop6JtVOfARJbWU4UIg" alt="This image shows you the final process while trying to connect to the cluster" width="600" height="400" loading="lazy"></p>
<p><em>This image shows you the final process while trying to connect to the cluster</em></p>
<p>Copy the code shown to you into <code>model.py</code>:</p>
<p><img src="https://lh4.googleusercontent.com/s1M_HQA6uAKPxE6jZtyzh4iLHtk_4m-nIv9IOW9D0igHKEKMrwZjNAAsVIwKOzqCAhNQM-qIimO4pFbKqSAUKh78VJ9ZC02pIP5uCBT7ndw0k-6nte3CLay481osRVkDwDoMkCLaSx4ydTWDRBndVVDVHWIUFEhBjaeWoOkvZVS6SImAnjJ3BTHnT2yIMQ" alt="This image shows the code on how to connect to the MongoDB server using the pymongo driver." width="600" height="400" loading="lazy"></p>
<p><em>This image shows the code on how to connect to the MongoDB server using the pymongo driver.</em></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pymongo.mongo_client <span class="hljs-keyword">import</span> MongoClient
<span class="hljs-keyword">from</span> pymongo.server_api <span class="hljs-keyword">import</span> ServerApi

uri = <span class="hljs-string">f"mongodb+srv://adejumoridwan:&lt;password&gt;@cluster0.d9jr4ev.mongodb.net/?retryWrites=true&amp;w=majority"</span>

<span class="hljs-comment"># Send a ping to confirm a successful connection</span>
<span class="hljs-keyword">try</span>:
    client.admin.command(<span class="hljs-string">'ping'</span>)
    print(<span class="hljs-string">"Pinged your deployment. You successfully connected to MongoDB!"</span>)
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    print(e)
</code></pre>
<p>Give your password to run the code and connect to the MongoDB server. You don’t want anyone to see this. Go to the <code>.env</code> file you created and store your password there.</p>
<pre><code class="lang-text">MONGO_SECRET=&lt;password&gt;
</code></pre>
<p>Next, update <code>model.py</code> to access the <code>.env</code> file.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pymongo.mongo_client <span class="hljs-keyword">import</span> MongoClient
<span class="hljs-keyword">from</span> pymongo.server_api <span class="hljs-keyword">import</span> ServerApi
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os

load_dotenv()
password = os.environ.get(<span class="hljs-string">'MONGO_SECRET'</span>)

uri = <span class="hljs-string">f"mongodb+srv://adejumoridwan:<span class="hljs-subst">{password}</span>@cluster0.d9jr4ev.mongodb.net/?retryWrites=true&amp;w=majority"</span>

<span class="hljs-comment"># Send a ping to confirm a successful connection</span>
<span class="hljs-keyword">try</span>:
    client.admin.command(<span class="hljs-string">'ping'</span>)
    print(<span class="hljs-string">"Pinged your deployment. You successfully connected to MongoDB!"</span>)
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    print(e)
</code></pre>
<ul>
<li><p><code>load_dotenv()</code> loads the variables in .env</p>
</li>
<li><p><code>os.environ.get('MONGO_SECRET')</code> receives the password from .env, which stores the variable password</p>
</li>
</ul>
<p>Run the script to connect to the MongoDB server. You can create a collection by clicking on your Cluster name and going to Collections. Collections are NoSQL versions of SQL tables.</p>
<p>Click on <strong>Create Database</strong> to create your database:</p>
<p><img src="https://lh6.googleusercontent.com/yrP5_ej7jaGl3mQH9uXQEkmubLt-50NOiDcd6XG_8bV3yWe-cJZnA6snsN_xoJqpp7XQDFpXMk4xVQZHuY23D4fscJbYJfSExfDzWoeiEOQIXBjosDRchgJPu7ZWtCilXdQg5nuKBmaE10hh_Ht-zAkuGFn-RUoTwuIiH8xY2FCXXKi4TW8T3NyJEo6LyQ" alt="This shows information and tabs regarding the created clusters such as Overview, Collections and so on
" width="600" height="400" loading="lazy"></p>
<p><em>This shows information and tabs regarding the created clusters such as Overview, Collections and so on</em></p>
<p>Give the database and collection names. The database and collection names for this tutorial are <strong>MY_DB</strong> and <strong>dictionary</strong>, respectively.</p>
<p><img src="https://lh4.googleusercontent.com/HdO8oJJXmQhD1n6XeiZdP0qicncdy5U7pyraNu3zX0xi2Qpu7j1JbN_ZmV8FVK4CZu8XHoCAE4wNIwEXu5J0e3ltvITtMjZ7GzI7XxwCPE7UyFnNAlJc9ycRNfmWCNeZiIXBe4hNlf7_F9HvejXz1CKQ644WvgGRFXMBvKqqG3pqM9iBMZHZStS8p93ROA" alt="The window shows you the option to fill in when you want to create a database, such as a database name, collection name, and additional preferences." width="600" height="400" loading="lazy"></p>
<p><em>The window shows you the option to fill in when you want to create a database, such as a database name, collection name, and additional preferences.</em></p>
<p>Go to <code>models.py</code> and update the code to create a new client and connect to the server.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pymongo.mongo_client <span class="hljs-keyword">import</span> MongoClient
<span class="hljs-keyword">from</span> pymongo.server_api <span class="hljs-keyword">import</span> ServerApi
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os

load_dotenv()
password = os.environ.get(<span class="hljs-string">'MONGO_SECRET'</span>)

uri = <span class="hljs-string">f"mongodb+srv://adejumoridwan:<span class="hljs-subst">{password}</span>@cluster0.d9jr4ev.mongodb.net/?retryWrites=true&amp;w=majority"</span>

<span class="hljs-comment"># Create a new client and connect to the server</span>
client = MongoClient(uri, server_api=ServerApi(<span class="hljs-string">'1'</span>))

dictionary_collection = client[<span class="hljs-string">"MY_DB"</span>][<span class="hljs-string">"dictionary"</span>]

<span class="hljs-comment"># Send a ping to confirm a successful connection</span>
<span class="hljs-keyword">try</span>:
    client.admin.command(<span class="hljs-string">'ping'</span>)
    print(<span class="hljs-string">"Pinged your deployment. You successfully connected to MongoDB!"</span>)
<span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
    print(e)
</code></pre>
<p><code>dictionary_collection</code> is what you will use to update entries into the server later.</p>
<h2 id="heading-how-to-configure-twilio-sandbox-for-whatsapp">How to Configure Twilio Sandbox for WhatsApp</h2>
<p>To configure the Twilio sandbox for WhatsApp, go to Twilio Console. Under Develop, click on Messaging, then Try it out. Under Try it out, click on Send a WhatsApp Message.</p>
<p><img src="https://lh6.googleusercontent.com/QTLWWiK_pb8FYHgyeXucP7r5WB1YpgGpBsC-spLRJWjTsT5rMkmREk7rVwT9aawQFMd-ZxCt1nLfjk57EzA2XfEmFPKSzsZYM9NVFr3XQLwDwCN9m7am7BTSEEFZUOQFSV2BQY82wgNVSCTWZD4DHV7JLo1r3mx49NXJO6eQsG0BxcM62fx-I101wjX9oA" alt="This sandbox allows you to send WhatsApp messages to your number" width="600" height="400" loading="lazy"></p>
<p><em>This sandbox allows you to send WhatsApp messages to your number</em></p>
<p>To connect to the WhatsApp Sandbox, save the number provided on the sandbox on your device and send the message join manner-free to the number, or you can scan the QR code on your device.</p>
<p><img src="https://lh6.googleusercontent.com/k1L4We_5SE5PH8ASRHfrocoEizzY0eKSOnHUEvGi-qD41nowBJtxLEDk6amQboYi59SKxYdW32PC1G74Rj2uP4qo3aLU-GTbThlEnHgj9bUStP5K9_kBVNX5ZkCcAadQZDS1YYtchfIpGCrtWlyVo2UjjXZeIFCYI7UU4HxSDmicSCjZR_l1u9ViUS8eqg" alt="Here you see the sandbox to connect to whatsapp sandbox to your number
" width="600" height="400" loading="lazy"></p>
<p><em>Here you see the sandbox to connect to whatsapp sandbox to your number</em></p>
<p>Once the connection is successful, copy the code and paste it into the file <code>utils.py</code>:</p>
<p><img src="https://lh5.googleusercontent.com/0exaxbIgwJQYtD4kKeqcWnAlUy0bmCyap4f4pvGF8txH_meTEmckcEggzLwjL0dGszckstN8whxkYY9iz2EqXTxHfcxO6mfF_wt70NDbGqZVB56lZNZJBEi_Gv9Ee_OVTFF91czWx3AgedQKY5uhim16saPGYcbXTbknc4IL0lMQ0VDHODGJJfE8vjCJFA" alt="Here you can see how to connect to WhatsApp API in various languages. Currently, the image is showing how to connect to WhatsApp API so that you can send messages from Twilio." width="600" height="400" loading="lazy"></p>
<p><em>Here you can see how to connect to WhatsApp API in various languages. Currently, the image is showing how to connect to WhatsApp API so that you can send messages from Twilio.</em></p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> twilio.rest <span class="hljs-keyword">import</span> Client

account_sid = <span class="hljs-string">'&lt;account_sid&gt;'</span>
auth_token = <span class="hljs-string">'[AuthToken]'</span>
client = Client(account_sid, auth_token)

message = client.messages.create(
  from_=<span class="hljs-string">'whatsapp:+14155238886'</span>,
  body=<span class="hljs-string">'Your appointment is coming up on July 21 at 3PM'</span>,
  to=<span class="hljs-string">'whatsapp:&lt;to_number&gt;
)

print(message.sid)</span>
</code></pre>
<p>The <code>client.messages.create()</code> function allows you to send messages to your WhatsApp from the WhatsApp sandbox. It takes three parameters:</p>
<ul>
<li><p><code>from_</code> is where the message is coming from, that is, from the WhatsApp Sandbox</p>
</li>
<li><p><code>body</code> takes in the body of your message</p>
</li>
<li><p><code>to</code> is the WhatsApp number you are sending to</p>
</li>
</ul>
<h2 id="heading-how-to-connect-to-the-twilio-api">How to Connect to the Twilio API</h2>
<p>Go to the .env file to store your Twilio authentication token, account SID, Twilio sandbox number, and WhatsApp number.</p>
<pre><code class="lang-text">MONGO_SECRET="&lt;password&gt;"
TWILIO_ACCOUNT_SID="&lt;account_sid&gt;"
TWILIO_AUTH_TOKEN="&lt;auth_token&gt;"
TWILIO_NUMBER="&lt;twilio_number&gt;"
TO_NUMBER="&lt;to_number&gt;"
</code></pre>
<p>Update the <code>utils.py</code> file to access these variables:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> twilio.rest <span class="hljs-keyword">import</span> Client
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os

load_dotenv()

account_sid = os.getenv(<span class="hljs-string">"TWILIO_ACCOUNT_SID"</span>)
auth_token = os.getenv(<span class="hljs-string">"TWILIO_AUTH_TOKEN"</span>)
client = Client(account_sid, auth_token)
twilio_number = os.getenv(<span class="hljs-string">'TWILIO_NUMBER'</span>)
to_number = os.getenv(“TO_NUMBER”)

message = client.messages.create(
  from_=<span class="hljs-string">f'whatsapp:<span class="hljs-subst">{twilio_number}</span>'</span>,
  body=<span class="hljs-string">'Your appointment is coming up on July 21 at 3PM'</span>,
  to=<span class="hljs-string">f'whatsapp:<span class="hljs-subst">{to_number}</span>
)</span>
</code></pre>
<p><code>load_dotenv()</code> loads the environment variables, and <code>os.getenv</code> gets these variables from the environment.</p>
<p>Next is to define a <code>send_message</code> function. This function is going to have two arguments: <code>to_number</code> and <code>text</code>. The function will send a message defined in <code>text</code> to <code>to_number</code>.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> twilio.rest <span class="hljs-keyword">import</span> Client
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os

load_dotenv()

account_sid = os.getenv(<span class="hljs-string">"TWILIO_ACCOUNT_SID"</span>)
auth_token = os.getenv(<span class="hljs-string">"TWILIO_AUTH_TOKEN"</span>)
client = Client(account_sid, auth_token)
twilio_number = os.getenv(<span class="hljs-string">'TWILIO_NUMBER'</span>)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_message</span>(<span class="hljs-params">to_number, text</span>):</span>
      message = client.messages.create(
          from_=<span class="hljs-string">f"whatsapp:<span class="hljs-subst">{twilio_number}</span>"</span>,
          body=text,
          to=<span class="hljs-string">f"whatsapp:<span class="hljs-subst">{to_number}</span>"</span>
          )
</code></pre>
<p>Update the <code>send_message</code> function to configure logging in case errors are encountered when sending messages.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> twilio.rest <span class="hljs-keyword">import</span> Client

load_dotenv()

account_sid = os.getenv(<span class="hljs-string">"TWILIO_ACCOUNT_SID"</span>)
auth_token = os.getenv(<span class="hljs-string">"TWILIO_AUTH_TOKEN"</span>)
client = Client(account_sid, auth_token)
twilio_number = os.getenv(<span class="hljs-string">'TWILIO_NUMBER'</span>)

<span class="hljs-comment"># Set up logging</span>
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

<span class="hljs-comment"># Sending message logic through Twilio Messaging API</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_message</span>(<span class="hljs-params">to_number, text</span>):</span>
    <span class="hljs-keyword">try</span>:
        message = client.messages.create(
            from_=<span class="hljs-string">f"whatsapp:<span class="hljs-subst">{twilio_number}</span>"</span>,
            body=text,
            to=<span class="hljs-string">f"whatsapp:<span class="hljs-subst">{to_number}</span>"</span>
            )
        logger.info(<span class="hljs-string">f"Message sent to <span class="hljs-subst">{to_number}</span>: <span class="hljs-subst">{message.body}</span>"</span>)
    <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
        logger.error(<span class="hljs-string">f"Error sending message to <span class="hljs-subst">{to_number}</span>: <span class="hljs-subst">{e}</span>"</span>)
</code></pre>
<h2 id="heading-how-to-build-the-fastapi-backend">How to Build the FastAPI Backend</h2>
<p>Inside the <code>main.py</code>, set up a basic FastAPI application.</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI

app = FastAPI()

<span class="hljs-meta">@app.get("/")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">index</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"message"</span>: <span class="hljs-string">"I love FreeCodeCamp"</span>}
</code></pre>
<p>The code below sets up a basic FastAPI backend, creating a new instance of the <code>FastAPI</code> class and assigning it to the app variable.</p>
<p>The <code>@app.get</code> decorator creates a new endpoint that you can access with an HTTP GET request. The endpoint is at the root URL <code>/</code> and returns a JSON response with a single key-value pair: <code>"message": "I love FreeCodeCamp"</code>.</p>
<p>To run the app, run the following command in your terminal:</p>
<pre><code class="lang-shell">uvicorn main:app --reload
</code></pre>
<p>On your browser, open the host <code>http://127.0.0.1:8000</code>. You will see a JSON response of <code>{"message": "I love FreeCodeCamp"}</code>. You can also access an interactive API doc provided by swagger on the host <code>http://127.0.0.1:8000/doc</code> which allows you to interact with your API and see if you have any errors.</p>
<p><img src="https://lh3.googleusercontent.com/3WeI9jW_jeI0WMYhYrWjMUFsbwD6cAANv1DuYNkC2hhl2-HyBMQjEeX5SABC4GdbMyArs88-DkxdgTNES2ogHjFNBMqvNNFyUq3825kBxFGgazth3TWrMv72Qm_9kGs6atTFUSl6NnjeYkdaxHlV7XqFg8JMK9v6t5Dq5rUCxKU7SHv-wZnBPpIy3U31ag" alt="This interactive documentation provided by swagger shows how to interact with your API to check for errors." width="600" height="400" loading="lazy"></p>
<p><em>This interactive documentation provided by swagger shows how to interact with your API to check for errors.</em></p>
<h2 id="heading-how-to-set-up-ngrok">How to Set Up ngrok</h2>
<p>To receive Twilio messages on the backend, you will use <code>ngrok</code> to host the local host on a public server. Read this <a target="_blank" href="https://www.twilio.com/blog/using-ngrok-2022#:~:text=Open%20a%20terminal%20window%20and,under%20your%20account%2C%20without%20restrictions.">post</a> to learn how to set up ngrok on your machine.</p>
<p>On ngrok administrator, run the command ngrok http 8000. This makes your host public, and you can receive messages on your backend.</p>
<p><img src="https://lh6.googleusercontent.com/6oErUtc-WBw1YBNLaw8yjfiMioLW7PpTmUWJY4Y6nrOJ3Q0iKC5fnN8YAvVxBWMYHO296TkMwo7NyKptqOWS_tv_ftUe1Mqetkoz57HCP_rzp3lrjgpvreSkJlPKPV-GA9kQVNbp2biQVSIg-l2uGKKVMFEGG-EHSl57fF_HqiOBE-iXP_4BwAwfHc7KIA" alt="Here you can see ngrok up and running on your local machine" width="600" height="400" loading="lazy"></p>
<p><em>Here you can see ngrok up and running on your local machine</em></p>
<p>Go to your WhatsApp Sandbox, under Sandbox settings, and paste the forwarding URL <code>https://b5a6-105-112-120-51.ngrok-free.app</code> appending it with <code>/message</code> under <strong>When a message comes in</strong> and click Save.</p>
<p><img src="https://lh6.googleusercontent.com/VejaBgdyh6Zf5bUCIQyyCZjTvQhWSpEadbP0FliUyOH64QC8fQn9P7wRDgElxo8h7_T7SuleXD01PAeH8hirp2Gn4CaCBDi7IDuIC8V5CfFsjzU-yKxL1_67rCQZ2H77IRpPr0R7P6IegfjyQ72JH-xAjEk0Y-RyckeBM7mFOaoRSrtjMBX3wHDJ1l3QZg" alt="This image shows how to configure your sandbox and link to the ngrok url" width="600" height="400" loading="lazy"></p>
<p><em>This image shows how to configure your sandbox and link to the ngrok url</em></p>
<h2 id="heading-how-to-connect-to-the-merriam-webster-api">How to Connect to the Merriam-Webster API</h2>
<p>To set up a Merriam-Webster dictionary account, <a target="_blank" href="https://www.dictionaryapi.com/register/index">go here</a> and fill out your credentials:</p>
<p><img src="https://lh4.googleusercontent.com/hQ5PhqXwHF1sVLOzsPIf5ehtM7VaGRwEwbA9l11HtIcZnhtkU0HAfR2R9dvXVl0lfmtO1ORIQwFUtNH3tl1Ck-S5e5AeGrHUU4Yq6IusrAKDI4iX4RI5u71whmBiw2jCH_cj0dolHjYjXxEVHInwrxFdH6qrbU3KGKRyKoC-rkEH2qv_DbUiBSCM-pviSg" alt="Image showing page to register for Merriam Webster Developer Center" width="600" height="400" loading="lazy"></p>
<p><em>Image showing page to register for Merriam Webster Developer Center</em></p>
<p>You can register for two keys: Collegiate Dictionary and Collegiate Thesaurus, though this tutorial uses only <strong>Collegiate Dictionary</strong>.</p>
<p><img src="https://lh3.googleusercontent.com/UUUGKxVa_DHx0yelDHCwFzX0uFj3K5tfp6nt4RczGAHnlj1UNZlRxjvXBAsS5_TMcbpnm2jYXWKed_Ek9vAM-qS544fS7mXJTAvdNXYH8tGih6bS7qXozWv5J4-52x1V9Cw1i76-MevLUiMwWuPmi_0EwzB-v2VyzV0kSpaun2Ok5TlmjqXAM6b-w84PvQ" alt="This shows the various keys one can apply for on Merriam-Webster" width="600" height="400" loading="lazy"></p>
<p><em>This shows the various keys one can apply for on Merriam-Webster</em></p>
<p>After filling in all your details, click on Register and Login.</p>
<p>In the home tab, go to Keys to get your API keys.</p>
<p><img src="https://lh6.googleusercontent.com/y_AGpxS8q4Pj-jscWFggBGDXgqE7amA7GygGPXknOtNTcATjdU716lvbIgHGOPo-cxIbdQ4q4RBfP8C4sXwGJlAw5SrpOH3uXf6rDCPh78jweyHYSYDyB_NSJ4SsYhnOqJWP788Yj3swTSUq2ostt2kHssn1KL3mFo3e-L_Y400-7amgsiWqHYfvbJZewA" alt="This key tab shows the keys you have registered for " width="600" height="400" loading="lazy"></p>
<p><em>This key tab shows the keys you have registered for</em></p>
<p>Update the .env file, saving the key as <code>DICTIONARY_KEY</code>.</p>
<pre><code class="lang-text">MONGO_SECRET="&lt;password&gt;"
TWILIO_ACCOUNT_SID="&lt;account_sid&gt;"
TWILIO_AUTH_TOKEN="&lt;auth_token&gt;"
TWILIO_NUMBER="&lt;twilio_number&gt;"
TO_NUMBER="&lt;to_number&gt;"
DICTIONARY_API_KEY="&lt;dictionary_key&gt;"
</code></pre>
<p>Update the <code>main.py</code> as follows:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Form
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> utils <span class="hljs-keyword">import</span> send_message
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List

load_dotenv()

app = FastAPI()
whatsapp_number = os.getenv(<span class="hljs-string">"TO_NUMBER"</span>)
api_key = os.getenv(<span class="hljs-string">"DICTIONARY_API_KEY"</span>)

<span class="hljs-meta">@app.post("/message")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reply</span>(<span class="hljs-params">Body: str = Form(<span class="hljs-params"></span>)</span>):</span>
    url = <span class="hljs-string">f"&lt;https://www.dictionaryapi.com/api/v3/references/collegiate/json/<span class="hljs-subst">{Body}</span>?key=<span class="hljs-subst">{api_key}</span>&gt;"</span>
    response = requests.get(url)
    <span class="hljs-comment"># Extract the JSON data from the response</span>
    data = response.json()

    definition = data[<span class="hljs-number">0</span>][<span class="hljs-string">"shortdef"</span>][<span class="hljs-number">0</span>]

    send_message(whatsapp_number, definition)
</code></pre>
<p><code>@app.post("/message")</code> is a decorator in the fastAPI framework that defines a POST request route to the URL <code>/message</code>. The reply function defined above is called when a POST request is sent to this URL.</p>
<p>The <code>reply</code> function takes in a Body parameter in the request body, which is the message sent to the chatbot (the word you want to get the definition of). It then sends an HTTP request to the Merriam-Webster API to retrieve the word's meaning.</p>
<p>The <code>url</code> variable stores the link to Merriam-Webster API, which takes the <code>Body</code> and the <code>api_key</code> to get details regarding the provided word.</p>
<p>You can make requests from <code>url</code> using <code>requests</code> from the <code>request</code> library and storing <code>request.get(url)</code> into the <code>response</code> variable.</p>
<p>You then extract the JSON data from the response using <code>response.json()</code> and store it in variable data.</p>
<p><code>data[0]["shortdef"][0]</code> allows you to access the short definition of a word stored in the variable definition.</p>
<p><code>send_message()</code> takes the definition and sends it to <code>whatsapp_number</code>.</p>
<p>Next, you'll need to handle situations where someone sends a sentence instead of a word, or a word containing punctuations or characters. So update <code>main.py</code> as follows:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Form
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> utils <span class="hljs-keyword">import</span> send_message
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List
<span class="hljs-keyword">from</span> models <span class="hljs-keyword">import</span> dictionary_collection

load_dotenv()

app = FastAPI()
whatsapp_number = os.getenv(<span class="hljs-string">"TO_NUMBER"</span>)
api_key = os.getenv(<span class="hljs-string">"DICTIONARY_API_KEY"</span>)

<span class="hljs-meta">@app.post("/message")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reply</span>(<span class="hljs-params">Body: str = Form(<span class="hljs-params"></span>)</span>):</span>
    url = <span class="hljs-string">f"&lt;https://www.dictionaryapi.com/api/v3/references/collegiate/json/<span class="hljs-subst">{Body}</span>?key=<span class="hljs-subst">{api_key}</span>&gt;"</span>
    flag=<span class="hljs-string">"Please give a valid word"</span>

    <span class="hljs-keyword">if</span> Body.isalpha():
        response = requests.get(url)
        <span class="hljs-comment"># Extract the JSON data from the response</span>
        data = response.json()

        definition = data[<span class="hljs-number">0</span>][<span class="hljs-string">"shortdef"</span>][<span class="hljs-number">0</span>]

        send_message(whatsapp_number, definition)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> send_message(whatsapp_number, flag)

    <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>
</code></pre>
<p><code>flag</code> is a variable storing the message to give if you provide a sentence or a word with characters.</p>
<p>The <code>if</code> condition checks if a message is a word through <code>Body.isaplha()</code>, if true it gets the definition from Merriam-Webster API if false it returns the function <code>send_message()</code> telling the user to <em>Please give a valid word.</em></p>
<p>To store the words and their meanings to the MongoDB database, update <code>main.py</code> as follows:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Form
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> utils <span class="hljs-keyword">import</span> send_message
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List
<span class="hljs-keyword">from</span> models <span class="hljs-keyword">import</span> dictionary_collection

load_dotenv()

app = FastAPI()
whatsapp_number = os.getenv(<span class="hljs-string">"TO_NUMBER"</span>)
api_key = os.getenv(<span class="hljs-string">"DICTIONARY_API_KEY"</span>)

<span class="hljs-meta">@app.post("/message")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">reply</span>(<span class="hljs-params">Body: str = Form(<span class="hljs-params"></span>)</span>):</span>
    url = <span class="hljs-string">f"&lt;https://www.dictionaryapi.com/api/v3/references/collegiate/json/<span class="hljs-subst">{Body}</span>?key=<span class="hljs-subst">{api_key}</span>&gt;"</span>
    flag=<span class="hljs-string">"Please give a valid word"</span>

    <span class="hljs-keyword">if</span> Body.isalpha():
        response = requests.get(url)
        <span class="hljs-comment"># Extract the JSON data from the response</span>
        data = response.json()

        definition = data[<span class="hljs-number">0</span>][<span class="hljs-string">"shortdef"</span>][<span class="hljs-number">0</span>]

        send_message(whatsapp_number, definition)

        dictionary_db = {<span class="hljs-string">"word"</span>:Body, <span class="hljs-string">"definition"</span>:definition}
        dictionary_collection.insert_one(dictionary_db)

    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> send_message(whatsapp_number, flag)

    <span class="hljs-keyword">return</span> <span class="hljs-string">""</span>
</code></pre>
<p><code>dictionary_db = {"word": Body, "definition": definition}</code> creates a dictionary with two keys, <em>word</em> and <em>definition</em>, and values <em>Body</em> and <em>definition</em>, respectively.</p>
<p><code>dictionary_collection.insert_one(dictionary_db)</code> inserts the dictionary into the MongoDB collection, named <code>dictionary_collection</code>.</p>
<p>You can go to your dashboard and view the items added to the collection.</p>
<p><img src="https://lh6.googleusercontent.com/57-VBBJTN9D7J-0Nhv7gm15kFnf8QrOqajdYc1-1THtnfMhDes2B8ZOtEr02JyskDjTEcwPO4Lpvbe5Hd0-OabelyEcYeh9-jUhtOmEvSDDSHxitTQeScjH6bZivfgadAXT7z3T1yJpOnDRb0i2AvI9PEdPSFI_zAO7F0xa0fQhKGkxR6Aeru0j6aXs3WA" alt="This contains the items that have been sent and received on your application " width="600" height="400" loading="lazy"></p>
<p><em>This contains the items that have been sent and received on your application</em></p>
<h2 id="heading-test-the-chatbot">Test the ChatBot</h2>
<p>Now you can chat with the chatbot and ask for definitions of words:</p>
<p><img src="https://lh3.googleusercontent.com/mZnfLJrDEJwMI7oLO9_a1ILmJjGq_NbEllAy_zbOETOEXZ60OV7XScul8GOWE2kvYM4nFvFX_n0QWQ-ffSTf2p2wfsIO4i66uKVxG80SFjDgLaY99EqWPykryl3EgABNYQaE8sYEaFo_69hgfMm0rd78UtPk-zT7NNfUjfSxC2znO6_JZ1oS7e4TIguJPw" alt="Image showing a chat with the dictionary chatbot" width="600" height="400" loading="lazy"></p>
<p><em>Image showing a chat with the dictionary chatbot</em></p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this tutorial, you learned how to create a WhatsApp dictionary chatbot. You learned how to use FastAPI to power the backend of your application, how to interact with Twilio MessagingX WhatsApp API, and how to use NoSQL database like MongoDB to store your data.</p>
<p>You can extend the chatbot to get synonyms and definitions of words with more than one explanation by accessing more metadata from the Merriam-Webster API.</p>
<p>You can check out the Merriam-Webster <a target="_blank" href="https://www.dictionaryapi.com/products/json">API documentation</a> for the various responses you can get. Ensure you read the <a target="_blank" href="https://www.twilio.com/docs/whatsapp/quickstart/python">Twilio WhatsApp API</a> docs for more advanced functionalities like getting media replies and word pronunciation.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Chatbot With the ChatGPT API ]]>
                </title>
                <description>
                    <![CDATA[ OpenAI's ChatGPT is a great tool for getting information as quickly as possible for your coding projects. Even better, you can now integrate the artificial intelligence-powered chat capability of OpenAI's models directly into your application. Recent... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-chatbot-with-the-chatgpt-api/</link>
                <guid isPermaLink="false">66d4618137bd2215d1e24604</guid>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ chatgpt ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Kingsley Ubah ]]>
                </dc:creator>
                <pubDate>Wed, 26 Jul 2023 15:48:02 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/levart_photographer-drwpcjkvxuU-unsplash--1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>OpenAI's ChatGPT is a great tool for getting information as quickly as possible for your coding projects. Even better, you can now integrate the artificial intelligence-powered chat capability of OpenAI's models directly into your application.</p>
<p>Recently, the OpenAI team expanded their API by giving developers access to their pretrained AI models (DALL-E, Codex, and GPT-3). This means that you can send a question to the API, get the response, and use the data in your application, all within seconds.</p>
<p>In this article, you'll learn how to create an OpenAI account, retrieve your API keys and query OpenAI's GPT-3 model from your Node.js application. Let's dive right in!</p>
<h2 id="heading-how-to-sign-up-for-a-chatgpt-account"><strong>How to Sign Up for a ChatGPT Account</strong></h2>
<p>The first thing you need to do is sign up for an <a target="_blank" href="https://platform.openai.com/overview">OpenAI account</a> if you don't already have one. Once you're in, you'll be redirected back to the homepage.</p>
<p>At the top right corner of the page, click on your profile image, then click on Manage Account. On the sidebar, click API Keys and then click the <strong>create new secret key</strong> button to generate a secret key:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Screenshot_of_OpenAI_API_key.jpg" alt="Screenshot_of_OpenAI_API_key" width="600" height="400" loading="lazy"></p>
<p><em>Screenshot of secret key</em></p>
<p>Copy the secret key and paste it somewhere safe and accessible because you'll need it later to connect your application with the OpenAI API.</p>
<p>With the key safely stored, the next step is to create a Node.js project and spin up an Express server on top of it. Let's start with the installation and basic setup.</p>
<h2 id="heading-how-to-set-up-the-project"><strong>How to Set up the Project</strong></h2>
<p>To follow along with this project, you need to have Node.js and npm installed on your local machine. The latest version of Node.js comes with npm, and it's available on the <a target="_blank" href="https://nodejs.org/en/download">official Node.js website</a>.</p>
<p>Start by creating an empty directory in your computer. Next, launch the command prompt and <code>cd</code> into the folder you just created:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> path/to/project
</code></pre>
<p>Once pointed to the directory, run the following command to create a Node project:</p>
<pre><code class="lang-bash">npm init -y
</code></pre>
<p>Next, run the following command to install <code>express</code> and <code>openai</code> libraries from npm:</p>
<pre><code class="lang-bash">npm i express openai
</code></pre>
<p>The next step is to create the server.</p>
<h2 id="heading-how-to-create-an-express-server"><strong>How to Create an Express Server</strong></h2>
<p>For now, this server will only serve the static files. We'll implement the chat API towards the end of this article.</p>
<p>Start by creating a file named <strong>server.js</strong> inside the root folder of your project. Next, open the file with a code editor and add the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> express =  <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>)
<span class="hljs-keyword">const</span> app = express()

app.use(express.static(<span class="hljs-string">'public'</span>))

app.listen(<span class="hljs-number">5000</span>, <span class="hljs-function">()=&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Server is active"</span>)
})
</code></pre>
<p>With this code, you've created a web server that serves static files (i.e. HTML, CSS) from the <strong>/public</strong> folder.</p>
<p>Next, we'll create the HTML file that renders the chat interface on the web page, as well as the stylesheet file and the JavaScript file.</p>
<h2 id="heading-how-to-create-the-chatbot"><strong>How to Create the Chatbot</strong></h2>
<p>Start by creating a folder named public inside the root of your project. Then inside the <strong>/public</strong> directory, create a file named <strong>index.html</strong>.</p>
<p>Open the file with a text editor and add the following markup in the file:</p>
<pre><code class="lang-html"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">charset</span>=<span class="hljs-string">"UTF-8"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"viewport"</span> <span class="hljs-attr">content</span>=<span class="hljs-string">"width=device-width, initial-scale=1.0"</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Document<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chat-area"</span>&gt;</span>

    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"submit-form"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"input"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"input"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"input"</span> <span class="hljs-attr">cols</span>=<span class="hljs-string">"40"</span> <span class="hljs-attr">rows</span>=<span class="hljs-string">"3"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"btn"</span>&gt;</span>Submit<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>    
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>    

<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<p>As you can see above, the page comprises of the chat area (where the messages are displayed) and the submit area (comprising the text area and the submit button).</p>
<p>To style the page, add the following stylesheet between the opening and closing <code>&lt;head&gt;</code> tags in your <strong>/public/index.html</strong> file:</p>
<pre><code class="lang-html">    <span class="hljs-tag">&lt;<span class="hljs-name">style</span>&gt;</span><span class="css">
        <span class="hljs-selector-id">#chat-area</span> {
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;
            <span class="hljs-attribute">height</span>: <span class="hljs-number">500px</span>;
            <span class="hljs-attribute">overflow</span>:scroll;
            <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid gray;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;
        }

        <span class="hljs-selector-class">.input</span> {            
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }

        <span class="hljs-selector-class">.submit-area</span>{
            <span class="hljs-attribute">justify-content</span>: center;
            <span class="hljs-attribute">display</span>: flex;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">20px</span> auto;
            <span class="hljs-attribute">width</span>: <span class="hljs-number">80%</span>;            
        }       

        <span class="hljs-selector-tag">textarea</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
        }

        <span class="hljs-selector-class">.box</span> {
            <span class="hljs-attribute">width</span>: <span class="hljs-number">96%</span>;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">10px</span>;
            <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#C4DBFE</span>;
            <span class="hljs-attribute">margin</span>: <span class="hljs-number">10px</span> auto;            
        }

        <span class="hljs-selector-class">.answer</span> {
            <span class="hljs-attribute">background-color</span>: aquamarine;
        }

        <span class="hljs-selector-tag">button</span> {
            <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#004089</span>;
            <span class="hljs-attribute">color</span>: white;
            <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span> <span class="hljs-number">10px</span>;
            <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">5px</span>;
            <span class="hljs-attribute">border</span>: none;
        }
    </span><span class="hljs-tag">&lt;/<span class="hljs-name">style</span>&gt;</span>
</code></pre>
<p>If you save the file and check the browser, you should find your page like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Document--1-.png" alt="Document--1-" width="600" height="400" loading="lazy"></p>
<p><em>Screenshot of the page</em></p>
<p>The chat area is empty for now because we haven't submitted any messages yet. To do that, we need to bring in JavaScript.</p>
<h2 id="heading-how-to-add-front-end-javascript"><strong>How to Add Front-end JavaScript</strong></h2>
<p>When the user inputs a message in the text area and clicks on the submit button, we'll send the message to the backend, get the response from the API and display it on the page.</p>
<p>Start by adding an empty script element within the <code>&lt;body&gt;</code> tags in <strong>index.html</strong>:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">script</span>&gt;</span>

<span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
</code></pre>
<p>Inside the script tags, we're to call the <code>getResponse()</code> function whenever the user clicks on the submit button:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> btn = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"btn"</span>)

btn.addEventListener(<span class="hljs-string">'click'</span>, getResponse)
</code></pre>
<p>The <code>getResponse</code> function essentially gets the user's question, sends it to our Node.js backend to fetch the answer, and displays the response on the page.</p>
<p>Inside the function, we start by accessing the prompt submitted by the user:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getResponse</span>(<span class="hljs-params"></span>) </span>{                  
  <span class="hljs-keyword">var</span> inputText = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"input"</span>).value           
  <span class="hljs-keyword">const</span> parentDiv = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"chat-area"</span>) 

  <span class="hljs-comment">// The remaining code goes inside this function</span>
}
</code></pre>
<p>If the value of the text area is empty, we simply return nothing:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(inputText === <span class="hljs-string">''</span>) { <span class="hljs-keyword">return</span> }
</code></pre>
<p>Otherwise, we first add the question to the chat area in the UI:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> question = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
question.innerHTML = inputText
question.classList.add(<span class="hljs-string">"box"</span>)
parentDiv.appendChild(question)
</code></pre>
<p>Next, we reset the text area so it's blank:</p>
<pre><code class="lang-javascript"><span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">"input"</span>).value = <span class="hljs-string">''</span>
</code></pre>
<p>Then we send the question to our server so that the server can send it to the OpenAI API and send us back a response:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">let</span> res = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">'http://localhost:5000/chat'</span>, 
  {
    <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
    <span class="hljs-attr">headers</span>: {
      <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">'application/json'</span>                
    },
    <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
      <span class="hljs-attr">question</span>: inputText          
    })
  }
)

<span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> res.json()
</code></pre>
<p>If the response has a <code>message</code> property, we add the message content to the chat area in the UI:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span>(data.message) {
  <span class="hljs-keyword">const</span> answer = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
  answer.innerHTML = data.message
  answer.classList.add(<span class="hljs-string">"box"</span>, <span class="hljs-string">"answer"</span>)
  parentDiv.appendChild(answer)
}
</code></pre>
<p>Now the frontend is all set. Let's move our focus back to the backend.</p>
<h2 id="heading-how-to-send-the-api-response-to-the-client"><strong>How to Send the API Response to the Client</strong></h2>
<p>Our backend will serve as the middleman between the frontend and OpenAI's API. Basically, we'll get the prompt from the client, send it to the API, and send the response back to the client.</p>
<p>In <strong>server.js</strong>, import these at the top of the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { OpenAI } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"openai"</span>)
</code></pre>
<p>Next, create an instance of the <code>openai</code> connection using the API key you generated earlier:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAI({
    <span class="hljs-comment">// replace your-api-key with your API key from ChatGPT</span>
    <span class="hljs-attr">apiKey</span>: <span class="hljs-string">'your-api-key'</span>
})
</code></pre>
<p>Finally, create the route:</p>
<pre><code class="lang-javascript">app.post(<span class="hljs-string">'/chat'</span>, <span class="hljs-keyword">async</span> (req, res)=&gt; {   
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> resp = <span class="hljs-keyword">await</span> openai.chat.completions.create({
      <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
        <span class="hljs-attr">messages</span>: [
          { <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: req.body.question}
        ]  
    })           

    res.status(<span class="hljs-number">200</span>).json({<span class="hljs-attr">message</span>: resp.choices[<span class="hljs-number">0</span>].message.content})
  } <span class="hljs-keyword">catch</span>(e) {
      res.status(<span class="hljs-number">400</span>).json({<span class="hljs-attr">message</span>: e.message})
  }
})
</code></pre>
<p>Save the file changes, then go to your browser and submit a question. You should get back a response after a few seconds.</p>
<p>You can ask as many questions as you want, but you'll have to wait for a response to each question from the backend.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/07/Document.png" alt="Document" width="600" height="400" loading="lazy"></p>
<p><em>Screenshot of the questions and corresponding answers from ChatGPT</em></p>
<p>If any error is encountered, check the console in your browser to inspect the error message.</p>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>OpenAI's API offers you a way to include AI-powered chatbots in your application using JavaScript or even <a target="_blank" href="https://letsusetech.com/the-awesome-things-you-can-do-with-htmx">HTMX</a> (if you're knowledgeable of HTML but not JavaScript).</p>
<p>Connect with me on <a target="_blank" href="https://twitter.com/kingchuuks">Twitter</a> and <a target="_blank" href="https://www.linkedin.com/in/kingchuks/">LinkedIn</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a ChatBot using the GPT-4 API – Full Project-Based Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ By Tom Chant Chatbots are transforming the way we interact online. Thanks to the OpenAI API, crafting intelligent, context-aware chatbots is now well within the reach of any budding web developer.  In this tutorial, I will teach you everything you ne... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-gpt-4-api-chatbot-turorial/</link>
                <guid isPermaLink="false">66d46149a326133d12440a84</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ openai ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 21 Jun 2023 14:07:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/Slide-16_9---15.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Tom Chant</p>
<p>Chatbots are transforming the way we interact online. Thanks to the OpenAI API, crafting intelligent, context-aware chatbots is now well within the reach of any budding web developer. </p>
<p>In this tutorial, I will teach you everything you need to know to build your own chatbot using the GPT-4 API. </p>
<p>For a deeper dive into the OpenAI API, I have created a 4.5 hour course, "Build AI Apps with ChatGPT, DALL-E, and GPT-4", which you can find on <a target="_blank" href="https://www.youtube.com/watch?v=jlogLBkPZ2A">FreeCodeCamp’s YouTube Channel</a> and <a target="_blank" href="https://scrimba.com/links/openai-api-course">Scrimba</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><a class="post-section-overview" href="#heading-the-app-were-building">The app we're building</a></li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-the-html-and-css-for-the-app">The HTML and CSS for the app</a></li>
<li><a class="post-section-overview" href="#heading-how-to-store-the-api-key">How to store the API key</a></li>
<li><a class="post-section-overview" href="#heading-how-to-import-the-api-key-to-indexjs">How to import the API key to index.js</a></li>
<li><a class="post-section-overview" href="#heading-how-to-install-the-openai-dependency">How to install the OpenAI dependency</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-openai-dependency">How to use the OpenAI dependency</a></li>
<li><a class="post-section-overview" href="#heading-the-flow-of-this-app">The flow of this app</a></li>
<li><a class="post-section-overview" href="#heading-an-array-to-store-the-conversation">An array to store the conversation</a> </li>
<li><a class="post-section-overview" href="#heading-how-to-handle-the-users-input">How to handle the user's input</a></li>
<li><a class="post-section-overview" href="#heading-time-for-the-ai">Time for the AI</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-typewriter-effect">How to implement the typewriter effect</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ul>
<h2 id="heading-the-app-were-building">The App We’re Building</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-168.png" alt="KnowItAll Chatbot" width="600" height="400" loading="lazy">
<em>Screenshot of the app you'll build</em></p>
<p>Meet <em>KnowItAll</em>, a ChatBot with extraordinary conversational powers. You can ask it questions, have it create content, correct language, suggest edits, or translate. It can even write code upon request.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>In this tutorial you will be using HTML, CSS and vanilla JavaScript. A basic understanding of JavaScript is enough – you don’t need to be super advanced.</p>
<p>You’ll also need a free OpenAI account, which you can get <a target="_blank" href="https://openai.com/">here</a>. The complimentary credits you get on signing up should be more than enough to complete this tutorial. As you go through the sign-up process, be sure to copy and paste your API key somewhere safe, as you will need it soon.</p>
<p>This tutorial uses the GPT-4 model. At time of writing, there is a waiting list for GPT-4 (you can join it <a target="_blank" href="https://openai.com/waitlist/gpt-4-api">here</a>). But don’t worry if you haven’t got access to it yet, the GPT-3.5-turbo model is fully compatible with everything we do in this tutorial, and it is available to all now. </p>
<p>And if you don’t know what an OpenAI model is, don’t worry, we will be talking more about them in just a moment.</p>
<p>OK, let’s get down to some code!</p>
<h2 id="heading-the-html-and-css-for-the-app">The HTML and CSS for the App</h2>
<p>Before we dive into the JavaScript and AI components, we need to establish the HTML and CSS foundations of this app. Below, I’ve embedded a Scrim, which is an interactive code editor/screencast. In this scrim, you can:</p>
<ul>
<li>Browse the HTML and CSS used in this project</li>
<li>Click on PREVIEW to see the project in a mini browser</li>
<li>Watch a walkthrough explanation of the HTML and CSS and pause anytime to play with the code. </li>
</ul>
<p>And if you want to run this code locally, you can click the gear icon (⚙️) bottom right and select <em>Download as zip.</em> You will get a zipped folder with all of the HTML, CSS and the image assets. You can unzip that folder and open it in VS Code or whichever dev environment you favour. </p>
<p>Click <a target="_blank" href="https://scrimba.com/learn/buildaiapps/starter-code-coa28453fbd547f14691eb135">here</a> for a full-screen version of this scrim.</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/learn/buildaiapps/starter-code-coa28453fbd547f14691eb135" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<p>I want to draw your attention to lines 22-24 of index.html which contain a hard-coded speech bubble from the chatbot to get the conversation started:</p>
<pre><code class="lang-html"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"speech speech-ai"</span>&gt;</span>
    How can I help you?
<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
</code></pre>
<p>As you can see from the screenshot near the top of this article, each conversation starts with the chatbot asking <em>How can I help you?</em> Note the two CSS classes <code>speech</code> and <code>speech-ai</code>, which style the speech bubble.</p>
<p>Apart from that, there’s nothing particularly unusual going on with this HTML and CSS and we won’t be referring to it much in this tutorial. But do take a moment to either go through it or watch the walkthrough before moving on.</p>
<h2 id="heading-how-to-store-the-api-key">How to Store the API Key</h2>
<p>As the OpenAI API is central to this project, you need to store the OpenAI API key in the app.</p>
<p>⚠️ Remember – your API key is vulnerable in this front-end only project. When you run this app in a browser, your API key will be visible in dev tools, under the network tab. That means you should only run this project locally. </p>
<p>If you’d like to deploy this project so you can share it and include it in your portfolio, in the third section of the full <a target="_blank" href="https://www.youtube.com/watch?v=jlogLBkPZ2A">YouTube</a>/<a target="_blank" href="https://scrimba.com/learn/buildaiapps">Scrimba</a> course I show how you can use Netlify Serverless Functions to keep the API key safely hidden when deploying.</p>
<p>Ok, with that warning out of the way, let’s move forward. In your project folder, create a new file called <code>env.js</code> to hold your API key.</p>
<p>Inside <code>env.js</code> set up a const <code>process</code> which will hold an object with a single property <code>env</code>. This will store your API key in a key/value pair, where the key is <code>OPENAI_API_KEY</code> and the value is the API key itself. (Please note, the API key in the code below is not real!)</p>
<pre><code class="lang-js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> process = {
    <span class="hljs-attr">env</span>: {
        <span class="hljs-attr">OPENAI_API_KEY</span>: <span class="hljs-string">"sk-123456789123456789123456789123456789123456789123"</span>
    }
}
</code></pre>
<p>Here’s an updated scrim showing all of the project code so far:</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/co7b84323b2e1ffea0d0ea4bf" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<h2 id="heading-how-to-import-the-api-key-to-indexjs">How to Import the API Key to <code>index.js</code></h2>
<p>Next, at the top of <code>index.js</code>, import your API key from <code>env.js</code> with this import statement.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { process } <span class="hljs-keyword">from</span> <span class="hljs-string">'/env.js'</span>
</code></pre>
<p>This is a <em>named import</em> which means you include the name of the entity you are importing in curly braces. Now, the entire <code>process</code> object will be available in <code>index.js</code>.</p>
<p>As you are using JavaScript from more than one file, you need to tell the browser to expect modular JavaScript. So add <code>type=”module”</code> to the script tag in <code>index.html</code>.</p>
<pre><code class="lang-js">&lt;script src=<span class="hljs-string">"index.js"</span> type=<span class="hljs-string">"module"</span>&gt;&lt;/script&gt;
</code></pre>
<p>And just to check you haven’t made any typos that cause you bugs down the line, log out <code>process</code> from within <code>index.js</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { process } <span class="hljs-keyword">from</span> <span class="hljs-string">'/env.js'</span>

<span class="hljs-built_in">console</span>.log(process)
<span class="hljs-comment">//{env: {OPENAI_API_KEY: "sk-123456789123456789123456789123456789123456789123"}}</span>
</code></pre>
<p>Here’s a scrim with the code so far:</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/cob3249dfab8737357f28fd34" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<h2 id="heading-how-to-install-the-openai-dependency">How to Install the OpenAI Dependency</h2>
<p>You can access the OpenAI API with a regular <code>fetch</code> request, but it’s much easier to use the OpenAI dependency. If you’re working locally you can add it using NPM:</p>
<pre><code class="lang-terminal">$ npm install openai
</code></pre>
<p>Or if you are working in Scrimba, hover your cursor over DEPENDENCIES and click on the three dot icon which appears.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-169.png" alt="A three dot menu appears next to dependencies" width="600" height="400" loading="lazy">
<em>The three dot menu adjacent to dependencies</em></p>
<p>From the dropdown, select Add module.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-170.png" alt="Dropdown menu with 'add module' option" width="600" height="400" loading="lazy">
<em>Dropdown menu with 'add module' option</em></p>
<p>This will bring up a dialogue box. Type in <code>openai</code>, click ADD, and Scrimba will do the rest. </p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-171.png" alt="The Add NPM package dialogue box" width="600" height="400" loading="lazy">
<em>The Add NPM package dialogue box</em></p>
<p>You will then see the OpenAI dependency listed in the sidebar.</p>
<p>Whether you’re working locally or in Scrimba, you now have the OpenAI dependency installed.</p>
<h2 id="heading-how-to-use-the-openai-dependency">How to Use the OpenAI Dependency</h2>
<p>You need to import two constructors from the dependency. They are needed to configure the app to use the API. They are <code>Configuration</code> and <code>OpenAIApi</code> and you can import them by adding the following line of code to <code>index.js</code>:</p>
<pre><code class="lang-js"><span class="hljs-keyword">import</span> { Configuration, OpenAIApi } <span class="hljs-keyword">from</span> <span class="hljs-string">'openai'</span>
</code></pre>
<p>To interact with the API you need to set up your own <code>configuration</code> (note the lowercase ‘c’) object using the <code>Configuration</code> constructor.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration()
</code></pre>
<p>Next, pass it an object with a key/value pair. The key will be <code>apiKey</code> and the value will be our API key which you have imported from <code>process</code> and can access with <code>process.env.OPENAI_API_KEY</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration({
    <span class="hljs-attr">apiKey</span>: process.env.OPENAI_API_KEY
})
</code></pre>
<p>Then, you need to pass <code>configuration</code> to the <code>OpenAIApi</code> constructor. Save this new instance of <code>OpenAIApi</code> to a const <code>openai</code> (note the lowercase ‘o’).</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAIApi(configuration)
</code></pre>
<p>And to check it’s working, log out <code>openai</code>.</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(openai)
<span class="hljs-comment">//OpenAIApi$1 {basePath: "&lt;https://api.openai.com/v1&gt;", configuration: {apiKey: "sk-12345678912345678912345678...", baseOptions: {headers: {User-Agent: "OpenAI/NodeJS/3.2.1", Authorization: "Bearer sk-1234567891234567891..."}}}}</span>
</code></pre>
<p>Here’s a scrim with all the code so far:</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/cocbf43a78e2d0c1915a3cd7e" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<p>Remember you can click the gear icon (⚙️) to download all of the code in one zipped folder.</p>
<p>Now that you have finished setting up the OpenAI API dependency, you can proceed to its usage. But before you continue writing more code, let's take a moment to envision how this chatbot will work.</p>
<h2 id="heading-the-flow-of-this-app">The Flow of this App</h2>
<p>One major challenge when working with OpenAI models is that they don't retain the queries or replies from previous interactions. In fact, they have no memory of their past conversations with you at all. This can lead to disjointed and confusing conversations like this:</p>
<p><code>Human: Who was the first person to walk on the moon?</code></p>
<p><code>AI: Neil Armstrong.</code></p>
<p><code>Human: How old was he at the time?</code></p>
<p><code>AI: How old was who?</code></p>
<p><code>Human: ???</code></p>
<p>Therefore, to create a chatbot capable of engaging in a coherent conversation, we need to provide the OpenAI model with a form of memory. </p>
<p>Fortunately, there is a straightforward way of doing that: we include the entire conversation as it currently stands with each API call. This allows the API to utilise the conversation's context when forming completions to better understand the questions it is being asked.</p>
<p>So the app works like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-172.png" alt="1. The user types a question into the input field and hits send." width="600" height="400" loading="lazy">
<em>The user types a question into the input field and hits send.</em></p>
<p>The hard-coded message <em>How can I help you?</em> is displayed. The user types in a question or a request and hits <em>enter</em> or presses the send button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-21-at-11.28.11.png" alt="2. The question is rendered to the DOM." width="600" height="400" loading="lazy">
<em>The question is rendered to the DOM.</em></p>
<p>The question is rendered to the DOM in a green speech bubble and the input is cleared.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-174.png" alt="3. The question is stored in an array. This array is the single source of truth for the conversation." width="600" height="400" loading="lazy">
<em>The question is stored in an array. This array is the single source of truth for the conversation.</em></p>
<p>The question is stored in an array. This is the array that will hold the entire conversation and acts as a single source of truth. This allows the app to have a "memory" of the conversation so it can understand requests and contextualise its responses.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-175.png" alt="4. The array is sent to the OpenAI API." width="600" height="400" loading="lazy">
<em>The array is sent to the OpenAI API.</em></p>
<p>The array is sent off to the API. As the conversation grows, this array will hold more and more elements.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-176.png" alt="5. The OpenAI API sends back a response with the answer. (This is known as a  completion.)" width="600" height="400" loading="lazy">
<em>The OpenAI API sends back a response with the answer. (This is known as a completion.)</em></p>
<p>The OpenAI API sends back a response. Within that response is the actual language generated by the AI model. This is called the <em>completion</em>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/Screenshot-2023-06-21-at-11.30.23.png" alt="6. The completion is stored in the array and rendered to the DOM." width="600" height="400" loading="lazy">
<em>The completion is stored in the array and rendered to the DOM.</em></p>
<p>The completion is added to the array holding the conversation so that it can be used to contextualise any future requests to the API. The completion is also rendered to the DOM so the user can see it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-178.png" alt="7. The user continues the conversation." width="600" height="400" loading="lazy">
<em>The user continues the conversation.</em></p>
<p>The user now continues the conversation. And whatever they type in is rendered to the DOM.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-179.png" alt="8. The user’s input is added to the conversation array and the entire array is sent off to the API." width="600" height="400" loading="lazy">
<em>The user’s input is added to the conversation array and the entire array is sent off to the API.</em></p>
<p>❗️Step 8 is particularly important because here the question <em>How many people live there?</em> does not mention <em>Paris</em>, so the API can only answer correctly if it is getting the context of the conversation from the array we are sending with each request.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-180.png" alt="9. The OpenAI API’s response shows it understands the context of the question." width="600" height="400" loading="lazy">
<em>The OpenAI API’s response shows it understands the context of the question.</em></p>
<p>From its response, we can see that the API does have the context of the conversation from the array – it knew we were talking about Paris even though Paris was not mentioned in the question <em>How many people live there?</em> So now we can be sure that we will be able to have a logical, flowing conversation with the chatbot.</p>
<p>Now you have an overview of how the app is going to work, let’s get into the nuts and bolts of the AI.</p>
<h2 id="heading-an-array-to-store-the-conversation">An Array to Store the Conversation</h2>
<p>As mentioned previously, the OpenAI API needs to be provided with the conversation as it exists at that time with each API call. The conversation should be structured as an array of objects, with each object following a specific format.</p>
<p>First, set up an array and call it <code>conversationArr</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> conversationArr = []
</code></pre>
<p>Each element in this array will be an object with two key/value pairs. The first key will be <code>role</code> and the second key will be <code>content</code>. This structure will be consistent for all objects stored in the array throughout the project.</p>
<p>The first object in the array will contain instructions for the chatbot. This object, known as the <em>instruction object</em>, allows you to control the chatbot's personality and provide behavioural instructions, specify response length, and more. </p>
<p>The instruction object’s <code>role</code> property should contain the string <code>‘system'</code> and the <code>content</code> should hold your instruction in a string. </p>
<p>Here are a few example instructions:</p>
<p><code>‘You are a useful assistant'</code></p>
<p><code>‘You reply in French'</code></p>
<p><code>‘You translate whatever I say into Spanish'</code></p>
<p><code>‘You are a useful assistant that gives long, detailed answers'</code></p>
<p>So <code>conversationArr</code> with the instruction object looks like this.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> conversationArr = [
    { 
        <span class="hljs-attr">role</span>: <span class="hljs-string">'system'</span>,
        <span class="hljs-attr">content</span>: <span class="hljs-string">'You are a useful assistant.'</span> <span class="hljs-comment">// this is the instruction</span>
    }
]
</code></pre>
<p>And as the instruction object won’t change, let’s hard code it and put it in <code>index.js</code>.</p>
<p>So far, our code looks like this:</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/co8dc4d75b48980490dba860e" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<p>Before we move on, let’s look at the other two types of Object that you will be storing in <code>conversationArr</code>. And just to be clear, you won’t be hard-coding these in <code>index.js</code> now, but adding them programmatically as needed.</p>
<p>When the user submits some text, that text will be stored in an object in <code>conversationArr</code> and it will look like this, with the <code>role</code> being <code>‘user'</code> and the <code>content</code> being the text the user has submitted.</p>
<pre><code class="lang-js">{ 
    <span class="hljs-attr">role</span>: <span class="hljs-string">'user'</span>,
    <span class="hljs-attr">content</span>: <span class="hljs-string">'What is the capital of France?'</span> <span class="hljs-comment">// your question</span>
}
</code></pre>
<p>And the API’s response will also be stored in an Object. Here the role will be <code>‘assistant'</code> and the <code>content</code> will be the completion text, for example:</p>
<pre><code class="lang-js">{ 
    <span class="hljs-attr">role</span>: <span class="hljs-string">'assistant'</span>,
    <span class="hljs-attr">content</span>: <span class="hljs-string">'The capital of France is Paris.'</span> <span class="hljs-comment">// the completion</span>
}
</code></pre>
<p>All of the objects that end up in <code>conversationArr</code> as it grows will follow this same pattern, with <code>role</code> and <code>content</code> properties.</p>
<p>So now let’s deal with what happens when the user types in some text and presses the submit button.</p>
<h2 id="heading-how-to-handle-the-users-input">How to Handle the User’s Input</h2>
<p>Your next task is to take the user’s input and render it to the DOM. The div that holds the conversation in <code>index.html</code> has the id of <code>chatbot-conversation</code>. So in <code>index.js</code> take control of that div and save it to a const <code>chatbotConversation</code>.</p>
<pre><code class="lang-js"><span class="hljs-keyword">const</span> chatbotConversation = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'chatbot-conversation'</span>)
</code></pre>
<p>And now you need to hook up an event listener which fires when a user submits some text. Another quick look at index.html confirms that the button is inside a form element:</p>
<pre><code class="lang-js">&lt;form id=<span class="hljs-string">"form"</span> <span class="hljs-class"><span class="hljs-keyword">class</span></span>=<span class="hljs-string">"chatbot-input-container"</span>&gt;
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span> <span class="hljs-attr">name</span>=<span class="hljs-string">"user-input"</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"user-input"</span> <span class="hljs-attr">required</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"submit-btn"</span> <span class="hljs-attr">class</span>=<span class="hljs-string">"submit-btn"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">img</span> 
            <span class="hljs-attr">src</span>=<span class="hljs-string">"images/send-btn-icon.png"</span> 
            <span class="hljs-attr">class</span>=<span class="hljs-string">"send-btn-icon"</span>
            &gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span></span>
</code></pre>
<p>So clicking on the button or hitting enter will trigger a <code>submit</code> event. That’s what you need the event listener to listen out for. And as you want to prevent the browser reloading with a query string in the URL, you need to add <code>e.preventDefault()</code>.</p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault()
})
</code></pre>
<h3 id="heading-rendering-the-users-message">Rendering the User’s Message</h3>
<p>When a user submits a message, you need to render it. There are five stages to that process:</p>
<ol>
<li>Take control of the text input field, which has the id <code>user-input</code>.</li>
<li>Use <code>createElement</code> to create a new div.</li>
<li>Add the CSS classes the div needs: <code>speech</code> which is the generic speech bubble class, and <code>speech-human</code> which applies styles that differentiate the human speech bubble from the AI speech bubble.</li>
<li>Append that speech bubble to <code>chatbotConversation</code>.</li>
<li>Set the speech bubble’s <code>textContent</code> to the user’s input, which you can get from <code>userInput.value</code>.</li>
</ol>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault()
    <span class="hljs-comment">// 1. take control of the text input field</span>
    <span class="hljs-keyword">const</span> userInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'user-input'</span>)  
    <span class="hljs-comment">// 2. create the new element</span>
    <span class="hljs-keyword">const</span> newSpeechBubble = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
    <span class="hljs-comment">// 3. give it CSS classes</span>
    newSpeechBubble.classList.add(<span class="hljs-string">'speech'</span>, <span class="hljs-string">'speech-human'</span>)
    <span class="hljs-comment">// 4. append the speech bubble to the conversation</span>
    chatbotConversation.appendChild(newSpeechBubble)
    <span class="hljs-comment">// 5. add the user's inputted text to the speech bubble</span>
    newSpeechBubble.textContent = userInput.value
})
</code></pre>
<h3 id="heading-updating-conversationarr-with-the-users-input">Updating <code>conversationArr</code> with the User's Input</h3>
<p>Having rendered the message to the DOM, you now need to push an object to <code>conversationArr</code> in the format we looked at previously. This object will have a <code>role</code> of <code>‘user’</code> and the <code>content</code> property will hold whatever the user has typed in the text input.</p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault()

    <span class="hljs-keyword">const</span> userInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'user-input'</span>)  
    <span class="hljs-keyword">const</span> newSpeechBubble = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
    newSpeechBubble.classList.add(<span class="hljs-string">'speech'</span>, <span class="hljs-string">'speech-human'</span>)
    chatbotConversation.appendChild(newSpeechBubble)
    newSpeechBubble.textContent = userInput.value

    <span class="hljs-comment">// push object to conversationArr</span>
    conversationArr.push({ 
        <span class="hljs-attr">role</span>: <span class="hljs-string">'user'</span>,
        <span class="hljs-attr">content</span>: userInput.value
    })

})
</code></pre>
<h3 id="heading-clearing-the-input-field-and-scrolling-to-the-bottom">Clearing the input field and scrolling to the Bottom</h3>
<p>There are two final jobs this event listener’s function needs to do. </p>
<p>Firstly, let’s clear the text input by setting it to an empty string. And secondly, let’s scroll the conversation to the bottom so the user doesn’t have to manually scroll down. You could use the <code>scrollIntoView</code> method to do that, but because I coded this for a mini browser and <code>scrollIntoView</code> was causing problems, I used a slightly different technique as shown below:</p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault()

    <span class="hljs-keyword">const</span> userInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'user-input'</span>)  
    <span class="hljs-keyword">const</span> newSpeechBubble = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
    newSpeechBubble.classList.add(<span class="hljs-string">'speech'</span>, <span class="hljs-string">'speech-human'</span>)
    chatbotConversation.appendChild(newSpeechBubble)
    newSpeechBubble.textContent = userInput.value

    conversationArr.push({ 
        <span class="hljs-attr">role</span>: <span class="hljs-string">'user'</span>,
        <span class="hljs-attr">content</span>: userInput.value
    })

    <span class="hljs-comment">// empty the text input</span>
    userInput.value = <span class="hljs-string">''</span>
    <span class="hljs-comment">// scroll the conversation to the bottom</span>
    chatbotConversation.scrollTop = chatbotConversation.scrollHeight
})
</code></pre>
<p>So our code so far looks like this:</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/co5a74d69b0ff18ce5551c4df" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<p>Now, if you ask the question <em>What is the capital of France?</em> and log out <code>conversationArr</code> from within the event listener’s function, you get this:</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(conversationArr)
<span class="hljs-comment">//[{role: "system", content: "You are a useful assistant."}, {role: "user", content: "What is the capital of France?"}]</span>
</code></pre>
<h2 id="heading-time-for-the-ai">Time for the AI</h2>
<p>It’s time to use the OpenAI API to actually generate some text. The dependency makes it a really easy API to use – you just need three pieces of information.</p>
<ol>
<li>An endpoint</li>
<li>A model</li>
<li>Our conversation in an array</li>
</ol>
<p>You’ve already set up <code>conversationArr</code> to deal with number 3 on that list, but before you make a request to the API, let’s look at 1 and 2 in more detail.</p>
<h3 id="heading-endpoints">Endpoints</h3>
<p>The OpenAI API has various endpoints available. Which one you use depends on what you want the AI to do (generate language, generate code, create images from text prompts, and so on). </p>
<p>For this chatbot, we will be using the <code>chat/completion</code> endpoint, which at the time of writing is the most advanced endpoint for natural language generation in the OpenAI stable.</p>
<h3 id="heading-models">Models</h3>
<p>The model (sometimes called the engine) is what actually creates the language. Our model for this project is <code>GPT-4</code>. <code>GPT-4</code> is on limited release via a <a target="_blank" href="https://openai.com/waitlist/gpt-4-api">waiting list</a> at present, so if you can’t access it right now, you can use <code>GPT-3.5-turbo</code> instead. All code in this project works with both models, and <code>GPT-3.5-turbo</code> is also highly capable.</p>
<p>OK, let’s make a call to the API.</p>
<h3 id="heading-the-fetchreply-function">The fetchReply Function</h3>
<p>We need a function to take on the job of making a request to the API. Let's call this function <code>fetchReply</code>. The basics of the function look like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchReply</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> openai.createChatCompletion() 
}
</code></pre>
<p>Inside the body of the function, we’ve got a const <code>response</code> and we’ve awaited a call to the <code>chat/completion</code> endpoint which you can reach by taking the instance of the <code>OpenAiAPI</code> constructor you stored in the const <code>openai</code> earlier and calling the <code>createChatCompletion</code> method on it. (<code>createChatCompletion</code> is an OpenAI API method that gives us access to the <code>chat/completion</code> endpoint. For more info, check the <a target="_blank" href="https://platform.openai.com/docs/api-reference">OpenAI API docs</a>.)</p>
<p>Because the dependency is making a fetch request, you need to use the <code>await</code> keyword and make this an <code>async</code> function.</p>
<p>Next, you need to pass <code>createChatCompletion</code> an object, and that object needs two properties: <code>model</code> and <code>messages</code>.</p>
<h3 id="heading-model">Model</h3>
<p>Our model is GPT-4. You can specify this by putting it in a string in lowercase in a key/value pair: <code>model: 'gpt-4'</code>. This is where you could also use <code>model: ’gpt-3.5-turbo'</code> if you don’t have access to GPT-4 yet.</p>
<h3 id="heading-messages">Messages</h3>
<p>The messages property just needs to hold our conversation, which you have stored as an array of objects in the const <code>conversationArr</code>.</p>
<p>For now, log out the response. So the finished code looks like this:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchReply</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> openai.createChatCompletion({
        <span class="hljs-attr">model</span>: <span class="hljs-string">'gpt-4'</span>, <span class="hljs-comment">// or 'gpt-3.5-turbo'</span>
        <span class="hljs-attr">messages</span>: conversationArr,
    })
    <span class="hljs-built_in">console</span>.log(response)
}
</code></pre>
<p>Now, call fetchReply from within the event listener’s function.</p>
<pre><code class="lang-js"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'submit'</span>, <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault()
    <span class="hljs-keyword">const</span> userInput = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'user-input'</span>)   
    conversationArr.push({ 
        <span class="hljs-attr">role</span>: <span class="hljs-string">'user'</span>,
        <span class="hljs-attr">content</span>: userInput.value
    })
    <span class="hljs-keyword">const</span> newSpeechBubble = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
    newSpeechBubble.classList.add(<span class="hljs-string">'speech'</span>, <span class="hljs-string">'speech-human'</span>)
    chatbotConversation.appendChild(newSpeechBubble)
    newSpeechBubble.textContent = userInput.value
    userInput.value = <span class="hljs-string">''</span>
    chatbotConversation.scrollTop = chatbotConversation.scrollHeight
    <span class="hljs-built_in">console</span>.log(conversationArr)

    <span class="hljs-comment">// call fetch reply to trigger the API call</span>
    fetchReply()
})
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-181.png" alt="Chatbot with question from the user." width="600" height="400" loading="lazy">
<em>Chatbot with question from the user.</em></p>
<p>And when you type in <em>What is the capital of France?</em> and hit send, you get back this massive object: (Feel free to scroll through it, but don't be intimidated!)</p>
<pre><code class="lang-js">{<span class="hljs-attr">data</span>: {<span class="hljs-attr">id</span>: <span class="hljs-string">"chatcmpl-7MuziItZYyiFpPG2KHQawd19rD54U"</span>, <span class="hljs-attr">object</span>: <span class="hljs-string">"chat.completion"</span>, <span class="hljs-attr">created</span>: <span class="hljs-number">1685696658</span>, <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-4-0314"</span>, <span class="hljs-attr">usage</span>: {<span class="hljs-attr">prompt_tokens</span>: <span class="hljs-number">28</span>, <span class="hljs-attr">completion_tokens</span>: <span class="hljs-number">36</span>, <span class="hljs-attr">total_tokens</span>: <span class="hljs-number">64</span>}, <span class="hljs-attr">choices</span>: [{<span class="hljs-attr">message</span>: {<span class="hljs-attr">role</span>: <span class="hljs-string">"assistant"</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">"The capital of France is Paris."</span>}, <span class="hljs-attr">finish_reason</span>: <span class="hljs-string">"stop"</span>, <span class="hljs-attr">index</span>: <span class="hljs-number">0</span>}]}, <span class="hljs-attr">status</span>: <span class="hljs-number">200</span>, <span class="hljs-attr">statusText</span>: <span class="hljs-string">""</span>, <span class="hljs-attr">headers</span>: {cache-control: <span class="hljs-string">"no-cache, must-revalidate"</span>, content-type: <span class="hljs-string">"application/json"</span>}, <span class="hljs-attr">config</span>: {<span class="hljs-attr">transitional</span>: {<span class="hljs-attr">silentJSONParsing</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">forcedJSONParsing</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">clarifyTimeoutError</span>: <span class="hljs-literal">false</span>}, <span class="hljs-attr">adapter</span>: xhrAdapter(e), <span class="hljs-attr">transformRequest</span>: [transformRequest(e,t)], <span class="hljs-attr">transformResponse</span>: [transformResponse(e)], <span class="hljs-attr">timeout</span>: <span class="hljs-number">0</span>, <span class="hljs-attr">xsrfCookieName</span>: <span class="hljs-string">"XSRF-TOKEN"</span>, <span class="hljs-attr">xsrfHeaderName</span>: <span class="hljs-string">"X-XSRF-TOKEN"</span>, <span class="hljs-attr">maxContentLength</span>: <span class="hljs-number">-1</span>, <span class="hljs-attr">maxBodyLength</span>: <span class="hljs-number">-1</span>, <span class="hljs-attr">validateStatus</span>: validateStatus(e), <span class="hljs-attr">headers</span>: {<span class="hljs-attr">Accept</span>: <span class="hljs-string">"application/json, text/plain, */*"</span>, Content-Type: <span class="hljs-string">"application/json"</span>, User-Agent: <span class="hljs-string">"OpenAI/NodeJS/3.2.1"</span>, <span class="hljs-attr">Authorization</span>: <span class="hljs-string">"Bearer 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"</span>}, <span class="hljs-attr">method</span>: <span class="hljs-string">"post"</span>, <span class="hljs-attr">data</span>: <span class="hljs-string">"{"</span>model<span class="hljs-string">":"</span>gpt<span class="hljs-number">-4</span><span class="hljs-string">","</span>messages<span class="hljs-string">":[{"</span>role<span class="hljs-string">":"</span>system<span class="hljs-string">","</span>content<span class="hljs-string">":"</span>You are a highly knowledgeable assistant that is always happy to help.<span class="hljs-string">"},{"</span>role<span class="hljs-string">":"</span>user<span class="hljs-string">","</span>content<span class="hljs-string">":"</span>What is the capital <span class="hljs-keyword">of</span> france?<span class="hljs-string">"}]}"</span>, <span class="hljs-attr">url</span>: <span class="hljs-string">"https://api.openai.com/v1/chat/completions"</span>}, <span class="hljs-attr">request</span>: XMLHttpRequest {}}
</code></pre>
<p>There’s lots of useful info here, but we need to focus in on this one part, which I have formatted for readability:</p>
<pre><code class="lang-js">choices: [
    {
        <span class="hljs-attr">message</span>: {
            <span class="hljs-attr">role</span>: <span class="hljs-string">"assistant"</span>, 
            <span class="hljs-attr">content</span>: <span class="hljs-string">"The capital of France is Paris."</span>
        }, 
        <span class="hljs-attr">finish_reason</span>: <span class="hljs-string">"stop"</span>, <span class="hljs-attr">index</span>: <span class="hljs-number">0</span>
    }
]
</code></pre>
<p>This is where we can see the completion: <em>The capital of France is Paris.</em> And that is what you need to render to the DOM in a speech bubble. And you can use dot and bracket notation to get to that text.</p>
<p>Let’s log that out.</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(response.data.choices[<span class="hljs-number">0</span>].message.content)
<span class="hljs-comment">//The capital of France is Paris.</span>
</code></pre>
<p>But before you render anything, remember you also need to include each piece of dialogue in <code>conversationArr</code>. And the format that you need for that is an object with two key/value pairs where one key is <code>role</code> and has the value <code>’assistant’</code>, and the other is <code>content</code> and holds the completion as its value.  </p>
<p>And that object is exactly what is given to you in <code>response.data.choices[0].message</code> – yes, the object that you need to add to <code>conversationArr</code> is actually provided to you by the API!</p>
<p>You can adjust the above <code>console.log</code> to prove it:</p>
<pre><code class="lang-js"><span class="hljs-built_in">console</span>.log(response.data.choices[<span class="hljs-number">0</span>].message)
<span class="hljs-comment">//{role: "assistant", content: "The capital of France is Paris."}</span>
</code></pre>
<p>Now you can go ahead and make <code>fetchReply</code> push this object to <code>conversationArr</code>.</p>
<p>And let’s log out <code>conversationArr</code> to check that it works:</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchReply</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> openai.createChatCompletion({
        <span class="hljs-attr">model</span>: <span class="hljs-string">'gpt-4'</span>,
        <span class="hljs-attr">messages</span>: conversationArr,
    })
    conversationArr.push(response.data.choices[<span class="hljs-number">0</span>].message)
    <span class="hljs-built_in">console</span>.log(conversationArr)
}

<span class="hljs-comment">// [{role: "system", content: "You are a useful assistant."}, {role: "user", content: "What is the capital of France?"}, {role: "assistant", content: "The capital of France is Paris."}]</span>
</code></pre>
<p>The project code should now look like this:</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/co5f9419c95e33d38a87a200c" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<p>Now all that’s left to do is render out the completion.</p>
<h2 id="heading-how-to-implement-the-typewriter-effect">How to Implement the Typewriter Effect</h2>
<p>The last task is to make our chatBot type out its response. There are thousands of ways you could do this, and it is possible to do it only with CSS. We are going to do it with JavaScript.</p>
<p>Create a function called <code>renderTypewriterText</code>. This function will take in a parameter which will be the text string you get from the response. </p>
<p>The <code>renderTypewriterText</code> function needs to create a new speech bubble element, give it CSS classes, and append it to <code>chatbotConversation</code>. This is almost the same code as you used before for the user’s input, but note here that you will also need to give the speech bubble the <code>blinking-cursor</code> class, which uses a CSS animation to create a cursor effect while the text is being rendered. See <code>index.css</code> lines 151 onwards in the above scrim for the CSS.</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderTypewriterText</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> newSpeechBubble = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
    newSpeechBubble.classList.add(<span class="hljs-string">'speech'</span>, <span class="hljs-string">'speech-ai'</span>, <span class="hljs-string">'blinking-cursor'</span>)
    chatbotConversation.appendChild(newSpeechBubble)
}
</code></pre>
<p>Now add some logic to render each character one by one:</p>
<pre><code class="lang-js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">renderTypewriterText</span>(<span class="hljs-params">text</span>) </span>{
    <span class="hljs-keyword">const</span> newSpeechBubble = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'div'</span>)
    newSpeechBubble.classList.add(<span class="hljs-string">'speech'</span>, <span class="hljs-string">'speech-ai'</span>, <span class="hljs-string">'blinking-cursor'</span>)
    chatbotConversation.appendChild(newSpeechBubble)

    <span class="hljs-comment">// render each character one by one </span>
    <span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>
    <span class="hljs-keyword">const</span> interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
        newSpeechBubble.textContent += text.slice(i<span class="hljs-number">-1</span>, i)
        <span class="hljs-keyword">if</span> (text.length === i) {
            <span class="hljs-built_in">clearInterval</span>(interval)
            newSpeechBubble.classList.remove(<span class="hljs-string">'blinking-cursor'</span>)
        }
        i++
        chatbotConversation.scrollTop = chatbotConversation.scrollHeight
    }, <span class="hljs-number">50</span>)
}
</code></pre>
<p>That’s quite a jumble of JavaScript, so let’s work through it step by step.</p>
<ol>
<li><code>let i = 0</code>: This initialises a variable <code>i</code> with a value of 0. It will be used to keep track of the current index of the <code>text</code> string.</li>
<li><code>const interval = setInterval(() =&gt; { ... }, 50)</code>: This creates an interval that repeatedly executes the arrow function every 50 milliseconds. The arrow function contains the code that will be executed at each interval.</li>
<li><code>newSpeechBubble.textContent += text.slice(i-1, i)</code>: This line appends a portion of the <code>text</code> string to the content of the <code>newSpeechBubble</code> element. It uses the <code>slice</code> method to extract a single character from the <code>text</code> string based on the current value of <code>i</code>.</li>
<li><code>if (text.length === i) { ... }</code>: This condition checks if the entire <code>text</code> string has been appended to the speech bubble. If the length of the <code>text</code> string is equal to <code>i</code>, it means that all characters have been appended.</li>
<li><code>clearInterval(interval)</code>: This line clears the interval, stopping the execution of the function.</li>
<li><code>newSpeechBubble.classList.remove('blinking-cursor')</code>: This removes the CSS class  <code>'blinking-cursor'</code> from the <code>newSpeechBubble</code> element. It removes the blinking cursor effect once the entire <code>text</code> string has been displayed. You only want the blinking cursor while the typewriter is running.</li>
<li><code>i++</code>: This increments the value of <code>i</code> by 1, moving to the next character in the <code>text</code> string for the next interval execution.</li>
<li><code>chatbotConversation.scrollTop = chatbotConversation.scrollHeight</code>: This scrolls the conversation container to the bottom, ensuring that the new content is always visible.</li>
</ol>
<p>To finish wiring this up, call <code>renderTypewriterText</code> from inside fetchReply, remembering to pass in the text completion that comes back from the API.</p>
<pre><code class="lang-js"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchReply</span>(<span class="hljs-params"></span>)</span>{
    <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> openai.createChatCompletion({
        <span class="hljs-attr">model</span>: <span class="hljs-string">'gpt-4'</span>,
        <span class="hljs-attr">messages</span>: conversationArr,
    }) 
    conversationArr.push(response.data.choices[<span class="hljs-number">0</span>].message)

    <span class="hljs-comment">// call renderTypewriterText passing in the completion</span>
    renderTypewriterText(response.data.choices[<span class="hljs-number">0</span>].message.content)
}
</code></pre>
<p>And you are done!</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/06/image-182.png" alt="The finished app with a conversation between a user and the AI chatbot" width="600" height="400" loading="lazy">
<em>The finished app with a conversation between a user and the AI chatbot</em></p>
<p>We now have a fully working chatbot using the GPT-4 API and you can continue the conversation for as long as you want! </p>
<p>Well, that’s not quite true. There is a theoretical limit to how long the conversation can be, but you would have to carrying on chatting for a long time to reach it. We talk about that more in the full course. Also, it's important to note that at some point, you may hit your credit limit.</p>
<p>Here’s the finished code. And as before, you can hit the gear icon ⚙️ and download it.</p>
<div class="embed-wrapper"><iframe src="https://scrimba.com/scrim/co6f54980a423391dcd15fe81" style="width:100%;height:400px" title="Embedded content" loading="lazy"></iframe></div>

<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations on successfully building your own chatbot using the GPT-4 API! With GPT-4, you've unlocked a world of possibilities in natural language processing and conversation generation. </p>
<p>As you continue on your AI journey, remember to stay curious, keep learning, and explore the evolving field of artificial intelligence. Share your creations, collaborate with others, and be part of the AI community. Happy coding!</p>
<p>Always feel free to reach out to me on Twitter. I am <a target="_blank" href="https://twitter.com/Tpchant">@tpchant</a>.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ LangChain Tutorial – How to Build a Custom-Knowledge Chatbot ]]>
                </title>
                <description>
                    <![CDATA[ By Shane Duggan You may have read about the large number of AI apps that have been released over the last couple of months. You may have even started using some of them. AI tools such as ChatPDF and CustomGPT AI have become very useful to people – an... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/langchain-how-to-create-custom-knowledge-chatbots/</link>
                <guid isPermaLink="false">66d460f9182810487e0ce1b0</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ chatgpt ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 01 Jun 2023 15:41:20 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/06/ThumbnailArticle--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Shane Duggan</p>
<p>You may have read about the large number of AI apps that have been released over the last couple of months. You may have even started using some of them.</p>
<p>AI tools such as <a target="_blank" href="https://www.chatpdf.com/">ChatPDF</a> and <a target="_blank" href="https://customgpt.ai/use-cases/">CustomGPT AI</a> have become very useful to people – and for good reason. Gone are the days where you need to scroll through a 50-page document just to find a simple answer. Instead, you can rely on AI to do the heavy lifting.</p>
<p>But how exactly are all these developers creating and using these tools? Well, many of them are using an open source framework called LangChain.</p>
<p>In this article, I'm going to introduce you to LangChain and show you how it's being used in combination with OpenAI's API to create these game-changing tools. Hopefully, I'll inspire one of you to come up with one of your own. So let's jump in!</p>
<h2 id="heading-what-is-langchain">What Is LangChain?</h2>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/05/Screenshot-2023-05-29-at-5.40.38-PM.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><a target="_blank" href="https://github.com/hwchase17/langchain/">LangChain</a> is an open source framework that allows AI developers to combine Large Language Models (LLMs) like GPT-4 with external data. It's offered in Python or JavaScript (TypeScript) packages.</p>
<p>As you may know, GPT models have been trained on data up until 2021, which can be a significant limitation. And while these models' general knowledge is great, being able to connect them to custom data and computations opens up many doors. That's exactly what LangChain does.</p>
<p>Essentially, it allows your LLM to reference entire databases when coming up with its answers. So you can now have your GPT models access up-to-date data in the form of reports, documents, and website info.</p>
<p>Recently, LangChain has experienced a significant surge in popularity, especially after the launch of GPT-4 in March. This was thanks to its versatility and the many possibilities it opens up when paired with a powerful LLM. </p>
<h2 id="heading-how-does-langchain-work">How Does LangChain Work?</h2>
<p>While you may be thinking that LangChain sounds pretty complicated, it's actually quite approachable.</p>
<p>In short, LangChain just composes large amounts of data that can easily be referenced by a LLM with as little computation power as possible. It works by taking a big source of data, take for example a 50-page PDF, and breaking it down into "chunks" which are then embedded into a Vector Store.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/05/------LangChain.png" alt="Image" width="600" height="400" loading="lazy">
<em>Simple Diagram of creating a Vector Store</em></p>
<p>Now that we have vectorized representations of the large document, we can use this in conjunction with the LLM to retrieve only the information we need to be referenced when creating a prompt-completion pair.</p>
<p>When we insert a prompt into our new chatbot, LangChain will query the Vector Store for relevant information. Think of it as a mini-Google for your document. Once the relevant information is retrieved, we use that in conjunction with the prompt to feed to the LLM to generate our answer.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2023/05/ByteSizedThumbnail--1200---800-px---10-.png" alt="Image" width="600" height="400" loading="lazy">
<em>How LangChain Works With OpenAI's LLMs</em></p>
<p>LangChain also allows you to create apps that can take actions – such as surf the web, send emails, and complete other API-related tasks. Check out <a target="_blank" href="https://agentgpt.reworkd.ai/">AgentGPT</a>, a great example of this.</p>
<p>There are many possible use-cases for this – here are just a few off the top of my head:</p>
<ul>
<li>Personal AI Email Assistant</li>
<li>AI Study Buddy</li>
<li>AI Data Analytics</li>
<li>Custom Company Customer Service Chatbots</li>
<li>Social Media Content Creation Assistant</li>
</ul>
<p>And the list goes on. I will cover proper build tutorials in future articles, so stay tuned for that.</p>
<h2 id="heading-how-to-get-started-with-langchain">How to Get Started with LangChain</h2>
<p>A LangChain application consists of 5 main components:</p>
<ol>
<li>Models (LLM Wrappers)</li>
<li>Prompts</li>
<li>Chains</li>
<li>Embeddings and Vector Stores</li>
<li>Agents</li>
</ol>
<p>I am going to give you an overview of each, so that you can get a high-level understanding of how LangChain works. Moving forward, you should be able to apply the concepts to start to craft your own use-cases and create your own apps.</p>
<p>I'll be explaining everything with short code snippets from Rabbitmetrics (<a target="_blank" href="https://github.com/rabbitmetrics/langchain-13-min/blob/main/notebooks/langchain-13-min.ipynb">Github</a>). He provides great tutorials on this topic. These snippets should allow you to get all set up and ready to use LangChain. </p>
<p>First, let's get our environment set up. You can pip install 3 libraries that you'll need for this:</p>
<pre><code>pip install -r requirements.txt
</code></pre><pre><code class="lang-requirements.txt">python-dotenv==1.0.0
langchain==0.0.137
pinecone-client==2.2.1
</code></pre>
<p><a target="_blank" href="https://www.pinecone.io/">Pinecone</a> is the Vector Store that we will be using in conjunction with LangChain. With these, make sure to store your API keys for OpenAI, Pinecone Environment, and Pinecone API into your environment file. You will be able to find this info at their respective websites. Then we just load that environment file with the following:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Load environment variables</span>

<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv,find_dotenv
load_dotenv(find_dotenv())
</code></pre>
<p>Now, we're ready to get started!</p>
<h3 id="heading-models-llm-wrappers">Models (LLM Wrappers)</h3>
<p>To interact with our LLMs, we are going to instantiate a wrapper for OpenAI's GPT models. In this case, we are going to use OpenAI's GPT-3.5-turbo, as it's the most cost-efficient. But if you have access, feel free to use the more powerful GPT4.</p>
<p>To import these, we can use the following code:</p>
<pre><code class="lang-python"><span class="hljs-comment"># import schema for chat messages and ChatOpenAI in order to query chatmodels GPT-3.5-turbo or GPT-4</span>

<span class="hljs-keyword">from</span> langchain.schema <span class="hljs-keyword">import</span> (
    AIMessage,
    HumanMessage,
    SystemMessage
)
<span class="hljs-keyword">from</span> langchain.chat_models <span class="hljs-keyword">import</span> ChatOpenAI


chat = ChatOpenAI(model_name=<span class="hljs-string">"gpt-3.5-turbo"</span>,temperature=<span class="hljs-number">0.3</span>)
messages = [
    SystemMessage(content=<span class="hljs-string">"You are an expert data scientist"</span>),
    HumanMessage(content=<span class="hljs-string">"Write a Python script that trains a neural network on simulated data "</span>)
]
response=chat(messages)

print(response.content,end=<span class="hljs-string">'\n'</span>)
</code></pre>
<p>In essence, the SystemMessage provides context to the GPT-3.5-turbo module that it will reference for each prompt-completion pair. The HumanMessage refers to what you would type into the ChatGPT interface – your prompt.</p>
<p>But with a custom-knowledge chatbot, we often abstract away the repetitive components of a prompt. For example, if I was creating a Tweet generator app, I wouldn't want to keep typing "Write me a Tweet about...". In fact, that's how simple <a target="_blank" href="https://ilampadman.com/best-ai-writer-best-ai-copywriter">AI writing tools</a> are developed!</p>
<p>So let's look at how we can abstract that away with prompt templates.</p>
<h3 id="heading-prompts">Prompts</h3>
<p>LangChain provides PromptTemplates that allow you to dynamically change the prompts with user input, similar to how regex are used.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Import prompt and define PromptTemplate</span>

<span class="hljs-keyword">from</span> langchain <span class="hljs-keyword">import</span> PromptTemplate

template = <span class="hljs-string">"""
You are an expert data scientist with an expertise in building deep learning models. 
Explain the concept of {concept} in a couple of lines
"""</span>

prompt = PromptTemplate(
    input_variables=[<span class="hljs-string">"concept"</span>],
    template=template,
)


<span class="hljs-comment"># Run LLM with PromptTemplate</span>

llm(prompt.format(concept=<span class="hljs-string">"autoencoder"</span>))
llm(prompt.format(concept=<span class="hljs-string">"regularization"</span>))
</code></pre>
<p>You can vary these in different ways to fit your use-case. If you are familiar with using ChatGPT, this should be comfortable for you.</p>
<h3 id="heading-chains">Chains</h3>
<p>Chains allow you to take simple PromptTemplates and build functionality on top of them. Essentially, chains are like <a target="_blank" href="https://www.freecodecamp.org/news/function-composition-in-javascript/">composite functions</a> that allow you to integrate your PromptTemplates and LLMs together.</p>
<p>Using the wrappers and PromptTemplates from earlier, we can run the same prompts with a single chain that takes a PromptTemplate and composes it with an LLM:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Import LLMChain and define chain with language model and prompt as arguments.</span>

<span class="hljs-keyword">from</span> langchain.chains <span class="hljs-keyword">import</span> LLMChain
chain = LLMChain(llm=llm, prompt=prompt)

<span class="hljs-comment"># Run the chain only specifying the input variable.</span>
print(chain.run(<span class="hljs-string">"autoencoder"</span>))
</code></pre>
<p>On top of that, as the name suggests, we can chain these together to create even bigger compositions. </p>
<p>For example, I can take the result from one chain and pass it into another chain. In this snippet, Rabbitmetrics takes the completion from the first chain and passes it into the second chain to explain it to a 5 year old.</p>
<p>You can then combine those chains into a larger chain and run that.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Define a second prompt </span>

second_prompt = PromptTemplate(
    input_variables=[<span class="hljs-string">"ml_concept"</span>],
    template=<span class="hljs-string">"Turn the concept description of {ml_concept} and explain it to me like I'm five in 500 words"</span>,
)
chain_two = LLMChain(llm=llm, prompt=second_prompt)

<span class="hljs-comment"># Define a sequential chain using the two chains above: the second chain takes the output of the first chain as input</span>

<span class="hljs-keyword">from</span> langchain.chains <span class="hljs-keyword">import</span> SimpleSequentialChain
overall_chain = SimpleSequentialChain(chains=[chain, chain_two], verbose=<span class="hljs-literal">True</span>)

<span class="hljs-comment"># Run the chain specifying only the input variable for the first chain.</span>
explanation = overall_chain.run(<span class="hljs-string">"autoencoder"</span>)
print(explanation)
</code></pre>
<p>With chains, you can create a huge array of functionality, which is what makes LangChain so versatile. But where it really shines is using it in conjunction with a Vector Store as discussed earlier. Let's introduce that component.</p>
<h3 id="heading-embeddings-and-vector-stores">Embeddings and Vector Stores</h3>
<p>This is where we incorporate the custom data aspect of LangChain. As mentioned earlier, the idea behind embeddings and Vector Stores is to break large data into chunks and store those to be queried when relevant. </p>
<p>LangChain has a text splitter function to do this:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Import utility for splitting up texts and split up the explanation given above into document chunks</span>

<span class="hljs-keyword">from</span> langchain.text_splitter <span class="hljs-keyword">import</span> RecursiveCharacterTextSplitter

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size = <span class="hljs-number">100</span>,
    chunk_overlap  = <span class="hljs-number">0</span>,
)

texts = text_splitter.create_documents([explanation])
</code></pre>
<p>Splitting up text requires two parameters: How big a chunk is (chunk_size) and how much each chunk overlaps (chunk_overlap). Having an overlap between each chunk is important to help identify relevant adjoining chunks.</p>
<p>Each of those chunks can be retrieved as such:</p>
<pre><code class="lang-python">texts[<span class="hljs-number">0</span>].page_content
</code></pre>
<p>After we have those chunks, we need to turn them into embeddings. This allows the Vector Store to find and return each chunk when queried. We'll use OpenAI's embedding model to do this.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Import and instantiate OpenAI embeddings</span>

<span class="hljs-keyword">from</span> langchain.embeddings <span class="hljs-keyword">import</span> OpenAIEmbeddings

embeddings = OpenAIEmbeddings(model_name=<span class="hljs-string">"ada"</span>)


<span class="hljs-comment"># Turn the first text chunk into a vector with the embedding</span>

query_result = embeddings.embed_query(texts[<span class="hljs-number">0</span>].page_content)
print(query_result)
</code></pre>
<p>And finally, we need to have a place to store these vectorized embeddings. As mentioned earlier, we will be using Pinecone for this. Using the API keys from the environment file earlier, we can initialize Pinecone to store our embeddings.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Import and initialize Pinecone client</span>

<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> pinecone
<span class="hljs-keyword">from</span> langchain.vectorstores <span class="hljs-keyword">import</span> Pinecone


pinecone.init(
    api_key=os.getenv(<span class="hljs-string">'PINECONE_API_KEY'</span>),  
    environment=os.getenv(<span class="hljs-string">'PINECONE_ENV'</span>)  
)


<span class="hljs-comment"># Upload vectors to Pinecone</span>

index_name = <span class="hljs-string">"langchain-quickstart"</span>
search = Pinecone.from_documents(texts, embeddings, index_name=index_name)


<span class="hljs-comment"># Do a simple vector similarity search</span>

query = <span class="hljs-string">"What is magical about an autoencoder?"</span>
result = search.similarity_search(query)

print(result)
</code></pre>
<p>And now we are able to query relevant information from our Pinecone Vector Store! All that's left to do is to combine what we have learned to create our specific use case – giving us our specialized AI "Agent".</p>
<h3 id="heading-agents">Agents</h3>
<p>An agent is essentially an autonomous AI that takes in inputs and completes those as tasks sequentially until the end goal is reached. This involves our AI using other APIs that allows it to complete tasks such as sending emails or doing math problems. Used in conjunction with our LLM + prompt chains, we can string together a proper AI app.</p>
<p>Now, explaining this part will be extensive, so here's a simple example of how a Python agent can be used in LangChain to solve a simple mathematical problem. This agent in this case solves the problem by connecting our LLM to run Python code, and finding the roots with NumPy:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Import Python REPL tool and instantiate Python agent</span>

<span class="hljs-keyword">from</span> langchain.agents.agent_toolkits <span class="hljs-keyword">import</span> create_python_agent
<span class="hljs-keyword">from</span> langchain.tools.python.tool <span class="hljs-keyword">import</span> PythonREPLTool
<span class="hljs-keyword">from</span> langchain.python <span class="hljs-keyword">import</span> PythonREPL
<span class="hljs-keyword">from</span> langchain.llms.openai <span class="hljs-keyword">import</span> OpenAI

agent_executor = create_python_agent(
    llm=OpenAI(temperature=<span class="hljs-number">0</span>, max_tokens=<span class="hljs-number">1000</span>),
    tool=PythonREPLTool(),
    verbose=<span class="hljs-literal">True</span>
)


<span class="hljs-comment"># Execute the Python agent</span>

agent_executor.run(<span class="hljs-string">"Find the roots (zeros) if the quadratic function 3 * x**2 + 2*x -1"</span>)
</code></pre>
<p>A custom-knowledge chatbot is essentially an agent that chains together prompts and actions that query the Vectorized Storage, take the results, and chain that with the original question!</p>
<p>If you would like to read more about AI agents, <a target="_blank" href="https://lablab.ai/t/ai-agents-tutorial-how-to-use-and-create-them">this</a> is a great resource.</p>
<h2 id="heading-other-variations">Other Variations</h2>
<p>Even with your newfound basic understanding of the functionality of LangChain, I'm sure you are bubbling with ideas at this point. </p>
<p>But we've only looked at one OpenAI model so far, and that's the text-based GPT-3.5-turbo. OpenAI has an array of models that you could use with LangChain – including image generation with Dall-E. Applying the same concepts we've discussed, we can create <a target="_blank" href="https://julianlankstead.com/ai-nft-art-generator">AI Art Generator</a> agents, Website Builder agents, and much more.</p>
<p>Take some time to explore the AI landscape and I'm confident that you'll start getting more and more ideas.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I hope you've learnt a bit more on what's going on behind the scenes of all of these new AI tools. Understanding how LangChain works is a valuable skill to have as a programmer these days and can open up the possibilities for your AI development.</p>
<p>If you enjoyed this article and you would like to find out more about the cool new tools AI creators are building, you can stay up-to-date with my <a target="_blank" href="https://bytesizedai.beehiiv.com/subscribe">Byte-Sized AI Newsletter</a>. There are tons of exciting stories of what people are building in the AI space, and I'd love for you to join our community. </p>
<p>You can also drop me a follow on <a target="_blank" href="https://twitter.com/Shuggggan">Twitter</a>, where we can also get in touch.</p>
<p>Other than that, start experimenting with LangChain and create some nifty AI projects.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an AI-Powered ChatBot with OpenAI, ChatGPT, Node.js, and React ]]>
                </title>
                <description>
                    <![CDATA[ By Njoku Samson Ebere Artificial Intelligence (AI) has been making waves lately, with ChatGPT revolutionizing the internet with the chat completion functionality.  You can do a lot with it: drafting an email or other piece of writing, answering quest... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-chatbot-with-openai-chatgpt-nodejs-and-react/</link>
                <guid isPermaLink="false">66d84fa25e1ebbdb6468edcf</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ chatgpt ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ openai ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 05 May 2023 22:08:13 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/05/pexels-sanket-mishra-16629368.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Njoku Samson Ebere</p>
<p>Artificial Intelligence (AI) has been making waves lately, with <a target="_blank" href="https://platform.openai.com/">ChatGPT</a> revolutionizing the internet with the <a target="_blank" href="https://platform.openai.com/docs/guides/chat">chat completion</a> functionality. </p>
<p>You can do a lot with it: drafting an email or other piece of writing, answering questions about a set of documents, creating conversational agents, giving your software a natural language interface, tutoring in various subjects, translating languages, and so on.  </p>
<p>This article will teach the basics of building a chat application using the <a target="_blank" href="https://platform.openai.com/docs/guides/chat">chat completion</a> functionality to make it easy for every programmer to get on board. It is not as tough as it looks. You will see this as you follow this tutorial. </p>
<p>You will learn the following:</p>
<ul>
<li>How to create a CLI chat app with Node.js only.</li>
<li>How to build a chat application using just React.</li>
<li>How to combine React and Node.js to create better chat AI software.</li>
</ul>
<p>This tutorial will be based on the <a target="_blank" href="https://platform.openai.com/docs/models/gpt-3-5"><code>gpt-3.5-turbo</code></a> model.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/T-9-_1w82Jg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>This tutorial requires basic knowledge of JavaScript, CSS, React, and Node.js.  </p>
<p>You also need an account on the OpenAI platform where chatGPT is hosted. It is free, so you can create one <a target="_blank" href="https://platform.openai.com/overview">here</a>.</p>
<h2 id="heading-how-to-create-a-cli-chat-ai-app-with-nodejs">How to Create a CLI Chat AI App With Node.js</h2>
<p>This section will focus on creating a chat application that will run only on the terminal using <a target="_blank" href="https://nodejs.org/">Node.js</a>.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/4uTO3xZx5r4" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>Begin by creating a directory for the project:</p>
<pre><code class="lang-cmd">mkdir nodejs-chatgpt-tutorial
</code></pre>
<p>Navigate into the folder:</p>
<pre><code class="lang-cmd">cd nodejs-chatgpt-tutorial
</code></pre>
<p>Initialize the project:</p>
<pre><code>npm init -y
</code></pre><p>This will create a <code>package.json</code> file to keep track of the project details</p>
<p>Add the following line of code to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>
</code></pre>
<p>This will enable you to use the ES6 module import statement.  </p>
<p>Install <a target="_blank" href="https://openai.com/">OpenAI</a> with the command below:</p>
<pre><code class="lang-cmd">npm i openai
</code></pre>
<p>Create a file where all the code will live. Name it <code>index.js</code>:</p>
<pre><code class="lang-cmd">touch index.js
</code></pre>
<p>Import <code>Configuration</code> and <code>OpenAIApi</code> from the <a target="_blank" href="https://openai.com/">OpenAI</a> module and <code>readline</code> from the <a target="_blank" href="https://nodejs.org/api/readline.html">readline</a> module:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Configuration, OpenAIApi } <span class="hljs-keyword">from</span> <span class="hljs-string">"openai"</span>;
<span class="hljs-keyword">import</span> readline <span class="hljs-keyword">from</span> <span class="hljs-string">"readline"</span>;
</code></pre>
<p>Build the OpenAI configuration like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration({
  <span class="hljs-attr">organization</span>: <span class="hljs-string">"org-0nmrFWw6wSm6xIJXSbx4FpTw"</span>,
  <span class="hljs-attr">apiKey</span>: <span class="hljs-string">"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"</span>,
});
</code></pre>
<p>This code creates a new instance of the <code>Configuration</code> object. Inside it, you'll enter values for your <code>organization</code> and <code>apiKey</code>. You can find details of your organization in <a target="_blank" href="https://platform.openai.com/account/org-settings">settings</a> and your apiKey info in <a target="_blank" href="https://platform.openai.com/account/api-keys">API</a> <a target="_blank" href="https://platform.openai.com/account/api-keys">keys</a>. If you do not have an existing API Key, you can create it.  </p>
<p>Enter the following code after the configuration to create a new instance of the OpenAI API:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAIApi(configuration);
</code></pre>
<p>You'll use this throughout the project.</p>
<p>Type the code below to test the <code>createChatCompletion</code> function:</p>
<pre><code class="lang-javascript">openai
  .createChatCompletion({
    <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
    <span class="hljs-attr">messages</span>: [{ <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: <span class="hljs-string">"Hello"</span> }],
  })
  .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(res.data.choices[<span class="hljs-number">0</span>].message.content);
  })
  .catch(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(e);
  });
</code></pre>
<p>This code calls the <code>createChatCompletion</code> function that triggers an endpoint (<code>https://api.openai.com/v1/chat/completions</code>). The function accepts an object of arguments (the <code>model</code> of chatGPT in use and an array of <code>messages</code> between the user and the AI. We will look at how to use the <code>messages</code> array to keep a history of the chat and improve the app in the next section).  </p>
<p>Each message is an object containing the <code>role</code> (that is, who sent the message. The value can be <code>assistant</code> if it is from the AI or <code>user</code> when the message originates from a human) and the <code>content</code> (the information sent).  </p>
<p>Finally, the code prints the response (<code>res.data.choices[0].message.content</code>) from the AI. Run the file in the terminal with this command:</p>
<pre><code class="lang-cmd">node index
</code></pre>
<p>This will return a response from the AI after some seconds.  </p>
<p>And that is all it takes to create the chatbot!   </p>
<p>But it will be helpful to make the application more interactive by requesting input from the user instead of hardcoding the message content into the code. The <a target="_blank" href="https://nodejs.org/api/readline.html">readline</a> module will help us out in this regard.  </p>
<p>To make it interactive, remove the last code you typed and add the following:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> userInterface = readline.createInterface({
  <span class="hljs-attr">input</span>: process.stdin,
  <span class="hljs-attr">output</span>: process.stdout,
});
</code></pre>
<p>This code creates a UI in the terminal that allows users to type in their questions.</p>
<p>Next, prompt the user to enter a message using the code below:</p>
<pre><code class="lang-javascript">userInterface.prompt();
</code></pre>
<p>Finally, enter the following code:</p>
<pre><code class="lang-javascript">userInterface.on(<span class="hljs-string">"line"</span>, <span class="hljs-keyword">async</span> (input) =&gt; {
  <span class="hljs-keyword">await</span> openai
    .createChatCompletion({
      <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
      <span class="hljs-attr">messages</span>: [{ <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: input }],
    })
    .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(res.data.choices[<span class="hljs-number">0</span>].message.content);
      userInterface.prompt();
    })
    .catch(<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(e);
    });
});
</code></pre>
<p>In the code above,</p>
<ul>
<li>When a user types something and hits <code>Enter</code>, the code above triggers a callback function.</li>
<li>It passes whatever was typed by the user as <code>input</code>.</li>
<li>The <code>input</code> is now used as the <code>content</code>.</li>
<li>After the response of the AI is displayed, the user is prompted for another message in the <code>then</code> block.</li>
</ul>
<p>See all the code on <a target="_blank" href="https://github.com/EBEREGIT/nodejs-chatgpt-tutorial">GitHub</a>.  </p>
<p>Run the file and have a conversation with the AI. It will look like the image below:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682935202577_Screenshot+2023-05-01+at+10.58.02.png" alt="Image" width="2880" height="1436" loading="lazy">
<em>CLI chat with AI</em></p>
<p>Great! That is an interactive CLI chat.  </p>
<p>This is useful to a few people (like engineers), but it has good security because it is on the server side.   </p>
<p>But how about others who might not understand how to use a CLI application? They will need something easier to use with a better user interface (UI) and user experience (UX). The following section will focus on building that kind of application using <a target="_blank" href="https://react.dev/">React</a>.</p>
<h2 id="heading-how-to-create-a-chat-application-using-react">How to Create a Chat Application Using React</h2>
<p>This section is aimed at helping frontend developers get up to speed with the ChatGPT API for creating a chat app and building a better user interface to give users better experiences. You can apply the knowledge that you gain here to other frontend frameworks or libraries.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/JrfaQ5dYbWg" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p>The first thing to do is to set up a basic React boilerplate. I will use <a target="_blank" href="https://vitejs.dev/guide/#scaffolding-your-first-vite-project">Vite</a> for this purpose. You can use Vite to scaffold any modern JavaScript frontend project. Use the command below:</p>
<pre><code class="lang-javascript">npm create vite@latest
</code></pre>
<p>This command will prompt you to create a name and folder for your project and choose a framework or library (this tutorial uses React). After that, you will navigate into the folder and run the following command:</p>
<pre><code class="lang-javascript">npm install
npm run dev
</code></pre>
<p>These commands will install the necessary dependencies and start the local server on port <code>5173</code>  </p>
<p>Next, install <a target="_blank" href="https://openai.com/">OpenAI</a> with the command below:</p>
<pre><code class="lang-javascript">npm i openai
</code></pre>
<p>This module gives access to all we need to create the chat app.  </p>
<p>Now we are set to start writing the code!  </p>
<p>Navigate into the <code>src/App.jsx</code> file and delete all of its content. Then add the following import statements:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> { Configuration, OpenAIApi } <span class="hljs-keyword">from</span> <span class="hljs-string">"openai"</span>;
</code></pre>
<p>The code above imports the <code>Configuration</code> for setting up config values and <code>OpenAIApi</code> to give us access to the chat completion functionalities.  </p>
<p>After that, build the configuration like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration({
  <span class="hljs-attr">organization</span>: <span class="hljs-string">"org-0nmrFWw6wSm6xIJXSbx4FpTw"</span>,
  <span class="hljs-attr">apiKey</span>: <span class="hljs-string">"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"</span>,
});
</code></pre>
<p>This code creates a new instance of the <code>Configuration</code> object. Inside it, you enter values for your <code>organization</code> and <code>apiKey</code>. You can find details of your organization in <a target="_blank" href="https://platform.openai.com/account/org-settings">settings</a> and your apiKey info in <a target="_blank" href="https://platform.openai.com/account/api-keys">API</a> <a target="_blank" href="https://platform.openai.com/account/api-keys">keys</a>. If you do not have an existing API Key, you can create it.  </p>
<p>Enter the following code after the configuration to create a new instance of the OpenAI API:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAIApi(configuration);
</code></pre>
<p>We'll use this throughout the project.  </p>
<p>Create and export a default function:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>Chat AI Tutorial<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">main</span>/&gt;</span>
  );
}
export default App;</span>
</code></pre>
<p>This function will house the rest of the code.  </p>
<p>Set up the following states before the <code>return</code> statement:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [chats, setChats] = useState([]);
  <span class="hljs-keyword">const</span> [isTyping, setIsTyping] = useState(<span class="hljs-literal">false</span>);
</code></pre>
<ul>
<li>The <code>message</code> will hold the information sent from the app to the AI.</li>
<li>The <code>chats</code> array will keep track of all the messages sent by both parties (user and AI).</li>
<li>The <code>isTyping</code> variable will notify the user whether the bot is typing or not.</li>
</ul>
<p>Type the following lines of code under the h1 tag</p>
<pre><code class="lang-javascript">      &lt;div className={isTyping ? <span class="hljs-string">""</span> : <span class="hljs-string">"hide"</span>}&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">i</span>&gt;</span>{isTyping ? "Typing" : ""}<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
      &lt;/div&gt;
</code></pre>
<p>The code above will display <code>Typing</code> whenever the user is waiting for a response from the AI.  </p>
<p>Create a form in which a user can type in a message by adding the code below into the <code>main</code> element:</p>
<pre><code class="lang-javascript">      &lt;form action=<span class="hljs-string">""</span> onSubmit={<span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> chat(e, message)}&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{message}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Type a message here and hit Enter..."</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setMessage(e.target.value)}
        /&gt;</span>
      &lt;/form&gt;
</code></pre>
<p>This code creates a form with one input. Whenever the form is submitted by hitting the <code>Enter</code> key, it triggers the <code>chat</code> function.  </p>
<p>The chat function will take two (2) arguments (<code>e</code> and <code>message</code>) like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> chat = <span class="hljs-keyword">async</span> (e, message) =&gt; {

}
</code></pre>
<p>Enter the following lines in the function:</p>
<pre><code class="lang-javascript">    e.preventDefault();

    <span class="hljs-keyword">if</span> (!message) <span class="hljs-keyword">return</span>;
    setIsTyping(<span class="hljs-literal">true</span>);
</code></pre>
<p>The code above prevents the <code>form</code> from reloading the web page, checks if a message was typed before submission, and sets <code>isTyping</code> to <code>true</code> to indicate that the app has started working on the input provided.   </p>
<p>ChatGPT has a format in which messages should be. It takes the following pattern:</p>
<pre><code>{<span class="hljs-attr">role</span>: user | assistant, <span class="hljs-attr">content</span>: message to be sent
</code></pre><p>Every message (<code>content</code>) must show who sent it. The role is <code>assistant</code> when the chat is from the AI but <code>user</code> if it is from a human. So, before sending the message, be sure to format it properly and add it to the array (<code>chats</code>) like this:</p>
<pre><code class="lang-javascript">    <span class="hljs-keyword">let</span> msgs = chats;
    msgs.push({ <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: message });
    setChats(msgs);

    setMessage(<span class="hljs-string">""</span>);
</code></pre>
<p>The last line above clears the input for a user to type another note.  </p>
<p>Now we will call the <code>createChatCompletion</code> endpoint by triggering the <code>createChatCompletion</code> function using the code below:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">await</span> openai
      .createChatCompletion({
        <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
        <span class="hljs-attr">messages</span>: [
          {
            <span class="hljs-attr">role</span>: <span class="hljs-string">"system"</span>,
            <span class="hljs-attr">content</span>:
              <span class="hljs-string">"You are a EbereGPT. You can help with graphic design tasks"</span>,
          },
          ...chats,
        ],
      })
</code></pre>
<p>The <code>createChatCompletion</code> function takes at least two (2) arguments (<code>model</code> and <code>messages</code>):</p>
<ul>
<li>The model specifies the version of chatGPT in use.</li>
<li>The messages is a list of all the messages between a user and the AI so far and a system message that gives the AI an idea of what sort of assistance it can provide.</li>
</ul>
<pre><code class="lang-javascript">          {
            <span class="hljs-attr">role</span>: <span class="hljs-string">"system"</span>,
            <span class="hljs-attr">content</span>:
              <span class="hljs-string">"You are a EbereGPT. You can help with graphic design tasks"</span>,
          }
</code></pre>
<p>You can change the content to whatever suits you.  </p>
<p>The <code>messages</code> don’t have to contain more than one object in the array. It can be just one message. But when it is an array, it provides a message history that the AI can rely on to give better replies in the future, and it makes the user type less since there might be no need to be overly descriptive all the time.  </p>
<p>The <code>createChatCompletion</code> function returns a promise. So use a <code>then…catch…</code> block to get the response.</p>
<pre><code class="lang-javascript">      .then(<span class="hljs-function">(<span class="hljs-params">res</span>) =&gt;</span> {
        msgs.push(res.data.choices[<span class="hljs-number">0</span>].message);
        setChats(msgs);
        setIsTyping(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(error);
      });
</code></pre>
<p>This code adds the message returned from the AI into the <code>chats</code> array and sets <code>isTyping</code> to false, indicating that the AI is done replying.   </p>
<p>You should now receive feedback (<code>Typing</code>) each time you send a message:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682702095176_Screenshot+2023-04-28+at+18.14.08.png" alt="Image" width="2422" height="1429" loading="lazy">
<em>Chat application giving feedback when the AI is about to respond</em></p>
<p>It is time to display the chat history for the user to see.  </p>
<p>Type the following code just below the <code>h1</code> tag:</p>
<pre><code class="lang-javascript">      &lt;section&gt;
        {chats &amp;&amp; chats.length
          ? chats.map(<span class="hljs-function">(<span class="hljs-params">chat, index</span>) =&gt;</span> (
              <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{chat.role</span> === <span class="hljs-string">"user"</span> ? "<span class="hljs-attr">user_msg</span>" <span class="hljs-attr">:</span> ""}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>{chat.role.toUpperCase()}<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{chat.content}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span></span>
            ))
          : <span class="hljs-string">""</span>}
      &lt;/section&gt;
</code></pre>
<p>The code above loops through the <code>chats</code> and displays them one after another to the user. It outputs the <code>role</code> in upper case and the <code>content</code> of the message side by side.  </p>
<p>Here is what the output should look like:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682702531307_Screenshot+2023-04-28+at+18.21.23.png" alt="Image" width="2047" height="1328" loading="lazy">
<em>ChatBot Working As Expected without CSS</em></p>
<p>That looks cool!   </p>
<p>But adding some styling will give it an engaging look like <a target="_blank" href="https://www.whatsapp.com/">WhatsApp</a> or <a target="_blank" href="https://www.messenger.com/">Messenger</a>.   </p>
<p>Replace the content of the <code>src/index.css</code> file with the following:</p>
<pre><code class="lang-css"><span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">font-family</span>: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;
  <span class="hljs-attribute">color-scheme</span>: light dark;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.87</span>);
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#242424</span>;
  <span class="hljs-attribute">font-synthesis</span>: none;
  <span class="hljs-attribute">text-rendering</span>: optimizeLegibility;
  <span class="hljs-attribute">-webkit-font-smoothing</span>: antialiased;
  <span class="hljs-attribute">-moz-osx-font-smoothing</span>: grayscale;
  <span class="hljs-attribute">-webkit-text-size-adjust</span>: <span class="hljs-number">100%</span>;
}
<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3.2em</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.1</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">position</span>: sticky;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#242424</span>;
}
<span class="hljs-selector-tag">main</span>{
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">500px</span>;
  <span class="hljs-attribute">margin</span>: auto;
}
<span class="hljs-selector-tag">p</span>{
  <span class="hljs-attribute">background-color</span>: darkslategray;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">70%</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50px</span>;
}
<span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">span</span>{
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span>;
}
<span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">span</span><span class="hljs-selector-pseudo">:first-child</span>{
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0</span>;
}
<span class="hljs-selector-class">.user_msg</span>{
  <span class="hljs-attribute">text-align</span>: right;
  <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">30%</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: row-reverse;
}
<span class="hljs-selector-class">.hide</span> {
  <span class="hljs-attribute">visibility</span>: hidden;
  <span class="hljs-attribute">display</span>: none;
}
<span class="hljs-selector-tag">form</span>{
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">position</span>: sticky;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
}
<span class="hljs-selector-tag">input</span>{
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
}
<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span>{
  <span class="hljs-attribute">outline</span>: none;
}
</code></pre>
<p>And remove all the styles from the <code>src/App.css</code> file.</p>
<p>You can find the <a target="_blank" href="https://github.com/EBEREGIT/react-chatgpt-tutorial">complete code on GitHub</a>.  </p>
<p>The application should now have a new look:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682704193641_Screenshot+2023-04-28+at+18.48.44.png" alt="Image" width="2050" height="1481" loading="lazy">
<em>ChatBot Working As Expected with CSS</em></p>
<p>That wraps it up for creating a chatbot with React and ChatGPT. It isn’t as tough as it sounds.  </p>
<p>But frontend applications like this one are best for demonstration, not production. The problem with creating the application this way is that the frontend exposes the API Key to cyber attacks. </p>
<p>To fix this problem, it may be wise to save the API Key and Organisation Id somewhere safe in the cloud and reference it or build a backend for your application with better security.  </p>
<p>The following section will work on the problem.</p>
<h2 id="heading-how-to-combine-react-and-nodejs-to-make-a-fullstack-chat-ai-software">How to Combine React and Node.js to Make a Fullstack Chat AI Software</h2>
<p>This section will now join the powers of the previous sections to build a more secure application while exhibiting better UI and UX. </p>
<p>We will improve the Node section by using a server to expose an endpoint for the frontend’s consumption and simplify the frontend to interact with the backend instead of contacting <a target="_blank" href="https://openai.com/">OpenAI</a> directly.</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/OJ7AgZVH118" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h3 id="heading-how-to-setup-the-project">How to Setup the Project</h3>
<p>This part will create the folders and files necessary for the project.  </p>
<p>Create the project directory:</p>
<pre><code class="lang-cmd">mkdir react-node-chatgpt-tutorial
</code></pre>
<p>Navigate into the folder:</p>
<pre><code class="lang-cmd">cd react-node-chatgpt-tutorial
</code></pre>
<p>Install React using Vite and name the folder <code>frontend</code>. Use this command:</p>
<pre><code class="lang-cmd">npm create vite@latest
</code></pre>
<p>After that, you will navigate into the folder and run the following command:</p>
<pre><code class="lang-cmd">npm install
npm run dev
</code></pre>
<p>These commands will install the necessary dependencies and start the local server on port <code>5173</code>.  </p>
<p>Create the backend folder:</p>
<pre><code class="lang-cmd">mkdir backend
</code></pre>
<p>Now navigate into the backend folder and initialize the project with this command:</p>
<pre><code class="lang-cmd">npm init -y
</code></pre>
<p>This will create a <code>package.json</code> file to keep track of the project details.  </p>
<p>Add the following line of code to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-string">"type"</span>: <span class="hljs-string">"module"</span>
</code></pre>
<p>This will enable the use of the ES6 module import statement.  </p>
<p>Install <a target="_blank" href="https://openai.com/">OpenAI</a> and other dependencies with the command below:</p>
<pre><code class="lang-cmd">npm i openai body-parser cors express
</code></pre>
<p>Create a file where all the code will live. Name it <code>index.js</code>:</p>
<pre><code>touch index.js
</code></pre><p>That completes the project setup. There are now two folders (<code>frontend</code> and <code>backend</code>).</p>
<h3 id="heading-how-to-build-a-server">How to Build a Server</h3>
<p>This part will focus on creating a local server to listen on port <code>8000</code>.</p>
<p>The first thing to do is to import the necessary modules like this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Configuration, OpenAIApi } <span class="hljs-keyword">from</span> <span class="hljs-string">"openai"</span>;
<span class="hljs-keyword">import</span> express <span class="hljs-keyword">from</span> <span class="hljs-string">"express"</span>;
<span class="hljs-keyword">import</span> bodyParser <span class="hljs-keyword">from</span> <span class="hljs-string">"body-parser"</span>;
<span class="hljs-keyword">import</span> cors <span class="hljs-keyword">from</span> <span class="hljs-string">"cors"</span>;
</code></pre>
<p>Next, set up <code>express</code>, a <code>port</code> to listen to, the <code>body-parser</code> for receiving input, and <code>cors</code> to allow free communications between the frontend and backend. Use the code below:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> app = express();
<span class="hljs-keyword">const</span> port = <span class="hljs-number">8000</span>;
app.use(bodyParser.json());
app.use(cors());
</code></pre>
<p>Finally, type the following code:</p>
<pre><code class="lang-javascript">app.listen(port, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`listening on port <span class="hljs-subst">${port}</span>`</span>);
});
</code></pre>
<p>This completes the server setup.  </p>
<p>When you run the <code>index.js</code>, you should get the following output:</p>
<pre><code>listening on port <span class="hljs-number">8000</span>
</code></pre><h3 id="heading-how-to-create-an-endpoint">How to Create an Endpoint</h3>
<p>In this part, we will build an endpoint that will receive messages from the frontend using the request body and return a response to the caller.  </p>
<p>Begin by establishing the configuration parameters as we did in previous sections:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> configuration = <span class="hljs-keyword">new</span> Configuration({
  <span class="hljs-attr">organization</span>: <span class="hljs-string">"org-0nmrFWw6wSm6xIJXSbx4FpTw"</span>,
  <span class="hljs-attr">apiKey</span>: <span class="hljs-string">"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"</span>,
});
<span class="hljs-keyword">const</span> openai = <span class="hljs-keyword">new</span> OpenAIApi(configuration);
</code></pre>
<p>Next, create an asynchronous POST route using the code below:</p>
<pre><code class="lang-javascript">app.post(<span class="hljs-string">"/"</span>, <span class="hljs-keyword">async</span> (request, response) =&gt; {

});
</code></pre>
<p>This endpoint will be called using <code>http://localhost:8000/</code>  </p>
<p>In the callback function, enter the code below to receive the <code>chats</code> input from the request body (<code>request.body</code>):</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { chats } = request.body;
</code></pre>
<p>Now call the <code>createChatCompletion</code> endpoint as we did in the React section:</p>
<pre><code class="lang-javascript">  <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> openai.createChatCompletion({
    <span class="hljs-attr">model</span>: <span class="hljs-string">"gpt-3.5-turbo"</span>,
    <span class="hljs-attr">messages</span>: [
      {
        <span class="hljs-attr">role</span>: <span class="hljs-string">"system"</span>,
        <span class="hljs-attr">content</span>: <span class="hljs-string">"You are a EbereGPT. You can help with graphic design tasks"</span>,
      },
      ...chats,
    ],
  });
</code></pre>
<p>The difference here is that instead of using the <code>then…catch…</code> block, we assigned it to a variable (<code>result</code>) and returned the response using <code>response.json()</code> as in the following code:</p>
<pre><code class="lang-javascript">  response.json({
    <span class="hljs-attr">output</span>: result.data.choices[<span class="hljs-number">0</span>].message,
  });
</code></pre>
<p>Find the code for this part on <a target="_blank" href="https://github.com/EBEREGIT/react-nodejs-chatgpt-tutorial/tree/master/backend">GitHub here</a>.  </p>
<p>Here is the output when tested on Postman:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682943795836_Screenshot+2023-05-01+at+13.22.17.png" alt="Image" width="1787" height="1512" loading="lazy">
<em>Output From Postman</em></p>
<p>That concludes the backend portion of the code. The next part will connect the frontend to the backend using the endpoint ( <code>http://localhost:8000/</code>) just created.</p>
<h3 id="heading-how-to-connect-to-the-backend-from-the-frontend">How to Connect to the Backend from the Frontend.</h3>
<p>This part takes us to the frontend, where we will create a form. The form will send a message to the backend via the API endpoint and receive a response through the same medium.  </p>
<p>Navigate to the <code>frontend/src/App.jsx</code> file and type the following code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [message, setMessage] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [chats, setChats] = useState([]);
  <span class="hljs-keyword">const</span> [isTyping, setIsTyping] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">const</span> chat = <span class="hljs-keyword">async</span> (e, message) =&gt; {
    e.preventDefault();

    <span class="hljs-keyword">if</span> (!message) <span class="hljs-keyword">return</span>;
    setIsTyping(<span class="hljs-literal">true</span>);

    <span class="hljs-keyword">let</span> msgs = chats;
    msgs.push({ <span class="hljs-attr">role</span>: <span class="hljs-string">"user"</span>, <span class="hljs-attr">content</span>: message });
    setChats(msgs);

    setMessage(<span class="hljs-string">""</span>);

    alert(message);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span>&gt;</span>FullStack Chat AI Tutorial<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">section</span>&gt;</span>
        {chats &amp;&amp; chats.length
          ? chats.map((chat, index) =&gt; (
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{chat.role</span> === <span class="hljs-string">"user"</span> ? "<span class="hljs-attr">user_msg</span>" <span class="hljs-attr">:</span> ""}&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">b</span>&gt;</span>{chat.role.toUpperCase()}<span class="hljs-tag">&lt;/<span class="hljs-name">b</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>:<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span>&gt;</span>{chat.content}<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            ))
          : ""}
      <span class="hljs-tag">&lt;/<span class="hljs-name">section</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{isTyping</span> ? "" <span class="hljs-attr">:</span> "<span class="hljs-attr">hide</span>"}&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">i</span>&gt;</span>{isTyping ? "Typing" : ""}<span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">action</span>=<span class="hljs-string">""</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> chat(e, message)}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
          <span class="hljs-attr">name</span>=<span class="hljs-string">"message"</span>
          <span class="hljs-attr">value</span>=<span class="hljs-string">{message}</span>
          <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Type a message here and hit Enter..."</span>
          <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setMessage(e.target.value)}
        /&gt;
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>
  );
}
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>This code is similar to the code from the previous section. But we have removed the OpenAI configurations as we will no longer need them in this section. </p>
<p>At this point, an alert pops up whenever the form is submitted. This will change in a moment.  </p>
<p>In the chat function, get rid of the <code>alert</code> message and type the following:</p>
<pre><code class="lang-javascript">fetch(<span class="hljs-string">"http://localhost:8000/"</span>, {
      <span class="hljs-attr">method</span>: <span class="hljs-string">"POST"</span>,
      <span class="hljs-attr">headers</span>: {
        <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
      },
      <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify({
        chats,
      }),
    })
      .then(<span class="hljs-function">(<span class="hljs-params">response</span>) =&gt;</span> response.json())
      .then(<span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
        msgs.push(data.output);
        setChats(msgs);
        setIsTyping(<span class="hljs-literal">false</span>);
      })
      .catch(<span class="hljs-function">(<span class="hljs-params">error</span>) =&gt;</span> {
        <span class="hljs-built_in">console</span>.log(error);
      });
</code></pre>
<p>The code above calls the endpoint we created and passes in the <code>chats</code> array for it to process. It then returns a response that is added to the <code>chats</code> and displayed in the UI.  </p>
<p>The following is how the UI looks at the moment:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682945738011_Screenshot+2023-05-01+at+13.55.10.png" alt="Image" width="2880" height="1645" loading="lazy">
<em>Fullstack Chat UI before Styling</em></p>
<p>The UI can look better if you add the following styles to the <code>frontend/src/index.css</code> file:</p>
<pre><code class="lang-css">
<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">font-family</span>: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.5</span>;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;

  <span class="hljs-attribute">color-scheme</span>: light dark;
  <span class="hljs-attribute">color</span>: <span class="hljs-built_in">rgba</span>(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">0.87</span>);
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#242424</span>;

  <span class="hljs-attribute">font-synthesis</span>: none;
  <span class="hljs-attribute">text-rendering</span>: optimizeLegibility;
  <span class="hljs-attribute">-webkit-font-smoothing</span>: antialiased;
  <span class="hljs-attribute">-moz-osx-font-smoothing</span>: grayscale;
  <span class="hljs-attribute">-webkit-text-size-adjust</span>: <span class="hljs-number">100%</span>;
}

<span class="hljs-selector-tag">html</span>, <span class="hljs-selector-tag">body</span>{
  <span class="hljs-attribute">scroll-behavior</span>: smooth;
}

<span class="hljs-selector-tag">h1</span> {
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">3.2em</span>;
  <span class="hljs-attribute">line-height</span>: <span class="hljs-number">1.1</span>;
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">position</span>: sticky;
  <span class="hljs-attribute">top</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#242424</span>;
}

<span class="hljs-selector-tag">main</span>{
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">800px</span>;
  <span class="hljs-attribute">margin</span>: auto;
}

<span class="hljs-selector-tag">p</span>{
  <span class="hljs-attribute">background-color</span>: darkslategray;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">70%</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">15px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50px</span>;
}

<span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">span</span>{
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">5px</span>;
}

<span class="hljs-selector-tag">p</span> <span class="hljs-selector-tag">span</span><span class="hljs-selector-pseudo">:first-child</span>{
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-class">.user_msg</span>{
  <span class="hljs-attribute">text-align</span>: right;
  <span class="hljs-attribute">margin-left</span>: <span class="hljs-number">30%</span>;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: row-reverse;
}

<span class="hljs-selector-class">.hide</span> {
  <span class="hljs-attribute">visibility</span>: hidden;
  <span class="hljs-attribute">display</span>: none;
}

<span class="hljs-selector-tag">form</span>{
  <span class="hljs-attribute">text-align</span>: center;
  <span class="hljs-attribute">position</span>: sticky;
  <span class="hljs-attribute">bottom</span>: <span class="hljs-number">0</span>;
}

<span class="hljs-selector-tag">input</span>{
  <span class="hljs-attribute">width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">10px</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">1.2rem</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-built_in">rgb</span>(<span class="hljs-number">28</span>, <span class="hljs-number">23</span>, <span class="hljs-number">23</span>);
}

<span class="hljs-selector-tag">input</span><span class="hljs-selector-pseudo">:focus</span>{
  <span class="hljs-attribute">outline</span>: none;
}
</code></pre>
<p>And remove all the styles from the <code>frontend/src/App.css</code> file.</p>
<p>The code for this part is on <a target="_blank" href="https://github.com/EBEREGIT/react-nodejs-chatgpt-tutorial/tree/master/frontend">GitHub</a>.  </p>
<p>Now here is the final output:</p>
<p><img src="https://paper-attachments.dropboxusercontent.com/s_D592C23061BDAFBA9E611AEDC8048F685A5679FCF2C57746CD5AE80A3DAD15B0_1682946018213_Screenshot+2023-05-01+at+13.59.37.png" alt="Image" width="2871" height="1641" loading="lazy">
<em>FullStack ChatBot Working As Expected with CSS</em></p>
<p>Congratulations on finishing this project!  </p>
<p>The full stack chatbot was more work, but it helped us separate concerns, build a more secure and attractive application, and offer a better experience to users. So it was worth the effort.  </p>
<p>You can find the <a target="_blank" href="https://github.com/EBEREGIT/react-nodejs-chatgpt-tutorial">code for this section on GitHub</a>.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>This tutorial hopefully showed you that anyone with basic programming knowledge can build AI-powered software. You learned how to build a chatbot using React and Nodejs, and we discussed the pros and cons of each technology. Finally, we built a solution that was both functional, secure, and visually appealing.  </p>
<p>After reading this tutorial, you can now explore AI’s functionalities like image manipulation and audio interaction. Take your time to go through the <a target="_blank" href="https://platform.openai.com/docs/introduction">documentation</a> and see how you can expand on what we covered here.  </p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Serverless ChatGPT App in 10 Minutes ]]>
                </title>
                <description>
                    <![CDATA[ Since OpenAI released an official API for ChatGPT in March 2023, many developers and entrepreneurs are interested in integrating it into their own business operations. But some significant barriers remain that make it difficult for them to do this: ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/create-a-serverless-chatgpt-app/</link>
                <guid isPermaLink="false">66d4604433b83c4378a51806</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ serverless ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Michael Yuan ]]>
                </dc:creator>
                <pubDate>Mon, 20 Mar 2023 16:52:42 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/03/c0fd422e-f234-49e2-85a6-24f91b0b9991-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Since OpenAI <a target="_blank" href="https://openai.com/blog/introducing-chatgpt-and-whisper-apis">released an official API for ChatGPT</a> in March 2023, many developers and entrepreneurs are interested in integrating it into their own business operations.</p>
<p>But some significant barriers remain that make it difficult for them to do this:</p>
<ul>
<li><p>OpenAI provides <a target="_blank" href="https://platform.openai.com/docs/guides/chat">a simple stateless API</a> for ChatGPT. The developer needs to keep track of the history and context of each conversation in a cache or database managed by the application. The developer also needs to manage and safeguard the API keys. There is a lot of boilerplate code unrelated to the application’s business logic.</p>
</li>
<li><p>The “natural” UI for the ChatGPT API application is a threaded chat. But it is difficult to create a “chat UI” in a traditional web or app framework. In fact, the most commonly used chat UI already exists in messaging apps like Slack, Discord, and even forums (for example, GitHub Discussions). We need a simple way to connect ChatGPT API responses to an existing messaging service.</p>
</li>
</ul>
<p>In this article, I will show you how to create a serverless GitHub bot. The bot allows GitHub users to chat with ChatGPT and each other in GitHub Issues. You can <a target="_blank" href="https://github.com/second-state/chat-with-chatgpt/issues/new">try it by asking a question</a>, or <a target="_blank" href="https://github.com/second-state/chat-with-chatgpt/issues">joining another conversation thread</a> by leaving a comment. In other words, this project uses GitHub Issues’ threaded messages UI as its own chat UI.</p>
<p><img src="https://i.imgur.com/7eWhQ8I.png" alt="Image" width="1842" height="1862" loading="lazy"></p>
<p><em>Figure 1. Learning Rust with ChatGPT. see</em> <a target="_blank" href="https://github.com/second-state/chat-with-chatgpt/issues/31"><em>https://github.com/second-state/chat-with-chatgpt/issues/31</em></a></p>
<p>The bot is a serverless function written in Rust. Just fork the example, deploy your fork on <a target="_blank" href="https://www.freecodecamp.org/news/p/dfeeb7b1-d632-448e-97b3-9fcd7df30bce/flows.network">flows.network</a>, and configure it to interact with your own GitHub repos and OpenAI keys. You will have a fully functional GitHub bot in 10 minutes. There is no need to set up a web server, or a webhook for GitHub API, or a cache / database server.</p>
<h2 id="heading-how-to-fork-the-template-repo">How to Fork the Template Repo</h2>
<p>First, <a target="_blank" href="https://github.com/flows-network/chatgpt-github-app">fork this template repo from GitHub</a>.</p>
<p>The <a target="_blank" href="https://github.com/flows-network/chatgpt-github-app/blob/main/src/lib.rs"><code>src/lib.rs</code></a> file contains the bot application (also known as the flow function). The <code>run()</code> function is called upon starting up. It listens for <code>issue_comment</code> and <code>issues</code> events from the GitHub repo <a target="_blank" href="https://github.com/second-state/chat-with-chatgpt"><code>owner/repo</code></a>. Those events are emitted when a new issue comment or a new issue is created in the repo.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[no_mangle]</span>
<span class="hljs-meta">#[tokio::main(flavor = <span class="hljs-meta-string">"current_thread"</span>)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">run</span></span>() {
    <span class="hljs-comment">// Setup variables for</span>
    <span class="hljs-comment">//   ower: GitHub org to install the bot</span>
    <span class="hljs-comment">//   repo: GitHub repo to install the bot</span>
    <span class="hljs-comment">//   openai_key_name: Name for your OpenAI API key</span>
    <span class="hljs-comment">// All the values can be set in the source code or as env vars</span>

    listen_to_event(&amp;owner, &amp;repo, <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"issue_comment"</span>, <span class="hljs-string">"issues"</span>], |payload| {
        handler(&amp;owner, &amp;repo, &amp;openai_key_name, payload)
    })
    .<span class="hljs-keyword">await</span>;
}
</code></pre>
<p>The <code>handler()</code> function processes the events received by <code>listen_to_event()</code>. If the event is a new comment in an issue, the bot calls OpenAI's ChatGPT API to add the comment text into an existing conversation identified by the <code>issue.number</code>. It receives a response from ChatGPT, and adds a comment in the issue.</p>
<p>The flow function here automatically and transparently manages the conversation history with the ChatGPT API in a local storage. The OpenAI API key is also stored in the local storage so that instead of putting the secret text in the source code, the key can be identified by a string name in <code>openai_key_name</code>.</p>
<pre><code class="lang-rust">EventPayload::IssueCommentEvent(e) =&gt; {
    <span class="hljs-keyword">if</span> e.comment.user.r#<span class="hljs-class"><span class="hljs-keyword">type</span> != "<span class="hljs-title">Bot</span></span><span class="hljs-string">" {
        if let Some(b) = e.comment.body {
            if let Some(r) = chat_completion (
                    openai_key_name,
                    &amp;format!("</span>issue#{}<span class="hljs-string">", e.issue.number),
                    &amp;b,
                    &amp;ChatOptions::default(),
            ) {
                if let Err(e) = issues.create_comment(e.issue.number, r.choice).await {
                    write_error_log!(e.to_string());
                }
            }
        }
    }
}</span>
</code></pre>
<p>If the event is a new issue, the flow function creates a new conversation identified by <code>issue.number</code>, and requests a response from ChatGPT.</p>
<pre><code class="lang-rust">EventPayload::IssuesEvent(e) =&gt; {
    <span class="hljs-keyword">if</span> e.action == IssuesEventAction::Closed {
        <span class="hljs-keyword">return</span>;
    }

    <span class="hljs-keyword">let</span> title = e.issue.title;
    <span class="hljs-keyword">let</span> body = e.issue.body.unwrap_or(<span class="hljs-string">""</span>.to_string());
    <span class="hljs-keyword">let</span> q = title + <span class="hljs-string">"\n"</span> + &amp;body;
    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(r) = chat_completion (
            openai_key_name,
            &amp;<span class="hljs-built_in">format!</span>(<span class="hljs-string">"issue#{}"</span>, e.issue.number),
            &amp;q,
            &amp;ChatOptions::default(),
    ) {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Err</span>(e) = issues.create_comment(e.issue.number, r.choice).<span class="hljs-keyword">await</span> {
            write_error_log!(e.to_string());
        }
    }
}
</code></pre>
<h2 id="heading-how-to-deploy-the-serverless-flow-function">How to Deploy the Serverless Flow Function</h2>
<p>As we can see, the flow function code calls SDK APIs to perform complex operations. For example,</p>
<ul>
<li><p>The <code>listen_to_event()</code> function registers a webhook URL through GitHub API so that the <code>handler()</code> function will be called when certain events occur in GitHub.</p>
</li>
<li><p>The <code>chat_completion()</code> function calls the ChatGPT API with the named API key and past history / context of the specified conversation. The API key and conversation history are stored in a Redis cache.</p>
</li>
</ul>
<p>The webhook server and the Redis cache are both external services the SDK depends on. That means the flow function must run inside a managed host environment that provides such external services. <a target="_blank" href="https://flows.network/">Flows.network</a> is a PaaS (Platform as a Service) host for the flow function SDKs.</p>
<p>In order to deploy the flow function on flows.network, you simply need to import its source code to the PaaS.</p>
<p>First, sign into flows.network from your GitHub account. Import your forked GitHub repo that contains the flow function source code and choose "With Environment Variables".</p>
<p>Note that this is NOT the GitHub repo where you want to deploy the bot. This is the repo for your forked flow function source code.</p>
<p><img src="https://i.imgur.com/CH1nUf8.png" alt="Image" width="1362" height="1182" loading="lazy"></p>
<p><em>Figure 2. Import the GitHub repo you forked from the flow function template into flows.network.</em></p>
<p>Set the environment variables to point the flow function to the OpenAI API key name (<code>open_ai_key</code>) and GitHub repo (<code>owner</code> and <code>repo</code>).</p>
<p>The GitHub <code>owner</code> and <code>repo</code> variables here point to the GitHub repo where you want to deploy the bot, NOT the repo for the flow function source code.</p>
<p><img src="https://i.imgur.com/5gcTKMv.png" alt="Image" width="726" height="527" loading="lazy"></p>
<p><em>Figure 3. Set the environment variables for the GitHub repo where you want to deploy the bot, as well as the OpenAI API key name.</em></p>
<p>Flows.network will fetch the source code and build the Rust source code into Wasm bytecode using the standard <code>cargo</code> toolchain. It will then run the Wasm flow function in the <a target="_blank" href="https://github.com/WasmEdge/WasmEdge">WasmEdge Runtime</a>.</p>
<h2 id="heading-how-to-connect-the-flow-function-to-github-and-openai">How to Connect the Flow Function to GitHub and OpenAI</h2>
<p>While the flow function requires connections to the OpenAI and GitHub APIs, the source code has no hardcoded API keys, access tokens, or OAUTH logic. The flows function SDKs have made it easy and safe for developers to interact with external SaaS API services.</p>
<p>Flows.network discovers that the flow function requires connections to the OpenAI and GitHub APIs. It presents UI workflows for the developers to:</p>
<ul>
<li><p>Log into GitHub, authorize access to events, and register the flow function as the webhook for receiving those events.</p>
</li>
<li><p>Associate an OpenAI API key with the name <code>openai_key_name</code>.</p>
</li>
</ul>
<p><img src="https://i.imgur.com/CpLDrub.png" alt="Image" width="1393" height="484" loading="lazy"></p>
<p><em>Figure 4. The external services required by the flow function are connected and turned green.</em></p>
<p>Once the external SaaS APIs are connected and authorized, they turn green on the flow function dashboard. The flow function will now receive the events it <code>listen_to_event()</code> for. It will also get transparent access to Redis for the named OpenAI API key and the cached conversation context to support the <code>chat_completion()</code> SDK function.</p>
<h2 id="heading-whats-next">What's next</h2>
<p>The GitHub bot is just one of many bot types the flows.network can support. By connecting the flow function to a Slack channel, you can get ChatGPT to participate in your group discussion. Here is an example of a Slack-based ChatGPT bot.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/flows-network/collaborative-chat">https://github.com/flows-network/collaborative-chat</a></div>
<p> </p>
<p><img src="https://i.imgur.com/voB27bj.png" alt="Image" width="1446" height="842" loading="lazy"></p>
<p><em>Figure 5. The Slack ChatGPT bot.</em></p>
<p>Another example is to have ChatGPT answering legal questions in a Slack channel. The flow function prepends the legal question with a prompt.</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/flows-network/robo-lawyer">https://github.com/flows-network/robo-lawyer</a></div>
<p> </p>
<p><img src="https://i.imgur.com/afDM5im.png" alt="Image" width="1544" height="978" loading="lazy"></p>
<p><em>Figure 6. The Slack robo lawyer bot.</em></p>
<p>Besides GitHub and Slack, there are many SaaS products you can integrate into flows.network through their APIs.</p>
<p>While the example flow functions are written in Rust, we aim to support JavaScript-based flow function SDKs. In another word, platform SDK functions such as <code>listen_to_event()</code> and <code>chat_completion()</code> will have a JavaScript version. The JavaScript flow function runs inside the <a target="_blank" href="https://github.com/WasmEdge/WasmEdge">WasmEdge Runtime</a> on the flows.network platform through the <a target="_blank" href="https://wasmedge.org/docs/develop/javascript/intro">WasmEdge-QuickJS</a> module.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Telegram Bot using Python ]]>
                </title>
                <description>
                    <![CDATA[ Automated chatbots are quite useful for stimulating interactions. We can create chatbots for Slack, Discord, and other platforms.  In this article, I'll teach you how to build a Telegram chatbot that will tell you your horoscope. So, let’s get starte... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-telegram-bot-using-python/</link>
                <guid isPermaLink="false">66ba0e8bc003a1736007f4a4</guid>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Ashutosh Krishna ]]>
                </dc:creator>
                <pubDate>Fri, 16 Dec 2022 17:42:10 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/12/Telegram-Bot.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Automated chatbots are quite useful for stimulating interactions. We can create chatbots for Slack, Discord, and other platforms. </p>
<p>In this article, I'll teach you how to build a Telegram chatbot that will tell you your horoscope. So, let’s get started!</p>
<h2 id="heading-how-to-get-your-bot-token">How to Get Your Bot Token</h2>
<p>To set up a new bot, you will need to talk to BotFather. No, he’s not a person – he’s also a bot, and he's the boss of all the Telegram bots.</p>
<ol>
<li>Search for @botfather in Telegram.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-092357.png" alt="Image" width="600" height="400" loading="lazy">
<em>BotFather Telegram Bot</em></p>
<ol start="2">
<li>Start a conversation with BotFather by clicking on the Start button.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-092531.png" alt="Image" width="600" height="400" loading="lazy">
<em>Click on Start Button</em></p>
<ol start="3">
<li>Type <code>/newbot</code>, and follow the prompts to set up a new bot. The BotFather will give you a token that you will use to authenticate your bot and grant it access to the Telegram API.</li>
</ol>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-093337.png" alt="Image" width="600" height="400" loading="lazy">
<em>Getting access token</em></p>
<p><strong>Note:</strong> Make sure you store the token securely. Anyone with your token access can easily manipulate your bot.</p>
<h2 id="heading-how-to-set-up-your-coding-environment">How to Set Up Your Coding Environment</h2>
<p>Let’s set up the coding environment. While there are various libraries available to create a Telegram bot, we’ll use the <a target="_blank" href="https://pypi.org/project/pyTelegramBotAPI/">pyTelegramBotAPI</a> library. It is a simple but extensible Python implementation for the Telegram Bot API with both synchronous and asynchronous capabilities.</p>
<p>Install the pyTelegramBotAPI library using pip:</p>
<pre><code class="lang-bash">pip install pyTelegramBotAPI
</code></pre>
<p>Next, open your favorite code editor and create a <code>.env</code> file to store your token as below:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">export</span> BOT_TOKEN=your-bot-token-here
</code></pre>
<p>After that, run the <code>source .env</code> command to read the environment variables from the <code>.env</code> file.</p>
<h2 id="heading-how-to-create-your-first-bot">How to Create Your First Bot</h2>
<p>All the API implementations are stored in a single class called <code>TeleBot</code>. It offers many ways to listen for incoming messages as well as functions like <code>send_message()</code>, <code>send_document()</code>, and others to send messages.</p>
<p>Create a new <code>bot.py</code> file and paste the following code there:</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os

<span class="hljs-keyword">import</span> telebot

BOT_TOKEN = os.environ.get(<span class="hljs-string">'BOT_TOKEN'</span>)

bot = telebot.TeleBot(BOT_TOKEN)
</code></pre>
<p>In the above code, we use the <code>os</code> library in order to read the environment variables stored in our system. </p>
<p>If you remember, we exported an environment variable called <code>BOT_TOKEN</code> in the previous step. The value of <code>BOT_TOKEN</code> is read in a variable called <code>BOT_TOKEN</code>. Further, we use the <code>TeleBot</code> class to create a bot instance and passed the <code>BOT_TOKEN</code> to it.</p>
<p>We then need to register message handlers. These message handlers contain filters that a message must pass. If a message passes the filter, the decorated function is called and the incoming message is supplied as an argument.</p>
<p>Let's define a message handler that handles incoming <code>/start</code> and <code>/hello</code> commands.</p>
<pre><code class="lang-python"><span class="hljs-meta">@bot.message_handler(commands=['start', 'hello'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_welcome</span>(<span class="hljs-params">message</span>):</span>
    bot.reply_to(message, <span class="hljs-string">"Howdy, how are you doing?"</span>)
</code></pre>
<p>Any name is acceptable for a function that is decorated by a message handler, but it can only have one parameter (the message).</p>
<p>Let’s add another handler that echoes all incoming text messages back to the sender.</p>
<pre><code class="lang-python"><span class="hljs-meta">@bot.message_handler(func=lambda msg: True)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">echo_all</span>(<span class="hljs-params">message</span>):</span>
    bot.reply_to(message, message.text)
</code></pre>
<p>The above code uses a <code>lambda</code> expression to test a message. Since we need to echo all the messages, we always return <code>True</code> from the <code>lambda</code> function.</p>
<p>You now have a simple bot that responds to the <code>/start</code> and <code>/hello</code> commands with a static message and echoes all the other sent messages. Add the following to the end of your file to launch the bot:</p>
<pre><code class="lang-python">bot.infinity_polling()
</code></pre>
<p>That’s it! We have a Telegram bot ready. Let’s run the Python file and go to Telegram to test the bot. </p>
<p>Search for the bot using its username if you’re unable to find it. You can test it by sending the commands like <code>/hello</code> and <code>/start</code> and other random texts.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/12/Screenshot-2022-12-16-101334.png" alt="Image" width="600" height="400" loading="lazy">
<em>Testing the bot</em></p>
<p>Note: All the message handlers are tested in the order in which they were declared in the source file.</p>
<p>For more information on using the pyTelegramBotAPI library, you can refer to their <strong><a target="_blank" href="https://github.com/eternnoir/pyTelegramBotAPI">documentation</a></strong>.</p>
<h2 id="heading-how-to-code-the-horoscope-bot">How to Code the Horoscope Bot</h2>
<p>Let’s shift our attention to building our Horoscope Bot now. We will use message chaining in the bot. The bot will first ask for your zodiac sign, and then the day, and then it will respond with the horoscope for that particular day. </p>
<p>Under the hood, the bot interacts with an API to get the horoscope data.</p>
<p>We are going to use the <a target="_blank" href="https://horoscope-app-api.vercel.app/">Horoscope API</a> that I built in another tutorial. If you wish to learn how to build one, you can go through <a target="_blank" href="https://ashutoshkrris.hashnode.dev/how-to-create-a-horoscope-api-with-beautiful-soup-and-flask">this tutorial</a>. Make sure you explore the APIs <a target="_blank" href="https://horoscope-app-api.vercel.app/">here</a> before getting started.</p>
<h3 id="heading-how-to-fetch-the-horoscope-data">How to fetch the horoscope data</h3>
<p>Let’s create a utility function to fetch the horoscope data for a particular day.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> requests

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_daily_horoscope</span>(<span class="hljs-params">sign: str, day: str</span>) -&gt; dict:</span>
    <span class="hljs-string">"""Get daily horoscope for a zodiac sign.
    Keyword arguments:
    sign:str - Zodiac sign
    day:str - Date in format (YYYY-MM-DD) OR TODAY OR TOMORROW OR YESTERDAY
    Return:dict - JSON data
    """</span>
    url = <span class="hljs-string">"https://horoscope-app-api.vercel.app/api/v1/get-horoscope/daily"</span>
    params = {<span class="hljs-string">"sign"</span>: sign, <span class="hljs-string">"day"</span>: day}
    response = requests.get(url, params)

    <span class="hljs-keyword">return</span> response.json()
</code></pre>
<p>In the above Python code, we created a function that accepts two string arguments – <code>sign</code> and <code>day</code> – and returns JSON data. We send a GET request on the API URL and pass <code>sign</code> and <code>day</code> as the query parameters. </p>
<p>If you test the function, you will get an output similar to below:</p>
<pre><code class="lang-json">{
   <span class="hljs-attr">"data"</span>:{
      <span class="hljs-attr">"date"</span>: <span class="hljs-string">"Dec 15, 2022"</span>,
      <span class="hljs-attr">"horoscope_data"</span>: <span class="hljs-string">"Lie low during the day and try not to get caught up in the frivolous verbiage that dominates the waking hours. After sundown, feel free to speak your mind. You may notice that there is a sober tone and restrictive sensation today that leaves you feeling like you will never be able to break free from your current situation. Don't get caught in this negative mindset."</span>
   },
   <span class="hljs-attr">"status"</span>: <span class="hljs-number">200</span>,
   <span class="hljs-attr">"success"</span>: <span class="hljs-literal">true</span>
}
</code></pre>
<p>Note: You can explore more about the <code>requests</code> library in Python in <a target="_blank" href="https://ashutoshkrris.hashnode.dev/how-to-interact-with-web-services-using-python">this tutorial</a>.</p>
<h3 id="heading-how-to-add-a-message-handler">How to add a message handler</h3>
<p>Now that we have a function that returns the horoscope data, let’s create a message handler in our bot that asks for the zodiac sign of the user.</p>
<pre><code class="lang-python"><span class="hljs-meta">@bot.message_handler(commands=['horoscope'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">sign_handler</span>(<span class="hljs-params">message</span>):</span>
    text = <span class="hljs-string">"What's your zodiac sign?\nChoose one: *Aries*, *Taurus*, *Gemini*, *Cancer,* *Leo*, *Virgo*, *Libra*, *Scorpio*, *Sagittarius*, *Capricorn*, *Aquarius*, and *Pisces*."</span>
    sent_msg = bot.send_message(message.chat.id, text, parse_mode=<span class="hljs-string">"Markdown"</span>)
    bot.register_next_step_handler(sent_msg, day_handler)
</code></pre>
<p>The above function is a bit different from the other functions we defined earlier. The bot’s horoscope functionality will be invoked by the <code>/horoscope</code> command. We are sending a text message to the user, but notice that we have set the <code>parse_mode</code> to <strong>Markdown</strong> while sending the message. </p>
<p>Since we’ll use message chaining, we used the <code>register_next_step_handler()</code> method. This method accepts two parameters: <strong>the message</strong> sent by the user and <strong>the callback function</strong> which should be called after the message. Thus, we pass the <code>sent_msg</code> variable and a new <code>day_handler</code> function that we’ll define next.</p>
<p>Let’s define the <code>day_handler()</code> function that accepts the message.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">day_handler</span>(<span class="hljs-params">message</span>):</span>
    sign = message.text
    text = <span class="hljs-string">"What day do you want to know?\nChoose one: *TODAY*, *TOMORROW*, *YESTERDAY*, or a date in format YYYY-MM-DD."</span>
    sent_msg = bot.send_message(
        message.chat.id, text, parse_mode=<span class="hljs-string">"Markdown"</span>)
    bot.register_next_step_handler(
        sent_msg, fetch_horoscope, sign.capitalize())
</code></pre>
<p>We fetch the zodiac sign from the <code>message.text</code> attribute. Similar to the previous function, it also asks the day for which you want to know the horoscope. </p>
<p>In the end, we use the same <code>register_next_step_handler()</code> method and pass the <code>sent_msg</code>, the <code>fetch_horoscope</code> callback function, and the <code>sign</code>.</p>
<p>Let’s now define the <code>fetch_horoscope()</code> function that accepts the message and the sign.</p>
<pre><code class="lang-python"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">fetch_horoscope</span>(<span class="hljs-params">message, sign</span>):</span>
    day = message.text
    horoscope = get_daily_horoscope(sign, day)
    data = horoscope[<span class="hljs-string">"data"</span>]
    horoscope_message = <span class="hljs-string">f'*Horoscope:* <span class="hljs-subst">{data[<span class="hljs-string">"horoscope_data"</span>]}</span>\\n*Sign:* <span class="hljs-subst">{sign}</span>\\n*Day:* <span class="hljs-subst">{data[<span class="hljs-string">"date"</span>]}</span>'</span>
    bot.send_message(message.chat.id, <span class="hljs-string">"Here's your horoscope!"</span>)
    bot.send_message(message.chat.id, horoscope_message, parse_mode=<span class="hljs-string">"Markdown"</span>)
</code></pre>
<p>This is the final function where we get the sign from the function parameter and the day from the <code>message.text</code> attribute. </p>
<p>Next, we fetch the horoscope using the <code>get_daily_horoscope()</code> function and construct our message. In the end, we send the message with the horoscope data.</p>
<h2 id="heading-bot-demo">Bot Demo</h2>
<p>Once you run the Python file, you can test this functionality. Here’s the demo:</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/nTHI2rPV_RE" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<h2 id="heading-recommended-next-steps">Recommended Next Steps</h2>
<p>As of now, the bot stops working as soon as we stop our Python application. In order to make it run always, you can deploy the bot on platforms like Heroku, Render, and so on.</p>
<p>Here's a link to the <a target="_blank" href="https://github.com/ashutoshkrris/Telegram-Horoscope-Bot">GitHub repo for this project</a> - feel free to check it out.</p>
<p>You can also add more functionalities to the bot by exploring the <a target="_blank" href="https://core.telegram.org/">Telegram APIs</a>.</p>
<p>Thanks for reading! You can follow me on <a target="_blank" href="https://twitter.com/ashutoshkrris">Twitter</a><a target="_blank" href="https://ireadblog.com/">.</a></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ The AI Chatbot Handbook – How to Build an AI Chatbot with Redis, Python, and GPT ]]>
                </title>
                <description>
                    <![CDATA[ By Stephen Sanwo In order to build a working full-stack application, there are so many moving parts to think about. And you'll need to make many decisions that will be critical to the success of your app. For example, what language will you use and w... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-ai-chatbot-with-redis-python-and-gpt/</link>
                <guid isPermaLink="false">66d4614abd438296f45cd3b8</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Redis ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 27 Jul 2022 20:16:44 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/07/The-AI-Chatbot-Handbook-Cover--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Stephen Sanwo</p>
<p>In order to build a working full-stack application, there are so many moving parts to think about. And you'll need to make many decisions that will be critical to the success of your app.</p>
<p>For example, what language will you use and what platform will you deploy on? Are you going to deploy a containerised software on a server, or make use of serverless functions to handle the backend? Do you plan to use third-party APIs to handle complex parts of your application, like authentication or payments? Where do you store the data? </p>
<p>In addition to all this, you'll also need to think about the user interface, design and usability of your application, and much more. </p>
<p>This is why complex large applications require a multifunctional development team collaborating to build the app.</p>
<p>One of the best ways to learn how to develop full stack applications is to build projects that cover the end-to-end development process. You'll go through designing the architecture, developing the API services, developing the user interface, and finally deploying your application. </p>
<p>So this tutorial will take you through the process of building an AI chatbot to help you learn these concepts in depth.</p>
<p>Some of the topics we will cover include:</p>
<ul>
<li>How to build APIs with Python, FastAPI, and WebSockets</li>
<li>How to build real-time systems with Redis</li>
<li>How to build a chat User Interface with React</li>
</ul>
<p><strong>Important Note:</strong>
This is an intermediate full stack software development project that requires some basic Python and JavaScript knowledge. </p>
<p>I've carefully divided the project into sections to ensure that you can easily select the phase that is important to you in case you do not wish to code the full application.</p>
<p>You can download the full repository on <a target="_blank" href="https://github.com/stephensanwo/fullstack-ai-chatbot">My Github here</a>.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<h3 id="heading-section-1">Section 1</h3>
<ul>
<li><a class="post-section-overview" href="#application-architecture">Application Architecture</a></li>
<li><a class="post-section-overview" href="#how-to-set-up-the-development-environment">How to Set Up the Development Environment</a><h3 id="heading-section-2">Section 2</h3>
</li>
<li><a class="post-section-overview" href="#how-to-build-a-chat-server-with-python-fastapi-and-websockets">How to Build a Chat Server with Python, FastAPI, and WebSockets</a><ul>
<li><a class="post-section-overview" href="#how-to-set-up-the-python-environment">How to Set Up the Python Environment</a></li>
<li><a class="post-section-overview" href="#fastapi-server-setup">FastAPI Server Setup</a></li>
<li><a class="post-section-overview" href="#how-to-add-routes-to-the-api">How to Add Routes to the API</a></li>
<li><a class="post-section-overview" href="#how-to-generate-a-chat-session-token-with-uuid">How to Generate a Chat Session Token with UUID</a></li>
<li><a class="post-section-overview" href="#how-to-test-the-api-with-postman">How to Test the API with Postman</a></li>
<li><a class="post-section-overview" href="#websockets-and-connection-manager">Websockets and Connection Manager</a></li>
<li><a class="post-section-overview" href="#dependency-injection-in-fastapi">Dependency Injection in FastAPI</a><h3 id="heading-section-3">Section 3</h3>
</li>
</ul>
</li>
<li><a class="post-section-overview" href="#how-to-build-real-time-systems-with-redis">How to build Real-Time Systems with Redis</a><ul>
<li><a class="post-section-overview" href="#redis-and-distributed-messaging-queues">Redis and Distributed Messaging Queues</a></li>
<li><a class="post-section-overview" href="#how-to-connect-to-a-redis-cluster-in-python-with-a-redis-client">How to Connect to a Redis Cluster in Python with a Redis Client</a></li>
<li><a class="post-section-overview" href="#how-to-work-with-redis-streams">How to Work with Redis Streams</a></li>
<li><a class="post-section-overview" href="#how-to-model-the-chat-data">How to Model the Chat Data</a></li>
<li><a class="post-section-overview" href="#how-to-work-with-redis-json">How to Work with Redis JSON</a></li>
<li><a class="post-section-overview" href="#how-to-update-the-token-dependency">How to Update the Token Dependency</a><h3 id="heading-section-4">Section 4</h3>
</li>
</ul>
</li>
<li><a class="post-section-overview" href="#how-to-add-intelligence-to-chatbots-with-ai-models">How to Add Intelligence to Chatbots with AI models</a><ul>
<li><a class="post-section-overview" href="#how-to-get-started-with-huggingface">How to Get Started with Huggingface</a></li>
<li><a class="post-section-overview" href="#how-to-interact-with-the-language-model">How to Interact with the Language Model</a></li>
<li><a class="post-section-overview" href="#how-to-simulate-short-term-memory-for-the-ai-model">How to Simulate Short-term Memory for the AI Model</a></li>
<li><a class="post-section-overview" href="#stream-consumer-and-real-time-data-pull-from-the-message-queue">Stream Consumer and Real-timeDdata Pull from the Message Queue</a></li>
<li><a class="post-section-overview" href="#how-to-update-the-chat-client-with-the-ai-response">How to Update the Chat Client with the AI Response</a></li>
<li><a class="post-section-overview" href="#refresh-token">Refresh Token</a></li>
<li><a class="post-section-overview" href="#how-to-test-the-chat-with-multiple-clients-in-postman">How to Test the Chat with Multiple Clients in Postman</a></li>
</ul>
</li>
</ul>
<h2 id="heading-application-architecture">Application Architecture <a></a></h2>
<p>Sketching out a solution architecture gives you a high-level overview of your application, the tools you intend to use, and how the components will communicate with each other. </p>
<p>I have drawn up a simple architecture below using <a target="_blank" href="http://draw.io">draw.io</a>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/full-stack-chatbot-architecture.drawio.svg" alt="Image" width="600" height="400" loading="lazy">
<em>Fullstack chatbot architecture</em></p>
<p>Let's go over the various parts of the architecture in more detail:</p>
<h3 id="heading-clientuser-interface">Client/User Interface</h3>
<p>We will use React version 18 to build the user interface. The Chat UI will communicate with the backend via WebSockets.</p>
<h3 id="heading-gpt-j-6b-and-huggingface-inference-api">GPT-J-6B and Huggingface Inference API</h3>
<p>GPT-J-6B is a generative language model which was trained with 6 Billion parameters and performs closely with OpenAI's GPT-3 on some tasks. </p>
<p>I have chosen to use GPT-J-6B because it is an open-source model and doesn’t require paid tokens for simple use cases. </p>
<p>Huggingface also provides us with an on-demand API to connect with this model pretty much free of charge. You can read more about <a target="_blank" href="https://huggingface.co/EleutherAI/gpt-j-6B?text=My+name+is+Teven+and+I+am">GPT-J-6B</a> and <a target="_blank" href="https://huggingface.co/inference-api">Hugging Face Inference API</a>.</p>
<h3 id="heading-redis">Redis</h3>
<p>When we send prompts to GPT, we need a way to store the prompts and easily retrieve the response. We will use Redis JSON to store the chat data and also use Redis Streams for handling the real-time communication with the huggingface inference API. </p>
<p>Redis is an in-memory key-value store that enables super-fast fetching and storing of JSON-like data. For this tutorial, we will use a managed free Redis storage provided by <a target="_blank" href="https://redis.info/3NBGJRT">Redis Enterprise</a> for testing purposes.</p>
<h3 id="heading-web-sockets-and-the-chat-api">Web Sockets and the Chat API</h3>
<p>To send messages between the client and server in real-time, we need to open a socket connection. This is because an HTTP connection will not be sufficient to ensure real-time bi-directional communication between the client and the server. </p>
<p>We will be using FastAPI for the chat server, as it provides a fast and modern Python server for our use. <a target="_blank" href="https://fastapi.tiangolo.com/advanced/websockets/?h=web">Check out the FastAPI documentation</a>) to learn more about WebSockets.</p>
<h2 id="heading-how-to-set-up-the-development-environment">How to Set Up the Development Environment <a></a></h2>
<p>You can use your desired OS to build this app – I am currently using MacOS, and Visual Studio Code. Just make sure you have Python and NodeJs installed. </p>
<p>To set up the project structure, create a folder named<code>fullstack-ai-chatbot</code>. Then create two folders within the project called <code>client</code> and <code>server</code>. The server will hold the code for the backend, while the client will hold the code for the frontend.</p>
<p>Next within the project directory, initialize a Git repository within the root of the project folder using the "git init" command. Then create a .gitignore file by using "touch .gitignore":</p>
<pre><code class="lang-bash">git init
touch .gitignore
</code></pre>
<p>In the next section, we will build our chat web server using FastAPI and Python.</p>
<h2 id="heading-how-to-build-a-chat-server-with-python-fastapi-and-websockets">How to Build a Chat Server with Python, FastAPI and WebSockets <a></a></h2>
<p>In this section, we will build the chat server using FastAPI to communicate with the user. We will use WebSockets to ensure bi-directional communication between the client and server so that we can send responses to the user in real-time.</p>
<h3 id="heading-how-to-set-up-the-python-environment">How to Set Up the Python Environment <a></a></h3>
<p>To start our server, we need to set up our Python environment. Open the project folder within VS Code, and open up the terminal.</p>
<p>From the project root, cd into the server directory and run <code>python3.8 -m venv env</code>. This will create a <a target="_blank" href="https://blog.stephensanwo.dev/virtual-environments-in-python"><strong>virtual environment</strong></a> for our Python project, which will be named <code>env</code>. To activate the virtual environment, run <code>source env/bin/activate</code></p>
<p>Next, install a couple of libraries in your Python environment.</p>
<pre><code class="lang-bash">pip install fastapi uuid uvicorn gunicorn WebSockets python-dotenv aioredis
</code></pre>
<p>Next create an environment file by running <code>touch .env</code> in the terminal. We will define our app variables and secret variables within the <code>.env</code> file. </p>
<p>Add your app environment variable and set it to "development" like so: <code>export APP_ENV=development</code>. Next, we will set up a development server with a FastAPI server.</p>
<h3 id="heading-fastapi-server-setup">FastAPI Server Setup <a></a></h3>
<p>At the root of the server directory, create a new file named <code>main.py</code> then paste the code below for the development sever:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request
<span class="hljs-keyword">import</span> uvicorn
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv

load_dotenv()

api = FastAPI()

<span class="hljs-meta">@api.get("/test")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"msg"</span>: <span class="hljs-string">"API is Online"</span>}


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    <span class="hljs-keyword">if</span> os.environ.get(<span class="hljs-string">'APP_ENV'</span>) == <span class="hljs-string">"development"</span>:
        uvicorn.run(<span class="hljs-string">"main:api"</span>, host=<span class="hljs-string">"0.0.0.0"</span>, port=<span class="hljs-number">3500</span>,
                    workers=<span class="hljs-number">4</span>, reload=<span class="hljs-literal">True</span>)
    <span class="hljs-keyword">else</span>:
      <span class="hljs-keyword">pass</span>
</code></pre>
<p>First we <code>import FastAPI</code> and initialize it as <code>api</code>. Then we <code>import load_dotenv</code> from the <code>python-dotenv</code> library, and initialize it to load the variables from the <code>.env</code> file,</p>
<p>Then we create a simple test route to test the API. The test route will return a simple JSON response that tells us the API is online. </p>
<p>Lastly, we set up the development server by using <code>uvicorn.run</code> and providing the required arguments. The API will run on port <code>3500</code>.</p>
<p>Finally, run the server in the terminal with <code>python main.py</code>. Once you see <code>Application startup complete</code> in the terminal, navigate to the URL <a target="_blank" href="http://localhost:3500/test">http://localhost:3500/test</a> on your browser, and you should get a web page like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/test-page.png" alt="Image" width="600" height="400" loading="lazy">
<em>API Test Page</em></p>
<h3 id="heading-how-to-add-routes-to-the-api">How to Add Routes to the API <a></a></h3>
<p>In this section, we will add routes to our API. Create a new folder named <code>src</code>. This is the directory where all our API code will live. </p>
<p>Create a subfolder named <code>routes</code>, cd into the folder, create a new file named <code>chat.py</code> and then add the code below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter, FastAPI, WebSocket,  Request

chat = APIRouter()

<span class="hljs-comment"># @route   POST /token</span>
<span class="hljs-comment"># @desc    Route to generate chat token</span>
<span class="hljs-comment"># @access  Public</span>

<span class="hljs-meta">@chat.post("/token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">token_generator</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>


<span class="hljs-comment"># @route   POST /refresh_token</span>
<span class="hljs-comment"># @desc    Route to refresh token</span>
<span class="hljs-comment"># @access  Public</span>

<span class="hljs-meta">@chat.post("/refresh_token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refresh_token</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>


<span class="hljs-comment"># @route   Websocket /chat</span>
<span class="hljs-comment"># @desc    Socket for chatbot</span>
<span class="hljs-comment"># @access  Public</span>

<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket = WebSocket</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>
</code></pre>
<p>We created three endpoints:</p>
<ul>
<li><code>/token</code> will issue the user a session token for access to the chat session. Since the chat app will be open publicly, we do not want to worry about authentication and just keep it simple – but we still need a way to identify each unique user session.</li>
<li><code>/refresh_token</code> will get the session history for the user if the connection is lost, as long as the token is still active and not expired.</li>
<li><code>/chat</code> will open a WebSocket to send messages between the client and server.</li>
</ul>
<p>Next, connect the chat route to our main API. First we need to <code>import chat from src.chat</code> within our <code>main.py</code> file. Then we will include the router by literally calling an <code>include_router</code> method on the initialized <code>FastAPI</code> class and passing chat as the argument. </p>
<p>Update your <code>api.py</code> code as shown below:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request
<span class="hljs-keyword">import</span> uvicorn
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">from</span> routes.chat <span class="hljs-keyword">import</span> chat

load_dotenv()

api = FastAPI()
api.include_router(chat)


<span class="hljs-meta">@api.get("/test")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">root</span>():</span>
    <span class="hljs-keyword">return</span> {<span class="hljs-string">"msg"</span>: <span class="hljs-string">"API is Online"</span>}


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    <span class="hljs-keyword">if</span> os.environ.get(<span class="hljs-string">'APP_ENV'</span>) == <span class="hljs-string">"development"</span>:
        uvicorn.run(<span class="hljs-string">"main:api"</span>, host=<span class="hljs-string">"0.0.0.0"</span>, port=<span class="hljs-number">3500</span>,
                    workers=<span class="hljs-number">4</span>, reload=<span class="hljs-literal">True</span>)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">pass</span>
</code></pre>
<h3 id="heading-how-to-generate-a-chat-session-token-with-uuid">How to Generate a Chat Session Token with UUID <a></a></h3>
<p>To generate a user token we will use <code>uuid4</code> to create dynamic routes for our chat endpoint. Since this is a publicly available endpoint, we won't need to go into details about JWTs and authentication. </p>
<p>If you didn't install <code>uuid</code> initially, run <code>pip install uuid</code>. Next in chat.py, import UUID, and update the <code>/token</code> route with the code below:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter, FastAPI, WebSocket,  Request, BackgroundTasks, HTTPException
<span class="hljs-keyword">import</span> uuid

<span class="hljs-comment"># @route   POST /token</span>
<span class="hljs-comment"># @desc    Route generating chat token</span>
<span class="hljs-comment"># @access  Public</span>

<span class="hljs-meta">@chat.post("/token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">token_generator</span>(<span class="hljs-params">name: str, request: Request</span>):</span>

    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span>:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail={
            <span class="hljs-string">"loc"</span>: <span class="hljs-string">"name"</span>,  <span class="hljs-string">"msg"</span>: <span class="hljs-string">"Enter a valid name"</span>})

    token = str(uuid.uuid4())

    data = {<span class="hljs-string">"name"</span>: name, <span class="hljs-string">"token"</span>: token}

    <span class="hljs-keyword">return</span> data
</code></pre>
<p>In the code above, the client provides their name, which is required. We do a quick check to ensure that the name field is not empty, then generate a token using uuid4. </p>
<p>The session data is a simple dictionary for the name and token. Ultimately we will need to persist this session data and set a timeout, but for now we just return it to the client.</p>
<h3 id="heading-how-to-test-the-api-with-postman">How to Test the API with Postman <a></a></h3>
<p>Because we will be testing a WebSocket endpoint, we need to use a tool like <a target="_blank" href="https://www.postman.com">Postman</a> that allows this (as the default swagger docs on FastAPI does not support WebSockets). </p>
<p>In Postman, create a collection for your development environment and send a POST request to <code>localhost:3500/token</code> specifying the name as a query parameter and passing it a value. You should get a response as shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/token-generator-postman.png" alt="Image" width="600" height="400" loading="lazy">
<em>Token Generator Postman</em></p>
<h3 id="heading-websockets-and-connection-manager">Websockets and Connection Manager <a></a></h3>
<p>In the src root, create a new folder named <code>socket</code> and add a file named <code>connection.py</code>. In this file, we will define the class that controls the connections to our WebSockets, and all the helper methods to connect and disconnect. </p>
<p>In <code>connection.py</code> add the code below:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> WebSocket

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ConnectionManager</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.active_connections: List[WebSocket] = []

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">connect</span>(<span class="hljs-params">self, websocket: WebSocket</span>):</span>
        <span class="hljs-keyword">await</span> websocket.accept()
        self.active_connections.append(websocket)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">disconnect</span>(<span class="hljs-params">self, websocket: WebSocket</span>):</span>
        self.active_connections.remove(websocket)

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_personal_message</span>(<span class="hljs-params">self, message: str, websocket: WebSocket</span>):</span>
        <span class="hljs-keyword">await</span> websocket.send_text(message)
</code></pre>
<p>The <code>ConnectionManager</code> class is initialized with an <code>active_connections</code> attribute that is a list of active connections. </p>
<p>Then the asynchronous <code>connect</code> method will accept a <code>WebSocket</code> and add it to the list of active connections, while the <code>disconnect</code> method will remove the <code>Websocket</code> from the list of active connections. </p>
<p>Lastly, the <code>send_personal_message</code> method will take in a message and the <code>Websocket</code> we want to send the message to and asynchronously send the message.</p>
<p>WebSockets are a very broad topic and we only scraped the surface here. This should however be sufficient to create multiple connections and handle messages to those connections asynchronously. </p>
<p>You can read more about <a target="_blank" href="https://fastapi.tiangolo.com/advanced/websockets/?h=depends#using-depends-and-others">FastAPI Websockets</a> and <a target="_blank" href="https://realpython.com/python-sockets/">Sockets Programming</a>.</p>
<p>To use the <code>ConnectionManager</code>, import and initialize it within the <code>src.routes.chat.py</code>, and update the <code>/chat</code> WebSocket route with the code below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> ..socket.connection <span class="hljs-keyword">import</span> ConnectionManager

manager = ConnectionManager()

<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket</span>):</span>
    <span class="hljs-keyword">await</span> manager.connect(websocket)
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            data = <span class="hljs-keyword">await</span> websocket.receive_text()
            print(data)
            <span class="hljs-keyword">await</span> manager.send_personal_message(<span class="hljs-string">f"Response: Simulating response from the GPT service"</span>, websocket)

    <span class="hljs-keyword">except</span> WebSocketDisconnect:
        manager.disconnect(websocket)
</code></pre>
<p>In the <code>websocket_endpoint</code> function, which takes a WebSocket, we add the new websocket to the connection manager and run a <code>while True</code> loop, to ensure that the socket stays open. Except when the socket gets disconnected. </p>
<p>While the connection is open, we receive any messages sent by the client with <code>websocket.receive_test()</code> and print them to the terminal for now. </p>
<p>Then we send a hard-coded response back to the client for now. Ultimately the message received from the clients will be sent to the AI Model, and the response sent back to the client will be the response from the AI Model.</p>
<p>In Postman, we can test this endpoint by creating a new WebSocket request, and connecting to the WebSocket endpoint <code>localhost:3500/chat</code>. </p>
<p>When you click connect, the Messages pane will show that the API client is connected to the URL, and a socket is open. </p>
<p>To test this, send a message "Hello Bot" to the chat server, and you should get an immediate test response "Response: Simulating response from the GPT service" as shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/postman-chat-test.png" alt="Image" width="600" height="400" loading="lazy">
<em>Postman Chat Test</em></p>
<h3 id="heading-dependency-injection-in-fastapi">Dependency Injection in FastAPI <a></a></h3>
<p>To be able to distinguish between two different client sessions and limit the chat sessions, we will use a timed token, passed as a query parameter to the WebSocket connection. </p>
<p>In the socket folder, create a file named <code>utils.py</code> then add the code below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> WebSocket, status, Query
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> Optional

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_token</span>(<span class="hljs-params">
    websocket: WebSocket,
    token: Optional[str] = Query(<span class="hljs-params">None</span>),
</span>):</span>
    <span class="hljs-keyword">if</span> token <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">or</span> token == <span class="hljs-string">""</span>:
        <span class="hljs-keyword">await</span> websocket.close(code=status.WS_1008_POLICY_VIOLATION)

    <span class="hljs-keyword">return</span> token
</code></pre>
<p>The get_token function receives a WebSocket and token, then checks if the token is None or null. </p>
<p>If this is the case, the function returns a policy violation status and if available, the function just returns the token. We will ultimately extend this function later with additional token validation.</p>
<p>To consume this function, we inject it into the <code>/chat</code> route. FastAPI provides a Depends class to easily inject dependencies, so we don't have to tinker with decorators. </p>
<p>Update the <code>/chat</code> route to the following:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> ..socket.utils <span class="hljs-keyword">import</span> get_token

<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket, token: str = Depends(<span class="hljs-params">get_token</span>)</span>):</span>
    <span class="hljs-keyword">await</span> manager.connect(websocket)
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            data = <span class="hljs-keyword">await</span> websocket.receive_text()
            print(data)
            <span class="hljs-keyword">await</span> manager.send_personal_message(<span class="hljs-string">f"Response: Simulating response from the GPT service"</span>, websocket)

    <span class="hljs-keyword">except</span> WebSocketDisconnect:
        manager.disconnect(websocket)
</code></pre>
<p>Now when you try to connect to the <code>/chat</code> endpoint in Postman, you will get a 403 error. Provide a token as query parameter and provide any value to the token, for now. Then you should be able to connect like before, only now the connection requires a token.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/postman-chat-test-token.png" alt="Image" width="600" height="400" loading="lazy">
<em>Postman Chat Test with Token</em></p>
<p>Congratulations on getting this far! Your <code>chat.py</code> file should now look like this:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter, FastAPI, WebSocket, WebSocketDisconnect, Request, Depends, HTTPException
<span class="hljs-keyword">import</span> uuid
<span class="hljs-keyword">from</span> ..socket.connection <span class="hljs-keyword">import</span> ConnectionManager
<span class="hljs-keyword">from</span> ..socket.utils <span class="hljs-keyword">import</span> get_token


chat = APIRouter()

manager = ConnectionManager()

<span class="hljs-comment"># @route   POST /token</span>
<span class="hljs-comment"># @desc    Route to generate chat token</span>
<span class="hljs-comment"># @access  Public</span>


<span class="hljs-meta">@chat.post("/token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">token_generator</span>(<span class="hljs-params">name: str, request: Request</span>):</span>
    token = str(uuid.uuid4())

    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span>:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail={
            <span class="hljs-string">"loc"</span>: <span class="hljs-string">"name"</span>,  <span class="hljs-string">"msg"</span>: <span class="hljs-string">"Enter a valid name"</span>})

    data = {<span class="hljs-string">"name"</span>: name, <span class="hljs-string">"token"</span>: token}

    <span class="hljs-keyword">return</span> data


<span class="hljs-comment"># @route   POST /refresh_token</span>
<span class="hljs-comment"># @desc    Route to refresh token</span>
<span class="hljs-comment"># @access  Public</span>


<span class="hljs-meta">@chat.post("/refresh_token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refresh_token</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>


<span class="hljs-comment"># @route   Websocket /chat</span>
<span class="hljs-comment"># @desc    Socket for chatbot</span>
<span class="hljs-comment"># @access  Public</span>

<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket, token: str = Depends(<span class="hljs-params">get_token</span>)</span>):</span>
    <span class="hljs-keyword">await</span> manager.connect(websocket)
    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            data = <span class="hljs-keyword">await</span> websocket.receive_text()
            print(data)
            <span class="hljs-keyword">await</span> manager.send_personal_message(<span class="hljs-string">f"Response: Simulating response from the GPT service"</span>, websocket)

    <span class="hljs-keyword">except</span> WebSocketDisconnect:
        manager.disconnect(websocket)
</code></pre>
<p>In the next part of this tutorial, we will focus on handling the state of our application and passing data between client and server.</p>
<h2 id="heading-how-to-build-real-time-systems-with-redis">How to Build Real-Time Systems with Redis <a></a></h2>
<p>Our application currently does not store any state, and there is no way to identify users or store and retrieve chat data. We are also returning a hard-coded response to the client during chat sessions. </p>
<p>In this part of the tutorial, we will cover the following:</p>
<ul>
<li>How to connect to a <strong>Redis Cluster</strong> in Python and set up a <strong>Redis Client</strong></li>
<li>How to store and retrieve data with <strong>Redis JSON</strong></li>
<li>How to set up <strong>Redis Streams</strong> as message queues between a web server and worker environment</li>
</ul>
<h3 id="heading-redis-and-distributed-messaging-queues">Redis and Distributed Messaging Queues <a></a></h3>
<p>Redis is an open source in-memory data store that you can use as a database, cache, message broker, and streaming engine. It supports a number of data structures and is a perfect solution for distributed applications with real-time capabilities. </p>
<p><strong>Redis Enterprise Cloud</strong> is a fully managed cloud service provided by Redis that helps us deploy Redis clusters at an infinite scale without worrying about infrastructure. </p>
<p>We will be using a free Redis Enterprise Cloud instance for this tutorial. You can <a target="_blank" href="https://redis.com/try-free/?utm_campaign=write_for_redis">Get started with Redis Cloud for free here</a> and follow <a target="_blank" href="https://developer.redis.com/create/rediscloud/">This tutorial to set up a Redis database and Redis Insight, a GUI to interact with Redis</a>.</p>
<p>Once you have set up your Redis database, create a new folder in the project root (outside the server folder) named <code>worker</code>. </p>
<p>We will isolate our worker environment from the web server so that when the client sends a message to our WebSocket, the web server does not have to handle the request to the third-party service. Also, resources can be freed up for other users. </p>
<p>The background communication with the inference API is handled by this worker service, through Redis.</p>
<p>Requests from all the connected clients are appended to the message queue (producer), while the worker consumes the messages, sends off the requests to the inference API, and appends the response to a response queue. </p>
<p>Once the API receives a response, it sends it back to the client. </p>
<p>During the trip between the producer and the consumer, the client can send multiple messages, and these messages will be queued up and responded to in order. </p>
<p>Ideally, we could have this worker running on a completely different server, in its own environment, but for now, we will create its own Python environment on our local machine.</p>
<p>You might be wondering – <strong>why do we need a worker?</strong> Imagine a scenario where the web server also creates the request to the third-party service. This means that while waiting for the response from the third party service during a socket connection, the server is blocked and resources are tied up till the response is obtained from the API. </p>
<p>You can try this out by creating a random sleep <code>time.sleep(10)</code> before sending the hard-coded response, and sending a new message. Then try to connect with a different token in a new postman session. </p>
<p>You will notice that the chat session will not connect until the random sleep times out.</p>
<p>While we can use asynchronous techniques and worker pools in a more production-focused server set-up, that also won't be enough as the number of simultaneous users grow. </p>
<p>Ultimately, we want to avoid tying up the web server resources by using Redis to broker the communication between our chat API and the third-party API.</p>
<p>Next open up a new terminal, cd into the worker folder, and create and activate a new Python virtual environment similar to what we did in part 1. </p>
<p>Next, install the following dependencies:</p>
<pre><code class="lang-bash">pip install aiohttp aioredis python-dotenv
</code></pre>
<h3 id="heading-how-to-connect-to-a-redis-cluster-in-python-with-a-redis-client">How to Connect to a Redis Cluster in Python with a Redis Client <a></a></h3>
<p>We will use the aioredis client to connect with the Redis database. We'll also use the requests library to send requests to the Huggingface inference API. </p>
<p>Create two files <code>.env</code>, and <code>main.py</code>. Then create a folder named <code>src</code>. Also, create a folder named <code>redis</code> and add a new file named <code>config.py</code>. </p>
<p>In the <code>.env</code> file, add the following code – and make sure you update the fields with the credentials provided in your Redis Cluster.</p>
<pre><code class="lang-txt">export REDIS_URL=&lt;REDIS URL PROVIDED IN REDIS CLOUD&gt;
export REDIS_USER=&lt;REDIS USER IN REDIS CLOUD&gt;
export REDIS_PASSWORD=&lt;DATABASE PASSWORD IN REDIS CLOUD&gt;
export REDIS_HOST=&lt;REDIS HOST IN REDIS CLOUD&gt;
export REDIS_PORT=&lt;REDIS PORT IN REDIS CLOUD&gt;
</code></pre>
<p>In config.py add the Redis Class below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> aioredis

load_dotenv()

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Redis</span>():</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""initialize  connection """</span>
        self.REDIS_URL = os.environ[<span class="hljs-string">'REDIS_URL'</span>]
        self.REDIS_PASSWORD = os.environ[<span class="hljs-string">'REDIS_PASSWORD'</span>]
        self.REDIS_USER = os.environ[<span class="hljs-string">'REDIS_USER'</span>]
        self.connection_url = <span class="hljs-string">f"redis://<span class="hljs-subst">{self.REDIS_USER}</span>:<span class="hljs-subst">{self.REDIS_PASSWORD}</span>@<span class="hljs-subst">{self.REDIS_URL}</span>"</span>

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_connection</span>(<span class="hljs-params">self</span>):</span>
        self.connection = aioredis.from_url(
            self.connection_url, db=<span class="hljs-number">0</span>)

        <span class="hljs-keyword">return</span> self.connection
</code></pre>
<p>We create a Redis object and initialize the required parameters from the environment variables. Then we create an asynchronous method <code>create_connection</code> to create a Redis connection and return the connection pool obtained from the <code>aioredis</code> method <code>from_url</code>.</p>
<p>Next, we test the Redis connection in main.py by running the code below. This will create a new Redis connection pool, set a simple key "key", and assign a string "value" to it.</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> src.redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">import</span> asyncio

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    redis = Redis()
    redis = <span class="hljs-keyword">await</span> redis.create_connection()
    print(redis)
    <span class="hljs-keyword">await</span> redis.set(<span class="hljs-string">"key"</span>, <span class="hljs-string">"value"</span>)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    asyncio.run(main())
</code></pre>
<p>Now open Redis Insight (if you followed the tutorial to download and install it) You should see something like this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/redis-insight-test.png" alt="Image" width="600" height="400" loading="lazy">
<em>Redis Insight Test</em></p>
<h3 id="heading-how-to-work-with-redis-streams">How to Work with Redis Streams <a></a></h3>
<p>Now that we have our worker environment setup, we can create a producer on the web server and a consumer on the worker. </p>
<p>First, let's create our Redis class again on the server. In <code>server.src</code> create a folder named <code>redis</code> and add two files, <code>config.py</code> and <code>producer.py</code>. </p>
<p>In <code>config.py</code>, add the code below as we did for the worker environment:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> aioredis

load_dotenv()

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Redis</span>():</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""initialize  connection """</span>
        self.REDIS_URL = os.environ[<span class="hljs-string">'REDIS_URL'</span>]
        self.REDIS_PASSWORD = os.environ[<span class="hljs-string">'REDIS_PASSWORD'</span>]
        self.REDIS_USER = os.environ[<span class="hljs-string">'REDIS_USER'</span>]
        self.connection_url = <span class="hljs-string">f"redis://<span class="hljs-subst">{self.REDIS_USER}</span>:<span class="hljs-subst">{self.REDIS_PASSWORD}</span>@<span class="hljs-subst">{self.REDIS_URL}</span>"</span>

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_connection</span>(<span class="hljs-params">self</span>):</span>
        self.connection = aioredis.from_url(
            self.connection_url, db=<span class="hljs-number">0</span>)

        <span class="hljs-keyword">return</span> self.connection
</code></pre>
<p>In the .env file, also add the Redis credentials:</p>
<pre><code class="lang-txt">export REDIS_URL=&lt;REDIS URL PROVIDED IN REDIS CLOUD&gt;
export REDIS_USER=&lt;REDIS USER IN REDIS CLOUD&gt;
export REDIS_PASSWORD=&lt;DATABASE PASSWORD IN REDIS CLOUD&gt;
export REDIS_HOST=&lt;REDIS HOST IN REDIS CLOUD&gt;
export REDIS_PORT=&lt;REDIS PORT IN REDIS CLOUD&gt;
</code></pre>
<p>Finally, in <code>server.src.redis.producer.py</code> add the following code:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> .config <span class="hljs-keyword">import</span> Redis

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Producer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, redis_client</span>):</span>
        self.redis_client = redis_client

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_to_stream</span>(<span class="hljs-params">self,  data: dict, stream_channel</span>):</span>
        <span class="hljs-keyword">try</span>:
            msg_id = <span class="hljs-keyword">await</span> self.redis_client.xadd(name=stream_channel, id=<span class="hljs-string">"*"</span>, fields=data)
            print(<span class="hljs-string">f"Message id <span class="hljs-subst">{msg_id}</span> added to <span class="hljs-subst">{stream_channel}</span> stream"</span>)
            <span class="hljs-keyword">return</span> msg_id

        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            print(<span class="hljs-string">f"Error sending msg to stream =&gt; <span class="hljs-subst">{e}</span>"</span>)
</code></pre>
<p>We created a Producer class that is initialized with a Redis client. We use this client to add data to the stream with the <code>add_to_stream</code> method, which takes the data and the Redis channel name. </p>
<p>The Redis command for adding data to a stream channel is <code>xadd</code> and it has both high-level and low-level functions in aioredis.</p>
<p>Next, to run our newly created Producer, update <code>chat.py</code> and the WebSocket <code>/chat</code> endpoint like below. Notice the updated channel name <code>message_channel</code>.</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> ..redis.producer <span class="hljs-keyword">import</span> Producer
<span class="hljs-keyword">from</span> ..redis.config <span class="hljs-keyword">import</span> Redis

chat = APIRouter()
manager = ConnectionManager()
redis = Redis()


<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket, token: str = Depends(<span class="hljs-params">get_token</span>)</span>):</span>
    <span class="hljs-keyword">await</span> manager.connect(websocket)
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    producer = Producer(redis_client)

    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            data = <span class="hljs-keyword">await</span> websocket.receive_text()
            print(data)
            stream_data = {}
            stream_data[token] = data
            <span class="hljs-keyword">await</span> producer.add_to_stream(stream_data, <span class="hljs-string">"message_channel"</span>)
            <span class="hljs-keyword">await</span> manager.send_personal_message(<span class="hljs-string">f"Response: Simulating response from the GPT service"</span>, websocket)

    <span class="hljs-keyword">except</span> WebSocketDisconnect:
        manager.disconnect(websocket)
</code></pre>
<p>Next, in Postman, create a connection and send any number of messages that say <code>Hello</code>. You should have the stream messages printed to the terminal like below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/terminal-channel-messages-test.png" alt="Image" width="600" height="400" loading="lazy">
<em>Terminal Channel Messages Test</em></p>
<p>In Redis Insight, you will see a new <code>mesage_channel</code> created and a time-stamped queue filled with the messages sent from the client. This timestamped queue is important to preserve the order of the messages.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/redis-insight-channel.png" alt="Image" width="600" height="400" loading="lazy">
<em>Redis Insight Channel</em></p>
<h3 id="heading-how-to-model-the-chat-data">How to Model the Chat Data <a></a></h3>
<p>Next, we'll create a model for our chat messages. Recall that we are sending text data over WebSockets, but our chat data needs to hold more information than just the text. We need to timestamp when the chat was sent, create an ID for each message, and collect data about the chat session, then store this data in a JSON format. </p>
<p>We can store this JSON data in Redis so we don't lose the chat history once the connection is lost, because our WebSocket does not store state.</p>
<p>In <code>server.src</code> create a new folder named <code>schema</code>. Then create a file named <code>chat.py</code> in <code>server.src.schema</code> add the following code:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List, Optional
<span class="hljs-keyword">import</span> uuid


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span>(<span class="hljs-params">BaseModel</span>):</span>
    id = uuid.uuid4()
    msg: str
    timestamp = str(datetime.now())


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Chat</span>(<span class="hljs-params">BaseModel</span>):</span>
    token: str
    messages: List[Message]
    name: str
    session_start = str(datetime.now())
</code></pre>
<p>We are using Pydantic's <code>BaseModel</code> class to model the chat data. The <code>Chat</code> class will hold data about a single Chat session. It will store the token, name of the user, and an automatically generated timestamp for the chat session start time using <code>datetime.now()</code>. </p>
<p>The messages sent and received within this chat session are stored with a <code>Message</code> class which creates a chat id on the fly using <code>uuid4</code>. The only data we need to provide when initializing this <code>Message</code> class is the message text.</p>
<h3 id="heading-how-to-work-with-redis-json">How to Work with Redis JSON <a></a></h3>
<p>In order to use Redis JSON's ability to store our chat history, we need to install <a target="_blank" href="https://github.com/RedisJSON/redisjson-py">rejson</a> provided by Redis labs. </p>
<p>In the terminal, cd into <code>server</code> and install rejson with <code>pip install rejson</code>. Then update your <code>Redis</code> class in <code>server.src.redis.config.py</code> to include the <code>create_rejson_connection</code> method:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> aioredis
<span class="hljs-keyword">from</span> rejson <span class="hljs-keyword">import</span> Client

load_dotenv()

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Redis</span>():</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""initialize  connection """</span>
        self.REDIS_URL = os.environ[<span class="hljs-string">'REDIS_URL'</span>]
        self.REDIS_PASSWORD = os.environ[<span class="hljs-string">'REDIS_PASSWORD'</span>]
        self.REDIS_USER = os.environ[<span class="hljs-string">'REDIS_USER'</span>]
        self.connection_url = <span class="hljs-string">f"redis://<span class="hljs-subst">{self.REDIS_USER}</span>:<span class="hljs-subst">{self.REDIS_PASSWORD}</span>@<span class="hljs-subst">{self.REDIS_URL}</span>"</span>
        self.REDIS_HOST = os.environ[<span class="hljs-string">'REDIS_HOST'</span>]
        self.REDIS_PORT = os.environ[<span class="hljs-string">'REDIS_PORT'</span>]

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_connection</span>(<span class="hljs-params">self</span>):</span>
        self.connection = aioredis.from_url(
            self.connection_url, db=<span class="hljs-number">0</span>)

        <span class="hljs-keyword">return</span> self.connection

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_rejson_connection</span>(<span class="hljs-params">self</span>):</span>
        self.redisJson = Client(host=self.REDIS_HOST,
                                port=self.REDIS_PORT, decode_responses=<span class="hljs-literal">True</span>, username=self.REDIS_USER, password=self.REDIS_PASSWORD)

        <span class="hljs-keyword">return</span> self.redisJson
</code></pre>
<p>We are adding the <code>create_rejson_connection</code> method to connect to Redis with the rejson <code>Client</code>. This gives us the methods to create and manipulate JSON data in Redis, which are not available with aioredis.</p>
<p>Next, in <code>server.src.routes.chat.py</code> we can update the <code>/token</code> endpoint to create a new <code>Chat</code> instance and store the session data in Redis JSON like so:</p>
<pre><code class="lang-py"><span class="hljs-meta">@chat.post("/token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">token_generator</span>(<span class="hljs-params">name: str, request: Request</span>):</span>
    token = str(uuid.uuid4())

    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span>:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail={
            <span class="hljs-string">"loc"</span>: <span class="hljs-string">"name"</span>,  <span class="hljs-string">"msg"</span>: <span class="hljs-string">"Enter a valid name"</span>})

    <span class="hljs-comment"># Create new chat session</span>
    json_client = redis.create_rejson_connection()

    chat_session = Chat(
        token=token,
        messages=[],
        name=name
    )

    <span class="hljs-comment"># Store chat session in redis JSON with the token as key</span>
    json_client.jsonset(str(token), Path.rootPath(), chat_session.dict())

    <span class="hljs-comment"># Set a timeout for redis data</span>
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    <span class="hljs-keyword">await</span> redis_client.expire(str(token), <span class="hljs-number">3600</span>)


    <span class="hljs-keyword">return</span> chat_session.dict()
</code></pre>
<p>NOTE: Because this is a demo app, I do not want to store the chat data in Redis for too long. So I have added a 60-minute time out on the token using the aioredis client (rejson does not implement timeouts). This means that after 60 minutes, the chat session data will be lost. </p>
<p>This is necessary because we are not authenticating users, and we want to dump the chat data after a defined period. This step is optional, and you don't have to include it.</p>
<p>Next, in Postman, when you send a POST request to create a new token, you will get a structured response like the one below. You can also check Redis Insight to see your chat data stored with the token as a JSON key and the data as a value.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/token-generator-updated.png" alt="Image" width="600" height="400" loading="lazy">
<em>Token Generator Updated</em></p>
<h3 id="heading-how-to-update-the-token-dependency">How to Update the Token Dependency <a></a></h3>
<p>Now that we have a token being generated and stored, this is a good time to update the <code>get_token</code> dependency in our <code>/chat</code> WebSocket. We do this to check for a valid token before starting the chat session. </p>
<p>In <code>server.src.socket.utils.py</code> update the <code>get_token</code> function to check if the token exists in the Redis instance. If it does then we return the token, which means that the socket connection is valid. If it doesn't exist, we close the connection. </p>
<p>The token created by <code>/token</code> will cease to exist after 60 minutes. So we can have some simple logic on the frontend to redirect the user to generate a new token if an error response is generated while trying to start a chat.</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> ..redis.config <span class="hljs-keyword">import</span> Redis

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_token</span>(<span class="hljs-params">
    websocket: WebSocket,
    token: Optional[str] = Query(<span class="hljs-params">None</span>),
</span>):</span>

    <span class="hljs-keyword">if</span> token <span class="hljs-keyword">is</span> <span class="hljs-literal">None</span> <span class="hljs-keyword">or</span> token == <span class="hljs-string">""</span>:
        <span class="hljs-keyword">await</span> websocket.close(code=status.WS_1008_POLICY_VIOLATION)

    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    isexists = <span class="hljs-keyword">await</span> redis_client.exists(token)

    <span class="hljs-keyword">if</span> isexists == <span class="hljs-number">1</span>:
        <span class="hljs-keyword">return</span> token
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">await</span> websocket.close(code=status.WS_1008_POLICY_VIOLATION, reason=<span class="hljs-string">"Session not authenticated or expired token"</span>)
</code></pre>
<p>To test the dependency, connect to the chat session with the random token we have been using, and you should get a 403 error. (Note that you have to manually delete the token in Redis Insight.) </p>
<p>Now copy the token generated when you sent the post request to the <code>/token</code> endpoint (or create a new request) and paste it as the value to the token query parameter required by the <code>/chat</code> WebSocket. Then connect. You should get a successful connection.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/chat-session-with-token.png" alt="Image" width="600" height="400" loading="lazy">
<em>Chat Session with Token</em></p>
<p>Bringing it all together, your chat.py should look like the below.</p>
<pre><code class="lang-py">
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> APIRouter, FastAPI, WebSocket, WebSocketDisconnect, Request, Depends
<span class="hljs-keyword">import</span> uuid
<span class="hljs-keyword">from</span> ..socket.connection <span class="hljs-keyword">import</span> ConnectionManager
<span class="hljs-keyword">from</span> ..socket.utils <span class="hljs-keyword">import</span> get_token
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">from</span> ..redis.producer <span class="hljs-keyword">import</span> Producer
<span class="hljs-keyword">from</span> ..redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">from</span> ..schema.chat <span class="hljs-keyword">import</span> Chat
<span class="hljs-keyword">from</span> rejson <span class="hljs-keyword">import</span> Path

chat = APIRouter()
manager = ConnectionManager()
redis = Redis()


<span class="hljs-comment"># @route   POST /token</span>
<span class="hljs-comment"># @desc    Route to generate chat token</span>
<span class="hljs-comment"># @access  Public</span>


<span class="hljs-meta">@chat.post("/token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">token_generator</span>(<span class="hljs-params">name: str, request: Request</span>):</span>
    token = str(uuid.uuid4())

    <span class="hljs-keyword">if</span> name == <span class="hljs-string">""</span>:
        <span class="hljs-keyword">raise</span> HTTPException(status_code=<span class="hljs-number">400</span>, detail={
            <span class="hljs-string">"loc"</span>: <span class="hljs-string">"name"</span>,  <span class="hljs-string">"msg"</span>: <span class="hljs-string">"Enter a valid name"</span>})

    <span class="hljs-comment"># Create nee chat session</span>
    json_client = redis.create_rejson_connection()
    chat_session = Chat(
        token=token,
        messages=[],
        name=name
    )

    print(chat_session.dict())

    <span class="hljs-comment"># Store chat session in redis JSON with the token as key</span>
    json_client.jsonset(str(token), Path.rootPath(), chat_session.dict())

    <span class="hljs-comment"># Set a timeout for redis data</span>
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    <span class="hljs-keyword">await</span> redis_client.expire(str(token), <span class="hljs-number">3600</span>)

    <span class="hljs-keyword">return</span> chat_session.dict()


<span class="hljs-comment"># @route   POST /refresh_token</span>
<span class="hljs-comment"># @desc    Route to refresh token</span>
<span class="hljs-comment"># @access  Public</span>


<span class="hljs-meta">@chat.post("/refresh_token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refresh_token</span>(<span class="hljs-params">request: Request</span>):</span>
    <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>


<span class="hljs-comment"># @route   Websocket /chat</span>
<span class="hljs-comment"># @desc    Socket for chat bot</span>
<span class="hljs-comment"># @access  Public</span>

<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket, token: str = Depends(<span class="hljs-params">get_token</span>)</span>):</span>
    <span class="hljs-keyword">await</span> manager.connect(websocket)
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    producer = Producer(redis_client)
    json_client = redis.create_rejson_connection()

    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            data = <span class="hljs-keyword">await</span> websocket.receive_text()
            stream_data = {}
            stream_data[token] = data
            <span class="hljs-keyword">await</span> producer.add_to_stream(stream_data, <span class="hljs-string">"message_channel"</span>)
            <span class="hljs-keyword">await</span> manager.send_personal_message(<span class="hljs-string">f"Response: Simulating response from the GPT service"</span>, websocket)

    <span class="hljs-keyword">except</span> WebSocketDisconnect:
        manager.disconnect(websocket)
</code></pre>
<p>Well done on reaching it this far! In the next section, we will focus on communicating with the AI model and handling the data transfer between client, server, worker, and the external API.</p>
<h2 id="heading-how-to-add-intelligence-to-chatbots-with-ai-models">How to Add Intelligence to Chatbots with AI Models <a></a></h2>
<p>In this section, we will focus on building a wrapper to communicate with the transformer model, send prompts from a user to the API in a conversational format, and receive and transform responses for our chat application.</p>
<h3 id="heading-how-to-get-started-with-huggingface">How to Get Started with Huggingface <a></a></h3>
<p>We will not be building or deploying any language models on Hugginface. Instead, we'll focus on using Huggingface's accelerated inference API to connect to pre-trained models. </p>
<p>The model we will be using is the <a target="_blank" href="https://huggingface.co/EleutherAI/gpt-j-6B">GPT-J-6B Model provided by EleutherAI</a>. It's a generative language model which was trained with 6 Billion parameters. </p>
<p>Huggingface provides us with an on-demand limited API to connect with this model pretty much free of charge.</p>
<p>To get started with Huggingface, <a target="_blank" href="https://huggingface.co/pricing">Create a free account</a>. In your settings, <a target="_blank" href="https://huggingface.co/settings/tokens">generate a new access token</a>. For up to 30k tokens, Huggingface provides access to the inference API for free. </p>
<p>You can <a target="_blank" href="https://api-inference.huggingface.co/dashboard/usage">Monitor your API usage here</a>. Make sure you keep this token safe and don't expose it publicly.</p>
<p>Note: We will use HTTP connections to communicate with the API because we are using a free account. But the PRO Huggingface account supports streaming with WebSockets <a target="_blank" href="https://huggingface.co/docs/api-inference/parallelism">see parallelism and batch jobs</a>. </p>
<p>This can help significantly improve response times between the model and our chat application, and I'll hopefully cover this method in a follow-up article.</p>
<h3 id="heading-how-to-interact-with-the-language-model">How to Interact with the Language Model <a></a></h3>
<p>First, we add the Huggingface connection credentials to the .env file within our worker directory.</p>
<pre><code class="lang-txt">export HUGGINFACE_INFERENCE_TOKEN=&lt;HUGGINGFACE ACCESS TOKEN&gt;
export MODEL_URL=https://api-inference.huggingface.co/models/EleutherAI/gpt-j-6B
</code></pre>
<p>Next, in <code>worker.src</code> create a folder named <code>model</code> then add a file <code>gptj.py</code>. Then add the GPT class below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> json

load_dotenv()

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GPT</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.url = os.environ.get(<span class="hljs-string">'MODEL_URL'</span>)
        self.headers = {
            <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">f"Bearer <span class="hljs-subst">{os.environ.get(<span class="hljs-string">'HUGGINFACE_INFERENCE_TOKEN'</span>)}</span>"</span>}
        self.payload = {
            <span class="hljs-string">"inputs"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-string">"parameters"</span>: {
                <span class="hljs-string">"return_full_text"</span>: <span class="hljs-literal">False</span>,
                <span class="hljs-string">"use_cache"</span>: <span class="hljs-literal">True</span>,
                <span class="hljs-string">"max_new_tokens"</span>: <span class="hljs-number">25</span>
            }

        }

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query</span>(<span class="hljs-params">self, input: str</span>) -&gt; list:</span>
        self.payload[<span class="hljs-string">"inputs"</span>] = input
        data = json.dumps(self.payload)
        response = requests.request(
            <span class="hljs-string">"POST"</span>, self.url, headers=self.headers, data=data)
        print(json.loads(response.content.decode(<span class="hljs-string">"utf-8"</span>)))
        <span class="hljs-keyword">return</span> json.loads(response.content.decode(<span class="hljs-string">"utf-8"</span>))

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    GPT().query(<span class="hljs-string">"Will artificial intelligence help humanity conquer the universe?"</span>)
</code></pre>
<p>The <code>GPT</code> class is initialized with the Huggingface model <code>url</code>, authentication <code>header</code>, and predefined <code>payload</code>. But the payload input is a dynamic field that is provided by the <code>query</code> method and updated before we send a request to the Huggingface endpoint.</p>
<p>Finally, we test this by running the query method on an instance of the GPT class directly. In the terminal, run <code>python src/model/gptj.py</code>, and you should get a response like this (just keep in mind that your response will certainly be different from this):</p>
<pre><code class="lang-bash">[{<span class="hljs-string">'generated_text'</span>: <span class="hljs-string">' (AI) could solve all the problems on this planet? I am of the opinion that in the short term artificial intelligence is much better than human beings, but in the long and distant future human beings will surpass artificial intelligence.\n\nIn the distant'</span>}]
</code></pre>
<p>Next, we add some tweaking to the input to make the interaction with the model more conversational by changing the format of the input. </p>
<p>Update the <code>GPT</code> class like so:</p>
<pre><code class="lang-py">
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GPT</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        self.url = os.environ.get(<span class="hljs-string">'MODEL_URL'</span>)
        self.headers = {
            <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">f"Bearer <span class="hljs-subst">{os.environ.get(<span class="hljs-string">'HUGGINFACE_INFERENCE_TOKEN'</span>)}</span>"</span>}
        self.payload = {
            <span class="hljs-string">"inputs"</span>: <span class="hljs-string">""</span>,
            <span class="hljs-string">"parameters"</span>: {
                <span class="hljs-string">"return_full_text"</span>: <span class="hljs-literal">False</span>,
                <span class="hljs-string">"use_cache"</span>: <span class="hljs-literal">False</span>,
                <span class="hljs-string">"max_new_tokens"</span>: <span class="hljs-number">25</span>
            }

        }

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">query</span>(<span class="hljs-params">self, input: str</span>) -&gt; list:</span>
        self.payload[<span class="hljs-string">"inputs"</span>] = <span class="hljs-string">f"Human: <span class="hljs-subst">{input}</span> Bot:"</span>
        data = json.dumps(self.payload)
        response = requests.request(
            <span class="hljs-string">"POST"</span>, self.url, headers=self.headers, data=data)
        data = json.loads(response.content.decode(<span class="hljs-string">"utf-8"</span>))
        text = data[<span class="hljs-number">0</span>][<span class="hljs-string">'generated_text'</span>]
        res = str(text.split(<span class="hljs-string">"Human:"</span>)[<span class="hljs-number">0</span>]).strip(<span class="hljs-string">"\n"</span>).strip()
        <span class="hljs-keyword">return</span> res


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    GPT().query(<span class="hljs-string">"Will artificial intelligence help humanity conquer the universe?"</span>)
</code></pre>
<p>We updated the input with a string literal <code>f"Human: {input} Bot:"</code>. The human input is placed in the string and the Bot provides a response. This input format turns the GPT-J6B into a conversational model. Other changes you may notice include</p>
<ul>
<li>use_cache: you can make this False if you want the model to create a new response when the input is the same. I suggest leaving this as True in production to prevent exhausting your free tokens if a user just keeps spamming the bot with the same message. Using cache does not actually load a new response from the model.</li>
<li>return_full_text: is False, as we do not need to return the input – we already have it. When we get a response, we strip the "Bot:" and leading/trailing spaces from the response and return just the response text.</li>
</ul>
<h3 id="heading-how-to-simulate-short-term-memory-for-the-ai-model">How to Simulate Short-term Memory for the AI Model <a></a></h3>
<p>For every new input we send to the model, there is no way for the model to remember the conversation history. This is important if we want to hold context in the conversation. </p>
<p>But remember that as the number of tokens we send to the model increases, the processing gets more expensive, and the response time is also longer. </p>
<p>So we will need to find a way to retrieve short-term history and send it to the model. We will also need to figure out a sweet spot - how much historical data do we want to retrieve and send to the model?</p>
<p>To handle chat history, we need to fall back to our JSON database. We'll use the <code>token</code> to get the last chat data, and then when we get the response, append the response to the JSON database.</p>
<p>Update <code>worker.src.redis.config.py</code> to include the <code>create_rejson_connection</code> method. Also, update the .env file with the authentication data, and ensure rejson is installed.</p>
<p>Your <code>worker.src.redis.config.py</code> should look like this:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> aioredis
<span class="hljs-keyword">from</span> rejson <span class="hljs-keyword">import</span> Client


load_dotenv()


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Redis</span>():</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-string">"""initialize  connection """</span>
        self.REDIS_URL = os.environ[<span class="hljs-string">'REDIS_URL'</span>]
        self.REDIS_PASSWORD = os.environ[<span class="hljs-string">'REDIS_PASSWORD'</span>]
        self.REDIS_USER = os.environ[<span class="hljs-string">'REDIS_USER'</span>]
        self.connection_url = <span class="hljs-string">f"redis://<span class="hljs-subst">{self.REDIS_USER}</span>:<span class="hljs-subst">{self.REDIS_PASSWORD}</span>@<span class="hljs-subst">{self.REDIS_URL}</span>"</span>
        self.REDIS_HOST = os.environ[<span class="hljs-string">'REDIS_HOST'</span>]
        self.REDIS_PORT = os.environ[<span class="hljs-string">'REDIS_PORT'</span>]

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_connection</span>(<span class="hljs-params">self</span>):</span>
        self.connection = aioredis.from_url(
            self.connection_url, db=<span class="hljs-number">0</span>)

        <span class="hljs-keyword">return</span> self.connection

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">create_rejson_connection</span>(<span class="hljs-params">self</span>):</span>
        self.redisJson = Client(host=self.REDIS_HOST,
                                port=self.REDIS_PORT, decode_responses=<span class="hljs-literal">True</span>, username=self.REDIS_USER, password=self.REDIS_PASSWORD)

        <span class="hljs-keyword">return</span> self.redisJson
</code></pre>
<p>While your .env file should look like this:</p>
<pre><code class="lang-txt">export REDIS_URL=&lt;REDIS URL PROVIDED IN REDIS CLOUD&gt;
export REDIS_USER=&lt;REDIS USER IN REDIS CLOUD&gt;
export REDIS_PASSWORD=&lt;DATABASE PASSWORD IN REDIS CLOUD&gt;
export REDIS_HOST=&lt;REDIS HOST IN REDIS CLOUD&gt;
export REDIS_PORT=&lt;REDIS PORT IN REDIS CLOUD&gt;
export HUGGINFACE_INFERENCE_TOKEN=&lt;HUGGINGFACE ACCESS TOKEN&gt;
export MODEL_URL=https://api-inference.huggingface.co/models/EleutherAI/gpt-j-6B
</code></pre>
<p>Next, in <code>worker.src.redis</code> create a new file named <code>cache.py</code> and add the code below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> .config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">from</span> rejson <span class="hljs-keyword">import</span> Path

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cache</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, json_client</span>):</span>
        self.json_client = json_client

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_chat_history</span>(<span class="hljs-params">self, token: str</span>):</span>
        data = self.json_client.jsonget(
            str(token), Path.rootPath())

        <span class="hljs-keyword">return</span> data
</code></pre>
<p>The cache is initialized with a rejson client, and the method <code>get_chat_history</code> takes in a token to get the chat history for that token, from Redis. Make sure you import the Path object from rejson.</p>
<p>Next, update the <code>worker.main.py</code> with the code below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> src.redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">import</span> asyncio
<span class="hljs-keyword">from</span> src.model.gptj <span class="hljs-keyword">import</span> GPT
<span class="hljs-keyword">from</span> src.redis.cache <span class="hljs-keyword">import</span> Cache

redis = Redis()

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    json_client = redis.create_rejson_connection()
    data = <span class="hljs-keyword">await</span> Cache(json_client).get_chat_history(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>)
    print(data)

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    asyncio.run(main())
</code></pre>
<p>I have hard-coded a sample token created from previous tests in Postman. If you don't have a token created, just send a new request to <code>/token</code> and copy the token, then run <code>python main.py</code> in the terminal. You should see the data in the terminal like so:</p>
<pre><code class="lang-bash">{<span class="hljs-string">'token'</span>: <span class="hljs-string">'18196e23-763b-4808-ae84-064348a0daff'</span>, <span class="hljs-string">'messages'</span>: [], <span class="hljs-string">'name'</span>: <span class="hljs-string">'Stephen'</span>, <span class="hljs-string">'session_start'</span>: <span class="hljs-string">'2022-07-16 13:20:01.092109'</span>}
</code></pre>
<p>Next, we need to add an <code>add_message_to_cache</code> method to our <code>Cache</code> class that adds messages to Redis for a specific token.</p>
<pre><code class="lang-py">
  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_message_to_cache</span>(<span class="hljs-params">self, token: str, message_data: dict</span>):</span>
      self.json_client.jsonarrappend(
          str(token), Path(<span class="hljs-string">'.messages'</span>), message_data)
</code></pre>
<p>The <code>jsonarrappend</code> method provided by rejson appends the new message to the message array. </p>
<p>Note that to access the message array, we need to provide <code>.messages</code> as an argument to the Path. If your message data has a different/nested structure, just provide the path to the array you want to append the new data to.</p>
<p>To test this method, update the main function in the main.py file with the code below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    json_client = redis.create_rejson_connection()

    <span class="hljs-keyword">await</span> Cache(json_client).add_message_to_cache(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>, message_data={
        <span class="hljs-string">"id"</span>: <span class="hljs-string">"1"</span>,
        <span class="hljs-string">"msg"</span>: <span class="hljs-string">"Hello"</span>,
        <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"2022-07-16 13:20:01.092109"</span>
    })

    data = <span class="hljs-keyword">await</span> Cache(json_client).get_chat_history(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>)
    print(data)
</code></pre>
<p>We are sending a hard-coded message to the cache, and getting the chat history from the cache. When you run <code>python main.py</code> in the terminal within the worker directory, you should get something like this printed in the terminal, with the message added to the message array.</p>
<pre><code class="lang-bash">{<span class="hljs-string">'token'</span>: <span class="hljs-string">'18196e23-763b-4808-ae84-064348a0daff'</span>, <span class="hljs-string">'messages'</span>: [{<span class="hljs-string">'id'</span>: <span class="hljs-string">'1'</span>, <span class="hljs-string">'msg'</span>: <span class="hljs-string">'Hello'</span>, <span class="hljs-string">'timestamp'</span>: <span class="hljs-string">'2022-07-16 13:20:01.092109'</span>}], <span class="hljs-string">'name'</span>: <span class="hljs-string">'Stephen'</span>, <span class="hljs-string">'session_start'</span>: <span class="hljs-string">'2022-07-16 13:20:01.092109'</span>}
</code></pre>
<p>Finally, we need to update the main function to send the message data to the GPT model, and update the input with the <strong>last 4</strong> messages sent between the client and the model. </p>
<p>First let's update our <code>add_message_to_cache</code> function with a new argument "source" that will tell us if the message is a human or bot. We can then use this arg to add the "Human:" or "Bot:" tags to the data before storing it in the cache.</p>
<p>Update the <code>add_message_to_cache</code> method in the Cache class like so:</p>
<pre><code class="lang-py">  <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_message_to_cache</span>(<span class="hljs-params">self, token: str, source: str, message_data: dict</span>):</span>
      <span class="hljs-keyword">if</span> source == <span class="hljs-string">"human"</span>:
          message_data[<span class="hljs-string">'msg'</span>] = <span class="hljs-string">"Human: "</span> + (message_data[<span class="hljs-string">'msg'</span>])
      <span class="hljs-keyword">elif</span> source == <span class="hljs-string">"bot"</span>:
          message_data[<span class="hljs-string">'msg'</span>] = <span class="hljs-string">"Bot: "</span> + (message_data[<span class="hljs-string">'msg'</span>])

      self.json_client.jsonarrappend(
          str(token), Path(<span class="hljs-string">'.messages'</span>), message_data)
</code></pre>
<p>Then update the main function in main.py in the worker directory, and run <code>python main.py</code> to see the new results in the Redis database.</p>
<pre><code class="lang-py"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    json_client = redis.create_rejson_connection()

    <span class="hljs-keyword">await</span> Cache(json_client).add_message_to_cache(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>, source=<span class="hljs-string">"human"</span>, message_data={
        <span class="hljs-string">"id"</span>: <span class="hljs-string">"1"</span>,
        <span class="hljs-string">"msg"</span>: <span class="hljs-string">"Hello"</span>,
        <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"2022-07-16 13:20:01.092109"</span>
    })

    data = <span class="hljs-keyword">await</span> Cache(json_client).get_chat_history(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>)
    print(data)
</code></pre>
<p>Next, we need to update the main function to add new messages to the cache, read the previous 4 messages from the cache, and then make an API call to the model using the query method. It'll have a payload consisting of a composite string of the last 4 messages.</p>
<p>You can always tune the number of messages in the history you want to extract, but I think 4 messages is a pretty good number for a demo.</p>
<p>In <code>worker.src</code>, create a new folder schema. Then create a new file named <code>chat.py</code> and paste our message schema in chat.py like so:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
<span class="hljs-keyword">from</span> pydantic <span class="hljs-keyword">import</span> BaseModel
<span class="hljs-keyword">from</span> typing <span class="hljs-keyword">import</span> List, Optional
<span class="hljs-keyword">import</span> uuid


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Message</span>(<span class="hljs-params">BaseModel</span>):</span>
    id = str(uuid.uuid4())
    msg: str
    timestamp = str(datetime.now())
</code></pre>
<p>Next, update the main.py file like below:</p>
<pre><code class="lang-py"><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>

    json_client = redis.create_rejson_connection()

    <span class="hljs-keyword">await</span> Cache(json_client).add_message_to_cache(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>, source=<span class="hljs-string">"human"</span>, message_data={
        <span class="hljs-string">"id"</span>: <span class="hljs-string">"3"</span>,
        <span class="hljs-string">"msg"</span>: <span class="hljs-string">"I would like to go to the moon to, would you take me?"</span>,
        <span class="hljs-string">"timestamp"</span>: <span class="hljs-string">"2022-07-16 13:20:01.092109"</span>
    })

    data = <span class="hljs-keyword">await</span> Cache(json_client).get_chat_history(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>)

    print(data)

    message_data = data[<span class="hljs-string">'messages'</span>][<span class="hljs-number">-4</span>:]

    input = [<span class="hljs-string">""</span> + i[<span class="hljs-string">'msg'</span>] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> message_data]
    input = <span class="hljs-string">" "</span>.join(input)

    res = GPT().query(input=input)

    msg = Message(
        msg=res
    )

    print(msg)
    <span class="hljs-keyword">await</span> Cache(json_client).add_message_to_cache(token=<span class="hljs-string">"18196e23-763b-4808-ae84-064348a0daff"</span>, source=<span class="hljs-string">"bot"</span>, message_data=msg.dict())
</code></pre>
<p>In the code above, we add new message data to the cache. This message will ultimately come from the message queue. Next we get the chat history from the cache, which will now include the most recent data we added. </p>
<p>Note that we are using the same hard-coded token to add to the cache and get from the cache, temporarily just to test this out. </p>
<p>Next, we trim off the cache data and extract only the last 4 items. Then we consolidate the input data by extracting the msg in a list and join it to an empty string. </p>
<p>Finally, we create a new Message instance for the bot response and add the response to the cache specifying the source as "bot"</p>
<p>Next, run <code>python main.py</code> a couple of times, changing the human message and id as desired with each run. You should have a full conversation input and output with the model. </p>
<p>Open Redis Insight and you should have something similar to the below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/conversation-chat.png" alt="Image" width="600" height="400" loading="lazy">
<em>Conversational Chat</em></p>
<h3 id="heading-stream-consumer-and-real-time-data-pull-from-the-message-queue">Stream Consumer and Real-time Data Pull from the Message Queue <a></a></h3>
<p>Next, we want to create a consumer and update our <code>worker.main.py</code> to connect to the message queue. We want it to pull the token data in real-time, as we are currently hard-coding the tokens and message inputs.</p>
<p>In <code>worker.src.redis</code> create a new file named <code>stream.py</code>. Add a <code>StreamConsumer</code> class with the code below:</p>
<pre><code class="lang-py"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StreamConsumer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, redis_client</span>):</span>
        self.redis_client = redis_client

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">consume_stream</span>(<span class="hljs-params">self, count: int, block: int,  stream_channel</span>):</span>

        response = <span class="hljs-keyword">await</span> self.redis_client.xread(
            streams={stream_channel:  <span class="hljs-string">'0-0'</span>}, count=count, block=block)

        <span class="hljs-keyword">return</span> response

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_message</span>(<span class="hljs-params">self, stream_channel, message_id</span>):</span>
        <span class="hljs-keyword">await</span> self.redis_client.xdel(stream_channel, message_id)
</code></pre>
<p>The <code>StreamConsumer</code> class is initialized with a Redis client. The <code>consume_stream</code> method pulls a new message from the queue from the message channel, using the <code>xread</code> method provided by aioredis.</p>
<p>Next, update the <code>worker.main.py</code> file with a while loop to keep the connection to the message channel alive, like so:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> src.redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">import</span> asyncio
<span class="hljs-keyword">from</span> src.model.gptj <span class="hljs-keyword">import</span> GPT
<span class="hljs-keyword">from</span> src.redis.cache <span class="hljs-keyword">import</span> Cache
<span class="hljs-keyword">from</span> src.redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">from</span> src.redis.stream <span class="hljs-keyword">import</span> StreamConsumer
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> src.schema.chat <span class="hljs-keyword">import</span> Message


redis = Redis()


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    json_client = redis.create_rejson_connection()
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    consumer = StreamConsumer(redis_client)
    cache = Cache(json_client)

    print(<span class="hljs-string">"Stream consumer started"</span>)
    print(<span class="hljs-string">"Stream waiting for new messages"</span>)

    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        response = <span class="hljs-keyword">await</span> consumer.consume_stream(stream_channel=<span class="hljs-string">"message_channel"</span>, count=<span class="hljs-number">1</span>, block=<span class="hljs-number">0</span>)

        <span class="hljs-keyword">if</span> response:
            <span class="hljs-keyword">for</span> stream, messages <span class="hljs-keyword">in</span> response:
                <span class="hljs-comment"># Get message from stream, and extract token, message data and message id</span>
                <span class="hljs-keyword">for</span> message <span class="hljs-keyword">in</span> messages:
                    message_id = message[<span class="hljs-number">0</span>]
                    token = [k.decode(<span class="hljs-string">'utf-8'</span>)
                             <span class="hljs-keyword">for</span> k, v <span class="hljs-keyword">in</span> message[<span class="hljs-number">1</span>].items()][<span class="hljs-number">0</span>]
                    message = [v.decode(<span class="hljs-string">'utf-8'</span>)
                               <span class="hljs-keyword">for</span> k, v <span class="hljs-keyword">in</span> message[<span class="hljs-number">1</span>].items()][<span class="hljs-number">0</span>]
                    print(token)

                    <span class="hljs-comment"># Create a new message instance and add to cache, specifying the source as human</span>
                    msg = Message(msg=message)

                    <span class="hljs-keyword">await</span> cache.add_message_to_cache(token=token, source=<span class="hljs-string">"human"</span>, message_data=msg.dict())

                    <span class="hljs-comment"># Get chat history from cache</span>
                    data = <span class="hljs-keyword">await</span> cache.get_chat_history(token=token)

                    <span class="hljs-comment"># Clean message input and send to query</span>
                    message_data = data[<span class="hljs-string">'messages'</span>][<span class="hljs-number">-4</span>:]

                    input = [<span class="hljs-string">""</span> + i[<span class="hljs-string">'msg'</span>] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> message_data]
                    input = <span class="hljs-string">" "</span>.join(input)

                    res = GPT().query(input=input)

                    msg = Message(
                        msg=res
                    )

                    print(msg)

                    <span class="hljs-keyword">await</span> cache.add_message_to_cache(token=token, source=<span class="hljs-string">"bot"</span>, message_data=msg.dict())

                <span class="hljs-comment"># Delete messaage from queue after it has been processed</span>

                <span class="hljs-keyword">await</span> consumer.delete_message(stream_channel=<span class="hljs-string">"message_channel"</span>, message_id=message_id)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    asyncio.run(main())
</code></pre>
<p>This is quite the update, so let's take it step by step:</p>
<p>We use a <code>while True</code> loop so that the worker can be online listening to messages from the queue. </p>
<p>Next, we await new messages from the message_channel by calling our <code>consume_stream</code> method. If we have a message in the queue, we extract the message_id, token, and message. Then we create a new instance of the Message class, add the message to the cache, and then get the last 4 messages. We set it as input to the GPT model <code>query</code> method. </p>
<p>Once we get a response, we then add the response to the cache using the <code>add_message_to_cache</code> method, then delete the message from the queue.</p>
<h3 id="heading-how-to-update-the-chat-client-with-the-ai-response">How to Update the Chat Client with the AI Response <a></a></h3>
<p>So far, we are sending a chat message from the client to the message_channel (which is received by the worker that queries the AI model) to get a response. </p>
<p>Next, we need to send this response to the client. As long as the socket connection is still open, the client should be able to receive the response. </p>
<p>If the connection is closed, the client can always get a response from the chat history using the <code>refresh_token</code> endpoint.</p>
<p>In <code>worker.src.redis</code> create a new file named <code>producer.py</code>, and add a <code>Producer</code> class similar to what we had on the chat web server:</p>
<pre><code class="lang-py">
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Producer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, redis_client</span>):</span>
        self.redis_client = redis_client

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_to_stream</span>(<span class="hljs-params">self,  data: dict, stream_channel</span>) -&gt; bool:</span>
        msg_id = <span class="hljs-keyword">await</span> self.redis_client.xadd(name=stream_channel, id=<span class="hljs-string">"*"</span>, fields=data)
        print(<span class="hljs-string">f"Message id <span class="hljs-subst">{msg_id}</span> added to <span class="hljs-subst">{stream_channel}</span> stream"</span>)
        <span class="hljs-keyword">return</span> msg_id
</code></pre>
<p>Next, in the <code>main.py</code> file, update the main function to initialize the producer, create a stream data, and send the response to a <code>response_channel</code> using the <code>add_to_stream</code> method:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> src.redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">import</span> asyncio
<span class="hljs-keyword">from</span> src.model.gptj <span class="hljs-keyword">import</span> GPT
<span class="hljs-keyword">from</span> src.redis.cache <span class="hljs-keyword">import</span> Cache
<span class="hljs-keyword">from</span> src.redis.config <span class="hljs-keyword">import</span> Redis
<span class="hljs-keyword">from</span> src.redis.stream <span class="hljs-keyword">import</span> StreamConsumer
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> src.schema.chat <span class="hljs-keyword">import</span> Message
<span class="hljs-keyword">from</span> src.redis.producer <span class="hljs-keyword">import</span> Producer


redis = Redis()


<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    json_client = redis.create_rejson_connection()
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    consumer = StreamConsumer(redis_client)
    cache = Cache(json_client)
    producer = Producer(redis_client)

    print(<span class="hljs-string">"Stream consumer started"</span>)
    print(<span class="hljs-string">"Stream waiting for new messages"</span>)

    <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
        response = <span class="hljs-keyword">await</span> consumer.consume_stream(stream_channel=<span class="hljs-string">"message_channel"</span>, count=<span class="hljs-number">1</span>, block=<span class="hljs-number">0</span>)

        <span class="hljs-keyword">if</span> response:
            <span class="hljs-keyword">for</span> stream, messages <span class="hljs-keyword">in</span> response:
                <span class="hljs-comment"># Get message from stream, and extract token, message data and message id</span>
                <span class="hljs-keyword">for</span> message <span class="hljs-keyword">in</span> messages:
                    message_id = message[<span class="hljs-number">0</span>]
                    token = [k.decode(<span class="hljs-string">'utf-8'</span>)
                             <span class="hljs-keyword">for</span> k, v <span class="hljs-keyword">in</span> message[<span class="hljs-number">1</span>].items()][<span class="hljs-number">0</span>]
                    message = [v.decode(<span class="hljs-string">'utf-8'</span>)
                               <span class="hljs-keyword">for</span> k, v <span class="hljs-keyword">in</span> message[<span class="hljs-number">1</span>].items()][<span class="hljs-number">0</span>]

                    <span class="hljs-comment"># Create a new message instance and add to cache, specifying the source as human</span>
                    msg = Message(msg=message)

                    <span class="hljs-keyword">await</span> cache.add_message_to_cache(token=token, source=<span class="hljs-string">"human"</span>, message_data=msg.dict())

                    <span class="hljs-comment"># Get chat history from cache</span>
                    data = <span class="hljs-keyword">await</span> cache.get_chat_history(token=token)

                    <span class="hljs-comment"># Clean message input and send to query</span>
                    message_data = data[<span class="hljs-string">'messages'</span>][<span class="hljs-number">-4</span>:]

                    input = [<span class="hljs-string">""</span> + i[<span class="hljs-string">'msg'</span>] <span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> message_data]
                    input = <span class="hljs-string">" "</span>.join(input)

                    res = GPT().query(input=input)

                    msg = Message(
                        msg=res
                    )

                    stream_data = {}
                    stream_data[str(token)] = str(msg.dict())

                    <span class="hljs-keyword">await</span> producer.add_to_stream(stream_data, <span class="hljs-string">"response_channel"</span>)

                    <span class="hljs-keyword">await</span> cache.add_message_to_cache(token=token, source=<span class="hljs-string">"bot"</span>, message_data=msg.dict())

                <span class="hljs-comment"># Delete messaage from queue after it has been processed</span>
                <span class="hljs-keyword">await</span> consumer.delete_message(stream_channel=<span class="hljs-string">"message_channel"</span>, message_id=message_id)


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    asyncio.run(main())
</code></pre>
<p>Next, we need to let the client know when we receive responses from the worker in the <code>/chat</code> socket endpoint. We do this by listening to the response stream. We do not need to include a while loop here as the socket will be listening as long as the connection is open.</p>
<p>Note that we also need to check which client the response is for by adding logic to check if the token connected is equal to the token in the response. Then we delete the message in the response queue once it's been read.</p>
<p>In <code>server.src.redis</code> create a new file named stream.py and add our <code>StreamConsumer</code> class like this:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> .config <span class="hljs-keyword">import</span> Redis

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">StreamConsumer</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, redis_client</span>):</span>
        self.redis_client = redis_client

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">consume_stream</span>(<span class="hljs-params">self, count: int, block: int,  stream_channel</span>):</span>
        response = <span class="hljs-keyword">await</span> self.redis_client.xread(
            streams={stream_channel:  <span class="hljs-string">'0-0'</span>}, count=count, block=block)

        <span class="hljs-keyword">return</span> response

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">delete_message</span>(<span class="hljs-params">self, stream_channel, message_id</span>):</span>
        <span class="hljs-keyword">await</span> self.redis_client.xdel(stream_channel, message_id)
</code></pre>
<p>Next, update the <code>/chat</code> socket endpoint like so:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> ..redis.stream <span class="hljs-keyword">import</span> StreamConsumer

<span class="hljs-meta">@chat.websocket("/chat")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">websocket_endpoint</span>(<span class="hljs-params">websocket: WebSocket, token: str = Depends(<span class="hljs-params">get_token</span>)</span>):</span>
    <span class="hljs-keyword">await</span> manager.connect(websocket)
    redis_client = <span class="hljs-keyword">await</span> redis.create_connection()
    producer = Producer(redis_client)
    json_client = redis.create_rejson_connection()
    consumer = StreamConsumer(redis_client)

    <span class="hljs-keyword">try</span>:
        <span class="hljs-keyword">while</span> <span class="hljs-literal">True</span>:
            data = <span class="hljs-keyword">await</span> websocket.receive_text()
            stream_data = {}
            stream_data[str(token)] = str(data)
            <span class="hljs-keyword">await</span> producer.add_to_stream(stream_data, <span class="hljs-string">"message_channel"</span>)
            response = <span class="hljs-keyword">await</span> consumer.consume_stream(stream_channel=<span class="hljs-string">"response_channel"</span>, block=<span class="hljs-number">0</span>)

            print(response)
            <span class="hljs-keyword">for</span> stream, messages <span class="hljs-keyword">in</span> response:
                <span class="hljs-keyword">for</span> message <span class="hljs-keyword">in</span> messages:
                    response_token = [k.decode(<span class="hljs-string">'utf-8'</span>)
                                      <span class="hljs-keyword">for</span> k, v <span class="hljs-keyword">in</span> message[<span class="hljs-number">1</span>].items()][<span class="hljs-number">0</span>]

                    <span class="hljs-keyword">if</span> token == response_token:
                        response_message = [v.decode(<span class="hljs-string">'utf-8'</span>)
                                            <span class="hljs-keyword">for</span> k, v <span class="hljs-keyword">in</span> message[<span class="hljs-number">1</span>].items()][<span class="hljs-number">0</span>]

                        print(message[<span class="hljs-number">0</span>].decode(<span class="hljs-string">'utf-8'</span>))
                        print(token)
                        print(response_token)

                        <span class="hljs-keyword">await</span> manager.send_personal_message(response_message, websocket)

                    <span class="hljs-keyword">await</span> consumer.delete_message(stream_channel=<span class="hljs-string">"response_channel"</span>, message_id=message[<span class="hljs-number">0</span>].decode(<span class="hljs-string">'utf-8'</span>))

    <span class="hljs-keyword">except</span> WebSocketDisconnect:
        manager.disconnect(websocket)
</code></pre>
<h3 id="heading-refresh-token">Refresh Token <a></a></h3>
<p>Finally, we need to update the <code>/refresh_token</code> endpoint to get the chat history from the Redis database using our <code>Cache</code> class. </p>
<p>In <code>server.src.redis</code>, add a <code>cache.py</code> file and add the code below:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> rejson <span class="hljs-keyword">import</span> Path

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Cache</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, json_client</span>):</span>
        self.json_client = json_client

    <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_chat_history</span>(<span class="hljs-params">self, token: str</span>):</span>
        data = self.json_client.jsonget(
            str(token), Path.rootPath())

        <span class="hljs-keyword">return</span> data
</code></pre>
<p>Next, in <code>server.src.routes.chat.py</code> import the <code>Cache</code> class and update the <code>/token</code> endpoint to the below:</p>
<pre><code class="lang-py">
<span class="hljs-keyword">from</span> ..redis.cache <span class="hljs-keyword">import</span> Cache

<span class="hljs-meta">@chat.get("/refresh_token")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">refresh_token</span>(<span class="hljs-params">request: Request, token: str</span>):</span>
    json_client = redis.create_rejson_connection()
    cache = Cache(json_client)
    data = <span class="hljs-keyword">await</span> cache.get_chat_history(token)

    <span class="hljs-keyword">if</span> data == <span class="hljs-literal">None</span>:
        <span class="hljs-keyword">raise</span> HTTPException(
            status_code=<span class="hljs-number">400</span>, detail=<span class="hljs-string">"Session expired or does not exist"</span>)
    <span class="hljs-keyword">else</span>:
        <span class="hljs-keyword">return</span> data
</code></pre>
<p>Now, when we send a GET request to the <code>/refresh_token</code> endpoint with any token, the endpoint will fetch the data from the Redis database. </p>
<p>If the token has not timed out, the data will be sent to the user. Or it'll send a 400 response if the token is not found.</p>
<h3 id="heading-how-to-test-the-chat-with-multiple-clients-in-postman">How to Test the Chat with multiple Clients in Postman <a></a></h3>
<p>Finally, we will test the chat system by creating multiple chat sessions in Postman, connecting multiple clients in Postman, and chatting with the bot on the clients. </p>
<p>Lastly, we will try to get the chat history for the clients and hopefully get a proper response.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2022/07/chat-static.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-recap">Recap <a></a></h2>
<p>Let's have a quick recap as to what we have achieved with our chat system. The chat client creates a token for each chat session with a client. This token is used to identify each client, and each message sent by clients connected to or web server is queued in a Redis channel (message_chanel), identified by the token.</p>
<p>Our worker environment reads from this channel. It does not have any clue who the client is (except that it's a unique token) and uses the message in the queue to send requests to the Huggingface inference API.</p>
<p>When it gets a response, the response is added to a response channel and the chat history is updated. The client listening to the response_channel immediately sends the response to the client once it receives a response with its token.</p>
<p>If the socket is still open, this response is sent. If the socket is closed, we are certain that the response is preserved because the response is added to the chat history. The client can get the history, even if a page refresh happens or in the event of a lost connection.</p>
<p>Congratulations on getting this far! You have been able to build a working chat system. </p>
<p>In follow-up articles, I will focus on building a chat user interface for the client, creating unit and functional tests, fine-tuning our worker environment for faster response time with WebSockets and asynchronous requests, and ultimately deploying the chat application on AWS.</p>
<p>This Article is part of a series on building full-stack intelligent chatbots with tools like Python, React, Huggingface, Redis, and so on. You can follow the full series on my blog: <a target="_blank" href="https://blog.stephensanwo.dev/series/build-ai-chatbot">blog.stephensanwo.dev - AI ChatBot Series</a>**</p>
<p><strong>You can download the full repository on <a target="_blank" href="https://github.com/stephensanwo/fullstack-ai-chatbot">My Github Repository</a></strong></p>
<p>I wrote this tutorial in collaboration with Redis. Need help getting started with Redis? Try the following resources:</p>
<ul>
<li><a target="_blank" href="https://redis.info/3NBGJRT">Try Redis Cloud free of charge</a></li>
<li><a target="_blank" href="https://redis.info/3Ga9YII">Watch this video on the benefits of Redis Cloud over other Redis providers</a></li>
<li><a target="_blank" href="https://redis.info/3LC4GqB">Redis Developer Hub - tools, guides, and tutorials about Redis</a></li>
<li><a target="_blank" href="https://redis.info/3wMR7PR">RedisInsight Desktop GUI</a></li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Discord AI Chatbot that Talks Like Your Favorite Character ]]>
                </title>
                <description>
                    <![CDATA[ Would you like to talk to a chatbot that speaks like your favorite character, fictional or non-fictional? Let's build one! In case you've seen my previous tutorial on this topic, stick with me as this version features lots of updates. You can follow ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/discord-ai-chatbot/</link>
                <guid isPermaLink="false">66d460199f2bec37e2da0645</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Lynn Zheng ]]>
                </dc:creator>
                <pubDate>Thu, 26 Aug 2021 19:30:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/08/lynns-thumbnail.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Would you like to talk to a chatbot that speaks like your favorite character, fictional or non-fictional? Let's build one!</p>
<p>In case you've seen my previous tutorial on this topic, stick with me as this version features lots of updates.</p>
<p>You can follow along with this tutorial using the code on my GitHub:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://github.com/RuolinZheng08/twewy-discord-chatbot">https://github.com/RuolinZheng08/twewy-discord-chatbot</a></div>
<p> </p>
<p>If you want, you can dive right into my video tutorial on YouTube – or read on for more details. 😎</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Rk8eM1p_xgM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-what-to-expect-from-this-tutorial">What to Expect from this Tutorial</h2>
<p>Here is an example of the Discord AI chatbot that we will have built by the end of this tutorial.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/discord.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Chat demo with my bot in Discord. I drew the bot's icon 😊</em></p>
<p>My chatbot project started as a joke with a friend when we were playing video games.</p>
<p>I'm honestly surprised by how popular it became – there were 5.9k views of my previous tutorial, plus, when I deployed my bot to a 1k+ user server, people flooded it with 300+ messages in an hour, effectively crashing the bot. 😳 You can <a target="_blank" href="https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/">read more about my deployment post-mortem in this post.</a></p>
<p>Since a lot of people are interested in building their own bots based on their favorite characters, I updated my tutorial to include an in-depth explanation on how to gather text data for any character, fictional or non-fictional.</p>
<p>You may also create a custom dataset that captures the speech between you and your friends and build a chatbot that speaks like yourself!</p>
<p>Other updates in this tutorial address changes in Hugging Face's model hosting services, including API changes that affect how we push the model to Hugging Face's model repositories.</p>
<h2 id="heading-outline-of-this-tutorial">Outline of this Tutorial</h2>
<p>The video version of this tutorial runs for a total of one hour and features the following topics:</p>
<ol>
<li><p>Gather text data for your character using one of these two methods: find pre-made datasets on <strong>Kaggle</strong> or make custom datasets from raw transcripts.</p>
</li>
<li><p>Train the model in <strong>Google Colab,</strong> a cloud-based Jupyter Notebook environment with free GPUs.</p>
</li>
<li><p>Deploy the model to <strong>Hugging Face,</strong> an AI model hosting service.</p>
</li>
<li><p>Build a Discord bot in either <strong>Python</strong> or <strong>JavaScript</strong>, your choice! 🤩</p>
</li>
<li><p>Set up the Discord bot's permissions so they don't spam non-bot channels</p>
</li>
<li><p>Host the bot on <strong>Repl.it.</strong></p>
</li>
<li><p>Keep the bot running indefinitely with <strong>Uptime Robot.</strong></p>
</li>
</ol>
<p>To learn more about how to build Discord bots, you may also find these two freeCodeCamp posts useful – there's a <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">Python version</a> and a <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/">JavaScript version</a>.</p>
<h2 id="heading-how-to-prepare-the-data">How to Prepare the Data</h2>
<p>For our chatbot to learn to converse, we need text data in the form of dialogues. This is essentially how our chatbot is going to respond to different exchanges and contexts.</p>
<h3 id="heading-is-your-favorite-character-on-kaggle">Is Your Favorite Character on Kaggle?</h3>
<p>There are a lot of interesting datasets on Kaggle for popular cartoons, TV shows, and other media. For example:</p>
<ul>
<li><p><a target="_blank" href="https://www.kaggle.com/andradaolteanu/rickmorty-scripts">Rick and Morty</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/gulsahdemiryurek/harry-potter-dataset?select=Harry+Potter+1.csv">Harry Potter</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/mitramir5/the-big-bang-theory-series-transcript">The Big Bang Theory</a></p>
</li>
<li><p><a target="_blank" href="https://www.kaggle.com/anderfj/game-of-thrones-series-scripts-breakdowns">Game of Thrones</a></p>
</li>
</ul>
<p>We only need two columns from these datasets: <strong>character name</strong> and <strong>dialogue line</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.07.59.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example dataset: Harry Potter movie transcript</em></p>
<h3 id="heading-cant-find-your-favorite-character-on-kaggle">Can't Find Your Favorite Character on Kaggle?</h3>
<p>Can't find your favorite character on Kaggle? No worries. We can create datasets from raw transcripts. A great place to look for transcripts is <a target="_blank" href="https://transcripts.fandom.com/wiki/Transcripts_Wiki">Transcript Wiki</a>. For example, check out <a target="_blank" href="https://transcripts.fandom.com/wiki/Peppa_Pig">this Peppa Pig transcript.</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.13.57.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><em>Example: Peppa Pig transcript</em></p>
<p>Using a regular expression like <code>([a-zA-Z|\s]+): (.+)</code>, we can extract out the two columns of interest, character name, and dialogue line.</p>
<p><a target="_blank" href="https://pythex.org/?regex=\(%5Ba-zA-Z%7C%5Cs%5D%2B\)%3A%20\(.%2B\)&amp;test_string=Peppa%20Pig%3A%20George%2C%20I%20could%20see%20you%20too%20easily.%0A%0ANarrator%3A%20Now%20it%20is%20Peppa%27s%20turn%20to%20hide.%0A%0AGeorge%3A%20One...%20um...%20three.%0A%0AMummy%20Pig%3A%20I%27ll%20help%20George%20to%20count.%20&amp;ignorecase=0&amp;multiline=0&amp;dotall=0&amp;verbose=0">Try it out on this Python regex website yourself!</a></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-14.58.35.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-train-the-model">How to Train the Model</h2>
<p>Under the hood, our model will be a <strong>Generative Pre-trained Transfomer (GPT),</strong> the most popular language model these days.</p>
<p>Instead of training from scratch, we will load <a target="_blank" href="https://huggingface.co/microsoft/DialoGPT-small">Microsoft's pre-trained GPT</a>, <code>DialoGPT-small</code>, and fine-tune it using our dataset.</p>
<p>My GitHub repo for this tutorial contains <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/model_train_upload_workflow.ipynb">the notebook file</a> named <code>model_train_upload_workflow.ipynb</code> to get you started. All you need to do is the following: (please refer to the video for a detailed walkthrough)</p>
<ol>
<li><p>Upload the file to <a target="_blank" href="https://colab.research.google.com/">Google Colab</a></p>
</li>
<li><p>Select <strong>GPU</strong> as the runtime, which will speed up our model training.</p>
</li>
<li><p>Change the dataset and the target character in code snippets like:</p>
</li>
</ol>
<pre><code class="lang-python">data = pd.read_csv(<span class="hljs-string">'MY-DATASET.csv'</span>)
CHARACTER_NAME = <span class="hljs-string">'MY-CHARACTER'</span>
</code></pre>
<p>Running through the training section of the notebook should take less than half an hour. I have about 700 lines and the training takes less than ten minutes. The model will be stored in a folder named <code>output-small</code> .</p>
<p>Want an even smarter and more eloquent model? Feel free to train a larger model like <code>DialoGPT-medium</code> or even <code>DialoGPT-large</code>. Model size here refers to the number of parameters in the model. More parameters will allow the model to pick up more complexity from the dataset.</p>
<p>You may also increase the number of training epochs by searching for <code>num_train_epochs</code> in the notebook. This is the number of times that the model will cycle through the training dataset. The model will generally get smarter when it has more exposure to the dataset.</p>
<p>However, do take care not to overfit the model: If the model is trained for too many epochs, it may memorize the dataset and recite back lines from the dataset when we try to converse with it. This isn't ideal as we want the conversation to be more organic.</p>
<h2 id="heading-how-to-host-the-model">How to Host the Model</h2>
<p>We will host the model on Hugging Face, which provides a free API for us to query the model.</p>
<p>Sign up for <a target="_blank" href="https://huggingface.co/">Hugging Face</a> and create a new model repository by clicking on <strong>New model.</strong> Obtain your API token by going to <strong>Edit profile &gt; API Tokens.</strong> We will need this token when we build the Discord bot.</p>
<p>Follow along with this section in my video to push the model. Also, remember to tag it as <strong>conversational</strong> in its Model Card (equivalently its <code>README.md</code>):</p>
<pre><code class="lang-pgsql"><span class="hljs-comment">---</span>
tags:
- conversational
<span class="hljs-comment">---</span>

# My Awesome Model
</code></pre>
<p>You will know that everything works fine if you are able to chat with the model in the browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/huggingface3.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-build-the-discord-bot">How to Build the Discord Bot</h2>
<p>Go to the <a target="_blank" href="https://discord.com/developers/applications">Discord Developer's page</a>, create an application, and add a bot to it. Since our chatbot is only going to respond to user messages, checking <strong>Text Permissions &gt; Send Messgaes</strong> in the Bot Permissions Setting is sufficient. Copy the bot's API token for later use.</p>
<p>Sign up for <a target="_blank" href="https://repl.it/">Repl.it</a> and create a new Repl, <strong>Python</strong> or <strong>Node.js</strong> for JavaScript, whichever you are working with.</p>
<p>Let's store our API tokens for <strong>Hugging Face</strong> and <strong>Discord</strong> as environment variables, named <code>HUGGINGFACE_TOKEN</code> and <code>DISCORD_TOKEN</code> respectively. This helps keep them secret.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/repl.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Copy <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/discord_bot.py">my Python script</a> for a Python bot and <a target="_blank" href="https://github.com/RuolinZheng08/twewy-discord-chatbot/blob/main/discord_bot.js">my JS script</a> for a JS bot. Note that for the JS bot, because of a version incompatibility with Repl.it's Node and NPM, we will need to explicitly specify a lower version of the Discord API in <code>package.json</code>.</p>
<pre><code class="lang-pgsql">"dependencies": {
    "discord.js": "^12.5.3",
}
</code></pre>
<p>With that, our bot is ready to go! Start the Repl script by hitting <strong>Run</strong>, add the bot to a server, type something in the channel, and enjoy the bot's witty response.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/discord-1.gif" alt="Image" width="600" height="400" loading="lazy"></p>
<h2 id="heading-how-to-keep-the-bot-online">How to Keep the Bot Online</h2>
<p>One problem with our bot is that it halts as soon as we <strong>stop</strong> the running Repl (equivalently, if we close the Repl.it browser window).</p>
<p>To get around this and keep our bot running indefinitely, we will set up a web server to contain the bot script, and use a service like <a target="_blank" href="https://uptimerobot.com/">Uptime Robot</a> to pin our server every five minutes so that our server stays alive.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/08/Screen-Shot-2021-08-25-at-15.29.06.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>In my video tutorial, I copied the server code from these two freeCodeCamp posts (<a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-python/">Python version</a>, <a target="_blank" href="https://www.freecodecamp.org/news/create-a-discord-bot-with-javascript-nodejs/">JavaScript version</a>). Then, I set up the monitor on Uptime Robot. Now my bot continues to reply to my messages even if I close the browser (or shut down my computer all together).</p>
<p>Congratulations on reaching the end of this tutorial! I hope you enjoyed creating the bot and have fun chatting with your favorite character! 🥳</p>
<h2 id="heading-tutorial-video-link">Tutorial Video Link</h2>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/Rk8eM1p_xgM" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<h2 id="heading-more-about-me-and-my-chatbot-project">More About Me and My Chatbot Project</h2>
<p>I'm Lynn, a software engineer at Salesforce. I graduated from the University of Chicago in 2021 with a joint BS/MS in Computer Science, specializing in Machine Learning. <a target="_blank" href="https://ruolinzheng08.github.io/">Come say hi on my personal website!</a></p>
<p>I post fun project tutorials like this on my YouTube channel. Feel free to subscribe to catch up on my latest content. 😃</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/undefined" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Want to learn more about my bot? Check out this 15-minute real-time chat demo featuring me, my friend, and my bot!</p>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/-n6uWu8PZzo" style="aspect-ratio: 16 / 9; width: 100%; height: auto;" title="YouTube video player" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="" loading="lazy"></iframe></div>
<p> </p>
<p>Interested in the model I trained? Check it out on Hugging Face:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://huggingface.co/r3dhummingbird/DialoGPT-medium-joshua">https://huggingface.co/r3dhummingbird/DialoGPT-medium-joshua</a></div>
<p> </p>
<p>My chatbot was so popular on a 1k+ user server that... it crashed. 🤯 Read about my deployment post-mortem in this post:</p>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/">https://www.freecodecamp.org/news/recovering-from-deployment-hell-what-i-learned-from-deploying-my-discord-bot-to-a-1000-user-server/</a></div>
<p> </p>
<p>Thanks for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Use TypeScript and MongoDB to Build a 100 Days of Code Discord Bot ]]>
                </title>
                <description>
                    <![CDATA[ The 100 Days of Code challenge is very popular among new coders and developers looking to level up their skills. It's so popular that our Discord server has an entire channel dedicated to it.  By popular demand, we recently built a Discord bot that h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-100-days-of-code-bot-for-discord-using-typescript-and-mongodb/</link>
                <guid isPermaLink="false">66ac7f0d297ff4b6f39a55f1</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Tue, 22 Jun 2021 16:20:36 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2021/06/news-header.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The <a target="_blank" href="https://www.freecodecamp.org/news/the-crazy-history-of-the-100daysofcode-challenge-and-why-you-should-try-it-for-2018-6c89a76e298d/">100 Days of Code challenge</a> is very popular among new coders and developers looking to level up their skills. It's so popular that our <a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-discord-chat-room-server/">Discord server</a> has an entire channel dedicated to it. </p>
<p>By popular demand, we recently built a Discord bot that helps people track their progress in the challenge.</p>
<p>Today I am going to show you how to build your own 100 Days of Code bot.</p>
<h2 id="heading-how-to-create-a-discord-bot-application">How to Create a Discord Bot Application</h2>
<p>Your first step is to set up a Discord bot application. Head over to the <a target="_blank" href="https://discord.com/developers">Discord Developer Portal</a>, sign in if needed, and select "Applications" from the sidebar.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-158.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the Developer Portal. If this is your first bot, you will not have any applications here.</em></p>
<p>Click the "New Application" button. Give it a name, and set it as a "Personal" application. You will now be taken to the application's settings. Here you can change the name, or give it an avatar.</p>
<p>Select "Bot" from the side bar, then click the "Add Bot" button. This will create a Discord Bot account for your application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-99.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the Bot settings page. If you did not set an avatar, you will see a default based on your bot's name.</em></p>
<p>This is the screen where you will get the bot token. It is <em>very</em> important to keep this token secret, as the token allows your code to connect to your bot. Keep it safe and do not share it with anyone.</p>
<p>Now you need to add the bot to a server to interact with it. Click the "OAuth2" option on the side bar. You should see a form under the "OAuth2 URL Generator" section. Leave the "Select Redirect URL" dropdown blank, and check the box for the "bot" scope.</p>
<p>An option to select permissions will appear. Check the boxes for the following permissions:</p>
<ul>
<li>Send Messages</li>
<li>Manage Messages</li>
<li>Embed Links</li>
<li>Read Message History</li>
<li>View Channels</li>
</ul>
<p>Above that section, you should see a URL generated. Click the "Copy" button to copy it, then paste it into your browser and go. </p>
<p>This will take you through Discord's process to add your new bot to a server. Note that you must have the Manage Server permission in the server you want to add the bot to. If you do not have this permission in any servers, you can create a server to test your bot in.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2021/06/image-156.png" alt="Image" width="600" height="400" loading="lazy">
<em>Screenshot of the OAuth screen with the correct settings marked.</em></p>
<p>Now you are ready to write some code!</p>
<h2 id="heading-how-to-set-up-your-project">How to Set Up Your Project</h2>
<p>You need to set up the infrastructure and tooling for your project.</p>
<h3 id="heading-prepare-the-packagejson">Prepare the <code>package.json</code></h3>
<p>Create a directory, or folder, for your project. Open your terminal pointing to that new folder. Run the command <code>npm init</code> to set up your <code>package.json</code> file. For this tutorial, the default values are sufficient, but feel free to edit them as you wish.</p>
<p>You should end up with a <code>package.json</code> similar to this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"tutorial"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"test"</span>: <span class="hljs-string">"echo \"Error: no test specified\" &amp;&amp; exit 1"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>
}
</code></pre>
<p>Now you need to make a couple of changes to get ready for the TypeScript implementation.</p>
<p>First, replace the <code>main</code> value of <code>index.js</code> with <code>./prod/index.js</code> – you will be setting your TypeScript to compile to a <code>prod</code> directory.</p>
<p>Then, remove the <code>test</code> script and add the following two scripts:</p>
<pre><code class="lang-json"><span class="hljs-string">"build"</span>: <span class="hljs-string">"tsc"</span>,
<span class="hljs-string">"start"</span>: <span class="hljs-string">"node -r dotenv/config ./prod/index.js"</span>
</code></pre>
<p>The <code>build</code> script will compile your TypeScript into JavaScript so <code>node</code> can run it, and the <code>start</code> script will run the <code>index.js</code> entrypoint file.</p>
<p>Adding the <code>-r dotenv/config</code> here will dynamically import and run the <code>config</code> method in the <code>dotenv</code> package, which loads your environment variables from the <code>.env</code> file.</p>
<p>Speaking of packages, your next step is to install dependencies. Using <code>npm install</code>, install these dependencies:</p>
<ul>
<li><code>discord.js</code> – this is the library that will handle connecting to the gateway and managing the Discord API calls.</li>
<li><code>dotenv</code> – a package that loads <code>.env</code> values into the node process.</li>
<li><code>mongoose</code> – A wrapper for the MongoDB connection which offers tools for structuring your data.</li>
</ul>
<p>Finally, install the development dependencies with <code>npm install --save-dev</code>. Development dependencies are packages that are required for working on your project in a development environment, but not required for running the codebase in production.</p>
<ul>
<li><code>typescript</code> – This is the package for the TypeScript language, which includes everything needed to write code in TypeScript and compile it into JavaScript.</li>
<li><code>@types/node</code> – TypeScript relies on type definitions to help understand the code you write. This package defines the types for the Node.js runtime environment, such as the <code>process.env</code> object.</li>
</ul>
<p>With these packages installed, you should now have a <code>package.json</code> similar to this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"tutorial"</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0.0"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"build"</span>: <span class="hljs-string">"tsc"</span>,
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"node -r dotenv/config ./prod/index.js"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"ISC"</span>,
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"discord.js"</span>: <span class="hljs-string">"^12.5.3"</span>,
    <span class="hljs-attr">"dotenv"</span>: <span class="hljs-string">"^10.0.0"</span>,
    <span class="hljs-attr">"mongoose"</span>: <span class="hljs-string">"^5.12.14"</span>
  },
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"@types/node"</span>: <span class="hljs-string">"^15.12.2"</span>,
    <span class="hljs-attr">"typescript"</span>: <span class="hljs-string">"^4.3.4"</span>
  }
}
</code></pre>
<h3 id="heading-prepare-typescript">Prepare TypeScript</h3>
<p>TypeScript's compiler offers a number of different settings to maximize your control over the resulting JavaScript. </p>
<p>You can typically modify compiler settings through a <code>tsconfig.json</code> file at the root of your project. You can generate the default boilerplate for this file with <code>npx tsc --init</code>, use an existing one if you set one up in another project, or even write one from scratch.</p>
<p>Because the compiler settings can significantly change the behaviour of TypeScript, it is best to use the same settings when following this tutorial. Here are settings you should use:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"compilerOptions"</span>: {
    <span class="hljs-attr">"target"</span>: <span class="hljs-string">"ES6"</span>,
    <span class="hljs-attr">"module"</span>: <span class="hljs-string">"CommonJS"</span>,
    <span class="hljs-attr">"rootDir"</span>: <span class="hljs-string">"./src"</span>,
    <span class="hljs-attr">"outDir"</span>: <span class="hljs-string">"./prod"</span>,
    <span class="hljs-attr">"strict"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"esModuleInterop"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"skipLibCheck"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"forceConsistentCasingInFileNames"</span>: <span class="hljs-literal">true</span>,
    <span class="hljs-attr">"resolveJsonModule"</span>: <span class="hljs-literal">true</span>
  }
}
</code></pre>
<p>The most important settings here are the <code>rootDir</code> and <code>outDir</code> settings. These tell the compiler that all of your code will be in the <code>src</code> directory, and the resulting JavaScript should go in the <code>prod</code> directory.</p>
<p>If you would like to test your settings, create a <code>src</code> directory and place an <code>index.ts</code> file inside. Write some code (such as a <code>console.log</code> statement) and run <code>npm run build</code> in your terminal. You should see a <code>prod</code> directory get created, with an <code>index.js</code> containing your compiled code.</p>
<h3 id="heading-additional-setup-notes">Additional Setup Notes</h3>
<p>If you are using <code>git</code> as a version control, you want to avoid pushing secrets and unnecessary code to your repository. Create a <code>.gitignore</code> file in your root project directory, and add the following content:</p>
<pre><code class="lang-txt">/node_modules/
/prod/
.env
</code></pre>
<p>The <code>.gitignore</code> file tells <code>git</code> not to track files/folders that match the patterns you enter. Ignoring the node modules folder keeps your repository from becoming bloated. </p>
<p>Pushing the compiled JavaScript is also unnecessary, as your project is typically compiled in production before runtime. <code>.env</code> files typically contain your secret values, such as API keys and tokens, so they should not be committed to a repository.</p>
<p>If you are using a UNIX based environment (such as Linux, or Git Bash on Windows), you can also add a <code>prebuild</code> script to your <code>package.json</code>. The <code>prebuild</code> script will automatically run before the <code>build</code> script when you use <code>npm run build</code>. I set mine to clean up the existing <code>prod</code> directory with <code>rm -r ./prod</code>.</p>
<h2 id="heading-how-to-create-the-discord-bot">How to Create the Discord Bot</h2>
<p>Your next step is to prepare the initial bot connection. If you did not do so earlier, create a <code>src</code> directory and an <code>index.ts</code> file within.</p>
<p>Start with an anonymous immediately-invoked function expression (IIFE) to allow for top-level <code>await</code> use:</p>
<pre><code class="lang-ts">(<span class="hljs-keyword">async</span> () =&gt; {

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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