<?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[ bots - 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[ bots - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Tue, 12 May 2026 15:10:25 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/tag/bots/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Create a Music Bot Using Discord.js – Step-by-Step Tutorial ]]>
                </title>
                <description>
                    <![CDATA[ By Gabriel Tanner The Discord API provides you with an easy tool to create and use your own bots and tools.  In this tutorial, you'll learn how you can create a basic music bot and add it to your server. The bot will be able to play, skip, and ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-create-a-music-bot-using-discord-js-4436f5f3f0f8/</link>
                <guid isPermaLink="false">66d45ee33a8352b6c5a2aa55</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 28 Feb 2024 13:00:00 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*rFQhPUqebJY9N4Ue" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Gabriel Tanner</p>
<p>The Discord API provides you with an easy tool to create and use your own bots and tools. </p>
<p>In this tutorial, you'll learn how you can create a basic music bot and add it to your server. The bot will be able to play, skip, and stop the music, and will also support queuing functionality.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-a-discord-bot">How to Set Up a Discord Bot</a><br>– <a class="post-section-overview" href="#heading-how-to-add-the-bot-to-your-server">How to add the bot to your server</a><br>– <a class="post-section-overview" href="#heading-how-to-create-your-project">How to create your project</a><br>– <a class="post-section-overview" href="#heading-discordjs-basics">Discord.js basics</a></li>
<li><a class="post-section-overview" href="#heading-discord-bot-version-013">Discord Bot Version 0.13</a><br>– <a class="post-section-overview" href="#heading-how-to-create-the-discord-player">How to create the Discord player</a><br>– <a class="post-section-overview" href="#heading-how-to-add-slash-commands">How to add slash commands</a><br>– <a class="post-section-overview" href="#id=&quot;how-to-implement-interactions&quot;">How to implement interactions</a><br>– <a class="post-section-overview" href="#heading-how-to-play-songs">How to play songs</a><br>– <a class="post-section-overview" href="#heading-how-to-skip-songs">How to skip songs</a><br>– <a class="post-section-overview" href="#heading-how-to-stop-songs">How to stop songs</a><br>– <a class="post-section-overview" href="#heading-complete-source-code-for-the-indexjs">Complete source code for index.js</a></li>
<li><a class="post-section-overview" href="#heading-discord-bot-version-012">Discord Bot Version 0.12</a><br>– <a class="post-section-overview" href="#heading-how-to-read-messages">How to read messages</a><br>– <a class="post-section-overview" href="#heading-how-to-add-songs">How to add songs</a><br>– <a class="post-section-overview" href="#heading-how-to-play-songs">How to play songs</a><br>– <a class="post-section-overview" href="#heading-how-to-skip-songs">How to skip songs</a><br>– <a class="post-section-overview" href="#heading-how-to-stop-songs">How to stop songs</a><br>– <a class="post-section-overview" href="#heading-complete-source-code-for-the-indexjs">Complete source code for index.js</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-prerequisites"><strong>Prerequisites</strong></h2>
<p>Before we get started creating the bot, make sure that you have installed all the tools you'll need:</p>
<ul>
<li><a target="_blank" href="https://nodejs.org/en/">Node</a></li>
<li><a target="_blank" href="https://www.npmjs.com/">NPM</a></li>
<li><a target="_blank" href="https://www.ffmpeg.org/">FFMPEG</a></li>
</ul>
<p>After you've installed these, you can continue by setting up your discord bot.</p>
<h2 id="heading-how-to-set-up-a-discord-bot"><strong>How to Set Up a Discord Bot</strong></h2>
<p>First, you need to create a new application on the discord development portal.</p>
<p>You can do so by visiting the <a target="_blank" href="https://discordapp.com/developers/applications/">portal</a> and clicking on New Application.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/Creating-application.webp.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Create a new Discord application</em></p>
<p>After that, you need to give your application a name and click the Create button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/create-bot.webp.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Give your bot whatever name you like - I've chosen "music-bot"</em></p>
<p>After that, select the bot tab and click on Add Bot.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/image-148.png" alt="Image" width="600" height="400" loading="lazy">
<em>Add your bot under the "Bot" tab</em></p>
<p>Now your bot is created and you can continue with inviting it to your server.</p>
<h3 id="heading-how-to-add-the-bot-to-your-server">How to add the bot to your server</h3>
<p>After creating your bot, you can invite it using the OAuth2 URL Generator.</p>
<p>For that, you need to navigate to the OAuth2 page and select bot in the scope tap.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/oauth-url-generator.png.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Selecting "bot" on the 0Auth2 Generator page</em></p>
<p>After that, you need to select the needed permissions to play music and read messages.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/bot-permissions.png.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Select the permissions you'll need - "read messages/view channels", "send messages", "manage messages", "add reactions", "use slash commands", "connect", and "speak.</em></p>
<p>Then you can copy your generated URL and paste it into your browser.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/bot-invite-url.png.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Copy the URL</em></p>
<p>After pasting it, add it to your server by selecting the server and clicking the authorize button.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/english-image.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-how-to-create-your-project">How to create your project</h3>
<p>Now you can start creating your project using the terminal.</p>
<p>First, create a directory and move into it. You can do so by using these two commands:</p>
<pre><code class="lang-bash">mkdir musicbot &amp;&amp; <span class="hljs-built_in">cd</span> musicbot
</code></pre>
<p>After that, create your project modules using the <code>npm init</code> command. After entering the command, you will be asked some questions – just answer them and continue.</p>
<p>Then you just need to create the two files you will work in.</p>
<pre><code>touch index.js &amp;&amp; touch config.json
</code></pre><p>Now, open your project in your text editor. I personally use VS Code and can open it with the following command:</p>
<pre><code class="lang-bash">code .
</code></pre>
<h3 id="heading-discordjs-basics">Discord.js basics</h3>
<p>Now you need to install some dependencies before we can get started.</p>
<pre><code>npm install discord.js@^<span class="hljs-number">12.5</span><span class="hljs-number">.3</span> ffmpeg fluent-ffmpeg @discordjs/opus ytdl-core --save
</code></pre><p>After the installation finishes, you can continue with writing your config.json file. Here, save the token of your bot and the prefix it should listen for.</p>
<pre><code class="lang-json">{
<span class="hljs-attr">"prefix"</span>: <span class="hljs-string">"!"</span>,
<span class="hljs-attr">"token"</span>: <span class="hljs-string">"your-token"</span>
}
</code></pre>
<p>To get your token, you need to visit the discord developer portal again and copy it from the bot section.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/get-token.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Get your bot token by clicking "Copy" and save it somewhere safe</em></p>
<p>Those are the only things you need to do in your <code>config.json</code> file. So now it's time to start writing your JavaScript code. </p>
<p>The article includes two versions: one for the new discord.js v13, which uses slash commands combined with the discord-player library to implement the music functionality, and one for discord.js v12.5.3, which implements the functionality without a library. </p>
<p>The older version is better for learning purposes, and the newer version works with the current discord.js and is a lot easier to implement – so choose which you prefer.</p>
<h2 id="heading-discord-bot-version-013"><strong>Discord Bot Version 0.13</strong></h2>
<p>Now you just need to install some more dependencies before we can get started.</p>
<pre><code>npm install discord.js discord-player @discordjs/opus
</code></pre><p>After installing the dependencies, import them in your dependencies.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { Client, GuildMember, Intents } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord.js"</span>);
<span class="hljs-keyword">const</span> { Player, QueryType } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord-player"</span>);
<span class="hljs-keyword">const</span> config = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./config.json"</span>);
</code></pre>
<p>After that, create your client and log in using your token.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
    <span class="hljs-attr">intents</span>: [Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILDS]
});
client.login(config.token);
</code></pre>
<p>Now add some basic listeners that console.log when they get executed.</p>
<pre><code>client.once(<span class="hljs-string">'ready'</span>, <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Ready!'</span>);
});

client.on(<span class="hljs-string">"error"</span>, <span class="hljs-built_in">console</span>.error);
client.on(<span class="hljs-string">"warn"</span>, <span class="hljs-built_in">console</span>.warn);
</code></pre><p>After that, you can start your bot using the <code>node</code> command and the bot should be online on Discord and print “Ready!” in the console.</p>
<pre><code class="lang-bash">node index.js
</code></pre>
<h3 id="heading-how-to-create-the-discord-player">How to create the Discord player</h3>
<p>Now that you've created the client for the discord bot, you can continue by initializing your player. This will allow you to play and manage music in your Discord channel.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> player = <span class="hljs-keyword">new</span> Player(client);
</code></pre>
<p>You can also add some error handlers that will be called if an error occurs.</p>
<pre><code class="lang-javascript">player.on(<span class="hljs-string">"error"</span>, <span class="hljs-function">(<span class="hljs-params">queue, error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[<span class="hljs-subst">${queue.guild.name}</span>] Error emitted from the queue: <span class="hljs-subst">${error.message}</span>`</span>);
});
player.on(<span class="hljs-string">"connectionError"</span>, <span class="hljs-function">(<span class="hljs-params">queue, error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[<span class="hljs-subst">${queue.guild.name}</span>] Error emitted from the connection: <span class="hljs-subst">${error.message}</span>`</span>);
});
</code></pre>
<p>The last thing you need to do is add listeners for the different player events like a song starting or being added.</p>
<pre><code class="lang-javascript">player.on(<span class="hljs-string">"trackStart"</span>, <span class="hljs-function">(<span class="hljs-params">queue, track</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">`🎶 | Started playing: **<span class="hljs-subst">${track.title}</span>** in **<span class="hljs-subst">${queue.connection.channel.name}</span>**!`</span>);
});

player.on(<span class="hljs-string">"trackAdd"</span>, <span class="hljs-function">(<span class="hljs-params">queue, track</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">`🎶 | Track **<span class="hljs-subst">${track.title}</span>** queued!`</span>);
});

player.on(<span class="hljs-string">"botDisconnect"</span>, <span class="hljs-function">(<span class="hljs-params">queue</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">"❌ | I was manually disconnected from the voice channel, clearing queue!"</span>);
});

player.on(<span class="hljs-string">"channelEmpty"</span>, <span class="hljs-function">(<span class="hljs-params">queue</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">"❌ | Nobody is in the voice channel, leaving..."</span>);
});

player.on(<span class="hljs-string">"queueEnd"</span>, <span class="hljs-function">(<span class="hljs-params">queue</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">"✅ | Queue finished!"</span>);
});
</code></pre>
<p>In most cases, you just send a message into the Discord text channel using the <code>send()</code> function.</p>
<h3 id="heading-how-to-add-slash-commands">How to add slash commands</h3>
<p>After you've set up the player successfully, you can continue by adding your Slash commands to your client. This step lets Discord know which commands the bot can execute.</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">"messageCreate"</span>, <span class="hljs-keyword">async</span> (message) =&gt; {
        <span class="hljs-keyword">if</span> (message.author.bot || !message.guild) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!client.application?.owner) <span class="hljs-keyword">await</span> client.application?.fetch();
});
</code></pre>
<p>You can do this by implementing a simple <code>!deploy</code> command that saves your commands in the <code>guild.commands</code> variable of a message. </p>
<p>A slash command has a name, a description, and an optional options field that contains the command’s parameters. For example, the play command takes a song query as an argument.</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">"messageCreate"</span>, <span class="hljs-keyword">async</span> (message) =&gt; {
        ...

        if (message.content === <span class="hljs-string">"!deploy"</span> &amp;&amp; message.author.id === client.application?.owner?.id) {
        <span class="hljs-keyword">await</span> message.guild.commands.set([
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"play"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"Plays a song from youtube"</span>,
                <span class="hljs-attr">options</span>: [
                    {
                        <span class="hljs-attr">name</span>: <span class="hljs-string">"query"</span>,
                        <span class="hljs-attr">type</span>: <span class="hljs-string">"STRING"</span>,
                        <span class="hljs-attr">description</span>: <span class="hljs-string">"The song you want to play"</span>,
                        <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
                    }
                ]
            },
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"skip"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"Skip to the current song"</span>
            },
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"queue"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"See the queue"</span>
            },
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"stop"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"Stop the player"</span>
            },
        ]);

        <span class="hljs-keyword">await</span> message.reply(<span class="hljs-string">"Deployed!"</span>);
    }
});
</code></pre>
<p>After entering <code>!deploy</code> in your Discord text chat, the slash commands will be added to your application. When typing <code>/</code> into the chat you should see something similar to this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/02/bot-slash-commands.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Example of using the slash commands</em></p>
<h3 id="heading-how-to-implement-interactions">How to implement interactions</h3>
<p>Once the interactions (slash commands) are defined, now you'll need to implement them. </p>
<p>All slash commands trigger the <code>interactionCreate</code> event and can be implemented inside the async function below. Before executing any functionality, run a few conditionals to check if the user is allowed to perform the given functionality.</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">"interactionCreate"</span>, <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">if</span> (!interaction.isCommand() || !interaction.guildId) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">if</span> (!(interaction.member <span class="hljs-keyword">instanceof</span> GuildMember) || !interaction.member.voice.channel) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.reply({ <span class="hljs-attr">content</span>: <span class="hljs-string">"You are not in a voice channel!"</span>, <span class="hljs-attr">ephemeral</span>: <span class="hljs-literal">true</span> });
    }

    <span class="hljs-keyword">if</span> (interaction.guild.me.voice.channelId &amp;&amp; interaction.member.voice.channelId !== interaction.guild.me.voice.channelId) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.reply({ <span class="hljs-attr">content</span>: <span class="hljs-string">"You are not in my voice channel!"</span>, <span class="hljs-attr">ephemeral</span>: <span class="hljs-literal">true</span> });
    }
});
</code></pre>
<p>After that, check which command is being executed by matching the <code>commandName</code> with the name of the commands you defined above.</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">"interactionCreate"</span>, <span class="hljs-keyword">async</span> (interaction) =&gt; {
    ...

        if (interaction.commandName === <span class="hljs-string">"play"</span>) {
            <span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Implement play command</span>
        }
});
</code></pre>
<p>You can then add the implementation inside of the <code>if</code> statement.</p>
<h3 id="heading-how-to-play-songs">How to play songs</h3>
<p>The play command requires you to search for the provided song and add the result to the current queue of songs. </p>
<p>Let’s start by retrieving the user-provided query using the <code>options.get()</code> function. After that you can use the <code>player.search()</code> function to search for the desired song.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"play"</span>) {
    <span class="hljs-keyword">await</span> interaction.deferReply();

    <span class="hljs-keyword">const</span> query = interaction.options.get(<span class="hljs-string">"query"</span>).value;
    <span class="hljs-keyword">const</span> searchResult = <span class="hljs-keyword">await</span> player
        .search(query, {
            <span class="hljs-attr">requestedBy</span>: interaction.user,
            <span class="hljs-attr">searchEngine</span>: QueryType.AUTO
        })
        .catch(<span class="hljs-function">() =&gt;</span> {});
    <span class="hljs-keyword">if</span> (!searchResult || !searchResult.tracks.length) <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"No results were found!"</span> });
}
</code></pre>
<p>Now that you have the song, you can create a queue for the songs (if there is already a queue, the <code>createQueue</code> function will return the existing one). </p>
<p>Once the queue is created, you can try joining the user’s voice channel. If that is successful, add the song to the current queue using the <code>addTracks</code> function.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"play"</span>) {
    ...

        const queue = <span class="hljs-keyword">await</span> player.createQueue(interaction.guild, {
        <span class="hljs-attr">metadata</span>: interaction.channel
    });

    <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">if</span> (!queue.connection) <span class="hljs-keyword">await</span> queue.connect(interaction.member.voice.channel);
    } <span class="hljs-keyword">catch</span> {
        <span class="hljs-keyword">void</span> player.deleteQueue(interaction.guildId);
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"Could not join your voice channel!"</span> });
    }

    <span class="hljs-keyword">await</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">`⏱ | Loading your <span class="hljs-subst">${searchResult.playlist ? <span class="hljs-string">"playlist"</span> : <span class="hljs-string">"track"</span>}</span>...`</span> });
    searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[<span class="hljs-number">0</span>]);
    <span class="hljs-keyword">if</span> (!queue.playing) <span class="hljs-keyword">await</span> queue.play();
}
</code></pre>
<p>Lastly, if the queue isn’t already playing, let’s start it using the <code>play()</code> function.</p>
<h3 id="heading-how-to-skip-songs">How to skip songs</h3>
<p>Skipping is quite easy – you can do it by calling the <code>skip()</code> function on the queue.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"skip"</span>) {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> queue = player.getQueue(interaction.guildId);
    <span class="hljs-keyword">if</span> (!queue || !queue.playing) <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"❌ | No music is being played!"</span> });
    <span class="hljs-keyword">const</span> currentTrack = queue.current;
    <span class="hljs-keyword">const</span> success = queue.skip();
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({
        <span class="hljs-attr">content</span>: success ? <span class="hljs-string">`✅ | Skipped **<span class="hljs-subst">${currentTrack}</span>**!`</span> : <span class="hljs-string">"❌ | Something went wrong!"</span>
    });
}
</code></pre>
<p>If the action is successful, you can write a message to the Discord text channel using <code>interaction.followUp()</code>.</p>
<h3 id="heading-how-to-stop-songs">How to stop songs</h3>
<p>The stop functionality will remove all the songs from the queue and the bot will leave the voice channel. You can do this by destroying the current queue which automatically makes the bot leave the voice channel (unless you configure it otherwise in the player configuration).</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"stop"</span>) {
        <span class="hljs-keyword">await</span> interaction.deferReply();
        <span class="hljs-keyword">const</span> queue = player.getQueue(interaction.guildId);
        <span class="hljs-keyword">if</span> (!queue || !queue.playing) <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"❌ | No music is being played!"</span> });
        queue.destroy();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"🛑 | Stopped the player!"</span> });
    }
</code></pre>
<h3 id="heading-complete-source-code-for-the-indexjs">Complete source code for the index.js:</h3>
<p>Here you can get the complete source code for the music bot:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> { Client, GuildMember, Intents } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord.js"</span>);
<span class="hljs-keyword">const</span> { Player, QueryType } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord-player"</span>);
<span class="hljs-keyword">const</span> config = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./config.json"</span>);

<span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Client({
    <span class="hljs-attr">intents</span>: [Intents.FLAGS.GUILD_VOICE_STATES, Intents.FLAGS.GUILD_MESSAGES, Intents.FLAGS.GUILDS]
});

client.on(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Bot is online!"</span>);
    client.user.setActivity({
        <span class="hljs-attr">name</span>: <span class="hljs-string">"🎶 | Music Time"</span>,
        <span class="hljs-attr">type</span>: <span class="hljs-string">"LISTENING"</span>
    });
});
client.on(<span class="hljs-string">"error"</span>, <span class="hljs-built_in">console</span>.error);
client.on(<span class="hljs-string">"warn"</span>, <span class="hljs-built_in">console</span>.warn);

<span class="hljs-keyword">const</span> player = <span class="hljs-keyword">new</span> Player(client);

player.on(<span class="hljs-string">"error"</span>, <span class="hljs-function">(<span class="hljs-params">queue, error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[<span class="hljs-subst">${queue.guild.name}</span>] Error emitted from the queue: <span class="hljs-subst">${error.message}</span>`</span>);
});
player.on(<span class="hljs-string">"connectionError"</span>, <span class="hljs-function">(<span class="hljs-params">queue, error</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`[<span class="hljs-subst">${queue.guild.name}</span>] Error emitted from the connection: <span class="hljs-subst">${error.message}</span>`</span>);
});

player.on(<span class="hljs-string">"trackStart"</span>, <span class="hljs-function">(<span class="hljs-params">queue, track</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">`🎶 | Started playing: **<span class="hljs-subst">${track.title}</span>** in **<span class="hljs-subst">${queue.connection.channel.name}</span>**!`</span>);
});

player.on(<span class="hljs-string">"trackAdd"</span>, <span class="hljs-function">(<span class="hljs-params">queue, track</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">`🎶 | Track **<span class="hljs-subst">${track.title}</span>** queued!`</span>);
});

player.on(<span class="hljs-string">"botDisconnect"</span>, <span class="hljs-function">(<span class="hljs-params">queue</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">"❌ | I was manually disconnected from the voice channel, clearing queue!"</span>);
});

player.on(<span class="hljs-string">"channelEmpty"</span>, <span class="hljs-function">(<span class="hljs-params">queue</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">"❌ | Nobody is in the voice channel, leaving..."</span>);
});

player.on(<span class="hljs-string">"queueEnd"</span>, <span class="hljs-function">(<span class="hljs-params">queue</span>) =&gt;</span> {
    queue.metadata.send(<span class="hljs-string">"✅ | Queue finished!"</span>);
});

client.on(<span class="hljs-string">"messageCreate"</span>, <span class="hljs-keyword">async</span> (message) =&gt; {
    <span class="hljs-keyword">if</span> (message.author.bot || !message.guild) <span class="hljs-keyword">return</span>;
    <span class="hljs-keyword">if</span> (!client.application?.owner) <span class="hljs-keyword">await</span> client.application?.fetch();

    <span class="hljs-keyword">if</span> (message.content === <span class="hljs-string">"!deploy"</span> &amp;&amp; message.author.id === client.application?.owner?.id) {
        <span class="hljs-keyword">await</span> message.guild.commands.set([
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"play"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"Plays a song from youtube"</span>,
                <span class="hljs-attr">options</span>: [
                    {
                        <span class="hljs-attr">name</span>: <span class="hljs-string">"query"</span>,
                        <span class="hljs-attr">type</span>: <span class="hljs-string">"STRING"</span>,
                        <span class="hljs-attr">description</span>: <span class="hljs-string">"The song you want to play"</span>,
                        <span class="hljs-attr">required</span>: <span class="hljs-literal">true</span>
                    }
                ]
            },
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"skip"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"Skip to the current song"</span>
            },
            {
                <span class="hljs-attr">name</span>: <span class="hljs-string">"stop"</span>,
                <span class="hljs-attr">description</span>: <span class="hljs-string">"Stop the player"</span>
            },
        ]);

        <span class="hljs-keyword">await</span> message.reply(<span class="hljs-string">"Deployed!"</span>);
    }
});

client.on(<span class="hljs-string">"interactionCreate"</span>, <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">if</span> (!interaction.isCommand() || !interaction.guildId) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">if</span> (!(interaction.member <span class="hljs-keyword">instanceof</span> GuildMember) || !interaction.member.voice.channel) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.reply({ <span class="hljs-attr">content</span>: <span class="hljs-string">"You are not in a voice channel!"</span>, <span class="hljs-attr">ephemeral</span>: <span class="hljs-literal">true</span> });
    }

    <span class="hljs-keyword">if</span> (interaction.guild.me.voice.channelId &amp;&amp; interaction.member.voice.channelId !== interaction.guild.me.voice.channelId) {
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.reply({ <span class="hljs-attr">content</span>: <span class="hljs-string">"You are not in my voice channel!"</span>, <span class="hljs-attr">ephemeral</span>: <span class="hljs-literal">true</span> });
    }

    <span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"play"</span>) {
        <span class="hljs-keyword">await</span> interaction.deferReply();

        <span class="hljs-keyword">const</span> query = interaction.options.get(<span class="hljs-string">"query"</span>).value;
        <span class="hljs-keyword">const</span> searchResult = <span class="hljs-keyword">await</span> player
            .search(query, {
                <span class="hljs-attr">requestedBy</span>: interaction.user,
                <span class="hljs-attr">searchEngine</span>: QueryType.AUTO
            })
            .catch(<span class="hljs-function">() =&gt;</span> {});
        <span class="hljs-keyword">if</span> (!searchResult || !searchResult.tracks.length) <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"No results were found!"</span> });

        <span class="hljs-keyword">const</span> queue = <span class="hljs-keyword">await</span> player.createQueue(interaction.guild, {
            <span class="hljs-attr">metadata</span>: interaction.channel
        });

        <span class="hljs-keyword">try</span> {
            <span class="hljs-keyword">if</span> (!queue.connection) <span class="hljs-keyword">await</span> queue.connect(interaction.member.voice.channel);
        } <span class="hljs-keyword">catch</span> {
            <span class="hljs-keyword">void</span> player.deleteQueue(interaction.guildId);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"Could not join your voice channel!"</span> });
        }

        <span class="hljs-keyword">await</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">`⏱ | Loading your <span class="hljs-subst">${searchResult.playlist ? <span class="hljs-string">"playlist"</span> : <span class="hljs-string">"track"</span>}</span>...`</span> });
        searchResult.playlist ? queue.addTracks(searchResult.tracks) : queue.addTrack(searchResult.tracks[<span class="hljs-number">0</span>]);
        <span class="hljs-keyword">if</span> (!queue.playing) <span class="hljs-keyword">await</span> queue.play();
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"skip"</span>) {
        <span class="hljs-keyword">await</span> interaction.deferReply();
        <span class="hljs-keyword">const</span> queue = player.getQueue(interaction.guildId);
        <span class="hljs-keyword">if</span> (!queue || !queue.playing) <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"❌ | No music is being played!"</span> });
        <span class="hljs-keyword">const</span> currentTrack = queue.current;
        <span class="hljs-keyword">const</span> success = queue.skip();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({
            <span class="hljs-attr">content</span>: success ? <span class="hljs-string">`✅ | Skipped **<span class="hljs-subst">${currentTrack}</span>**!`</span> : <span class="hljs-string">"❌ | Something went wrong!"</span>
        });
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (interaction.commandName === <span class="hljs-string">"stop"</span>) {
        <span class="hljs-keyword">await</span> interaction.deferReply();
        <span class="hljs-keyword">const</span> queue = player.getQueue(interaction.guildId);
        <span class="hljs-keyword">if</span> (!queue || !queue.playing) <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"❌ | No music is being played!"</span> });
        queue.destroy();
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">void</span> interaction.followUp({ <span class="hljs-attr">content</span>: <span class="hljs-string">"🛑 | Stopped the player!"</span> });
    } <span class="hljs-keyword">else</span> {
        interaction.reply({
            <span class="hljs-attr">content</span>: <span class="hljs-string">"Unknown command!"</span>,
            <span class="hljs-attr">ephemeral</span>: <span class="hljs-literal">true</span>
        });
    }
});

client.login(config.token);
</code></pre>
<h2 id="heading-discord-bot-version-012"><strong>Discord Bot Version 0.12</strong></h2>
<p>Now you'll just need to install some dependencies before we can get started.</p>
<pre><code>npm install discord.js ffmpeg fluent-ffmpeg @discordjs/opus ytdl-core --save
</code></pre><p>After installing the dependencies, import them in your dependencies.</p>
<pre><code><span class="hljs-keyword">const</span> Discord = <span class="hljs-built_in">require</span>(<span class="hljs-string">'discord.js'</span>);
<span class="hljs-keyword">const</span> {
    prefix,
    token,
} = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./config.json'</span>);
<span class="hljs-keyword">const</span> ytdl = <span class="hljs-built_in">require</span>(<span class="hljs-string">'ytdl-core'</span>);
</code></pre><p>After that, create your client and login using your token.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> client = <span class="hljs-keyword">new</span> Discord.Client();
client.login(token);
</code></pre>
<p>Now let’s add some basic listeners that console.log when they get executed.</p>
<pre><code>client.once(<span class="hljs-string">'ready'</span>, <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Ready!'</span>);
});
client.once(<span class="hljs-string">'reconnecting'</span>, <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Reconnecting!'</span>);
});
client.once(<span class="hljs-string">'disconnect'</span>, <span class="hljs-function">() =&gt;</span> {
 <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Disconnect!'</span>);
});
</code></pre><p>After that, you can start your bot using the <code>node</code> command and it should be online on Discord and print “Ready!” in the console.</p>
<pre><code class="lang-bash">node index.js
</code></pre>
<h3 id="heading-how-to-read-messages">How to read messages</h3>
<p>Now that your bot is on your server and able to go online, you can start reading chat messages and responding to them.</p>
<p>To read messages, you only need to write one simple function:</p>
<pre><code class="lang-javascript">client.on(<span class="hljs-string">'message'</span>, <span class="hljs-keyword">async</span> message =&gt; {

}
</code></pre>
<p>Here, you're creating a listener for the message event, getting the message, and saving it into a message object if it's triggered.</p>
<p>Now you need to check if the message is from your own bot and ignore it if it is.</p>
<pre><code><span class="hljs-keyword">if</span> (message.author.bot) <span class="hljs-keyword">return</span>;
</code></pre><p>In this line, you're checking if the author of the message is your bot and returning if it is.</p>
<p>After that, check if the message starts with the prefix you defined earlier and return if it doesn’t.</p>
<pre><code><span class="hljs-keyword">if</span> (!message.content.startsWith(prefix)) <span class="hljs-keyword">return</span>;
</code></pre><p>After that, you can check which command you need to execute. You can do so using some simple if statements:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> serverQueue = queue.get(message.guild.id);

<span class="hljs-keyword">if</span> (message.content.startsWith(<span class="hljs-string">`<span class="hljs-subst">${prefix}</span>play`</span>)) {
    execute(message, serverQueue);
    <span class="hljs-keyword">return</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (message.content.startsWith(<span class="hljs-string">`<span class="hljs-subst">${prefix}</span>skip`</span>)) {
    skip(message, serverQueue);
    <span class="hljs-keyword">return</span>;
} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (message.content.startsWith(<span class="hljs-string">`<span class="hljs-subst">${prefix}</span>stop`</span>)) {
    stop(message, serverQueue);
    <span class="hljs-keyword">return</span>;
} <span class="hljs-keyword">else</span> {
    message.channel.send(<span class="hljs-string">"You need to enter a valid command!"</span>);
}
</code></pre>
<p>In this code block, you're checking which command to execute and calling the command. If the input command isn’t valid, you're writing an error message into the chat using the <code>send()</code> function.</p>
<p>Now that you know which command you need to execute, you can start implementing these commands.</p>
<h3 id="heading-how-to-add-songs">How to add songs</h3>
<p>Let’s start by adding the play command. For that, you'll need a song and a guild (a guild represents an isolated collection of users and channels and is often referred to as a server). You'll also need the ytdl library you installed earlier.</p>
<p>First, create a map with the name of the queue where you save all the songs you type in the chat.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> queue = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();
</code></pre>
<p>After that, create an async function called execute and check if the user is in a voice chat and if the bot has the right permissions. If not, write an error message and return.</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">execute</span>(<span class="hljs-params">message, serverQueue</span>) </span>{
  <span class="hljs-keyword">const</span> args = message.content.split(<span class="hljs-string">" "</span>);

  <span class="hljs-keyword">const</span> voiceChannel = message.member.voice.channel;
  <span class="hljs-keyword">if</span> (!voiceChannel)
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"You need to be in a voice channel to play music!"</span>
    );
  <span class="hljs-keyword">const</span> permissions = voiceChannel.permissionsFor(message.client.user);
  <span class="hljs-keyword">if</span> (!permissions.has(<span class="hljs-string">"CONNECT"</span>) || !permissions.has(<span class="hljs-string">"SPEAK"</span>)) {
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"I need the permissions to join and speak in your voice channel!"</span>
    );
  }
}
</code></pre>
<p>Now you can continue with getting the song info and saving it into a song object. For that, use your <code>ytdl</code> library which gets the song information from the YouTube link.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> songInfo = <span class="hljs-keyword">await</span> ytdl.getInfo(args[<span class="hljs-number">1</span>]);
<span class="hljs-keyword">const</span> song = {
 <span class="hljs-attr">title</span>: songInfo.title,
 <span class="hljs-attr">url</span>: songInfo.video_url,
};
</code></pre>
<p>This will get the information of the song using the <code>ytdl</code> library you installed earlier. Then, save the information you need into a song object.</p>
<p>After saving the song info, you just need to create a contract you can add to your queue. </p>
<p>To do so, first check if your serverQueue is already defined which means that music is already playing. If so, add the song to your existing serverQueue and send a success message. If not, create it and try to join the voice channel and start playing music.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">if</span> (!serverQueue) {

}<span class="hljs-keyword">else</span> {
 serverQueue.songs.push(song);
 <span class="hljs-built_in">console</span>.log(serverQueue.songs);
 <span class="hljs-keyword">return</span> message.channel.send(<span class="hljs-string">`<span class="hljs-subst">${song.title}</span> has been added to the queue!`</span>);
}
</code></pre>
<p>Here, check if the <code>serverQueue</code>is empty and add the song to it if it’s not. Now you just need to create your contract if the <code>serverQueue</code> is null.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Creating the contract for our queue</span>
<span class="hljs-keyword">const</span> queueContruct = {
 <span class="hljs-attr">textChannel</span>: message.channel,
 <span class="hljs-attr">voiceChannel</span>: voiceChannel,
 <span class="hljs-attr">connection</span>: <span class="hljs-literal">null</span>,
 <span class="hljs-attr">songs</span>: [],
 <span class="hljs-attr">volume</span>: <span class="hljs-number">5</span>,
 <span class="hljs-attr">playing</span>: <span class="hljs-literal">true</span>,
};
<span class="hljs-comment">// Setting the queue using our contract</span>
queue.set(message.guild.id, queueContruct);
<span class="hljs-comment">// Pushing the song to our songs array</span>
queueContruct.songs.push(song);

<span class="hljs-keyword">try</span> {
 <span class="hljs-comment">// Here we try to join the voicechat and save our connection into our object.</span>
 <span class="hljs-keyword">var</span> connection = <span class="hljs-keyword">await</span> voiceChannel.join();
 queueContruct.connection = connection;
 <span class="hljs-comment">// Calling the play function to start a song</span>
 play(message.guild, queueContruct.songs[<span class="hljs-number">0</span>]);
} <span class="hljs-keyword">catch</span> (err) {
 <span class="hljs-comment">// Printing the error message if the bot fails to join the voicechat</span>
 <span class="hljs-built_in">console</span>.log(err);
 queue.delete(message.guild.id);
 <span class="hljs-keyword">return</span> message.channel.send(err);
}
</code></pre>
<p>In this code block, you created a contract and added your song to the songs array. After that, you tried to join the voice chat of the user and called your <code>play()</code> function you'll implement after that.</p>
<h3 id="heading-how-to-play-songs-1">How to play songs</h3>
<p>Now that you can add our songs to your queue and create a contract if there isn’t one yet, you can implement the play functionality.</p>
<p>First, create a function called play which takes two parameters (the guild and the song you want to play) and checks if the song is empty. If so, just leave the voice channel and delete the queue.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">play</span>(<span class="hljs-params">guild, song</span>) </span>{
  <span class="hljs-keyword">const</span> serverQueue = queue.get(guild.id);
  <span class="hljs-keyword">if</span> (!song) {
    serverQueue.voiceChannel.leave();
    queue.delete(guild.id);
    <span class="hljs-keyword">return</span>;
  }
}
</code></pre>
<p>After that, start playing your song using the <code>play()</code> function of the connection and passing the URL of your song.</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> dispatcher = serverQueue.connection
    .play(ytdl(song.url))
    .on(<span class="hljs-string">"finish"</span>, <span class="hljs-function">() =&gt;</span> {
        serverQueue.songs.shift();
        play(guild, serverQueue.songs[<span class="hljs-number">0</span>]);
    })
    .on(<span class="hljs-string">"error"</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.error(error));
dispatcher.setVolumeLogarithmic(serverQueue.volume / <span class="hljs-number">5</span>);
serverQueue.textChannel.send(<span class="hljs-string">`Start playing: **<span class="hljs-subst">${song.title}</span>**`</span>);
</code></pre>
<p>Here, you created a stream and passed it the URL of our song. You also added two listeners that handle the end and error events.</p>
<p><strong>Note:</strong> This is a recursive function which means that it calls itself over and over again. We're using recursion so it plays the next song when the song is finished.</p>
<p>Now you're ready to play a song by just typing the !play URL in the chat.</p>
<h3 id="heading-how-to-skip-songs-1">How to skip songs</h3>
<p>Now you can implement the skipping functionality. For that, you just need to end the dispatcher you created in your <code>play()</code> function so it starts the next song.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">skip</span>(<span class="hljs-params">message, serverQueue</span>) </span>{
  <span class="hljs-keyword">if</span> (!message.member.voice.channel)
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"You have to be in a voice channel to stop the music!"</span>
    );
  <span class="hljs-keyword">if</span> (!serverQueue)
    <span class="hljs-keyword">return</span> message.channel.send(<span class="hljs-string">"There is no song that I could skip!"</span>);
  serverQueue.connection.dispatcher.end();
}
</code></pre>
<p>Here, you're checking if the user that typed the command is in a voice channel and if there is a song to skip.</p>
<h3 id="heading-how-to-stop-songs-1">How to stop songs</h3>
<p>The <code>stop()</code> function is almost the same as <code>skip()</code>, except that you clear the songs array which will make your bot delete the queue and leave the voice chat.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stop</span>(<span class="hljs-params">message, serverQueue</span>) </span>{
  <span class="hljs-keyword">if</span> (!message.member.voice.channel)
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"You have to be in a voice channel to stop the music!"</span>
    );
  serverQueue.songs = [];
  serverQueue.connection.dispatcher.end();
}
</code></pre>
<h3 id="heading-complete-source-code-for-the-indexjs-1">Complete source code for the index.js:</h3>
<p>Here you can get the complete source code for the music bot:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> Discord = <span class="hljs-built_in">require</span>(<span class="hljs-string">"discord.js"</span>);
<span class="hljs-keyword">const</span> { prefix, token } = <span class="hljs-built_in">require</span>(<span class="hljs-string">"./config.json"</span>);
<span class="hljs-keyword">const</span> ytdl = <span class="hljs-built_in">require</span>(<span class="hljs-string">"ytdl-core"</span>);

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

<span class="hljs-keyword">const</span> queue = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();

client.once(<span class="hljs-string">"ready"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Ready!"</span>);
});

client.once(<span class="hljs-string">"reconnecting"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Reconnecting!"</span>);
});

client.once(<span class="hljs-string">"disconnect"</span>, <span class="hljs-function">() =&gt;</span> {
  <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Disconnect!"</span>);
});

client.on(<span class="hljs-string">"message"</span>, <span class="hljs-keyword">async</span> message =&gt; {
  <span class="hljs-keyword">if</span> (message.author.bot) <span class="hljs-keyword">return</span>;
  <span class="hljs-keyword">if</span> (!message.content.startsWith(prefix)) <span class="hljs-keyword">return</span>;

  <span class="hljs-keyword">const</span> serverQueue = queue.get(message.guild.id);

  <span class="hljs-keyword">if</span> (message.content.startsWith(<span class="hljs-string">`<span class="hljs-subst">${prefix}</span>play`</span>)) {
    execute(message, serverQueue);
    <span class="hljs-keyword">return</span>;
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (message.content.startsWith(<span class="hljs-string">`<span class="hljs-subst">${prefix}</span>skip`</span>)) {
    skip(message, serverQueue);
    <span class="hljs-keyword">return</span>;
  } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (message.content.startsWith(<span class="hljs-string">`<span class="hljs-subst">${prefix}</span>stop`</span>)) {
    stop(message, serverQueue);
    <span class="hljs-keyword">return</span>;
  } <span class="hljs-keyword">else</span> {
    message.channel.send(<span class="hljs-string">"You need to enter a valid command!"</span>);
  }
});

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">execute</span>(<span class="hljs-params">message, serverQueue</span>) </span>{
  <span class="hljs-keyword">const</span> args = message.content.split(<span class="hljs-string">" "</span>);

  <span class="hljs-keyword">const</span> voiceChannel = message.member.voice.channel;
  <span class="hljs-keyword">if</span> (!voiceChannel)
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"You need to be in a voice channel to play music!"</span>
    );
  <span class="hljs-keyword">const</span> permissions = voiceChannel.permissionsFor(message.client.user);
  <span class="hljs-keyword">if</span> (!permissions.has(<span class="hljs-string">"CONNECT"</span>) || !permissions.has(<span class="hljs-string">"SPEAK"</span>)) {
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"I need the permissions to join and speak in your voice channel!"</span>
    );
  }

  <span class="hljs-keyword">const</span> songInfo = <span class="hljs-keyword">await</span> ytdl.getInfo(args[<span class="hljs-number">1</span>]);
  <span class="hljs-keyword">const</span> song = {
    <span class="hljs-attr">title</span>: songInfo.title,
    <span class="hljs-attr">url</span>: songInfo.video_url
  };

  <span class="hljs-keyword">if</span> (!serverQueue) {
    <span class="hljs-keyword">const</span> queueContruct = {
      <span class="hljs-attr">textChannel</span>: message.channel,
      <span class="hljs-attr">voiceChannel</span>: voiceChannel,
      <span class="hljs-attr">connection</span>: <span class="hljs-literal">null</span>,
      <span class="hljs-attr">songs</span>: [],
      <span class="hljs-attr">volume</span>: <span class="hljs-number">5</span>,
      <span class="hljs-attr">playing</span>: <span class="hljs-literal">true</span>
    };

    queue.set(message.guild.id, queueContruct);

    queueContruct.songs.push(song);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">var</span> connection = <span class="hljs-keyword">await</span> voiceChannel.join();
      queueContruct.connection = connection;
      play(message.guild, queueContruct.songs[<span class="hljs-number">0</span>]);
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-built_in">console</span>.log(err);
      queue.delete(message.guild.id);
      <span class="hljs-keyword">return</span> message.channel.send(err);
    }
  } <span class="hljs-keyword">else</span> {
    serverQueue.songs.push(song);
    <span class="hljs-keyword">return</span> message.channel.send(<span class="hljs-string">`<span class="hljs-subst">${song.title}</span> has been added to the queue!`</span>);
  }
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">skip</span>(<span class="hljs-params">message, serverQueue</span>) </span>{
  <span class="hljs-keyword">if</span> (!message.member.voice.channel)
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"You have to be in a voice channel to stop the music!"</span>
    );
  <span class="hljs-keyword">if</span> (!serverQueue)
    <span class="hljs-keyword">return</span> message.channel.send(<span class="hljs-string">"There is no song that I could skip!"</span>);
  serverQueue.connection.dispatcher.end();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stop</span>(<span class="hljs-params">message, serverQueue</span>) </span>{
  <span class="hljs-keyword">if</span> (!message.member.voice.channel)
    <span class="hljs-keyword">return</span> message.channel.send(
      <span class="hljs-string">"You have to be in a voice channel to stop the music!"</span>
    );
  serverQueue.songs = [];
  serverQueue.connection.dispatcher.end();
}

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">play</span>(<span class="hljs-params">guild, song</span>) </span>{
  <span class="hljs-keyword">const</span> serverQueue = queue.get(guild.id);
  <span class="hljs-keyword">if</span> (!song) {
    serverQueue.voiceChannel.leave();
    queue.delete(guild.id);
    <span class="hljs-keyword">return</span>;
  }

  <span class="hljs-keyword">const</span> dispatcher = serverQueue.connection
    .play(ytdl(song.url))
    .on(<span class="hljs-string">"finish"</span>, <span class="hljs-function">() =&gt;</span> {
      serverQueue.songs.shift();
      play(guild, serverQueue.songs[<span class="hljs-number">0</span>]);
    })
    .on(<span class="hljs-string">"error"</span>, <span class="hljs-function"><span class="hljs-params">error</span> =&gt;</span> <span class="hljs-built_in">console</span>.error(error));
  dispatcher.setVolumeLogarithmic(serverQueue.volume / <span class="hljs-number">5</span>);
  serverQueue.textChannel.send(<span class="hljs-string">`Start playing: **<span class="hljs-subst">${song.title}</span>**`</span>);
}

client.login(token);
</code></pre>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>You made it all the way until the end! Hope that this article helped you understand the Discord API and how you can use it to create a simple bot. </p>
<p>If you want to see an example of a more advanced discord bot, you can visit my <a target="_blank" href="https://github.com/TannerGabriel/discord-bot">GitHub repository</a>.</p>
<p>If you have found this useful, please consider recommending and sharing it with other fellow developers.</p>
<p>If you have any questions or feedback, let me know and I'd be happy to help.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build and Deploy a python-telegram-bot v20 Webhook ]]>
                </title>
                <description>
                    <![CDATA[ By Chua Hui Shun The python-telegram-bot v20 release introduced major structural changes. According to the documentation, “All networking and I/O-related logic now works via coroutine functions (i.e. _async def ..._ functions). In particular, all me... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-and-deploy-python-telegram-bot-v20-webhooks/</link>
                <guid isPermaLink="false">66d45f31264384a65d5a952e</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ webhooks ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 20 Nov 2023 20:43:21 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2023/11/iPhone-8---1--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Chua Hui Shun</p>
<p>The python-telegram-bot v20 release introduced major structural changes. According to the documentation,</p>
<blockquote>
<p>“All networking and I/O-related logic now works via coroutine functions (i.e. <code>_async def ..._</code> functions). In particular, all methods of the <code>_telegram.Bot_</code> class that make requests to the Bot API are now coroutine functions and need to be <code>await</code>-ed.” — <a target="_blank" href="https://telegra.ph/Release-notes-for-python-telegram-bot-v200a0-05-06">python-telegram-bot v20 change logs</a></p>
</blockquote>
<p>The process of transitioning from python-telegram-bot version 13 to version 20 turned out to be more complex than I had originally anticipated. Converting synchronous <code>def</code> functions to <code>async def</code> and adding <code>await</code> to new coroutines was fairly easy. But the main difficulty was finding detailed documentation on how to <strong>build and deploy python-telegram-bot v20 webhooks in a production setting</strong>.</p>
<p>This article explains why and how I migrated from:</p>
<ol>
<li>python-telegram-bot v13 → v20</li>
<li>Flask → FastAPI</li>
<li>Gunicorn → Gunicorn + Uvicorn</li>
</ol>
<h2 id="heading-why-upgrade-from-v13-to-v20">Why Upgrade from v13 to v20?</h2>
<p>v13.x and older are <a target="_blank" href="https://stackoverflow.com/questions/76196067/the-python-telegram-bot-library-does-not-see-messages-in-a-group">no longer supported</a> by the python-telegram-bot dev team. If Telegram API introduces any new features, they will only be available on v20 and above.</p>
<h3 id="heading-why-use-a-webhook-and-not-polling">Why Use a Webhook and Not Polling?</h3>
<p>Most examples provided by the python-telegram-bot dev team use <code>Application.run_polling</code>. But webhooks are generally recommended over polling for most Telegram bot use cases, because polling requires your bot to constantly make requests to Telegram’s servers, which can consume significant resources. On the other hand, webhooks offer <a target="_blank" href="https://github.com/python-telegram-bot/python-telegram-bot/wiki/Frequently-requested-design-patterns#running-ptb-alongside-other-asyncio-frameworks">extended functionality</a>, update faster, and scale better.</p>
<h2 id="heading-the-challenge-of-using-flask-with-python-telegram-bot-v20">The Challenge of using Flask with python-telegram-bot v20</h2>
<h3 id="heading-using-a-wgsi-like-flask-with-python-telegram-bot-v20-is-awkward">Using a WGSI like Flask with python-telegram-bot v20 is awkward.</h3>
<p>Flask, a WSGI (Web Server Gateway Interface), is synchronous and can handle only one request at a time. But you can still <a target="_blank" href="https://www.reddit.com/r/flask/comments/xvw1vi/misunderstandings_about_how_async_works_with/">run async functions in Flask</a> using <code>asyncio.run()</code>, as in the <a target="_blank" href="https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html">custom webhook bot example</a> provided by the python-telegram-bot dev team. </p>
<p><code>asyncio.run()</code> starts an event loop and executes the given coroutine until it completes. If there are any asynchronous tasks running before or after the request is handled, those tasks will be executed in a separate event loop.</p>
<pre><code class="lang-python"><span class="hljs-comment"># Code snippet from https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html</span>

webserver = uvicorn.Server(
    config=uvicorn.Config(
        app=WsgiToAsgi(flask_app),
        port=PORT,
        use_colors=<span class="hljs-literal">False</span>,
        host=<span class="hljs-string">"127.0.0.1"</span>,
    )
)

<span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> application:
  <span class="hljs-keyword">await</span> application.start()
  <span class="hljs-keyword">await</span> webserver.serve() <span class="hljs-comment"># start bot's webserver</span>
  <span class="hljs-keyword">await</span> application.stop()
</code></pre>
<p>However, this implementation is slightly awkward because Flask is intrinsically <a target="_blank" href="https://sethmlarson.dev/flask-async-views-and-async-globals">incompatible with async globals</a>.</p>
<h3 id="heading-examples-in-the-documentationhttpsdocspython-telegram-botorgenv206examplescustomwebhookbothtml-are-not-suitable-for-production-environments">Examples in the <a target="_blank" href="https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html">documentation</a> are not suitable for production environments.</h3>
<p>Using <code>asyncio.run()</code> as an entry point in production is usually not recommended. The <code>asyncio.run()</code> function is designed for development and testing purposes, and it may not provide the same level of robustness and reliability as production servers like Gunicorn or UWSGI. </p>
<p>These production servers offer many additional features, such as logging, monitoring, and health checks, which are essential for ensuring the stability and security of a production application.</p>
<p>If you want to deploy your bot in production, it’s much cleaner to use an ASGI (Asynchronous Server Gateway Interface) with an ASGI web server implementation.</p>
<h2 id="heading-how-to-do-it-migration-and-deployment">How to Do It — Migration and Deployment</h2>
<h3 id="heading-from-flask-wsgi-to-fastapi-agsi">From Flask (WSGI) to FastAPI (AGSI)</h3>
<p>Migrating a Flask application to an ASGI is straightforward. I chose FastAPI because I found a comprehensive migration tutorial <a target="_blank" href="https://testdriven.io/blog/moving-from-flask-to-fastapi/">here</a>. The syntaxes of both frameworks are quite similar, which means you won’t have to make too many code changes.</p>
<pre><code class="lang-python"><span class="hljs-comment"># From python-telegram-bot v20</span>
application = (
    Application.builder()
    .updater(<span class="hljs-literal">None</span>)
    .token(&lt;your-bot-token&gt;) <span class="hljs-comment"># replace &lt;your-bot-token&gt;</span>
    .read_timeout(<span class="hljs-number">7</span>)
    .get_updates_read_timeout(<span class="hljs-number">42</span>)
    .build()
)

<span class="hljs-comment"># From FastAPI</span>
<span class="hljs-meta">@asynccontextmanager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lifespan</span>(<span class="hljs-params">app: FastAPI</span>):</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> application:
        <span class="hljs-keyword">await</span> application.start()
        <span class="hljs-keyword">yield</span>
        <span class="hljs-keyword">await</span> application.stop()
</code></pre>
<p>Quart appears to be a feasible alternative, but it <a target="_blank" href="https://pgjones.gitlab.io/quart/tutorials/deployment.html">does not offer support</a> for deployments using Uvicorn, which is the webserver implementation I was adapting from the <a target="_blank" href="https://docs.python-telegram-bot.org/en/v20.6/examples.customwebhookbot.html">script</a> provided by the python-telegram-bot team.</p>
<h3 id="heading-a-working-example">A working example</h3>
<p>The following code shows a minimal example that uses FastAPI to build a python-telegram-bot v20 webhook. This bot will respond with “starting…” when it receives the <code>/start</code> command.</p>
<pre><code class="lang-python"><span class="hljs-comment"># main.py</span>

<span class="hljs-keyword">from</span> contextlib <span class="hljs-keyword">import</span> asynccontextmanager
<span class="hljs-keyword">from</span> http <span class="hljs-keyword">import</span> HTTPStatus
<span class="hljs-keyword">from</span> telegram <span class="hljs-keyword">import</span> Update
<span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Application, CommandHandler
<span class="hljs-keyword">from</span> telegram.ext._contexttypes <span class="hljs-keyword">import</span> ContextTypes
<span class="hljs-keyword">from</span> fastapi <span class="hljs-keyword">import</span> FastAPI, Request, Response

<span class="hljs-comment"># Initialize python telegram bot</span>
ptb = (
    Application.builder()
    .updater(<span class="hljs-literal">None</span>)
    .token(&lt;your-bot-token&gt;) <span class="hljs-comment"># replace &lt;your-bot-token&gt;</span>
    .read_timeout(<span class="hljs-number">7</span>)
    .get_updates_read_timeout(<span class="hljs-number">42</span>)
    .build()
)

<span class="hljs-meta">@asynccontextmanager</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">lifespan</span>(<span class="hljs-params">_: FastAPI</span>):</span>
    <span class="hljs-keyword">await</span> ptb.bot.setWebhook(&lt;your-webhook-url&gt;) <span class="hljs-comment"># replace &lt;your-webhook-url&gt;</span>
    <span class="hljs-keyword">async</span> <span class="hljs-keyword">with</span> ptb:
        <span class="hljs-keyword">await</span> ptb.start()
        <span class="hljs-keyword">yield</span>
        <span class="hljs-keyword">await</span> ptb.stop()

<span class="hljs-comment"># Initialize FastAPI app (similar to Flask)</span>
app = FastAPI(lifespan=lifespan)

<span class="hljs-meta">@app.post("/")</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">process_update</span>(<span class="hljs-params">request: Request</span>):</span>
    req = <span class="hljs-keyword">await</span> request.json()
    update = Update.de_json(req, ptb.bot)
    <span class="hljs-keyword">await</span> ptb.process_update(update)
    <span class="hljs-keyword">return</span> Response(status_code=HTTPStatus.OK)

<span class="hljs-comment"># Example handler</span>
<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">start</span>(<span class="hljs-params">update, _: ContextTypes.DEFAULT_TYPE</span>):</span>
    <span class="hljs-string">"""Send a message when the command /start is issued."""</span>
    <span class="hljs-keyword">await</span> update.message.reply_text(<span class="hljs-string">"starting..."</span>)

ptb.add_handler(CommandHandler(<span class="hljs-string">"start"</span>, start))
</code></pre>
<p>To start the bot, pip install all required dependencies and run the start command: <code>gunicorn main:app -k uvicorn.workers.UvicornWorker</code>.</p>
<p>This code snippet is adapted from a real Telegram bot in production. Check out the source code for <a target="_blank" href="https://t.me/cron_telebot">@cron_telebot</a> <a target="_blank" href="https://github.com/hsdevelops/cron-telebot">here</a> to see how it is implemented. Feel free to adapt the script to suit your use case.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we learnt how to build and deploy a python-telegram-bot v20 webhook.</p>
<p>Hope this tutorial helped you. If you enjoyed this article, please <a target="_blank" href="https://huishun.medium.com/">follow me on Medium</a> to show your support. </p>
<p>Thank you for reading!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Build a 100 Days of Code Discord Bot with TypeScript, MongoDB, and Discord.js 13 ]]>
                </title>
                <description>
                    <![CDATA[ The 100 Days of Code challenge is very popular among new coders and developers looking to level up their skills. It's so popular that our Discord server has an entire channel dedicated to it. By popular demand, we built a Discord bot that helps peopl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-100-days-of-code-discord-bot-with-typescript-mongodb-and-discord-js-13/</link>
                <guid isPermaLink="false">66ac7eac11cd6758aec202a8</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ coding challenge ]]>
                    </category>
                
                    <category>
                        <![CDATA[ discord ]]>
                    </category>
                
                    <category>
                        <![CDATA[ freeCodeCamp.org ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Naomi Carrigan ]]>
                </dc:creator>
                <pubDate>Mon, 31 Jan 2022 21:41:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2022/01/pexels-kindel-media-8566473.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>The <a target="_blank" href="https://www.freecodecamp.org/news/the-crazy-history-of-the-100daysofcode-challenge-and-why-you-should-try-it-for-2018-6c89a76e298d/">100 Days of Code challenge</a> is very popular among new coders and developers looking to level up their skills. It's so popular that our <a target="_blank" href="https://www.freecodecamp.org/news/freecodecamp-discord-chat-room-server/">Discord server</a> has an entire channel dedicated to it.</p>
<p>By popular demand, we built a Discord bot that helps people track their progress in the challenge.</p>
<p>Today I am going to show you how to build your own 100 Days of Code bot.</p>
<blockquote>
<p>Note that discord.js has released version 14, which includes breaking changes. For compatibility with this tutorial, you will want to ensure you are using discord.js 13 - you can install this with <code>npm install discord.js@13</code>. If you have any questions, feel free to join my <a target="_blank" href="https://chat.nhcarrigan.com">discord server</a>. </p>
</blockquote>
<details>
    <summary>Contents</summary>
    <ul>
        <li>
            <a href="#create-a-discord-bot-application">Create a Discord Bot Application</a>
        </li>
        <li>
            <a href="#set-up-your-project">Set Up Your Project</a>
        </li>
        <li>
            <a href="#create-the-discord-bot">Create the Discord Bot</a>
        </li>
        <li>
            <a href="#gateway-events-in-discord">Gateway Events in Discord</a>
        </li>
        <li>
            <a href="#connect-to-the-database">Connect to the Database</a>
        </li>
        <li>
            <a href="#environment-variable-validation">Environment Variable Validation</a>
        </li>
        <li>
            <a href="#the-interaction-event">The "interaction" Event</a>
        </li>
        <li>
            <a href="#prepare-for-commands">Prepare for Commands</a>
        </li>
        <li>
            <a href="#database-model">Database Model</a>
        </li>
        <li>
            <a href="#write-bot-commands">Write Bot Commands</a>
        </li>
    </ul>
</details>

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> help: Command = {
  data: <span class="hljs-keyword">new</span> SlashCommandBuilder()
    .setName(<span class="hljs-string">"help"</span>)
    .setDescription(<span class="hljs-string">"Provides information on using this bot."</span>),
  run: <span class="hljs-keyword">async</span> (interaction) =&gt; {
    <span class="hljs-keyword">await</span> interaction.deferReply();
    <span class="hljs-keyword">const</span> helpEmbed = <span class="hljs-keyword">new</span> MessageEmbed();
    helpEmbed.setTitle(<span class="hljs-string">"100 Days of Code Bot!"</span>);
    helpEmbed.setDescription(
      <span class="hljs-string">"This discord bot is designed to help you track and share your 100 Days of Code progress."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Create today's update"</span>,
      <span class="hljs-string">"Use the `/100` command to create your update for today. The `message` will be displayed in your embed."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Edit today's update"</span>,
      <span class="hljs-string">"Do you see a typo in your embed? Right click it and copy the ID (you may need developer mode on for this), and use the `/edit` command to update that embed with a new message."</span>
    );
    helpEmbed.addField(
      <span class="hljs-string">"Show your progress"</span>,
      <span class="hljs-string">"To see your current progress in the challenge, and the day you last checked in, use `/view`."</span>
    );
    helpEmbed.setFooter({ text: <span class="hljs-string">`Version <span class="hljs-subst">${process.env.npm_package_version}</span>`</span> });
    <span class="hljs-keyword">await</span> interaction.editReply({ embeds: [helpEmbed] });
    <span class="hljs-keyword">return</span>;
  },
};
</code></pre>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Congratulations! You have successfully built a Discord bot for the 100 Days of Code challenge.</p>
<p>If you are interested in exploring further, you can view <a target="_blank" href="https://github.com/nhcarrigan/100-days-of-code-bot">the source code</a> for the live bot that inspired this tutorial, which includes custom error logging, external error reporting, and a documentation site.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Bot and Automate your Everyday Work ]]>
                </title>
                <description>
                    <![CDATA[ By Tim Grossmann Most jobs have repetitive tasks that you can automate, which frees up some of your valuable time. This makes automation a key skill to acquire. A small group of skilled automation engineers and domain experts may be able to automate ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-bots/</link>
                <guid isPermaLink="false">66d4614bffe6b1f641b5fa93</guid>
                
                    <category>
                        <![CDATA[ automation ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ node ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ social media ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 21 Jul 2020 18:36:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/06/freecodecamp_cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Tim Grossmann</p>
<p>Most jobs have repetitive tasks that you can automate, which frees up some of your valuable time. This makes automation a key skill to acquire.</p>
<p>A small group of skilled automation engineers and domain experts may be able to automate many of the most tedious tasks of entire teams.</p>
<p>In this article, we'll explore the basics of workflow automation using Python – a powerful and easy to learn programming language. We will use Python to write an easy and helpful little automation script that will clean up a given folder and put each file into its according folder. </p>
<p>Our goal won't be to write perfect code or create ideal architectures in the beginning.<br>We also won't build anything "illegal". Instead we'll look at how to create a script that automatically cleans up a given folder and all of its files. </p>
<h1 id="heading-table-of-contents">Table of contents</h1>
<ol>
<li><a class="post-section-overview" href="#heading-areas-of-automation-and-where-to-start">Areas of Automation and Where to Start</a> <ul>
<li>Simple Automation</li>
<li>Public API Automation</li>
<li>API Reverse Engineering</li>
</ul>
</li>
<li><a class="post-section-overview" href="#heading-ethical-considerations">Ethical Considerations of Automation</a></li>
<li><a class="post-section-overview" href="#heading-creating-a-directory-clean-up-script">Creating a Directory Clean-Up Script</a></li>
<li><a class="post-section-overview" href="#heading-a-complete-guide-to-bot-creation-and-automating-your-everyday-work">A Complete Guide to Bot Creation and Automating Your Everyday Work</a></li>
</ol>
<h2 id="heading-areas-of-automation-and-where-to-start">Areas of Automation and Where to Start</h2>
<p>Let's start with defining what kind of automations there are.</p>
<p>The art of automation applies to most sectors. For starters, it helps with tasks like extracting email addresses from a bunch of documents so you can do an email blast. Or more complex approaches like optimizing workflows and processes inside of large corporations.</p>
<p>Of course, going from small personal scripts to large automation infrastructure that replaces actual people involves a process of learning and improving. So let's see where you can start your journey.</p>
<h3 id="heading-simple-automations">Simple Automations</h3>
<p>Simple automations allow for a quick and straightforward entry point. This can cover small independent processes like project clean-ups and re-structuring of files inside of directories, or parts of a workflow like automatically resizing already saved files. </p>
<h3 id="heading-public-api-automations">Public API Automations</h3>
<p>Public API automations are the most common form of automation since we can access most functionality using HTTP requests to APIs nowadays. For example, if you want to automate the watering of your self-made smart garden at home.</p>
<p>To do that, you want to check the weather of the current day to see whether you need to water or if there is rain incoming.</p>
<h3 id="heading-api-reverse-engineering">API Reverse Engineering</h3>
<p>API reverse engineering-based automation is more common in actual bots and the "Bot Imposter" section of the chart in the "Ethical Considerations" section below. </p>
<p>By reverse-engineering an API, we understand the user flow of applications. One example could be the login into an online browser game. </p>
<p>By understanding the login and authentication process, we can duplicate that behaviour with our own script. Then we can create our own interface to work with the application even though they don't provide it themselves.</p>
<p>Whatever approach you're aiming at, always consider whether it's legal or not.</p>
<p>You don't want to get yourself into trouble, do you? ?</p>
<h2 id="heading-ethical-considerations">Ethical Considerations</h2>
<p>Some guy on GitHub once contacted me and told me this:</p>
<blockquote>
<p>“Likes and engagement are digital currency and you are devaluing them.”</p>
</blockquote>
<p>This stuck with me and made me question the tool I've built for exactly that purpose.</p>
<p>The fact that these interactions and the engagement can be automated and “faked” more and more leads to a distorted and broken social media system.</p>
<p>People who produce valuable and good content are invisible to other users and advertisement companies if they don’t use bots and other engagement systems.  </p>
<p>A friend of mine came up with the following association with Dante’s “Nine Circles of Hell” where with each step closer to becoming a social influencer you get less and less aware of how broken this whole system actually is. </p>
<p>I want to share this with you here since I think it's an extremely accurate representation of what I witnessed while actively working with Influencers with InstaPy.</p>
<p><strong>Level 1: Limbo -</strong> If you don’t bot at all<br><strong>Level 2: Flirtation</strong> - When you manually like and follow as many people as you can to get them to follow you back / like your posts<br><strong>Level 3: Conspiracy</strong> - when you join a Telegram group to like and comment on 10 photos so the next 10 people will like and comment on your photo<br><strong>Level 4: Infidelity</strong> - When you use a low-cost Virtual Assistant to like and follow on your behalf<br><strong>Level 5: Lust -</strong> When you use a bot to give likes, and don’t receive any likes back in return (but you don’t pay for it - for example, a Chrome extension)<br><strong>Level 6: Promiscuity -</strong> When you use a bot to Give 50+ likes to Get 50+ likes, but you don’t pay for it - for example, a Chrome extension<br><strong>Level 7: Avarice or Extreme Greed</strong> - When you use a bot to Like / Follow / Comment on between 200–700 photos, ignoring the chance of getting banned<br><strong>Level 8: Prostitution</strong> - When you pay an unknown 3rd party service to engage in automated reciprocal likes / follows for you, but they use your account to like / follow back<br><strong>Level 9: Fraud / Heresy</strong> - When you buy followers and likes and try to sell yourself to brands as an influencer</p>
<p>The level of botting on social media is so prevalent that <strong>if you don’t bot, you will be stuck in Level 1, Limbo</strong>, with no follower growth and low engagement relative to your peers.</p>
<p>In economic theory, this is known as a <strong>prisoner's dilemma and zero-sum game</strong>. If I don’t bot and you bot, you win. If you don’t bot and I bot, I win. If no one bots, everyone wins. But since there is no incentive for everyone not to bot, everyone bots, so no one wins.</p>
<blockquote>
<p>Be aware of this and never forget the implications this whole tooling has on social media.</p>
</blockquote>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/07/spectrum-bot-intent-ebook.png" alt="Image" width="600" height="400" loading="lazy">
<em>Source: SignalSciences.com</em></p>
<p>We want to avoid dealing with ethical implications and still work on an automation project here. This is why we will create a simple directory clean-up script that helps you organise your messy folders. </p>
<h2 id="heading-creating-a-directory-clean-up-script">Creating a Directory Clean-Up Script</h2>
<p>We now want to look at a quite simple script. It automatically cleans up a given directory by moving those files into according folders based on the file extension.</p>
<p>So all we want to do is this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2020/06/directory_clean_img.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-setting-up-the-argument-parser">Setting up the Argument Parser</h3>
<p>Since we are working with operating system functionality like moving files, we need to import the <code>os</code> library. In addition to that, we want to give the user some control over what folder is cleaned up. We will use the <code>argparse</code> library for this.</p>
<pre><code class="lang-python"><span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> argparse
</code></pre>
<p>After importing the two libraries, let's first set up the argument parser. Make sure to give a description and a help text to each added argument to give valuable help to the user when they type <code>--help</code>.</p>
<p>Our argument will be named <code>--path</code>. The double dashes in front of the name tell the library that this is an optional argument. By default we want to use the current directory, so set the default value to be <code>"."</code>.</p>
<pre><code class="lang-python">parser = argparse.ArgumentParser(
    description=<span class="hljs-string">"Clean up directory and put files into according folders."</span>
)

parser.add_argument(
    <span class="hljs-string">"--path"</span>,
    type=str,
    default=<span class="hljs-string">"."</span>,
    help=<span class="hljs-string">"Directory path of the to be cleaned directory"</span>,
)

<span class="hljs-comment"># parse the arguments given by the user and extract the path</span>
args = parser.parse_args()
path = args.path

print(<span class="hljs-string">f"Cleaning up directory <span class="hljs-subst">{path}</span>"</span>)
</code></pre>
<p>This already finishes the argument parsing section – it's quite simple and readable, right?</p>
<p>Let's execute our script and check for errors.</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; Cleaning up directory ./<span class="hljs-built_in">test</span>
</code></pre>
<p>Once executed, we can see the directory name being printed to the console, perfect.<br>Let's now use the <code>os</code> library to get the files of the given path. </p>
<h3 id="heading-getting-a-list-of-files-from-the-folder">Getting a list of files from the folder</h3>
<p>By using the <code>os.listdir(path)</code> method and providing it a valid path, we get a list of all the files and folders inside of that directory.</p>
<p>After listing all elements in the folder, we want to differentiate between files and folders since we don't want to clean up the folders, only the files.</p>
<p>In this case, we use a Python list comprehension to iterate through all the elements and put them into the new lists if they meet the given requirement of being a file or folder.</p>
<pre><code class="lang-python"><span class="hljs-comment"># get all files from given directory</span>
dir_content = os.listdir(path)

<span class="hljs-comment"># create a relative path from the path to the file and the document name</span>
path_dir_content = [os.path.join(path, doc) <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> dir_content]

<span class="hljs-comment"># filter our directory content into a documents and folders list</span>
docs = [doc <span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> path_dir_content <span class="hljs-keyword">if</span> os.path.isfile(doc)]
folders = [folder <span class="hljs-keyword">for</span> folder <span class="hljs-keyword">in</span> path_dir_content <span class="hljs-keyword">if</span> os.path.isdir(folder)]

<span class="hljs-comment"># counter to keep track of amount of moved files </span>
<span class="hljs-comment"># and list of already created folders to avoid multiple creations</span>
moved = <span class="hljs-number">0</span>
created_folders = []

print(<span class="hljs-string">f"Cleaning up <span class="hljs-subst">{len(docs)}</span> of <span class="hljs-subst">{len(dir_content)}</span> elements."</span>)
</code></pre>
<p>As always, let's make sure that our users get feedback. So add a print statement that gives the user an indication about how many files will be moved.</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; Cleaning up directory ./<span class="hljs-built_in">test</span>
=&gt; Cleaning up 60 of 60 elements.
</code></pre>
<p>After re-executing the python script, we can now see that the <code>/test</code> folder I created contains 60 files that will be moved.</p>
<h3 id="heading-creating-a-folder-for-every-file-extension">Creating a folder for every file extension</h3>
<p>The next and more important step now is to create the folder for each of the file extensions. We want to do this by going through all of our filtered files and if they have an extension for which there is no folder already, create one.</p>
<p>The <code>os</code> library helps us with more nice functionality like the splitting of the filetype and path of a given document, extracting the path itself and name of the document.  </p>
<pre><code class="lang-python"><span class="hljs-comment"># go through all files and move them into according folders</span>
<span class="hljs-keyword">for</span> doc <span class="hljs-keyword">in</span> docs:
    <span class="hljs-comment"># separte name from file extension</span>
    full_doc_path, filetype = os.path.splitext(doc)
    doc_path = os.path.dirname(full_doc_path)
    doc_name = os.path.basename(full_doc_path)

    print(filetype)
    print(full_doc_path)
    print(doc_path)
    print(doc_name)

    <span class="hljs-keyword">break</span>
</code></pre>
<p>The break statement at the end of the code above makes sure that our terminal does not get spammed if our directory contains dozens of files.</p>
<p>Once we've set this up, let's execute our script to see an output similar to this:</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; ...
=&gt; .pdf
=&gt; ./<span class="hljs-built_in">test</span>/test17
=&gt; ./<span class="hljs-built_in">test</span>
=&gt; test17
</code></pre>
<p>We can now see that the implementation above splits off the filetype and then extracts the parts from the full path.</p>
<p>Since we have the filetype now, we can check if a folder with the name of this type already exists. </p>
<p>Before we do that, we want to make sure to skip a few files. If we use the current directory <code>"."</code> as the path, we need to avoid moving the python script itself. A simple if condition takes care of that. </p>
<p>In addition to that, we don't want to move <a target="_blank" href="https://www.lifewire.com/what-is-a-hidden-file-2625898">Hidden Files</a>, so let's also include all files that start with a dot. The <code>.DS_Store</code> file on macOS is an example of a hidden file.</p>
<pre><code class="lang-python">    <span class="hljs-comment"># skip this file when it is in the directory</span>
    <span class="hljs-keyword">if</span> doc_name == <span class="hljs-string">"directory_clean"</span> <span class="hljs-keyword">or</span> doc_name.startswith(<span class="hljs-string">'.'</span>):
        <span class="hljs-keyword">continue</span>

    <span class="hljs-comment"># get the subfolder name and create folder if not exist</span>
    subfolder_path = os.path.join(path, filetype[<span class="hljs-number">1</span>:].lower())

    <span class="hljs-keyword">if</span> subfolder_path <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> folders:
        <span class="hljs-comment"># create the folder</span>
</code></pre>
<p>Once we've taken care of the python script and hidden files, we can now move on to creating the folders on the system.</p>
<p>In addition to our check, if the folder already was there when we read the content of the directory, in the beginning, we need a way to track the folders we've already created. That was the reason we declared the <code>created_folders = []</code> list. It will serve as the memory to track the names of folders.</p>
<p>To create a new folder, the <code>os</code> library provides a method called <code>os.mkdir(folder_path)</code> that takes a path and creates a folder with the given name there. </p>
<p>This method may throw an exception, telling us that the folder already exists. So let's also make sure to catch that error.</p>
<pre><code class="lang-python"><span class="hljs-keyword">if</span> subfolder_path <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> folders <span class="hljs-keyword">and</span> subfolder_path <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> created_folders:
        <span class="hljs-keyword">try</span>:
            os.mkdir(subfolder_path)
            created_folders.append(subfolder_path)
            print(<span class="hljs-string">f"Folder <span class="hljs-subst">{subfolder_path}</span> created."</span>)
        <span class="hljs-keyword">except</span> FileExistsError <span class="hljs-keyword">as</span> err:
            print(<span class="hljs-string">f"Folder already exists at <span class="hljs-subst">{subfolder_path}</span>... <span class="hljs-subst">{err}</span>"</span>)
</code></pre>
<p>After setting up the folder creation, let's re-execute our script.</p>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; ...
=&gt; Folder ./<span class="hljs-built_in">test</span>/pdf created.
</code></pre>
<p>On the first run of execution, we can see a list of logs telling us that the folders with the given types of file extensions have been created.</p>
<h3 id="heading-moving-each-file-into-the-right-subfolder">Moving each file into the right subfolder</h3>
<p>The last step now is to actually move the files into their new parent folders.</p>
<p>An important thing to understand when working with os operations is that sometimes operations can not be undone. This is, for example, the case with deletion. So it makes sense to first only log out the behavior our script would achieve if we execute it.</p>
<p>This is why the <code>os.rename(...)</code> method has been commented here.</p>
<pre><code class="lang-python"><span class="hljs-comment"># get the new folder path and move the file</span>
    new_doc_path = os.path.join(subfolder_path, doc_name) + filetype
    <span class="hljs-comment"># os.rename(doc, new_doc_path)</span>
    moved += <span class="hljs-number">1</span>

    print(<span class="hljs-string">f"Moved file <span class="hljs-subst">{doc}</span> to <span class="hljs-subst">{new_doc_path}</span>"</span>)
</code></pre>
<p>After executing our script and seeing the correct logging, we can now remove the comment hash before our <code>os.rename()</code> method and give it a final go.</p>
<pre><code class="lang-python"><span class="hljs-comment"># get the new folder path and move the file</span>
    new_doc_path = os.path.join(subfolder_path, doc_name) + filetype
    os.rename(doc, new_doc_path)
    moved += <span class="hljs-number">1</span>

    print(<span class="hljs-string">f"Moved file <span class="hljs-subst">{doc}</span> to <span class="hljs-subst">{new_doc_path}</span>"</span>)

print(<span class="hljs-string">f"Renamed <span class="hljs-subst">{moved}</span> of <span class="hljs-subst">{len(docs)}</span> files."</span>)
</code></pre>
<pre><code class="lang-bash">python directory_clean.py --path ./<span class="hljs-built_in">test</span> 

=&gt; ...
=&gt; Moved file ./<span class="hljs-built_in">test</span>/test17.pdf to ./<span class="hljs-built_in">test</span>/pdf/test17.pdf
=&gt; ...
=&gt; Renamed 60 of 60 files.
</code></pre>
<p>This final execution will now move all the files into their appropriate folders and our directory will be nicely cleaned up without the need for manual actions.</p>
<p>In the next step, we could now use the script we created above and, for example, schedule it to execute every Monday to clean up our Downloads folder for more structure.</p>
<p><strong>That is exactly what we are creating as a follow-up inside of <a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">our Bot Creation and Workflow Automation Udemy course</a>.</strong></p>
<h2 id="heading-a-complete-guide-to-bot-creation-and-automating-your-everyday-workhttpswwwudemycomcoursethe-complete-guide-to-bot-creation"><a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">A Complete Guide to Bot Creation and Automating Your Everyday Work</a></h2>
<p>Felix and I built an <strong>online video course to teach you how to create your own bots</strong> based on what we've learned building <strong>InstaPy</strong> and his <strong>Travian-Bot</strong>. In fact, he <strong>was even forced to take down since it was too effective.</strong></p>
<h3 id="heading-join-right-in-and-start-learninghttpswwwudemycomcoursethe-complete-guide-to-bot-creation"><a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">Join right in and start learning</a>.</h3>
<div class="embed-wrapper">
        <iframe width="560" height="315" src="https://www.youtube.com/embed/zw20WBPjsr0" 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>If you have any questions or feedback, feel free to reach out to us on <a target="_blank" href="https://twitter.com/timigrossmann">Twitter</a> or directly in the <a target="_blank" href="https://www.udemy.com/course/the-complete-guide-to-bot-creation/">discussion section of the course</a> ?</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build a SlackBot with Node.js and SlackBots.js ]]>
                </title>
                <description>
                    <![CDATA[ Slack is an American cloud-based set of proprietary team collaboration software tools and online services, developed by Slack Technologies. Slack is a workspace where teams can communicate and collaborate. Teamwork in Slack happens in channels — a si... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-a-slackbot-with-node-js-and-slackbots-js/</link>
                <guid isPermaLink="false">66d84e0d7211ea6be29e1b41</guid>
                
                    <category>
                        <![CDATA[ 100DaysOfCode ]]>
                    </category>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Bolaji Ayodeji ]]>
                </dc:creator>
                <pubDate>Mon, 12 Aug 2019 14:01:46 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2020/05/article-banner--4-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Slack is an American cloud-based set of proprietary team collaboration software tools and online services, developed by Slack Technologies. Slack is a workspace where teams can communicate and collaborate.</p>
<p>Teamwork in Slack happens in channels — a single place for messaging, tools and files — helping everyone save time and collaborate.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/organize-conversations-slack-product-desktop.png" alt="Image" width="600" height="400" loading="lazy"></p>
<hr>
<p>One of the awesome features of Slack is <a target="_blank" href="https://slack.com/apps">Slack Apps</a>, integrations and <a target="_blank" href="https://api.slack.com/bot-users">Slack Bots</a>.</p>
<p>A Slack bot is a type of Slack App designed to interact with users via conversation. Your bot can send DMs, it can be mentioned by users, it can post messages or upload files, and it can be invited to channels. Cool right?</p>
<p>If you use Slack already, you should be familiar with some creative Slack bots like <a target="_blank" href="https://standupbot.com/">Standupbot</a>, <a target="_blank" href="https://birthdaybot.io/">Birthdaybot</a> and more.</p>
<p>In this article, I'll walk you through building your first Slack bot from start to finish with <a target="_blank" href="http://nodejs.org/">Node.js</a> and <a target="_blank" href="https://github.com/mishk0/slack-bot-api">SlackBots.js</a></p>
<blockquote>
<p>PS: This article was published <a target="_blank" href="https://www.bolajiayodeji.com/building-a-slackbot-with-node-js-and-slackbots-js/">on my blog first</a>.</p>
</blockquote>
<h1 id="heading-slackbot-description">SlackBot Description</h1>
<p>We're going to build a simple Slackbot that displays random inspiring techie quotes and jokes for developers/designers.</p>
<p>I built a <a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggets">chrome extension</a> that displays random inspiring techie quotes for developers/designers on your new tab (you can download it <a target="_blank" href="https://chrome.google.com/webstore/detail/inspirenuggets-for-chrome/acnfgdioohhajabdofaadfdhmlkphmlb">here</a>). We'll be using the quotes JSON from this extension as our quotes API and the <a target="_blank" href="https://api.chucknorris.io/">Chuck Norris Jokes API</a> for the jokes.</p>
<p>When a user mentions our bot and adds <strong>inspire me</strong>, the bot returns a random quote from <a target="_blank" href="https://chrome.google.com/webstore/detail/inspirenuggets-for-chrome/acnfgdioohhajabdofaadfdhmlkphmlb">inspireNuggets</a>. When the user types <strong>random joke</strong>, it returns a random joke from the <a target="_blank" href="https://api.chucknorris.io/">Chuck Norris</a> API. And when the user types help, it returns the instruction guide.</p>
<blockquote>
<p>@inspirenuggets inspire me</p>
<p>@inspirenuggets random joke</p>
<p>@inspirenuggets help</p>
</blockquote>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture2.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This article is not really about what we'll be building - it's just to show you the concept behind Slack bots and how to build yours. After you go through it, you can think about something else and build a different bot, as there're many possibilities.</p>
<p>You can clone or fork the final project <a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggetsSlackBot">here</a>.</p>
<p>Pretty interesting right? Let's get started.</p>
<h1 id="heading-prerequisites">Prerequisites</h1>
<p>We'll build this bot with Node.js and SlackBots.js. You don't need to know how to write Node.js, since I'll walk you through it. Still, knowing it is an advantage. You should also have</p>
<ul>
<li><p>Basic JavaScript knowledge</p>
</li>
<li><p>ES6 JavaScript</p>
</li>
<li><p>Slack workspace</p>
</li>
<li><p>Some experience with Slack</p>
</li>
<li><p>Some version control skills</p>
</li>
</ul>
<h1 id="heading-setup-environment">Setup environment</h1>
<p>Let's set up and install Node.js and Npm first.</p>
<ul>
<li><p>Download node <a target="_blank" href="https://nodejs.org/en/">here</a>. If you have it installed already, skip this step. If you prefer to use a package manager to install, read <a target="_blank" href="https://nodejs.org/en/download/package-manager/#windows">this</a> for all operating systems.</p>
</li>
<li><p>Check if you have Node installed</p>
</li>
</ul>
<pre><code class="lang-python">node -v
</code></pre>
<ul>
<li>Node.js comes with Npm, so you don't have to install that again.</li>
</ul>
<pre><code class="lang-python">npm -v
</code></pre>
<p>Now that we have Node.js setup, let's initialize our project.</p>
<p>Create your project directory (I called mine Slackbot) and initialize git:</p>
<pre><code class="lang-python">git init
</code></pre>
<p>Next, create an <code>index.js</code> file:</p>
<pre><code class="lang-python">touch index.js
</code></pre>
<p>And initialize Npm:</p>
<pre><code class="lang-python">npm init
</code></pre>
<p>Simply answer all questions that come afterwards. If you're having issues, here's my own <code>package.json</code>:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"slackbot"</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">"A simple Slackbot that displays random inspiring techie quotes for developers/designers."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"index.js"</span>
  },
  <span class="hljs-attr">"repository"</span>: {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"git"</span>,
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"git+https://github.com/BolajiAyodeji/slackbot.git"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Bolaji Ayodeji"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"bugs"</span>: {
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/slackbot/issues"</span>
  },
  <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/slackbot#readme"</span>
}
</code></pre>
<h1 id="heading-install-dependencies">Install Dependencies</h1>
<p><strong>Now let's install and setup all the libraries we need.</strong></p>
<h2 id="heading-slackbotsjs">SlackBots.js</h2>
<p><a target="_blank" href="https://github.com/mishk0/slack-bot-api">SlackBots.js</a> is a Node.js library for easy operation with the Slack API.</p>
<pre><code class="lang-python">npm install slackbots
</code></pre>
<p>In <code>index.js</code>:</p>
<pre><code class="lang-python">const SlackBot = require(<span class="hljs-string">'slackbots'</span>);
</code></pre>
<h2 id="heading-axios">Axios</h2>
<p><a target="_blank" href="https://github.com/axios/axios">Axios</a> is a promise-based HTTP client for the browser and node.js. If you know Fetch or AJAX, this is just a library that does the same thing with way cooler features. You can see them <a target="_blank" href="https://github.com/axios/axios">here</a>.</p>
<pre><code class="lang-python">npm install axios
</code></pre>
<p>In <code>index.js</code>:</p>
<pre><code class="lang-python">const axios = require(<span class="hljs-string">'axios'</span>)
</code></pre>
<h2 id="heading-nodemon">Nodemon</h2>
<p>To run a script in Node.js, you have to run <code>node index.js</code>. Whenever you make changes to this file, you have to rerun <code>node index.js</code>. This sucks when you're making so many changes like we'll be doing. That's why we need <a target="_blank" href="https://github.com/remy/nodemon">nodemon</a>, a tool that helps develop node.js based applications by automatically restarting the node application when file changes in the directory are detected.</p>
<pre><code class="lang-python">npm install -g nodemon
</code></pre>
<p>In <code>package.json</code>, locate the scripts section and add a new start script:</p>
<pre><code class="lang-python"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"node index.js"</span>
  }
</code></pre>
<p>If you run <code>npm start</code>, the file will run but won't restart on change. To fix this, use the nodemon we installed instead of node like so:</p>
<pre><code class="lang-python"><span class="hljs-string">"scripts"</span>: {
    <span class="hljs-string">"start"</span>: <span class="hljs-string">"nodemon index.js"</span>
  }
</code></pre>
<h1 id="heading-dotenv">Dotenv</h1>
<p>I won't explain this in-depth. In a few days, I'll publish an article around environmental variables, but for now just know that we use this to hide secret keys and tokens like the Slack Access Token we would be using. This way you don't have to push your secret keys to GitHub.</p>
<p>There are several ways to do this, but I prefer using dotenv. <a target="_blank" href="https://github.com/motdotla/dotenv">Dotenv</a> is a zero-dependency module that loads environment variables from a .env file into process.env.</p>
<pre><code class="lang-python">npm install dotenv
</code></pre>
<p>In <code>index.js</code>:</p>
<pre><code class="lang-python">const dotenv = require(<span class="hljs-string">'dotenv'</span>)

dotenv.config()
</code></pre>
<p>After all installation, your <code>package.json</code> should look like this:</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"inspireNuggetsSlackBot"</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">"A simple Slackbot that displays random inspiring techie quotes and jokes for developers/designers."</span>,
  <span class="hljs-attr">"main"</span>: <span class="hljs-string">"index.js"</span>,
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"start"</span>: <span class="hljs-string">"nodemon index.js"</span>
  },
  <span class="hljs-attr">"repository"</span>: {
    <span class="hljs-attr">"type"</span>: <span class="hljs-string">"git"</span>,
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"git+https://github.com/BolajiAyodeji/inspireNuggetsSlackBot.git"</span>
  },
  <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Bolaji Ayodeji"</span>,
  <span class="hljs-attr">"license"</span>: <span class="hljs-string">"MIT"</span>,
  <span class="hljs-attr">"bugs"</span>: {
    <span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/inspireNuggetsSlackBot/issues"</span>
  },
  <span class="hljs-attr">"homepage"</span>: <span class="hljs-string">"https://github.com/BolajiAyodeji/inspireNuggetsSlackBot#readme"</span>,
  <span class="hljs-attr">"devDependencies"</span>: {
    <span class="hljs-attr">"dotenv"</span>: <span class="hljs-string">"^8.0.0"</span>
  },
  <span class="hljs-attr">"dependencies"</span>: {
    <span class="hljs-attr">"axios"</span>: <span class="hljs-string">"^0.19.0"</span>,
    <span class="hljs-attr">"slackbots"</span>: <span class="hljs-string">"^1.2.0"</span>
  }
}
</code></pre>
<h1 id="heading-create-your-slack-workspace">Create your Slack workspace</h1>
<p>Now that we have that all set up, we need a Slack workspace to run our bot in development. Creating a workspace is pretty easy, read <a target="_blank" href="https://get.slack.help/hc/en-us/articles/206845317-Create-a-Slack-workspace">this</a> to learn more.</p>
<h1 id="heading-register-your-slack-bot">Register your Slack Bot</h1>
<p>Now that you have a workspace, you should have a Slack URL with your workspace name. Mine is <code>mekafindteam.slack.com</code>.</p>
<p>Now you'll need to create a Slack App. Create one <a target="_blank" href="https://api.slack.com/apps/new">here</a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture5.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Enter your App name and ensure you're in the workspace you created if you're in multiple workspaces.</p>
<p>Now you'll see the settings &gt; Basic Information page. Click the first tab <code>Add features and functionality</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture6.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Since we're building a bot, select the <strong>Bots</strong> field.</p>
<p>Now you'll see the Bot user page:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture7.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the <code>Add a Bot User</code> button.</p>
<p>Your display name will automatically be filled in from your already chosen App name. You can update it, but I'll advise you use the same name everywhere with the same alphabet case to avoid errors.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture8.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now, toggle the <code>Always Show My Bot as Online</code> switch to always show your bot as Online. Remember this bot is just like a user in your workspace. Afterwards, click the <code>Add Bot User</code> button.</p>
<p>Save all changes now:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture9.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Next, return to the <code>Basic Information</code> page and select the <code>Install your app to your workspace</code> tab.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture10.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click the <code>Install App to Workspace</code>:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture11.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click allow and wait to be redirected back to the <code>Basic Information</code> page.</p>
<p>Note the <code>Manage distribution</code> tab: this section is needed when you want to make your Bot available for installation by others. For now we're just building in development and I won't be covering distribution in this article. In my next article, I'll show you how to deploy your Slack bot and make it available as an App to other workspaces.</p>
<p>If you check your Slack workspace now, you should see the App installed in the Apps section.</p>
<p>For now, it's offline - once we start building the bot, we'll turn this on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture15.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-customize-your-slack-bot">Customize your Slack bot</h1>
<p>Now we've created our bot, let's do some customization.</p>
<p>Still, on the <code>Basic Information</code> page, scroll down to the <code>Display Information</code> section:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture12.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is basic stuff: just upload a logo, change your background color, and add a short description.</p>
<p>Your icon should be <code>512x512px</code> or bigger and your background color should be in HEX. Read more on the App guidelines <a target="_blank" href="https://api.slack.com/docs/slack-apps-guidelines">here</a>.</p>
<p>Here's what mine looks like after customization:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture13.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h1 id="heading-slack-bot-oauth-tokens">Slack bot OAuth Tokens</h1>
<p>Now that we have our Slack bot setup, let's grab out token keys.</p>
<p>In the navigation bar, locate the Features section and click the <code>OAuth &amp; Permission</code> tab:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture16.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You'll see two Access Tokens:</p>
<ul>
<li><p>OAuth Access Token</p>
</li>
<li><p>Bot User OAuth Access Token</p>
</li>
</ul>
<p>Copy the <strong>Bot User OAuth Access Token.</strong></p>
<p>This will change every time you re-install this app or when you install it in another workspace. The token should start with <code>xoxb-</code>.</p>
<blockquote>
<p>Keeping credentials secure is important whether you're developing open source libraries and tools, internal integrations for your workspace, or Slack apps for distribution to workspaces across the world. - Slack</p>
</blockquote>
<p>This is why we have installed Dotenv - we'll set that up in the next section.</p>
<h1 id="heading-building-the-bot">Building the bot</h1>
<p>Now let's build our bot :).</p>
<h3 id="heading-first-lets-keep-our-access-token-somewhere">First, let's keep our Access Token somewhere.</h3>
<p>Create a <code>.env</code> file and add this:</p>
<pre><code class="lang-python">BOT_TOKEN=YOUR_SLACK_ACCESS_TOKEN_HERE
</code></pre>
<p>Now let's start our SlackBot.js:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> bot = <span class="hljs-keyword">new</span> SlackBot({
    <span class="hljs-attr">token</span>: <span class="hljs-string">`<span class="hljs-subst">${process.env.BOT_TOKEN}</span>`</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'inspirenuggets'</span>
})
</code></pre>
<p>We've just created a bot variable that initializes a new SlackBot instance which has two values, our token and app name.</p>
<p>I used the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals">ES6 template string syntax</a> to bring in our token key from our <code>.env</code> file. dotenv has this covered for us.</p>
<p>Make sure you use the same name you used while creating your Slack app, or else you'll have authentication errors.</p>
<p>Now start the app:</p>
<pre><code class="lang-python">npm start
</code></pre>
<p>nodemon should be running now and our Slack app should be online too.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture17.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture18.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-start-handler">Start handler</h3>
<p>Our Bot does nothing now even though it's running. Let's return a message.</p>
<pre><code class="lang-javascript">bot.on(<span class="hljs-string">'start'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':robot_face:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">'Get inspired while working with @inspirenuggets'</span>,
        params
    );
})
</code></pre>
<p>The <code>bot.on</code> handler sends the welcome message. We passed two parameters, the <code>'start'</code> and a function which holds a params variable which also holds the slack emoji. Slack emoji have codes, and you can find them <a target="_blank" href="https://slackmojis.com/">here</a>. I used <code>:robot_face:</code>, but you can change this to your preferred emoji.</p>
<p>We also initialized the <code>bot.postMessageToChannel</code> function which is a SlackBot.js method to post a message to a channel. In this function, we pass the channel name we want to post to, the message in a string, and the params variable we declared earlier for the emoji. I used the <strong>#random</strong> channel and sent <code>Get inspired while working with @inspirenuggets</code> to it. Your app should restart automatically and your bot should do this:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture19.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Cool right?<br>You can also post messages to users and groups.</p>
<pre><code class="lang-javascript">    <span class="hljs-comment">// define existing username instead of 'user_name'</span>
    bot.postMessageToUser(<span class="hljs-string">'user_name'</span>, <span class="hljs-string">'Hello world!'</span>, params); 


    <span class="hljs-comment">// define private group instead of 'private_group', where bot exist</span>
    bot.postMessageToGroup(<span class="hljs-string">'private_group'</span>, <span class="hljs-string">'Hello world!'</span>, params);
</code></pre>
<h3 id="heading-error-handler">Error Handler</h3>
<p>Let's also write a function to check for errors and return them:</p>
<pre><code class="lang-javascript">bot.on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err);
})
</code></pre>
<h3 id="heading-message-handler">Message Handler</h3>
<p>Now let's build the main bot functionality.</p>
<p>Like I said earlier, we'll be using the quotes JSON from the extension I built as our quotes API. The JSON can be found with this URL: <code>https://raw.githubusercontent.com/BolajiAyodeji/inspireNuggets/master/src/quotes.json</code></p>
<p>When a user mentions our bot and adds <strong>inspire me</strong>, the bot returns a random quote from <a target="_blank" href="https://chrome.google.com/webstore/detail/inspirenuggets-for-chrome/acnfgdioohhajabdofaadfdhmlkphmlb">inspireNuggets</a>. When the user types <strong>random joke</strong>, it returns a random joke from the <a target="_blank" href="https://api.chucknorris.io/">Chuck Norris</a> API. And when the user types <strong>help</strong>, it returns the instruction guide.</p>
<p>First, let's check for our command words from the user message (<strong>inspire me</strong>, <strong>random joke,</strong> and <strong>help</strong>):</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleMessage</span>(<span class="hljs-params">message</span>) </span>{
    <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' inspire me'</span>)) {
        inspireMe()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' random joke'</span>)) {
        randomJoke()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' help'</span>)) {
        runHelp()
    }
}
</code></pre>
<p>Now let's create the three function we need</p>
<p><strong>inspireMe()</strong></p>
<p>Our demo JSON is not really an API, it's just some JSON I used in the Chrome Extension. We're only accessing it from GitHub raw contents. You can use any API you prefer, you'll just have to iterate differently to get your data depending on if your API returns an array or object - whichever it returns, it's not a big deal.</p>
<p>Check out my previous articles on:</p>
<ul>
<li><p><a target="_blank" href="https://www.bolajiayodeji.com/manipulating-arrays-in-javascript/">Manipulating Arrays in JavaScript</a> and</p>
</li>
<li><p><a target="_blank" href="https://www.bolajiayodeji.com/iterating-through-javascript-objects-5-techniques-and-performance-tests/">Iterating through JavaScript Objects  -  5 Techniques and Performance Tests.</a></p>
</li>
</ul>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inspireMe</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://raw.githubusercontent.com/BolajiAyodeji/inspireNuggets/master/src/quotes.json'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> quotes = res.data;
            <span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * quotes.length);
            <span class="hljs-keyword">const</span> quote = quotes[random].quote
            <span class="hljs-keyword">const</span> author = quotes[random].author

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':male-technologist:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${quote}</span> - *<span class="hljs-subst">${author}</span>*`</span>,
                params
            );

      })
}
</code></pre>
<p>We just used Axios to get the JSON file which returns some data:</p>
<pre><code class="lang-json">[
    {
        <span class="hljs-attr">"number"</span>: <span class="hljs-string">"1"</span>,
        <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Von R. Glitschka"</span>,
        <span class="hljs-attr">"quote"</span>: <span class="hljs-string">"The client may be king, but he's not the art director."</span>
    },
    {
        <span class="hljs-attr">"number"</span>: <span class="hljs-string">"2"</span>,
        <span class="hljs-attr">"author"</span>: <span class="hljs-string">"Frank Capra"</span>,
        <span class="hljs-attr">"quote"</span>: <span class="hljs-string">"A hunch is creativity trying to tell you something."</span>
    },
.
.
.
.
]
</code></pre>
<p>This JSON currently contains 210 quotes and I update them frequently. So we want to get a random quote plus the author name every time the user request it. From our Axios response, we just do this:</p>
<pre><code class="lang-javascript">
<span class="hljs-keyword">const</span> quotes = res.data;
<span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * quotes.length);
<span class="hljs-keyword">const</span> quote = quotes[random].quote
<span class="hljs-keyword">const</span> author = quotes[random].author
</code></pre>
<p>And just like we did with the welcome message, we just return the quote and author instead of a string message:</p>
<pre><code class="lang-javascript"><span class="hljs-string">`:zap: <span class="hljs-subst">${quote}</span> - *<span class="hljs-subst">${author}</span>*`</span>
</code></pre>
<p>Let's test this:</p>
<p>Type <code>@inspirenuggets inspire me</code></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture20.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Yayyy! It worked!</p>
<p>PS: You can always change the emoji type for every request. If you noticed I changed the <code>inspireMe()</code> to <code>:male-technologist:</code></p>
<p><strong>randomJoke()</strong></p>
<p>We're getting the jokes from the Chuck Norris API from this endpoint <code>https://api.chucknorris.io/jokes/random</code>.</p>
<pre><code class="lang-json">{
<span class="hljs-attr">"categories"</span>: [],
<span class="hljs-attr">"created_at"</span>: <span class="hljs-string">"2016-05-01 10:51:41.584544"</span>,
<span class="hljs-attr">"icon_url"</span>: <span class="hljs-string">"https://assets.chucknorris.host/img/avatar/chuck-norris.png"</span>,
<span class="hljs-attr">"id"</span>: <span class="hljs-string">"6vUvusBeSVqdsU9C5-ZJZw"</span>,
<span class="hljs-attr">"updated_at"</span>: <span class="hljs-string">"2016-05-01 10:51:41.584544"</span>,
<span class="hljs-attr">"url"</span>: <span class="hljs-string">"https://api.chucknorris.io/jokes/6vUvusBeSVqdsU9C5-ZJZw"</span>,
<span class="hljs-attr">"value"</span>: <span class="hljs-string">"Chuck Norris once choked a wildcat to death with his sphincter muscle."</span>
}
</code></pre>
<p>This is a real API that returns a random joke on every request, so we don't have to do <code>Math.floor()</code> again.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">randomJoke</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://api.chucknorris.io/jokes/random'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> joke = res.data.value;

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':smile:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${joke}</span>`</span>,
                params
            );

      })
}
</code></pre>
<p>By now, you should understand how this works already. Make a post with the channel name, message and params.</p>
<p><strong>runHelp()</strong></p>
<p>This is similar to our welcome message: we just want to return a custom text when the user adds <strong>help</strong> to the request.</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">runHelp</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':question:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">`Type *@inspirenuggets* with *inspire me* to get an inspiring techie quote, *random joke* to get a Chuck Norris random joke and *help* to get this instruction again`</span>,
        params
    );
}
</code></pre>
<p>Now let's test all three commands:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2019/10/capture2-1.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Everything works fine now, congratulations!!!! You just built your SlackBot.</p>
<hr>
<p>There are an endless number of possibilities of Bots you can build with this to automate your own work or teamwork.</p>
<p>You can build a bot that:</p>
<ul>
<li><p>Fetches your tasks from somewhere and reminds you when you type <code>hey what next</code>,</p>
</li>
<li><p>Welcomes every user to your workspace (I built this during one of the <a target="_blank" href="https://hng.tech/">HNG Internship's</a>),</p>
</li>
<li><p>Gives you football matches updates while you're working,</p>
</li>
<li><p>Tells your team when you hit a milestone in number of registered users,</p>
</li>
</ul>
<p>and many more...</p>
<p>It's just about having somewhere to get the data from, and some basic iteration skills and the <code>bot.postMessageToChannel()</code> method.</p>
<p>Automation is one thing we should learn as developers. We have a lot to do, so we should automate the simpler tasks so we have time for the more difficult ones. I hope with this you can automate your tasks and I look forward to the creative ideas you'll bring to life.</p>
<hr>
<h1 id="heading-final-code">Final Code</h1>
<p>Here's our final <code>index.js</code></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> SlackBot = <span class="hljs-built_in">require</span>(<span class="hljs-string">'slackbots'</span>);
<span class="hljs-keyword">const</span> axios = <span class="hljs-built_in">require</span>(<span class="hljs-string">'axios'</span>)
<span class="hljs-keyword">const</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>)

dotenv.config()

<span class="hljs-keyword">const</span> bot = <span class="hljs-keyword">new</span> SlackBot({
    <span class="hljs-attr">token</span>: <span class="hljs-string">`<span class="hljs-subst">${process.env.BOT_TOKEN}</span>`</span>,
    <span class="hljs-attr">name</span>: <span class="hljs-string">'inspirenuggets'</span>
})

<span class="hljs-comment">// Start Handler</span>
bot.on(<span class="hljs-string">'start'</span>, <span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':robot_face:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">'Get inspired while working with @inspirenuggets'</span>,
        params
    );
})

<span class="hljs-comment">// Error Handler</span>
bot.on(<span class="hljs-string">'error'</span>, <span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(err);
})

<span class="hljs-comment">// Message Handler</span>
bot.on(<span class="hljs-string">'message'</span>, <span class="hljs-function">(<span class="hljs-params">data</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span>(data.type !== <span class="hljs-string">'message'</span>) {
        <span class="hljs-keyword">return</span>;
    }
    handleMessage(data.text);
})

<span class="hljs-comment">// Response Handler</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleMessage</span>(<span class="hljs-params">message</span>) </span>{
    <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' inspire me'</span>)) {
        inspireMe()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' random joke'</span>)) {
        randomJoke()
    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span>(message.includes(<span class="hljs-string">' help'</span>)) {
        runHelp()
    }
}

<span class="hljs-comment">// inspire Me</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inspireMe</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://raw.githubusercontent.com/BolajiAyodeji/inspireNuggets/master/src/quotes.json'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> quotes = res.data;
            <span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random() * quotes.length);
            <span class="hljs-keyword">const</span> quote = quotes[random].quote
            <span class="hljs-keyword">const</span> author = quotes[random].author

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':male-technologist:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${quote}</span> - *<span class="hljs-subst">${author}</span>*`</span>,
                params
            );

      })
}

<span class="hljs-comment">// Random Joke</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">randomJoke</span>(<span class="hljs-params"></span>) </span>{
    axios.get(<span class="hljs-string">'https://api.chucknorris.io/jokes/random'</span>)
      .then(<span class="hljs-function"><span class="hljs-params">res</span> =&gt;</span> {
            <span class="hljs-keyword">const</span> joke = res.data.value;

            <span class="hljs-keyword">const</span> params = {
                <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':smile:'</span>
            }

            bot.postMessageToChannel(
                <span class="hljs-string">'random'</span>,
                <span class="hljs-string">`:zap: <span class="hljs-subst">${joke}</span>`</span>,
                params
            );

      })
}

<span class="hljs-comment">// Show Help</span>
<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">runHelp</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-keyword">const</span> params = {
        <span class="hljs-attr">icon_emoji</span>: <span class="hljs-string">':question:'</span>
    }

    bot.postMessageToChannel(
        <span class="hljs-string">'random'</span>,
        <span class="hljs-string">`Type *@inspirenuggets* with *inspire me* to get an inspiring techie quote, *random joke* to get a Chuck Norris random joke and *help* to get this instruction again`</span>,
        params
    );
}
</code></pre>
<h1 id="heading-what-next">What Next?</h1>
<p>Our bot only runs in development now, and to use it we always have to <code>npm start</code>.</p>
<p>This isn't cool, right? We'll want to host it somewhere it can run every time. In my next article, I'll show you how to host this on either <a target="_blank" href="https://herokuapp.com/">Heroku</a>, <a target="_blank" href="https://zeit.co/">Zeit</a> or <a target="_blank" href="https://netlify.com">Netlify</a> and publish it to the Slack Apps store so anyone around the world can use it.<br>Also, don't forget to add this in your <code>.gitignore</code> before pushing to GitHub:</p>
<pre><code class="lang-python">
/.env
/node_modules
</code></pre>
<blockquote>
<p><strong>Subscribe to my</strong> <a target="_blank" href="https://tinyletter.com/bolajiayodeji/"><strong>newsletter</strong></a> <strong>to get updated.</strong></p>
</blockquote>
<h1 id="heading-useful-resources">Useful Resources</h1>
<ul>
<li><p><a target="_blank" href="https://api.slack.com/">Slack API</a></p>
</li>
<li><p><a target="_blank" href="https://api.slack.com/#read_the_docs">Slack API Docs</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/slackapi/node-slack-sdk">SlackBot.js</a></p>
</li>
<li><p><a target="_blank" href="https://slack.com/intl/en-in/apps">Slack Apps</a></p>
</li>
<li><p><a target="_blank" href="https://api.slack.com/docs/slack-apps-guidelines">Slack Apps Guidelines</a></p>
</li>
<li><p><a target="_blank" href="https://api.slack.com/start/overview">An introduction to Slack apps</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggets">inspireNuggets</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/BolajiAyodeji/inspireNuggetsSlackBot">inspireNuggetsSlackBot</a></p>
</li>
</ul>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build an End-to-End Conversational AI System using Behavior Trees ]]>
                </title>
                <description>
                    <![CDATA[ By Lior Messinger At their core, AI projects can be depicted as a simple pipeline of a few building blocks. The diagram above explains that pretty nicely: Unstructured content, usually in huge amounts of data, comes in from the left, and is fed into ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-an-end-to-end-conversational-ai-system-using-behavior-trees-658a7122e794/</link>
                <guid isPermaLink="false">66c350284f1fc448a367904e</guid>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Fri, 12 Apr 2019 15:48:13 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*gr-Ixi5QqOjf5bFR9ybNag.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Lior Messinger</p>
<p>At their core, AI projects can be depicted as a simple pipeline of a few building blocks. The diagram above explains that pretty nicely: Unstructured content, usually in huge amounts of data, comes in from the left, and is fed into AI classifiers. These pre-trained machine- or deep- learning models separate the wheat from the chaff and reduce the input to a few numerical or string output values.</p>
<p>For example, megas of pixels and colors in an image are reduced to a label: this is a giraffe. Or a zebra. In audio, millions of wave frequencies produce a sentence through Speech To Text models. And in conversational AI, that sentence can be reduced further to a few strings representing the intent of the speaker and the entities in the sentences.</p>
<p>Once the input has been <em>recognized,</em> we need to do things, and generate some meaningful output. For example, a car recognized too close should turn the wheel in an autonomous car. A request to book a flight should produce some RESTFul database queries and POST calls, and issue a confirmation, or a denial, to the user.</p>
<p>This last part, shown in the diagram as rule-based logic, is an inseparable part of any AI system, and there’s no change in sight to that. It has usually been done by coding, thousands and thousands of lines of code — if it’s a serious system — or a couple of scripts if it’s a toy chatbot.</p>
<h3 id="heading-behavior-trees">Behavior Trees</h3>
<p>A <a target="_blank" href="https://en.wikipedia.org/wiki/Behavior_tree_(artificial_intelligence,_robotics_and_control)">Behavior Tree</a> is a programming paradigm that emerged in video games to create human-like behaviors in non-player characters. They form an excellent visual language with which a software architect, a junior developer and even a non-coder, technical designer can all create complex scripts. In fact, since Behavior trees (BTs) allow logic operations like AND and OR, loops and conditions, any program that can be created by code, can be created with BTs.</p>
<p><a target="_blank" href="https://github.com/servo-ai/servo-platform">Servo</a> is an open-source AI conversational framework built on top of a JavaScript behavior tree framework called <a target="_blank" href="https://github.com/behavior3/">Behavior3</a>. It’s designed to do the needed orchestration of inputs and outputs for conversational AI systems. It’s what is called “low-code” framework: you only need to code a little, and most of the tasks can be done in the visual editor.</p>
<p>It’s not your usual newbie's toy: it was designed to be extended using real-life, debuggable JS source files and classes, and abide by any team project-management methodologies. Moreover, it’s suitable for teams that grow in size, allowing the introduction and reuse of new modules through abstracted and decoupled sub-processes.</p>
<p>I’m the main developer of Servo. After 30-something years of coding, feeling the pain in long-delayed projects and watching legacy systems break under their own weight, I wanted to achieve maximum flexibility with minimum coding. Here I’m going to explain the magic that can be done when one combines Behavior Trees with NLU/NLP engines, using Servo and <a target="_blank" href="https://www.wit.ai/">Wit.ai</a>.</p>
<p>Any developer can benefit from this tutorial, but it’s best if you are a developer with experience in building chat- or voice-bots and have knowledge working with NLU/NLP engines like LUIS, Wit.AI, Lex (the Alexa engine), or Dialogflow. If you do not, it’s ok, but I’ll be covering some subjects a bit briefly.</p>
<p>If you want to learn about NLU and NLP engines, there are excellent resources all over the Internet — just search for ‘Wit tutorial’. If you want to learn how to build a heavyweight assistant, then just continue reading.</p>
<h3 id="heading-getting-started-with-servo">Getting Started with Servo</h3>
<p>I won’t get into details of installing Servo here, but just say that getting started with Servo is really easy. You can read about it <a target="_blank" href="https://medium.com/datadriveninvestor/building-context-aware-stateful-bots-using-servo-a2dc3f557469">here</a>. In essence, you clone the <a target="_blank" href="https://github.com/servo-ai/servo-platform">Github re</a><a target="_blank" href="https://github.com/servo-ai/servo-platform">po</a>, npm-install it according to the readme, and run it locally. Then, every New Project would start you off with a small ready-made bot to get things going:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/6sE9DDHxTVQymZl9jk9I6SlhRyKBkgo6LQLt" alt="Image" width="800" height="391" loading="lazy">
<em>Every New Project has a starting tree</em></p>
<p>You can notice here the green hexagons named ‘chit-chat’, ‘cancel’ and others. By the end of this article, you’ll have a clear idea of what they are and how they work. But first, let’s tackle the first challenges.</p>
<h3 id="heading-building-an-nlu-model">Building An NLU Model</h3>
<p>Let’s talk about building a banking assistant, and specifically, one that works for the money transfer department. Had it been a web application, we would have a form with few fields, among which the amount and the transferee’s (also called beneficiary) account are the most important. Let’s use these here for this tutorial. Actually, when using NLU engines, we can still think of it as forms, with the fields now called <em>entities</em> (also <em>slots,</em> in Alexa lingo). The NLU engines also produce an intent, which can be viewed as the name of the form that will guide the assistant to the area of that functionality of the user’s intent.</p>
<p>We should train the NLU engine with a few sentences, such as:</p>
<ul>
<li><em>“I’d like to send some money”</em></li>
<li><em>“I’d like to send $100 “</em></li>
<li><em>“Please transfer $490 to account #01–10099988”</em></li>
</ul>
<p>And for these, we need to tell the engine to output the following:</p>
<ul>
<li>a <em>TransferIntent</em> intent for such sentences</li>
<li>A <em>wit/number</em> for the amount</li>
<li>An <em>accountNumberEntity</em> for the beneficiary account</li>
</ul>
<p>Let’s do that on Wit.ai. Again, I won’t get into a Wit tutorial — there are plenty of guides. Servo comes with a general Wit model which you can take from Servo’s Github <a target="_blank" href="https://github.com/servo-ai/servo-platform/tree/master/server/convocode/nlu-models/wit.ai">here</a>. Then, open your own Wit app and import it.</p>
<p>I created a <a target="_blank" href="https://wit.ai/docs/recipes#which-entity-should-i-use"><em>free-text</em></a> entity for the account numbers (as account number might include other symbols), and a <em>wit/number</em> entity for the amount. I found <a target="_blank" href="https://medium.com/wit-ai/introducing-composite-entities-ba2639a26e0">composite entities</a> work pretty well, too, although they need some training. For simplicity, for account numbers, I trained the model to be a # followed by 8 digits.</p>
<p>In general, it’s always better to experiment with different entity models. In our case, we might get two numbers in the same sentence (account number and amount) and we need a way to tell them apart, so it’s best if it’s two different entity names. But you can try other types: AI is still a very empirical science…</p>
<p>We then trained it with a few sentences and let Wit build the bank-transfer model. For convenience, I added it <a target="_blank" href="https://github.com/servo-ai/servo-platform/tree/master/server/convocode/nlu-models/wit.ai">here</a>, and also set the whole banking bot tutorial bot to come along with the pre-loaded examples.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/caZ8ij2dktQha7o2gUHDXBWU7HFeh1LtStdS" alt="Image" width="800" height="656" loading="lazy">
<em>Training Wit with sample bank transfer sentences</em></p>
<p>Last, we need to connect the NLU to the assistant. Go to <em>Settings</em> in Wit, and copy the <strong><em>access token</em></strong>. We need to paste it in our tree root’s properties. We do this by opening Servo’s editor, selecting the root, opening its properties, and pasting it under <strong><em>nlu</em></strong>. As you can see, Servo supports multi-language assistants and different NLU engines:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/LwtOTiDXrX4g1TmVNh428PR7A1dcm-errJBE" alt="Image" width="1024" height="706" loading="lazy"></p>
<h3 id="heading-start-the-bank-assistant">Start The Bank Assistant</h3>
<p>Now, we can turn to Servo. We should construct a small tree with a question for each entity and intent.</p>
<p>As a reminder, the basic rules of Servo behavior-trees is as follows</p>
<ol>
<li>The main loop of the tree is executing the root continuously</li>
<li>Each node executes its children</li>
<li>An AskAndMap node (the “Age” node in the diagram above) outputs a question to the user and waits for an answer</li>
<li>Once an answer comes in, the flow is routed to the appropriate child according to the intent and entities that the NLU engine gave it</li>
</ol>
<p>Let’s first change the main, topmost question from <strong><em>“Age?”</em></strong> into <strong><em>“What would you like to do?”</em></strong>. Also, let’s delete the first (that is, left-most) child and its nodes, as we are not going to use them anymore:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/6uwN7pG2TCHq8kU6oIm6-flNzeI91jYxNEEc" alt="Image" width="1194" height="706" loading="lazy">
<em>Type in the initial assistant question</em></p>
<p>Why are we seeing the red dashes around the node? Hover over it and you’ll see the error:</p>
<p><em>Count of contexts number should be equal to the number of children</em></p>
<p>We will fix that in a minute.</p>
<p>Now let’s build the transfer flow. We’ll assume that once the user says “I’d like to wire money”, we want to descend into the first, leftmost child. For that, we’ll select the “How can I help you” node and go into its properties. There, change the first context to have an <strong>intentId</strong> of <em>“TransferIntent”</em>:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/uTc0c33ugCcriSii5HG2tdvEapPuWmHfGVQx" alt="Image" width="800" height="568" loading="lazy"></p>
<p>This will cause any sentence that the Wit determines to have a TransferIntent, to be routed there.</p>
<h3 id="heading-mapping-an-entity">Mapping an entity</h3>
<p>Now, once the NLU has recognized our intent to transfer money, we should get all the different “fields”, or entities. Let’s add a node for the <strong><em>amount</em></strong>:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/l2FnqEyC9qTdllUVE-kAN34RzoNbewEQqf6m" alt="Image" width="1273" height="706" loading="lazy"></p>
<p>We added an AskAndMap node, and set its prompt to a question about the amount. We also changed its title — it’s always a good practice. Last, don’t forget to save your work using the Save button or Ctrl-S.</p>
<p>You can also notice the red warning disappeared from the <em>How can I help you</em> node.</p>
<p>Last, let’s add a <strong><em>number</em></strong> <em>entity</em> to one of the child contexts of the Amount node, and map the value into a field called <strong><em>amount</em></strong><em>.</em></p>
<pre><code>“contexts”: [
 {
   “entities”: [
    {
      “contextFieldName”: “amount”,
      “entityName”: “number”,
      “expectedValue”: “”,
      “entityIndex”: <span class="hljs-number">0</span>
    } 
   ]
 }
</code></pre><p>All this seems very simple, and it is: if a user says something like “I need to send some money”, they will be asked, “What is the amount?”. Once they enter the amount, the <strong><em>number</em></strong> will be extracted by the NLU and mapped to the <strong><em>context.amount</em></strong> in Servo. Then, we can use it later in the game. Visually, the flow started from the root:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/HBQ2me6T-igEkyhMaqKYPPWZstYk6yuS7ecX" alt="Image" width="596" height="150" loading="lazy"></p>
<p>And the assistant would ask:</p>
<p><strong><em>“How can I help you?”</em></strong></p>
<p>If the user answered:</p>
<p><strong><em>“I’d like to transfer some money”</em></strong></p>
<p>the NLU engine would output a <strong><em>TransferIntent</em></strong> and the flow would continue downstream to the context it identified — the leftmost child — and ask the next question, about the amount:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/X5dhFMpmKZMCnRvocGYljF-OKkPGOs0TFUcO" alt="Image" width="800" height="336" loading="lazy"></p>
<p>But what if the user doesn’t enter an amount?</p>
<h3 id="heading-building-helpers">Building Helpers</h3>
<p>AskAndMap nodes support another type of a context child, called a <strong><em>Helper</em></strong>. This context is selected when the user answered something that couldn’t be mapped to <em>any other context</em>. Let’s add one into our What’s the amount AskAndMap:</p>
<pre><code><span class="hljs-string">"contexts"</span>:[  
   {  
      <span class="hljs-string">"entities"</span>:[  
         {  
            <span class="hljs-string">"contextFieldName"</span>:<span class="hljs-string">"amount"</span>,
            <span class="hljs-string">"entityName"</span>:<span class="hljs-string">"number"</span>,
            <span class="hljs-string">"expectedValue"</span>:<span class="hljs-string">""</span>,
            <span class="hljs-string">"entityIndex"</span>:<span class="hljs-number">0</span>
         }
      ]
   },
   {  
      <span class="hljs-string">"helper"</span>:<span class="hljs-literal">true</span>
   }
]
</code></pre><p>Let’s now add a right-most child with a message help. Something like:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/kLl4cXh8KcX2cmKncUd8n1IC83VfUOxmcIEK" alt="Image" width="1522" height="706" loading="lazy"></p>
<p>Of course, there can be only one <em>helper</em> context child for the AskAndMap.</p>
<p>One could imagine an example of the flow:</p>
<p>User: <strong><em>“I’d like to transfer some money”</em></strong></p>
<p>Assistant: “<strong><em>What is the amount?</em></strong>”</p>
<p>User: “<strong><em>You think I’d know. But I’m not sure</em></strong>”</p>
<p>Assistant: “<strong><em>Please provide the amount to transfer</em></strong>”</p>
<p>That looks simple: obviously, the assistant didn’t understand the “<strong><em>You think I’d know. But I’m not sure”</em></strong> and went on with the helper message “<strong><em>Please provide the amount to transfer</em></strong>”.</p>
<p>But in fact, if you’ll run the bot, you will get a surprising sentence after that last line:</p>
<p>User: “<strong><em>You think I’d know. But I’m not sure</em></strong>”</p>
<p>Assistant: “<strong><em>Please provide the amount to transfer</em></strong>”</p>
<p>Assistant: <strong><em>“How can I help you?”</em></strong></p>
<p>What happened here? Where did the <strong><em>“How can I help you?”</em></strong> come from?</p>
<p>Here’s the flow. The helper node said its line and returned SUCCESS to its parent, the AskAndMap. This, in turn, returned SUCCESS too, and so on, until the root was reached. At which point, the whole tree was restarted, and we get the initial <strong><em>How can I help you?”</em></strong> question.</p>
<p>So, to avoid that, we need to put a loop before the AskAndMap, so that it won’t return until it <em>really</em> succeeded. That is done with something called a <em>decorator.</em></p>
<h3 id="heading-adding-a-repeat-decorator">Adding a repeat decorator</h3>
<p>Behavior Trees implement loops using <em>decorators</em>, which are nodes that have one parent and one child. Depicted as a rhombus ⧫, we will use here the RepeatUntillSuccess decorator to loop the AskAndMap until it is successfully completed. Receiving a help message would not complete it, so we need to return a FAILURE after the help message. We do that by sequencing a Failer node right after the message. All in all, that’s the decoration we add to the AskAndMap construct:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/X-2TTcbuxGni0WWV6eiiWV9lACdaIcxol55H" alt="Image" width="1522" height="706" loading="lazy"></p>
<p>Now it’s the time to add the next node that would map the beneficiary account number. Again, pretty straight-forward: as before, we add an an AskAndMap with the question for the account number and a map from accountNumberEntity to an accountNumber member on the context. We set it as a child of a RepeatUntilSuccess decorator, and a helper child that explains what’s needed for this entity.</p>
<p>Then, we should add the actual business logic to do the transfer. This would probably mean several API calls with the entities collected. We would simulate this with a message: <strong><em>we are going to transfer $X to account #Y</em></strong>. For that, you need to drag in a GeneralMessage as the first child of the accountNumberEntity, and make its properties as follows:</p>
<pre><code>“debug-log”:””,
 “runningTimeoutSec”:<span class="hljs-number">600</span>,
 “maxRetriesNumber”:<span class="hljs-number">5</span>,
 “replayActionOnReturnFromContextSwitch”:<span class="hljs-literal">true</span>,
 “view”:<span class="hljs-literal">false</span>,
 “prompt”:[ 
 “About to transfer &lt;%=context.amount%&gt; to account &lt;%=context.accountNumber%&gt;”
],
…
</code></pre><p>This is how the tree looks like now:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/04qfwmwrgiugey7IA8Xci0zOOLkIe3JOCyfU" alt="Image" width="800" height="372" loading="lazy"></p>
<p>The tree comes with Servo. It’s files are under <em>server/convocode/anonymous/drafts/bank-bot.</em></p>
<h3 id="heading-running-and-testing">Running and testing</h3>
<p>Let’s test the bot and see what happens with various inputs. Click on the Debugger tab, then the play button ▶️. On the right hand the simulator will pop:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/zPDBlRIMUzxFbWKRMLyQGI45p-sr1OQe3V3W" alt="Image" width="800" height="284" loading="lazy"></p>
<p>You can enter a sentence like:</p>
<p><strong><em>I’d like to send money.</em></strong></p>
<p>That would be answered, as expected, with</p>
<p>“<strong><em>What is the amount?</em></strong>”</p>
<p>And you can put in the amount, and continue.</p>
<p>But what if we say</p>
<p><strong><em>I’d like to send $14141</em></strong>??</p>
<p>Test it, and you’ll see how the assistant nicely jumps over the amount question straight to the account number:</p>
<p>“<strong><em>What is the account number?</em></strong>”</p>
<p>Now, let’s make its life even harder:</p>
<p><strong><em>I’d like to send money to account #87654321</em></strong></p>
<p>Nicely enough, it asks only for the amount. Say you enter $3400, it would then skip the account number (since it knows it already) into the final confirmation sentence:</p>
<p><strong><em>About to transfer 3400 to account #87654321.</em></strong></p>
<p>How does it know to do all that magic?</p>
<h3 id="heading-the-context-flow">The Context Flow</h3>
<p>Servo comes equipped with a powerful context-recognition set of algorithms that helps it do all that. What happened here shows a bit of it. Let’s take the last example. After the assistant asked:</p>
<p><strong><em>“How can I help you?”</em></strong></p>
<p>And the user answered:</p>
<p><strong><em>I’d like to send money to account #87654321</em></strong></p>
<p>The NLU engine output a <strong><em>TransferIntent</em></strong> and the flow continued downstream to the next question, about the amount:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/TzwMBZx2JMHoxXQDzU9-nGaX6jWb7YD27IZm" alt="Image" width="800" height="336" loading="lazy"></p>
<p>But the NLU also returned an <strong>accountNumberEntity!</strong> So before descending, this entity is saved on the <em>‘How can I help you’</em> context. And, every AskAndMap defines its own context.</p>
<p>That’s actually an important remark, so I’ll repeat it: <strong><em>every AskAndMap defines its own context.</em></strong></p>
<p>At any point in the flow, when an entity is mentioned, Servo searches back (read: upwards) in the conversation to find it. If it hasn’t, it would ask for it.</p>
<p>So after the amount is entered, once we continue to the account number node, Servo finds that the <strong><em>accountNumberEntity</em></strong> was already mentioned, and uses it.</p>
<p>By the way, a process of similar characteristics happens also when we get to the last confirming GeneralMessage node. Its prompt reads:</p>
<p><strong>About to transfer &lt;%=context.amount%&gt; to account &lt;%=context.accountNumber%&gt;</strong></p>
<p>To resolve that, Servo searches up the context tree to find the needed entities, or context members.</p>
<p>Does this remind you of something? Folks familiar with JavaScript prototypical inheritance would see that it basically uses the same design. In Servo, we implemented that since we need more control over the variables. But it’s always interesting to see how object-oriented concepts are actually applied to real life, natural conversations.</p>
<p>But what if the user asks something much more unrelated, like:</p>
<p><strong><em>“How much money do I have in my account?”</em></strong></p>
<p>Or more so</p>
<p><strong><em>“Who are you, for heaven’s sake??”</em></strong></p>
<p>To which to bot responds:</p>
<p><strong><em>“I’m an artificial intelligence assistant built by Servo Labs.”</em></strong></p>
<p>Whaaaaaaaaaat?? Where did this come from?</p>
<h3 id="heading-context-and-sub-trees">Context and sub-trees</h3>
<p>Almost all of the structural designs architects use to build manageable large systems can be divided into one of two categories:</p>
<ul>
<li>Reuse</li>
<li>Modularize</li>
</ul>
<p>If Servo is to stand as the infrastructure of large AI systems, it must provide some mechanism for allowing developers to achieve these goals. And that’s where sub-trees come into play.</p>
<p>We mentioned before the green hexagons:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/vOpQmHfU9hfBvwCk5vwPg-ZNakYT6ssQp3vT" alt="Image" width="251" height="106" loading="lazy"></p>
<p>This is a sub-tree. <strong>Double-click it</strong>, and you’ll enter a new tree, with that name. To create a new sub-tree, hover over the <strong>Trees</strong> on the left pane and select <strong>New:</strong></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/KeD7CXJx-i8DJr7Jne94zbddR-AMPJvRc4yG" alt="Image" width="250" height="64" loading="lazy"></p>
<p>A tree with a unique GUID name would appear. Change its name to something meaningful, and build it using any node from the left pane. Once built, you can drag, drop and connect it at any point in any other tree (including itself, by the way, but be very careful about that). Since sub-trees can have many leaf nodes, you can connect them only as leaves, too.</p>
<p>What happened when the user asked the assistant “Who are you”?</p>
<p>First, the NLU, already trained for such questions, returned a <strong><em>WhoAreYouIntent</em></strong>. Then context search was activated. If the conversation was somewhere down in the middle of a transfer conversation, the search went upwards, trying to find a context with WhoAreYouIntent. This context is found: it sits on the 4th context, in the <em>How can I help you</em> node. The flow then was redirected there, meaning, that route was made the active route. The flow here continued downstream into the chit-chat subtree, answered the question, returned up with a SUCCESS and the routing was returned to its previous context, the transfer one.</p>
<p>Here we learned something actually very important. <strong><em>The conversation flows down, but context is searched up.</em></strong> Never forget that:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ZPNg-j6-113QHHDPjO6D9Rfn2lHDmaRl2Z2U" alt="Image" width="779" height="325" loading="lazy">
<em><strong>The conversation flows down, but context is searched up</strong></em></p>
<h3 id="heading-connecting-to-a-messenger-client">Connecting to a messenger client</h3>
<p>Until now, we have used the internal simulator and debugger as our messaging client. Let’s connect our small assistant to a real Facebook messenger. There is one big important change on the root properties of our tree, and that is to change the channel name from the default channel <em>“chatsim”</em> to <em>“facebook”</em>:</p>
<p>"channels":"facebook"</p>
<p>On the Facebook side, these are the main high-level steps that one needs to take:</p>
<ol>
<li>Open a Facebook page under your Facebook account</li>
<li>Create a new Facebook app in the <a target="_blank" href="https://developers.facebook.com/">Facebook developer center</a></li>
<li>Add a messenger functionality to your app</li>
<li>Subscribe the app to listen to events in the page</li>
<li>Set the assistant callback address as the webhook to post to. Servo always publish its bot with the format of</li>
</ol>
<p>/entry//</p>
<p>So for a bank-bot assistant, running on <a target="_blank" href="http://www.mydomain.com%2C/">www.mydomain.com,</a> the address would be:</p>
<p><a target="_blank" href="https://www.mydomain.com/entry/fb/bank-bot">https:///entry/fb/bank-bot</a></p>
<p>You should set it in the page subscription section of the Facebook app, at the developer’s portal. You need to select at least <em>messages</em>, _messaging<em>postbacks</em>, and to match the <em>verify token</em> with the validation token you set in the bot’s root properties:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/TAxJMvN8uOHgzYe6d8u0UNj2ar9NeYh4CbEn" alt="Image" width="687" height="510" loading="lazy"></p>
<p>By the way, <a target="_blank" href="https://serveo.net/">https://serveo.net</a> is a great tunneling system (another alternative is ngrok), if you are developing your assistants, like me, on localhost.</p>
<p>On the assistant root properties, set the same verify token and re-publish it:</p>
<pre><code><span class="hljs-string">"facebook"</span>: {
    <span class="hljs-string">"validationToken"</span>: <span class="hljs-string">"mytoken123"</span>,
    <span class="hljs-string">"accessToken"</span>: <span class="hljs-string">"&lt;token here&gt;"</span>
  },
</code></pre><p>The access token should be set too, taken from Facebook’s messenger area:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/YVCodrLcDGHFb7gZC5xMJjLoPvHVwZk1Xj1z" alt="Image" width="800" height="210" loading="lazy"></p>
<p>Last, select a page to subscribe your webhook to the page events:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/EWZohg-wYOzch3vfNe8Qjc0LWhUBAOsornRk" alt="Image" width="452" height="118" loading="lazy"></p>
<p>and… once you connect all these ends, you should have, at last, a full, orchestrated, end-to-end conversational AI system!</p>
<h3 id="heading-connecting-backends">Connecting Backends</h3>
<p>Real life connections vary, but luckily, most of them these days are done by using RESTFul API. For these, check out the <a target="_blank" href="https://servo-ai.github.io/servo-platform/">documentation</a> on <strong><em>RetrieveJSONAction</em></strong> and <em>PostAction</em>. Once data is retrieved, or a response is received, it is set into a <a target="_blank" href="https://github.com/servo-ai/servo-platform/wiki/Building-context-aware,-stateful-chatbots-using-Servo#hierarchical-memory">memory field (context/global/volatile)</a>. You probably would want to query it. This is done using <strong><em>ArrayQueryAction,</em></strong> which implements an in-memory Mongo-like query language<em>.</em> For direct MongoDB queries, use the <strong><em>MongoQuery</em></strong> action.</p>
<h3 id="heading-in-summary">In Summary</h3>
<p>Servo is an open-source IDE and framework that uses a context-recognition search to place the user on the right conversation and output the right questions. We learned how to construct a simple conversation, and how to wrap such conversations in sub-trees for decoupling and re-use. Servo has many other features that are worth exploring, among which you could find</p>
<ul>
<li>Connectors to Facebook, Alexa, Twilio and Angular</li>
<li>Connectors to MongoDB, Couchbase and LokiJS databases</li>
<li>Harness for automated conversation testing</li>
<li>A Conversation debugger</li>
<li>More actions, conditions and decorators</li>
<li>Flow control mechanisms</li>
<li>Field assignment and compare</li>
<li>Context manipulation</li>
<li>Validation</li>
<li>In-memory mongo-like queries</li>
<li>And any customized action you come up with</li>
</ul>
<p>Feel free to check it out and ask questions on the Github forum or at my @lmessinger Github name. Enjoy!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to create a Twitter bot from scratch with Golang ]]>
                </title>
                <description>
                    <![CDATA[ By Kofo Okesola So a little background: I recently picked up Golang and decided to create a Twitter bot as a side project. Then came the problem. There is little to no documentation on using the Twitter API with Golang ?(particularly the oauth1 and C... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/creating-a-twitter-bot-from-scratch-with-golang-e1f37a66741/</link>
                <guid isPermaLink="false">66c34815a1d481faeda49b29</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ golang ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ technology ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 02 Apr 2019 17:33:50 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*W8H2r9xXvzAyI0U7PEqLOQ.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Kofo Okesola</p>
<p>So a little background: I recently picked up Golang and decided to create a <a target="_blank" href="https://twitter.com/_definethis">Twitter bot</a> as a side project. Then came the problem. There is little to no documentation on using the Twitter API with Golang ?(particularly the oauth1 and CRC encryption parts of it). So after some days of trial and error and finally completing it, I want to share the process. Hopefully, this helps someone out there.</p>
<h3 id="heading-what-are-we-going-to-build"><strong>What are we going to build?</strong></h3>
<p>We will build a Twitter bot that will be served from our local machine. It will respond to any tweet that is tagged in with a “hello world”.</p>
<p>Here’s a brief explanation as to what this go program will do. It will:</p>
<ul>
<li>Listen and respond to webhook <a target="_blank" href="https://developer.twitter.com/en/docs/accounts-and-users/subscribe-account-activity/guides/securing-webhooks">CRC validation</a>.</li>
<li>Register a webhook URL that points to it.</li>
<li>Listen for tweets and respond with “hello world”.</li>
</ul>
<h3 id="heading-what-do-you-need">What do you need?</h3>
<ul>
<li>Some basic knowledge of Golang</li>
<li>An approved Twitter developer account. <a target="_blank" href="https://developer.twitter.com/en/apply-for-access.html">How to apply</a>.</li>
<li>You should have an account activity API dev environment set up — call it <code>dev</code> for this project</li>
<li>A <a target="_blank" href="https://developer.twitter.com/en/apps">Twitter app</a> with generated consumer keys and access tokens (Read and write access)</li>
<li>Golang <a target="_blank" href="https://golang.org/doc/install">installed</a> on your development machine.</li>
<li>Some determination.</li>
</ul>
<h3 id="heading-ready-lets-go">Ready? Let’s Go</h3>
<p>First things first. Create your project folder in your <code>$GOPATH/src/</code> . We’ll be calling this project and our folder <code>hellobot</code> . In it create the intro file <code>/hellobot.go</code></p>
<p>The first thing we need to do is to create an endpoint for our app to listen to CRC checks and respond. Twitter sums up the requirements for the check pretty well.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*smwO5H254k6BLXfzciXgJA.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-setting-up-a-server">Setting up a server</h4>
<p>The first thing we do is load the <code>.env</code> file. For that we are using the <a target="_blank" href="https://github.com/joho/godotenv">godotenv</a> plugin. The .env file is usually in this format:</p>
<pre><code>CONSUMER_KEY=CONSUMER_SECRET=ACCESS_TOKEN_KEY=ACCESS_TOKEN_SECRET=WEBHOOK_ENV=devAPP_URL=
</code></pre><blockquote>
<p>Note: We will be using basic <code>go get</code> to install all our dependencies considering the tiny size of our project</p>
</blockquote>
<p>Then we set up our server using <a target="_blank" href="https://github.com/gorilla/mux">mux</a> as our handler, and listen to the base route and <code>webhook/twitter</code> . If you install this using <code>go install</code> and run <code>hellobot</code>, when you run it and navigate to your localhost:9090 you should see the message</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*3OhpTAjdKTs8LV63wwA1ZQ.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-crc-validation">CRC validation</h4>
<p>Now for the CRC, update your <code>CrcCheck()</code> function with the following code:</p>
<p>Here what we do in the function is:</p>
<ul>
<li>Set the header to ‘application/json’</li>
<li>Get the crc_token URL parameter</li>
<li>Encrypt it using Hmac sha256 and encode it</li>
<li>Print it to the response writer</li>
</ul>
<p>Make sure to replace the <code>CONSUMER_SECRET</code> with the actual consumer secret key for your app. Now if you navigate to <code>localhost:9090/webhook/twitter?crc_token=test</code> you should get a response similar to this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*0d-9kjmYKyloZwqVlhifkg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that we have a working CRC route, time to register our webhook. Now a couple of things to note here. Twitter will not accept <code>localhost</code> based URLs nor will it accept a URL with a port number or a non-https URL as a webhook. A way around that during development is to use a service like <a target="_blank" href="https://ngrok.com/">ngrok</a>. Simply <a target="_blank" href="https://ngrok.com/download">install</a> it and start up a dev server pointing to your 9090 port:</p>
<pre><code>ngrok http <span class="hljs-number">9090</span>
</code></pre><p>You should see a response similar to this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*wvDCZdXBVP6b2a07z9Yupw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now if you go to the .ngrok.io URL you should see the same response as the localhost:9090. Don’t forget to add the URL to your .env <code>file A</code>PP_ENV</p>
<h4 id="heading-registering-the-webhook">Registering the webhook</h4>
<p>For this tutorial we are going to check for the presence of a <code>register</code> flag in the arguments list. You can add this to your code:</p>
<p>Here our bot is checking for the presence of <code>-register</code> in the argument list. Then it runs <code>registerWebhook()</code> as a goroutine. We are defining the <code>registerWebhook()</code> function in a <code>client.go</code> file which we will use for all Twitter requests. Now, for the function’s body:</p>
<p>So, a break down of the new code. The first thing is to create a <code>CreateClient()</code> function. This function returns a pointer to an OAuth <code>http.Client</code> that we can then use to make all Twitter requests on behalf of our bot’s account. Remember to run <code>go get</code> in the project folder so it can fetch the <a target="_blank" href="https://github.com/dghubble/oauth1">neat go library</a> we use for all OAuth requests. In the <code>registerWebhook</code> function, we:</p>
<ul>
<li>Fetch a client</li>
<li>Pass our webhook’s URL as a parameter using <code>url.Values</code></li>
<li>Make a post response to the <a target="_blank" href="https://developer.twitter.com/en/docs/accounts-and-users/subscribe-account-activity/api-reference/aaa-premium#post-account-activity-all-env-name-webhooks">register webhook endpoint</a> then unmarshall (decode) and read the response</li>
</ul>
<p>Next, we need our code to <a target="_blank" href="https://developer.twitter.com/en/docs/accounts-and-users/subscribe-account-activity/api-reference/aaa-premium#post-account-activity-all-env-name-subscriptions">subscribe our webhook to events</a>.</p>
<blockquote>
<p>Note: You can use the <a target="_blank" href="https://github.com/twitterdev/account-activity-dashboard">account-activity-dashboard</a> app created by Twitter for managing webhooks during development</p>
</blockquote>
<p>Update your <code>client.go</code> file as shown below:</p>
<p>The code above is very straightforward. Cand heck after registering, subscribe to events and check for a status code of <code>204</code> . Now if you run <code>go install</code> on your code and run the code as <code>hellobot -register</code> you should get the following response:</p>
<pre><code>Starting ServerRegistering webhook...Webhook id <span class="hljs-keyword">of</span> &lt;hook_id&gt; has been registeredSubscribing webapp...Subscribed successfully
</code></pre><h4 id="heading-listening-for-events">Listening for events</h4>
<p>Now we need our webhook to actually to listen for events once the URL is called. Update your files as shown below:</p>
<p>What we are doing in the <code>hellobot.dev</code> is listening for post requests to our routes and passing them to the appropriate function. While in the <code>client.go</code> we are adding the appropriate structs we would use to <a target="_blank" href="https://www.sohamkamani.com/blog/2017/10/18/parsing-json-in-golang/">parse the JSON</a> payload to our bot.</p>
<p>Now update your code so it sends the tweet on the tag.</p>
<p>The updates we added to our source files are simply to respond to webhook events. Check if it was a <a target="_blank" href="https://developer.twitter.com/en/docs/accounts-and-users/subscribe-account-activity/guides/account-activity-data-objects#tweet_create_events">tweet_create_event</a> and send a response as a reply using the <code>SendTweet()</code> method in our <code>client.go</code> file.</p>
<blockquote>
<p>Note: Any tweet being sent as a reply needs to include the handle of the user it is replying to as the initial content of the reply</p>
</blockquote>
<p>Now if you run this with the appropriate credentials your bot should respond to tags and reply with “Hello World”.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Now that’s done, and since this is an extremely basic version of a bot, you can try adding a few things:</p>
<ul>
<li>Checking and ignoring retweet events</li>
<li>Adding a name to the response</li>
<li>Responding to the tweet in the case of an error anywhere on the app.</li>
</ul>
<p><em>The code for this walkthrough is on <a target="_blank" href="https://github.com/kofoworola/hellobot">Github here</a>. Feel free to fork and play around with it</em></p>
<p>Cheers!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to make your own sentiment analyzer using Python and Google’s Natural Language API ]]>
                </title>
                <description>
                    <![CDATA[ By Dzaky Widya Putra Imagine you are a product owner who wants to know what people are saying about your product in social media. Maybe your company launched a new product and you want to know how people reacted to it. You might want to use a sentime... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-make-your-own-sentiment-analyzer-using-python-and-googles-natural-language-api-9e91e1c493e/</link>
                <guid isPermaLink="false">66d45e3f4a7504b7409c337e</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Machine Learning ]]>
                    </category>
                
                    <category>
                        <![CDATA[ nlp ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 12 Feb 2019 22:44:02 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*KOfmj8IK7U5cvOF0Ogxfcw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dzaky Widya Putra</p>
<p>Imagine you are a product owner who wants to know what people are saying about your product in social media. Maybe your company launched a new product and you want to know how people reacted to it. You might want to use a sentiment analyzer like <a target="_blank" href="https://monkeylearn.com/">MonkeyLearn</a> or <a target="_blank" href="https://www.talkwalker.com/sentiment-analysis">Talkwalker</a>. But wouldn’t it be cool if we could make our own sentiment analyzer? Let’s make it then!</p>
<p>In this tutorial, we are going to make a Telegram Bot that will do the sentiment analysis of tweets related to the keyword that we define.</p>
<p>If this is your first time building a Telegram Bot, you might want to read <a target="_blank" href="https://medium.freecodecamp.org/learn-to-build-your-first-bot-in-telegram-with-python-4c99526765e4">this post</a> first.</p>
<h3 id="heading-getting-started">Getting started</h3>
<h4 id="heading-1-install-the-libraries">1. Install the libraries</h4>
<p>We are going to use <a target="_blank" href="http://www.tweepy.org/">tweepy</a> to gather the tweet data. We will use <a target="_blank" href="https://www.nltk.org/">nltk</a> to help us clean the tweets. <a target="_blank" href="https://cloud.google.com/natural-language/">Google Natural Language API</a> will do the sentiment analysis. <a target="_blank" href="https://github.com/python-telegram-bot/python-telegram-bot">python-telegram-bot</a> will send the result through Telegram chat.</p>
<pre><code>pip3 install tweepy nltk google-cloud-language python-telegram-bot
</code></pre><h4 id="heading-2-get-twitter-api-keys">2. Get Twitter API Keys</h4>
<p>To be able to gather the tweets from Twitter, we need to create a developer account to get the Twitter API Keys first.</p>
<p>Go to <a target="_blank" href="https://developer.twitter.com/">Twitter Developer</a> website, and create an account if you don’t have one.</p>
<p>Open <a target="_blank" href="https://developer.twitter.com/en/apps">Apps</a> page, click “Create an app”, fill out the form and click “Create”.</p>
<p>Click on “Keys and tokens” tab, copy the API Key and API Secret Key in the “Consumer API keys” section.</p>
<p>Click the “Create” button under “Access token &amp; access token secret” section. Copy the Access Token and Access Token Secret that have been generated.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ktyUR6lTB0kbnuxnCnQH1dsLICna7VvTFhgk" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Great! Now you should have four keys — API Key, API Secret Key, Access Token, and Access Token Secret. Save those keys for later use.</p>
<h4 id="heading-3-enable-google-natural-language-api">3. Enable Google Natural Language API</h4>
<p>We need to enable the Google Natural Language API first if we want to use the service.</p>
<p>Go to <a target="_blank" href="https://console.developers.google.com/">Google Developers Console</a> and create a new project (or select the one you have).</p>
<p>In the project dashboard, click “ENABLE APIS AND SERVICES”, and search for Cloud Natural Language API.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/7IhCqEQcrlj-FgvYhSvFiOE8mt67uTvQyhy8" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Click “ENABLE” to enable the API.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/2-1gPZpZjai17CcAYFz3ys0bn6gTEh7XfDnR" alt="Image" width="600" height="400" loading="lazy"></p>
<h4 id="heading-4-create-service-account-key">4. Create service account key</h4>
<p>If we want to use Google Cloud services like Google Natural Language, we need a service account key. This is like our credential to use Google’s services.</p>
<p>Go to <a target="_blank" href="https://console.developers.google.com/">Google Developers Console</a>, click “Credentials” tab, choose “Create credentials” and click “Service account key”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/yExVgBT6amhUC7vnTQRwklyK688PMXoCA5Qo" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Choose “App Engine default service account” and JSON as key type, then click “Create”.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/GqOItCmSRMD-JMxYNHS-EJFuYnc8lnkEEwJ7" alt="Image" width="600" height="400" loading="lazy"></p>
<p>There is a .json file that will be automatically downloaded, name it <code>creds.json</code>.</p>
<p>Set the <code>GOOGLE_APPLICATION_CREDENTIALS</code> with the path of our <code>creds.json</code> file in the terminal.</p>
<pre><code><span class="hljs-keyword">export</span> GOOGLE_APPLICATION_CREDENTIALS=<span class="hljs-string">'[PATH_TO_CREDS.JSON]'</span>
</code></pre><p>If everything is good, then it’s time to write our program.</p>
<h3 id="heading-write-the-program">Write the program</h3>
<p>This program will gather all the tweets containing the defined keyword in the last 24 hours with a maximum of 50 tweets. Then it will analyze the tweets’ sentiments one by one. We will send the result (average sentiment score) through Telegram chat.</p>
<p>This is a simple workflow of our program.</p>
<blockquote>
<p><strong>connect</strong> to the Twitter API -&amp;g<strong>t; sear</strong>ch tweets based on the keyword <strong>-&gt;</strong> clean all of the tweets -&gt; get <strong>tweet’s sentim</strong>ent <strong>score</strong> -&gt; send the result</p>
</blockquote>
<p>Let’s make a single function to define each flow.</p>
<h4 id="heading-1-connect-to-the-twitter-api">1. Connect to the Twitter API</h4>
<p>The first thing that we need to do is gather the tweets’ data, so we have to connect to the Twitter API first.</p>
<p>Import the tweepy library.</p>
<pre><code><span class="hljs-keyword">import</span> tweepy
</code></pre><p>Define the keys that we generated earlier.</p>
<pre><code>ACC_TOKEN = <span class="hljs-string">'YOUR_ACCESS_TOKEN'</span>
ACC_SECRET = <span class="hljs-string">'YOUR_ACCESS_TOKEN_SECRET'</span>
CONS_KEY = <span class="hljs-string">'YOUR_CONSUMER_API_KEY'</span>
CONS_SECRET = <span class="hljs-string">'YOUR_CONSUMER_API_SECRET_KEY'</span>
</code></pre><p>Make a function called <code>authentication</code> to connect to the API, with four parameters which are all of the keys.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">authentication</span>(<span class="hljs-params">cons_key, cons_secret, acc_token, acc_secret</span>):</span>
    auth = tweepy.OAuthHandler(cons_key, cons_secret)
    auth.set_access_token(acc_token, acc_secret)
    api = tweepy.API(auth)
    <span class="hljs-keyword">return</span> api
</code></pre>
<h4 id="heading-2-search-the-tweets">2. Search the tweets</h4>
<p>We can search the tweets with two criteria, based on time or quantity. If it’s based on time, we define the time interval and if it’s based on quantity, we define the total tweets that we want to gather. Since we want to gather the tweets from the last 24 hours with maximum tweets of 50, we will use both of the criteria.</p>
<p>Since we want to gather the tweets from the last 24 hours, let's take yesterday’s date as our time parameter.</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, timedelta

today_datetime = datetime.today().now()
yesterday_datetime = today_datetime - timedelta(days=<span class="hljs-number">1</span>)
today_date = today_datetime.strftime(<span class="hljs-string">'%Y-%m-%d'</span>)
yesterday_date = yesterday_datetime.strftime(<span class="hljs-string">'%Y-%m-%d'</span>)
</code></pre>
<p>Connect to the Twitter API using a function we defined before.</p>
<pre><code>api = authentication(CONS_KEY,CONS_SECRET,ACC_TOKEN,ACC_SECRET)
</code></pre><p>Define our search parameters. <code>q</code> is where we define our keyword, <code>since</code> is the start date for our search, <code>result_type='recent'</code> means we are going to take the newest tweets, <code>lang='en'</code> is going to take the English tweets only, and <code>items(total_tweets)</code> is where we define the maximum tweets that we are going to take.</p>
<pre><code class="lang-py">search_result = tweepy.Cursor(api.search, 
                              q=keyword, 
                              since=yesterday_date,
                              result_type=<span class="hljs-string">'recent'</span>, 
                              lang=<span class="hljs-string">'en'</span>).items(total_tweets)
</code></pre>
<p>Wrap those codes in a function called <code>search_tweets</code> with <code>keyword</code> and <code>total_tweets</code> as the parameters.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search_tweets</span>(<span class="hljs-params">keyword, total_tweets</span>):</span>
    today_datetime = datetime.today().now()
    yesterday_datetime = today_datetime - timedelta(days=<span class="hljs-number">1</span>)
    today_date = today_datetime.strftime(<span class="hljs-string">'%Y-%m-%d'</span>)
    yesterday_date = yesterday_datetime.strftime(<span class="hljs-string">'%Y-%m-%d'</span>)
    api = authentication(CONS_KEY,CONS_SECRET,ACC_TOKEN,ACC_SECRET)
    search_result = tweepy.Cursor(api.search, 
                                  q=keyword, 
                                  since=yesterday_date, 
                                  result_type=<span class="hljs-string">'recent'</span>, 
                                  lang=<span class="hljs-string">'en'</span>).items(total_tweets)
    <span class="hljs-keyword">return</span> search_result
</code></pre>
<h4 id="heading-3-clean-the-tweets">3. Clean the tweets</h4>
<p>Before we analyze the tweets sentiment, we need to clean the tweets a little bit so the Google Natural Language API can identify them better.</p>
<p>We will use the nltk and regex libraries to help us in this process.</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> re
<span class="hljs-keyword">from</span> nltk.tokenize <span class="hljs-keyword">import</span> WordPunctTokenizer
</code></pre>
<p>We remove the username in every tweet, so basically we can remove everything that begins with <code>@</code> and we use regex to do it.</p>
<pre><code>user_removed = re.sub(r<span class="hljs-string">'@[A-Za-z0-9]+'</span>,<span class="hljs-string">''</span>,tweet.decode(<span class="hljs-string">'utf-8'</span>))
</code></pre><p>We also remove links in every tweet.</p>
<pre><code>link_removed = re.sub(<span class="hljs-string">'https?://[A-Za-z0-9./]+'</span>,<span class="hljs-string">''</span>,user_removed)
</code></pre><p>Numbers are also deleted from all of the tweets.</p>
<pre><code>number_removed = re.sub(<span class="hljs-string">'[^a-zA-Z]'</span>,<span class="hljs-string">' '</span>,link_removed)
</code></pre><p>The last, convert all of the characters into lower space, then remove every unnecessary space.</p>
<pre><code class="lang-py">lower_case_tweet = number_removed.lower()
tok = WordPunctTokenizer()
words = tok.tokenize(lower_case_tweet)
clean_tweet = (<span class="hljs-string">' '</span>.join(words)).strip()
</code></pre>
<p>Wrap those codes into a function called <code>clean_tweets</code> with <code>tweet</code> as our parameter.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clean_tweets</span>(<span class="hljs-params">tweet</span>):</span>
    user_removed = re.sub(<span class="hljs-string">r'@[A-Za-z0-9]+'</span>,<span class="hljs-string">''</span>,tweet.decode(<span class="hljs-string">'utf-8'</span>))
    link_removed = re.sub(<span class="hljs-string">'https?://[A-Za-z0-9./]+'</span>,<span class="hljs-string">''</span>,user_removed)
    number_removed = re.sub(<span class="hljs-string">'[^a-zA-Z]'</span>, <span class="hljs-string">' '</span>, link_removed)
    lower_case_tweet= number_removed.lower()
    tok = WordPunctTokenizer()
    words = tok.tokenize(lower_case_tweet)
    clean_tweet = (<span class="hljs-string">' '</span>.join(words)).strip()
    <span class="hljs-keyword">return</span> clean_tweet
</code></pre>
<h4 id="heading-4-get-tweets-sentiment">4. Get tweet’s sentiment</h4>
<p>To be able to get a tweet’s sentiment, we will use Google Natural Language API.</p>
<p>The API provides Sentiment Analysis, Entities Analysis, and Syntax Analysis. We will only use the Sentiment Analysis for this tutorial.</p>
<p>In Google’s Sentiment Analysis, there are <code>score</code> and <code>magnitude</code>. <code>Score</code> is the score of the sentiment ranges from -1.0 (very negative) to 1.0 (very positive). <code>Magnitude</code> is the strength of sentiment and ranges from 0 to infinity.</p>
<p>For the sake of simplicity of this tutorial, we will only consider the <code>score</code>. If you are thinking of doing deep NLP analysis, you should consider the <code>magnitude</code> too.</p>
<p>Import the Google Natural Language library.</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> google.cloud <span class="hljs-keyword">import</span> language
<span class="hljs-keyword">from</span> google.cloud.language <span class="hljs-keyword">import</span> enums
<span class="hljs-keyword">from</span> google.cloud.language <span class="hljs-keyword">import</span> types
</code></pre>
<p>Make a function called <code>get_sentiment_score</code> which takes <code>tweet</code> as the parameter, and returns the <code>sentiment</code> score.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_sentiment_score</span>(<span class="hljs-params">tweet</span>):</span>
    client = language.LanguageServiceClient()
    document = types\
               .Document(content=tweet,
                         type=enums.Document.Type.PLAIN_TEXT)
    sentiment_score = client\
                      .analyze_sentiment(document=document)\
                      .document_sentiment\
                      .score
    <span class="hljs-keyword">return</span> sentiment_score
</code></pre>
<h4 id="heading-5-analyze-the-tweets">5. Analyze the tweets</h4>
<p>Let’s make a function that will loop the list of tweets we get from <code>search_tweets</code> function and get the sentiment’s score of every tweet using <code>get_sentiment_score</code> function. Then we’ll calculate the average. The average score will determine whether the given keyword has a positive, neutral, or negative sentiment.</p>
<p>Define <code>score</code> equals to <code>0</code> , then use <code>search_tweets</code> function to get the tweets related to the keyword that we define.</p>
<pre><code class="lang-py">score = <span class="hljs-number">0</span>
tweets = search_tweets(keyword, total_tweets)
</code></pre>
<p>Loop through the list of tweets, and do the cleaning using <code>clean_tweets</code> function that we created before.</p>
<pre><code class="lang-py"><span class="hljs-keyword">for</span> tweet <span class="hljs-keyword">in</span> tweets:
    cleaned_tweet = clean_tweets(tweet.text.encode(<span class="hljs-string">'utf-8'</span>))
</code></pre>
<p>Get the sentiment score using <code>get_sentiment_score</code> function, and increment the <code>score</code> by adding <code>sentiment_score</code>.</p>
<pre><code class="lang-py"><span class="hljs-keyword">for</span> tweet <span class="hljs-keyword">in</span> tweets:
    cleaned_tweet = clean_tweets(tweet.text.encode(<span class="hljs-string">'utf-8'</span>))
    sentiment_score = get_sentiment_score(cleaned_tweet)
    score += sentiment_score
</code></pre>
<p>Let’s print out each tweet and its sentiment so we can see the progress detail in the terminal.</p>
<pre><code class="lang-py"><span class="hljs-keyword">for</span> tweet <span class="hljs-keyword">in</span> tweets:
    cleaned_tweet = clean_tweets(tweet.text.encode(<span class="hljs-string">'utf-8'</span>))
    sentiment_score = get_sentiment_score(cleaned_tweet)
    score += sentiment_score
    print(<span class="hljs-string">'Tweet: {}'</span>.format(cleaned_tweet))
    print(<span class="hljs-string">'Score: {}\n'</span>.format(sentiment_score))
</code></pre>
<p>Calculate the average score and pass it to <code>final_score</code> variable. Wrap all of the codes into <code>analyze_tweets</code> function, with <code>keyword</code> and <code>total_tweets</code> as the parameters.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">analyze_tweets</span>(<span class="hljs-params">keyword, total_tweets</span>):</span>
    score = <span class="hljs-number">0</span>
    tweets = search_tweets(keyword, total_tweets)
    <span class="hljs-keyword">for</span> tweet <span class="hljs-keyword">in</span> tweets:
        cleaned_tweet = clean_tweets(tweet.text.encode(<span class="hljs-string">'utf-8'</span>))
        sentiment_score = get_sentiment_score(cleaned_tweet)
        score += sentiment_score
        print(<span class="hljs-string">'Tweet: {}'</span>.format(cleaned_tweet))
        print(<span class="hljs-string">'Score: {}\n'</span>.format(sentiment_score))
    final_score = round((score / float(total_tweets)),<span class="hljs-number">2</span>)
    <span class="hljs-keyword">return</span> final_score
</code></pre>
<h4 id="heading-6-send-the-tweets-sentiment-score">6. Send the tweet’s sentiment score</h4>
<p>Let’s make the last function in the workflow. This function will takes user’s keyword and calculate the average sentiment’s score. Then we’ll send it through Telegram Bot.</p>
<p>Get the keyword from the user.</p>
<pre><code>keyword = update.message.text
</code></pre><p>Use <code>analyze_tweets</code> function to get the final score, <code>keyword</code> as our parameter, and set the <code>total_tweets = 50</code> since we want to gather 50 tweets.</p>
<pre><code>final_score = analyze_tweets(keyword, <span class="hljs-number">50</span>)
</code></pre><p>We define whether a given score is considered negative, neutral, or positive using Google’s score range, as we see in the image below.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/LXfEOCy9YOWOx6CORPxYYM2RkRPPIo4vvlHY" alt="Image" width="600" height="400" loading="lazy">
_source: [https://cloud.google.com/natural-language/](https://cloud.google.com/natural-language/" rel="noopener" target="<em>blank" title=")</em></p>
<pre><code class="lang-py"><span class="hljs-keyword">if</span> final_score &lt;= <span class="hljs-number">-0.25</span>:
    status = <span class="hljs-string">'NEGATIVE ❌'</span>
<span class="hljs-keyword">elif</span> final_score &lt;= <span class="hljs-number">0.25</span>:
    status = <span class="hljs-string">'NEUTRAL ?'</span>
<span class="hljs-keyword">else</span>:
    status = <span class="hljs-string">'POSITIVE ✅'</span>
</code></pre>
<p>Lastly, send the <code>final_score</code> and the <code>status</code> through Telegram Bot.</p>
<pre><code class="lang-py">bot.send_message(chat_id=update.message.chat_id,
                 text=<span class="hljs-string">'Average score for '</span>
                       + str(keyword) 
                       + <span class="hljs-string">' is '</span> 
                       + str(final_score) 
                       + <span class="hljs-string">' '</span> 
                       + status)
</code></pre>
<p>Wrap the codes into a function called <code>send_the_result</code>.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_the_result</span>(<span class="hljs-params">bot, update</span>):</span>
    keyword = update.message.text
    final_score = analyze_tweets(keyword, <span class="hljs-number">50</span>)
    <span class="hljs-keyword">if</span> final_score &lt;= <span class="hljs-number">-0.25</span>:
        status = <span class="hljs-string">'NEGATIVE ❌'</span>
    <span class="hljs-keyword">elif</span> final_score &lt;= <span class="hljs-number">0.25</span>:
        status = <span class="hljs-string">'NEUTRAL ?'</span>
    <span class="hljs-keyword">else</span>:
        status = <span class="hljs-string">'POSITIVE ✅'</span>
    bot.send_message(chat_id=update.message.chat_id,
                     text=<span class="hljs-string">'Average score for '</span>
                           + str(keyword) 
                           + <span class="hljs-string">' is '</span> 
                           + str(final_score) 
                           + <span class="hljs-string">' '</span> 
                           + status)
</code></pre>
<h4 id="heading-7-main-program">7. Main program</h4>
<p>Lastly, create another function called <code>main</code> to run our program. <strong>Don’t forget to change</strong> <code>YOUR_TOKEN</code> to your bot’s token.</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Updater, MessageHandler, Filters

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    updater = Updater(<span class="hljs-string">'YOUR_TOKEN'</span>)
    dp = updater.dispatcher
    dp.add_handler(MessageHandler(Filters.text, send_the_result))
    updater.start_polling()
    updater.idle()

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    main()
</code></pre>
<p>In the end, your code should look like this</p>
<pre><code class="lang-py"><span class="hljs-keyword">import</span> tweepy
<span class="hljs-keyword">import</span> re

<span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Updater, MessageHandler, Filters
<span class="hljs-keyword">from</span> google.cloud <span class="hljs-keyword">import</span> language
<span class="hljs-keyword">from</span> google.cloud.language <span class="hljs-keyword">import</span> enums
<span class="hljs-keyword">from</span> google.cloud.language <span class="hljs-keyword">import</span> types
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime, timedelta
<span class="hljs-keyword">from</span> nltk.tokenize <span class="hljs-keyword">import</span> WordPunctTokenizer


ACC_TOKEN = <span class="hljs-string">'YOUR_ACCESS_TOKEN'</span>
ACC_SECRET = <span class="hljs-string">'YOUR_ACCESS_TOKEN_SECRET'</span>
CONS_KEY = <span class="hljs-string">'YOUR_CONSUMER_API_KEY'</span>
CONS_SECRET = <span class="hljs-string">'YOUR_CONSUMER_API_SECRET_KEY'</span>

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">authentication</span>(<span class="hljs-params">cons_key, cons_secret, acc_token, acc_secret</span>):</span>
    auth = tweepy.OAuthHandler(cons_key, cons_secret)
    auth.set_access_token(acc_token, acc_secret)
    api = tweepy.API(auth)
    <span class="hljs-keyword">return</span> api

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search_tweets</span>(<span class="hljs-params">keyword, total_tweets</span>):</span>
    today_datetime = datetime.today().now()
    yesterday_datetime = today_datetime - timedelta(days=<span class="hljs-number">1</span>)
    today_date = today_datetime.strftime(<span class="hljs-string">'%Y-%m-%d'</span>)
    yesterday_date = yesterday_datetime.strftime(<span class="hljs-string">'%Y-%m-%d'</span>)
    api = authentication(CONS_KEY,CONS_SECRET,ACC_TOKEN,ACC_SECRET)
    search_result = tweepy.Cursor(api.search, 
                                  q=keyword, 
                                  since=yesterday_date, 
                                  result_type=<span class="hljs-string">'recent'</span>, 
                                  lang=<span class="hljs-string">'en'</span>).items(total_tweets)
    <span class="hljs-keyword">return</span> search_result

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">clean_tweets</span>(<span class="hljs-params">tweet</span>):</span>
    user_removed = re.sub(<span class="hljs-string">r'@[A-Za-z0-9]+'</span>,<span class="hljs-string">''</span>,tweet.decode(<span class="hljs-string">'utf-8'</span>))
    link_removed = re.sub(<span class="hljs-string">'https?://[A-Za-z0-9./]+'</span>,<span class="hljs-string">''</span>,user_removed)
    number_removed = re.sub(<span class="hljs-string">'[^a-zA-Z]'</span>, <span class="hljs-string">' '</span>, link_removed)
    lower_case_tweet= number_removed.lower()
    tok = WordPunctTokenizer()
    words = tok.tokenize(lower_case_tweet)
    clean_tweet = (<span class="hljs-string">' '</span>.join(words)).strip()
    <span class="hljs-keyword">return</span> clean_tweet

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_sentiment_score</span>(<span class="hljs-params">tweet</span>):</span>
    client = language.LanguageServiceClient()
    document = types\
               .Document(content=tweet,
                         type=enums.Document.Type.PLAIN_TEXT)
    sentiment_score = client\
                      .analyze_sentiment(document=document)\
                      .document_sentiment\
                      .score
    <span class="hljs-keyword">return</span> sentiment_score

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">analyze_tweets</span>(<span class="hljs-params">keyword, total_tweets</span>):</span>
    score = <span class="hljs-number">0</span>
    tweets = search_tweets(keyword,total_tweets)
    <span class="hljs-keyword">for</span> tweet <span class="hljs-keyword">in</span> tweets:
        cleaned_tweet = clean_tweets(tweet.text.encode(<span class="hljs-string">'utf-8'</span>))
        sentiment_score = get_sentiment_score(cleaned_tweet)
        score += sentiment_score
        print(<span class="hljs-string">'Tweet: {}'</span>.format(cleaned_tweet))
        print(<span class="hljs-string">'Score: {}\n'</span>.format(sentiment_score))
    final_score = round((score / float(total_tweets)),<span class="hljs-number">2</span>)
    <span class="hljs-keyword">return</span> final_score

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">send_the_result</span>(<span class="hljs-params">bot, update</span>):</span>
    keyword = update.message.text
    final_score = analyze_tweets(keyword, <span class="hljs-number">50</span>)
    <span class="hljs-keyword">if</span> final_score &lt;= <span class="hljs-number">-0.25</span>:
        status = <span class="hljs-string">'NEGATIVE ❌'</span>
    <span class="hljs-keyword">elif</span> final_score &lt;= <span class="hljs-number">0.25</span>:
        status = <span class="hljs-string">'NEUTRAL ?'</span>
    <span class="hljs-keyword">else</span>:
        status = <span class="hljs-string">'POSITIVE ✅'</span>
    bot.send_message(chat_id=update.message.chat_id,
                     text=<span class="hljs-string">'Average score for '</span>
                           + str(keyword) 
                           + <span class="hljs-string">' is '</span> 
                           + str(final_score) 
                           + <span class="hljs-string">' '</span> 
                           + status)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    updater = Updater(<span class="hljs-string">'YOUR_TOKEN'</span>)
    dp = updater.dispatcher
    dp.add_handler(MessageHandler(Filters.text, send_the_result))
    updater.start_polling()
    updater.idle()

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    main()
</code></pre>
<p>Save the file and name it <code>main.py</code>, then run the program.</p>
<pre><code>python3 main.py
</code></pre><p>Go to your telegram bot by accessing this URL: <code>[https://telegram.me/YOUR_BOT_USERNAME](https://telegram.me/YOUR_BOT_USERNAME.)</code>. Type any product, person name, or whatever you want and send it to your bot. If everything runs, there should be a detailed sentiment score for each tweet in the terminal. The bot will reply with the average sentiment score.</p>
<p>The pictures below are an example if I type <code>valentino rossi</code> and send it to the bot.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/2s8ZyrggzF1W8hB1lS3T5TWH1dehW7OMOQ84" alt="Image" width="600" height="400" loading="lazy">
<em>the detailed sentiment score</em></p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/RPlvrzXG0c3o3befIDJ8cKC3KlmLRvRTEH4Q" alt="Image" width="600" height="400" loading="lazy">
<em>the bot’s response</em></p>
<p>If you managed to follow the steps until the end of this tutorial, that’s awesome! You have your sentiment analyzer now, how cool is that!?</p>
<p>You can also check out my <a target="_blank" href="https://github.com/dzakyputra/sentweetbot">GitHub</a> to get the code. Please do not hesitate to connect and leave a message in my <a target="_blank" href="https://www.linkedin.com/in/dzakywp/">Linkedin</a> profile if you want to ask about anything.</p>
<p>Please leave a comment if you think there are any errors in my code or writing.</p>
<p>Thank you and good luck! :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to set up push notifications in your Telegram bot ]]>
                </title>
                <description>
                    <![CDATA[ By Nikita Kholin Telegram is a great platform with lots of great users (I’m a Telegram user myself). And what would be the best way for Telegram users to receive notifications? We can’t know for sure. Maybe they like email or something else. But we c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/telegram-push-notifications-58477e71b2c2/</link>
                <guid isPermaLink="false">66c3605839357f944697662f</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ telegram ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Tue, 12 Feb 2019 22:36:36 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*mN13Q59wDCwvUWCF" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Nikita Kholin</p>
<p>Telegram is a great platform with lots of great users (I’m a Telegram user myself). And what would be the best way for Telegram users to receive notifications? We can’t know for sure. Maybe they like email or something else. But we can guess that sending notifications to Telegram would be pretty convenient.</p>
<p>If you would like to send Telegram notifications from your application, you’ve come to the right place. I’ve added this feature to <a target="_blank" href="https://musicnotifier.com/">my application</a> and I love it.</p>
<p>One quick note. In this article I provide examples of code in Python. But the ideas are not Python-specific and can be translated into another language without any hastle.</p>
<p>So without further ado, let’s dive into how we can do it.</p>
<h3 id="heading-create-a-telegram-bot">Create a Telegram bot</h3>
<p>First of all, you need to create a Telegram bot. To do this you need to use another Telegram bot, <a target="_blank" href="https://telegram.me/botfather">BotFather</a>. Just talk to him (press start).</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/K0IHhahp6VR-IJDs8ohcZRxBBlUMaRWuUt4g" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now you see what it can do. But what interests us is creating a new bot, so that’s what we’re going to choose (<code>/newbot</code>).</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/4TCF43Y1rArQcRQb-5F6XblOvTEMXUwWazrd" alt="Image" width="600" height="400" loading="lazy"></p>
<p>You’re quickly going to find out that bot’s name should end with “bot”. And since you’re like me and coming to the game too late, most bot names are already taken.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/yRoEugA8JJ1ccppRU8g3kcy40mGsJqCalzRG" alt="Image" width="600" height="400" loading="lazy"></p>
<p>But eventually, you’re going to find a name for your bot and get an access token we’re going to need.</p>
<p>Now that you have a bot, Telegram users can find and use it. But there is one problem — you can’t associate users that come from Telegram to the users in your application. Let me show you why.</p>
<p>Once a user presses the “Start” button on your bot, you will receive an “update”. You can check all bot’s updates even in your browser by visiting the following URL <code>https://api.telegram.org/bot{bot_token}/getUpdates</code> (don’t forget to use your access token in the URL). Here’s what I got:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/AydGLffhxTaXFcqwj3untIxDVGf-AyGE26pc" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Can’t read anything? Don’t worry. You can fix it by installing some JSON prettifier extension in your browser. I use <a target="_blank" href="https://chrome.google.com/webstore/detail/json-formatter/mhimpmpmffogbmmkmajibklelopddmjf">JSON Formatter</a> for Chrome. It looks so much better.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/iG2V6vR4rqS8Lmw-Cxg5FisuCHU9XAFEquzc" alt="Image" width="600" height="400" loading="lazy"></p>
<p>So as you can see we don’t get that much information about the person. From this information, we can get their full name. But it would be lucky if the user would provide their full name in your application, and doesn’t guarantee uniqueness of it. So we can’t use that to find a user in your applications.</p>
<p>Another piece of information we get is the username. That is more useful as it is unique between all Telegram users. But most likely you don’t have that available in your applications. So we would need to ask a user to enter their username somewhere in the application. It’s just too much work that I’m not sure anyone would do.</p>
<p>Another option to associate a user would be to ask them to provide the email that they used in your application to the bot. But this has too many flaws: the user can make a typo while entering the email, the user can enter the email of another user and exploit the system. This is just too bad.</p>
<p>Can we do better?</p>
<h3 id="heading-associating-a-user">Associating a user</h3>
<p>Of course we can. To associate the user, we’ll use a technique called <a target="_blank" href="https://core.telegram.org/bots#deep-linking">deep linking</a>.</p>
<p>First, you need to create a random unique token for each user. I’ve used the following code to generate the token using Python:</p>
<pre><code><span class="hljs-keyword">from</span> secrets <span class="hljs-keyword">import</span> token_urlsafetoken = token_urlsafe(<span class="hljs-number">8</span>)token# =&gt; <span class="hljs-string">'uEDbtJFHxKc'</span>
</code></pre><p>Then you need to save that token to be able to find a user with it later. You can save it to your database or use some other place like a cache for example. I have a <code>Notification</code> model so I added a field to a model’s table.</p>
<pre><code><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Notification</span>(<span class="hljs-title">models</span>.<span class="hljs-title">Model</span>):    <span class="hljs-title">user</span> </span>= models.ForeignKey(User, on_delete=models.CASCADE)    # ...    connect_token = models.CharField(max_length=<span class="hljs-number">64</span>, <span class="hljs-literal">null</span>=True)
</code></pre><p>So we’ve generated <code>uEDbtJFHxKc</code> token and saved it. Now we need to use this token in a URL to the telegram bot which the user needs to click to make it all work:</p>
<pre><code>telegram_url = <span class="hljs-string">'https://www.telegram.me'</span>bot_name = <span class="hljs-string">'music_notification_bot'</span>token = <span class="hljs-string">'uEDbtJFHxKc'</span>url = f<span class="hljs-string">'{telegram_url}/{bot_name}?start={token}'</span>
</code></pre><p>Now that we have our URL, <code>'https://telegram.me/music_notification_bot?start=uEDbtJFHxKc'</code>, it’s time to show it to the user. Just display it in any place of your application and wait for the user to click on it.</p>
<p>Once the user takes the bait and clicks “Start” you should receive another update:</p>
<pre><code>{    <span class="hljs-string">"ok"</span>: <span class="hljs-literal">true</span>,    <span class="hljs-string">"result"</span>: [        <span class="hljs-comment">// ...        // previous updates           // ...        {            "update_id": 599162365,            "message": {                "message_id": 174,                "from": { ... },                "chat": { ... },                "date": 1549788357,                "text": "/start uEDbtJFHxKc",                "entities": [ ... ]            }        }    ]}</span>
</code></pre><p>We can finally identify our user. The <code>text</code> field now contains our user token. Let’s go ahead and take it out of this field:</p>
<pre><code>bot_token = <span class="hljs-string">'your_bot_token'</span>updates_url = f<span class="hljs-string">'https://api.telegram.org/bot{bot_token}/getUpdates'</span><span class="hljs-keyword">import</span> requestsresponse = requests.get(updates_url).json()text = response[<span class="hljs-string">'result'</span>][<span class="hljs-number">0</span>][<span class="hljs-string">'message'</span>][<span class="hljs-string">'text'</span>]text# =&gt; <span class="hljs-string">'/start uEDbtJFHxKc'</span>splitted_text = text.split(<span class="hljs-string">' '</span>)# =&gt; [<span class="hljs-string">'/start'</span>, <span class="hljs-string">'uEDbtJFHxKc'</span>]token = splitted_text[<span class="hljs-number">-1</span>]# =&gt; <span class="hljs-string">'uEDbtJFHxKc'</span>
</code></pre><p>This token can be used to find the user. Your implementation depends on the way you saved the token in the first place. But here’s how I do it:</p>
<pre><code>notification = Notification.objects.get(channel=<span class="hljs-string">'telegram'</span>, connect_token=token)user = notification.user
</code></pre><p>So the user had pressed the “Start” button. But they see that nothing happened. Let’s welcome them at least.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/t3LnDt2icX4drb1ZwOr4t0gd8rHaoNhuOxoR" alt="Image" width="600" height="400" loading="lazy"></p>
<p>To welcome the user we need to discover that the user had started a conversation with our bot. There are two options for how we can do that: polling and webhooks.</p>
<p>You already know what polling is. You’ve already done it. Or at least have seen me do it. Once we’ve checked out the <code>https://api.telegram.org/bot{bot_token}/getUpdates</code> page we did one poll. Polling is checking for updates constantly, every 2 seconds for example. This way we can always know when someone interacted with the bot.</p>
<p>Webhooks take a bit of a different direction. Instead of checking every 2 seconds for updates, we just wait for an update to happen. And when it happens Telegram will send a request with the update data to a URL that we specify. This way we can give both our and Telegram servers some rest and just wait for the update to come.</p>
<p>Polling can be better if you’ve got high traffic but, unfortunately, it’s rather an exception so I’ve decided to go with the webhook.</p>
<h3 id="heading-webhooks">Webhooks</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/nn4BJScN-LpTraRBdaaZ6--uBVl7Ikrlfj6E" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Setting a webhook in Telegram is very easy. You just need to send a request to <code>https://api.telegram.org/bot{bot_token}/setWebhook?url={your_server_url}</code>. Opening this link in your browser works too. <code>your_server_url</code> is the URL Telegram will send updates to. Here’s what you should get in the response:</p>
<pre><code>{    <span class="hljs-string">"ok"</span>: <span class="hljs-literal">true</span>,    <span class="hljs-string">"result"</span>: <span class="hljs-literal">true</span>,    <span class="hljs-string">"description"</span>: <span class="hljs-string">"Webhook was set"</span>}
</code></pre><p>If you don’t trust yourself you can visit <code>https://api.telegram.org/bot{bot_token}/getWebhookInfo</code> just to doublecheck that everything is OK. You should see something like this:</p>
<pre><code>{    <span class="hljs-string">"ok"</span>: <span class="hljs-literal">true</span>,    <span class="hljs-string">"result"</span>: {        <span class="hljs-string">"url"</span>: <span class="hljs-string">"https://example.com/your_server_endpoint"</span>,        <span class="hljs-string">"has_custom_certificate"</span>: <span class="hljs-literal">false</span>,        <span class="hljs-string">"pending_update_count"</span>: <span class="hljs-number">0</span>,        <span class="hljs-string">"max_connections"</span>: <span class="hljs-number">40</span>    }}
</code></pre><p>Now. If something is not OK (like you’ve set a wrong URL) you can always delete the webhook by visiting <code>[https://api.telegram.org/bot{bot_token}/deleteWebhook](https://api.telegram.org/bot{bot_token}/deleteWebhook)</code> and then setting the webhook again.</p>
<h3 id="heading-local-development">Local development</h3>
<p>Before continuing I would like to say a few words about local development. Webhooks are not very suitable for it. Webhooks are sent to a URL and most likely you don’t know your computer’s URL. Also, a Telegram webhook requires the URL to be secure (HTTPS).</p>
<p>But there is a solution to this problem: <a target="_blank" href="https://ngrok.com/">ngrok</a>. ngrok is a tool that exposes your local environment to the world. <a target="_blank" href="https://ngrok.com/download">Download ngrok</a>, install it and start it with the port your server is running on. My server is running on <code>8000</code> port so I would need to run in a console</p>
<pre><code>/path/to/ngrok http <span class="hljs-number">8000</span>
</code></pre><p>Then ngrok should give you a URL that you can use to set a webhook up.</p>
<h3 id="heading-welcoming-a-user">Welcoming a user</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/g5chjEivn8kFMGCWNKvl1x5FmYxWRaXlfPgX" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Now that you’re ready to develop, let’s welcome our user — they’re waiting for it.</p>
<p>Once the user clicks “Start”, your Telegram will send an update to your server’s URL. The interesting parts of the update should look like this:</p>
<pre><code>{    <span class="hljs-string">"message"</span>: {        <span class="hljs-string">"chat"</span>: {            <span class="hljs-string">"id"</span>: <span class="hljs-number">457</span>        },        <span class="hljs-string">"text"</span>: <span class="hljs-string">"/start uEDbtJFHxKc"</span>,    }}
</code></pre><p>This is a perfect time to associate the user using message text. There is also an interesting piece of information, chat ID. Chat ID is what we need to send a message to that user. Telegram has an API endpoint to send a message that looks like this <code>https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={text}</code>. I’m not sure I need to explain how to use it but here’s what my code that processes the webhook looks like:</p>
<pre><code><span class="hljs-keyword">import</span> jsonimport requestsdef callback(request):    body = json.loads(request.body)    text = body[<span class="hljs-string">'message'</span>][<span class="hljs-string">'text'</span>]    token = text.split(<span class="hljs-string">' '</span>)[<span class="hljs-number">-1</span>]    associate_user_by_token(token)    bot_key = os.environ.get(<span class="hljs-string">'TELEGRAM_API_KEY'</span>)    chat_id = body[<span class="hljs-string">'message'</span>][<span class="hljs-string">'chat'</span>][<span class="hljs-string">'id'</span>]    text = <span class="hljs-string">"Welcome!"</span>    send_message_url = f<span class="hljs-string">'https://api.telegram.org/bot{bot_key}/sendMessage?chat_id={chat_id}&amp;text={text}'</span>    requests.post(send_message_url)
</code></pre><p>If we send a welcome message after the user clicked the famous “Start” button, the user won’t have any second thoughts whether everything is working or not.</p>
<h3 id="heading-pushing-notifications">Pushing notifications</h3>
<p><img src="https://cdn-media-1.freecodecamp.org/images/xfdeU5lYWIBYfeZ9wjFxW6x1zxjVNLnMvB2P" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Finally, we get to the point why we’re doing all of this — push notifications. You may want to notify the user about some information that happened in your application. For example, someone liked the user’s post or whatever. I use Telegram to notify about <a target="_blank" href="https://musicnotifier.com/">new music releases</a> from the user’s favorite artists.</p>
<p>You already know how to send notifications. You just need to send a message using <code>[https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={notification_text}](https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={notification_text}.)</code><a target="_blank" href="https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={notification_text}.">.</a></p>
<p>Of course, if you’re planning to send notifications not only when the user interacts with the bot, you need to save <code>chat_id</code> in your database.</p>
<p>You might also want to include links or some other formatting in your message. In this case, you would need to add another parameter to the send message URL, <code>parse_mode</code>. There are 2 parsing options: Markdown or HTML. I use Markdown since I find it simpler to use. If you’re not familiar with Markdown you can use HMTL, but I would recommend reading <a target="_blank" href="https://www.markdownguide.org/basic-syntax">how easy Markdown is</a>.</p>
<p>Here’s how the send message URL looks with <code>parse_mode</code> parameter <code>[https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={notification_text}&amp;parse_mode=markdown](https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={notification_text}&amp;parse_mode=markdown.)</code><a target="_blank" href="https://api.telegram.org/bot{bot_token}/sendMessage?chat_id={chat_id}&amp;text={notification_text}&amp;parse_mode=markdown.">.</a></p>
<p>I add links to new releases to the notifications text like so <code>{release.date}: {release.artist.name} - [{release.title}]({release.url})</code>. You can read more about how to format you messages <a target="_blank" href="https://core.telegram.org/bots/api#formatting-options">here</a>.</p>
<p>Also, there are <a target="_blank" href="https://core.telegram.org/bots/api#sendmessage">more parameters available</a> for the send message URL like <code>disable_notification</code>. There is always a place to explore.</p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>Now you should know how to</p>
<ul>
<li>create a bot in Telegram using BotFather</li>
<li>check for updates (and what is a better way — webhooks or polling)</li>
<li>associate user using deep linking</li>
<li>send a welcome message and continue sending notifications</li>
<li>format messages you send</li>
</ul>
<p>Hopefully, this article was useful to you. This is the fifth part of a series of articles about the <a target="_blank" href="http://musicnotifier.com/">MuN</a>. Stay tuned for part 6. You can find <a target="_blank" href="https://github.com/hmlON/mun">the code of this project</a>, as well as my other projects, on my <a target="_blank" href="https://github.com/hmlON">GitHub page</a>. Leave your comments down below and follow me if you liked this article.</p>
<p><em>Originally published at <a target="_blank" href="https://kholinlabs.com/telegram-push-notifications">https://kholinlabs.com/telegram-push-notifications</a> on February 12, 2019.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to get Facebook messenger to notify you about the weather ]]>
                </title>
                <description>
                    <![CDATA[ By Ekapope Viriyakovithya A complete DIY guide to build your own weather alert bot. The morning routine is always stressful. Wouldn’t it be wonderful if you had one less thing to worry about in the morning? What if you had a customizable weather aler... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-get-facebook-messenger-to-notify-you-about-the-weather-8b5e87a64540/</link>
                <guid isPermaLink="false">66d45e3fd14641365a0508a0</guid>
                
                    <category>
                        <![CDATA[ Facebook Messenger ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ how-to ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ weather ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 24 Jan 2019 18:22:42 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*RBHbkOfMOzl7o_crT4Rg7g.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Ekapope Viriyakovithya</p>
<h3 id="heading-a-complete-diy-guide-to-build-your-own-weather-alert-bot">A complete DIY guide to build your own weather alert bot.</h3>
<p>The morning routine is always stressful. Wouldn’t it be wonderful if you had one less thing to worry about in the morning?</p>
<p>What if you had a customizable weather alert bot that sent you a short message ONLY when there was a chance of rain above your pre-defined threshold?</p>
<p>Don’t waste your time checking the weather in a separate app. It can be live on your Facebook messenger chatbox!</p>
<h3 id="heading-what-do-you-need">What do you need?</h3>
<ul>
<li>Python 3.6 (or earlier) with pandas and <a target="_blank" href="https://github.com/carpedm20/fbchat">fbchat</a> packages installed</li>
</ul>
<pre><code class="lang-bash">pip install fbchat
</code></pre>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*Uy6HxLRFNAOcpu40NDfXSw.png" alt="Image" width="249" height="318" loading="lazy">
<em>AccuWeather Free Account</em></p>
<ul>
<li><a target="_blank" href="https://developer.accuweather.com/packages">AccuWeather developer account</a>, the free package should be enough. It provides 50 calls/day with 1 key/developer account.</li>
</ul>
<h3 id="heading-lets-get-started">Let’s get started!</h3>
<p>At the end of this how-to, you will have 3 files in the scripts folder:</p>
<p><a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/tree/master/scripts">keys.py</a> : to store your facebook email, password, and accuweather API key</p>
<p><a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> : to store the threshold and weather forecast location id</p>
<p><a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/main.py">main.py</a> : this is the main script, it will call the keys.py and params.py</p>
<h4 id="heading-1-setup-facebook-account-and-accuweather-api-key">1. Setup Facebook account and AccuWeather API key</h4>
<p>First, let’s put your account detail in the <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/keys.py">keys.py</a> file.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Your Facebook usersname (email)</span>
FB_USERNAME= <span class="hljs-string">""</span> 

<span class="hljs-comment"># Your Facebook password</span>
FB_PASSWORD= <span class="hljs-string">""</span> 

<span class="hljs-comment"># Your AccuWeather API key</span>
ACCUWEATHER_API_KEY= <span class="hljs-string">""</span>
</code></pre>
<h4 id="heading-2-setup-parameters"><strong>2. Setup parameters</strong></h4>
<p>In this step, we will define the threshold for the probability of rain or snow, delay time between each request and message, and also location.</p>
<p>Currently, we set the threshold at 25% for both rain and snow. We will get the alert message only if the AccuWeather data shows the probability ≥ 25%.</p>
<p>The scripts below will request data from AccuWeather every 1 hour ( UPDATE_INTERVAL_HR= 1) and will send a message every 4 hours ( DELAY_TIME_HR= 4).</p>
<p>These parameters will be stored in the <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> file.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Define % threshold for probability of rain and snow. </span>
<span class="hljs-comment"># The msg will be sent out if the % chance exceed the value</span>
RAIN_THRESHOLD = <span class="hljs-number">25</span>
SNOW_THRESHOLD = <span class="hljs-number">25</span>

<span class="hljs-comment"># time between Accuweather request (in hour)</span>
UPDATE_INTERVAL_HR = <span class="hljs-number">1</span> 

<span class="hljs-comment"># delay time between msg (in hour)</span>
DELAY_TIME_HR = <span class="hljs-number">4</span> 

<span class="hljs-comment"># location id</span>
<span class="hljs-comment"># for example, https://www.accuweather.com/en/fr/lille/135564/weather-forecast/135564</span>
<span class="hljs-comment"># location id is 135564</span>
LOCATION_ID = <span class="hljs-string">"135564"</span>
</code></pre>
<h4 id="heading-3-retrieve-data-from-accuweather">3. Retrieve data from AccuWeather</h4>
<p>Now here comes the fun part. We will now work on the main script.</p>
<p>If you plan to run it locally, setup your directory and import keys and params. Make sure you put <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/keys.py">keys.py</a> and <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> in the same folder as this <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/main.py">main.py</a> script.</p>
<pre><code class="lang-py"><span class="hljs-comment">#set the current directory</span>
<span class="hljs-keyword">import</span> os
os.chdir(<span class="hljs-string">r".\YOUR_PATH"</span>)
<span class="hljs-comment">###############################################################################</span>
<span class="hljs-comment">#import keys and parameters other scripts in the same folder</span>
<span class="hljs-keyword">from</span> keys <span class="hljs-keyword">import</span> FB_USERNAME,FB_PASSWORD,ACCUWEATHER_API_KEY
<span class="hljs-keyword">from</span> params <span class="hljs-keyword">import</span> RAIN_THRESHOLD,SNOW_THRESHOLD,UPDATE_INTERVAL_HR,DELAY_TIME_HR,LOCATION_ID
</code></pre>
<p>Import required modules.</p>
<pre><code class="lang-py"><span class="hljs-comment">#import required modules</span>
<span class="hljs-keyword">import</span> urllib
<span class="hljs-keyword">import</span> urllib.parse
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> time
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> pandas <span class="hljs-keyword">as</span> pd
<span class="hljs-keyword">import</span> logging
<span class="hljs-keyword">import</span> sys
<span class="hljs-keyword">from</span> fbchat <span class="hljs-keyword">import</span> Client
<span class="hljs-keyword">from</span> fbchat.models <span class="hljs-keyword">import</span> *
<span class="hljs-keyword">from</span> datetime <span class="hljs-keyword">import</span> datetime
</code></pre>
<p>Define ‘url_page’ to be requested, in this example, we will retrieve 12-hour hourly forecast. Convert our update/delay time into seconds.</p>
<pre><code class="lang-py">url_page = <span class="hljs-string">"http://dataservice.accuweather.com/forecasts/v1/hourly/12hour/"</span>+str(LOCATION_ID)+<span class="hljs-string">"?apikey="</span>+ACCUWEATHER_API_KEY+<span class="hljs-string">"&amp;details=true&amp;metric=true"</span>
<span class="hljs-comment">#convert hours to seconds</span>
update_interval_sec = <span class="hljs-number">60</span>*<span class="hljs-number">60</span>*UPDATE_INTERVAL_HR 
delay_time_sec = <span class="hljs-number">60</span>*<span class="hljs-number">60</span>*DELAY_TIME_HR
</code></pre>
<p>Then, request the data and put in pandas DataFrame called ‘json_df’.</p>
<p>At this point, we can inspect the retrieved table. Extract and rename the elements that we need. In this example, we will need AccuWeather link, % rain, % snow, date, and time in the desired format.</p>
<pre><code class="lang-py">    json_page = urllib.request.urlopen(url_page)
    json_data = json.loads(json_page.read().decode())
    json_df = pd.DataFrame(json_data)

    <span class="hljs-comment"># set maximum width, so the links are fully shown and clickable</span>
    pd.set_option(<span class="hljs-string">'display.max_colwidth'</span>, <span class="hljs-number">-1</span>)
    json_df[<span class="hljs-string">'Links'</span>] = json_df[<span class="hljs-string">'MobileLink'</span>].apply(<span class="hljs-keyword">lambda</span> x: <span class="hljs-string">'&lt;a href='</span>+x+<span class="hljs-string">'&gt;Link&lt;/a&gt;'</span>)

    json_df[<span class="hljs-string">'Real Feel (degC)'</span>] = json_df[<span class="hljs-string">'RealFeelTemperature'</span>].apply(<span class="hljs-keyword">lambda</span> x: x.get(<span class="hljs-string">'Value'</span>))
    json_df[<span class="hljs-string">'Weather'</span>] = json_df[<span class="hljs-string">'IconPhrase'</span>] 
    json_df[<span class="hljs-string">'Percent_Rain'</span>] = json_df[<span class="hljs-string">'RainProbability'</span>] 
    json_df[<span class="hljs-string">'Percent_Snow'</span>] = json_df[<span class="hljs-string">'SnowProbability'</span>]
</code></pre>
<p>If we look closely, the ‘DateTime’ column is a bit tricky to extract and needs some work. After clean up, save it in ‘current_retrieved_datetime’ variable.</p>
<pre><code class="lang-py">json_df[[<span class="hljs-string">'Date'</span>,<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'DateTime'</span>].str.split(<span class="hljs-string">'T'</span>, expand=<span class="hljs-literal">True</span>)
<span class="hljs-comment"># trim the time to hh:mm format, change to str</span>
json_df[[<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'Time'</span>].str.split(<span class="hljs-string">'+'</span>, expand=<span class="hljs-literal">True</span>)[<span class="hljs-number">0</span>].astype(str).str[:<span class="hljs-number">5</span>]

current_retrieved_datetime = str(json_df[<span class="hljs-string">'Date'</span>][<span class="hljs-number">0</span>])+<span class="hljs-string">' '</span>+str(json_df[<span class="hljs-string">'Time'</span>][<span class="hljs-number">0</span>])
</code></pre>
<p>Next, write an if-else condition to customize the alert message. The retrieved table provides us a 12-hr forecast. We will check each element of both the rain and snow columns and return a message if the probability is above the threshold.</p>
<p>First, initialize the alert message for each case.</p>
<pre><code class="lang-py">rain_msg=<span class="hljs-string">""</span>
snow_msg=<span class="hljs-string">""</span>
</code></pre>
<p>Check ‘Percent_Rain’ and ‘ Percent_Snow’ columns, flag with 1 if the % probability is above threshold (or 0 otherwise).</p>
<p>Sum the columns and modify the ‘rain_msg’ and ‘snow_msg’.</p>
<pre><code class="lang-py">    <span class="hljs-comment"># check % Rain column, return rain_msg</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &gt;= RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &lt; RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Rain_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        rain_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Rain'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of rain'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])

    <span class="hljs-comment"># check % Snow column</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &gt;= SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &lt; SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Snow_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        snow_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Snow'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of snow'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])
</code></pre>
<p>Initialize ‘alert_msg’, modify the messages if there is any ‘rain_msg’ or ‘snow_msg’.</p>
<pre><code class="lang-py">alert_msg =<span class="hljs-string">""</span>
<span class="hljs-keyword">if</span>(len(rain_msg)|len(snow_msg)!=<span class="hljs-number">0</span>):
     alert_msg = rain_msg +<span class="hljs-string">" "</span>+snow_msg
</code></pre>
<p>Add the link to variable ‘ link_for_click’ this will be attached to the message when we send later on.</p>
<pre><code class="lang-py">link_for_click = json_df[<span class="hljs-string">'MobileLink'</span>][<span class="hljs-number">0</span>]
</code></pre>
<p>Up until this point, we can now wrap them into a function. Don’t worry if you get lost, I have put them together below.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">func_get_weather</span>(<span class="hljs-params">url_page</span>):</span>

    json_page = urllib.request.urlopen(url_page)
    json_data = json.loads(json_page.read().decode())
    json_df = pd.DataFrame(json_data)

    <span class="hljs-comment"># set maximum width, so the links are fully shown and clickable</span>
    pd.set_option(<span class="hljs-string">'display.max_colwidth'</span>, <span class="hljs-number">-1</span>)
    json_df[<span class="hljs-string">'Links'</span>] = json_df[<span class="hljs-string">'MobileLink'</span>].apply(<span class="hljs-keyword">lambda</span> x: <span class="hljs-string">'&lt;a href='</span>+x+<span class="hljs-string">'&gt;Link&lt;/a&gt;'</span>)

    json_df[<span class="hljs-string">'Real Feel (degC)'</span>] = json_df[<span class="hljs-string">'RealFeelTemperature'</span>].apply(<span class="hljs-keyword">lambda</span> x: x.get(<span class="hljs-string">'Value'</span>))
    json_df[<span class="hljs-string">'Weather'</span>] = json_df[<span class="hljs-string">'IconPhrase'</span>] 
    json_df[<span class="hljs-string">'Percent_Rain'</span>] = json_df[<span class="hljs-string">'RainProbability'</span>] 
    json_df[<span class="hljs-string">'Percent_Snow'</span>] = json_df[<span class="hljs-string">'SnowProbability'</span>] 
    json_df[[<span class="hljs-string">'Date'</span>,<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'DateTime'</span>].str.split(<span class="hljs-string">'T'</span>, expand=<span class="hljs-literal">True</span>)
    <span class="hljs-comment"># trim the time to hh:mm format, change to str</span>
    json_df[[<span class="hljs-string">'Time'</span>]] = json_df[<span class="hljs-string">'Time'</span>].str.split(<span class="hljs-string">'+'</span>, expand=<span class="hljs-literal">True</span>)[<span class="hljs-number">0</span>].astype(str).str[:<span class="hljs-number">5</span>]

    current_retrieved_datetime = str(json_df[<span class="hljs-string">'Date'</span>][<span class="hljs-number">0</span>])+<span class="hljs-string">' '</span>+str(json_df[<span class="hljs-string">'Time'</span>][<span class="hljs-number">0</span>])

    rain_msg=<span class="hljs-string">""</span>
    snow_msg=<span class="hljs-string">""</span>

    <span class="hljs-comment"># check % Rain column, return rain_msg</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &gt;= RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Rain'</span>] &lt; RAIN_THRESHOLD, <span class="hljs-string">'Rain_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Rain_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        rain_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Rain'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of rain'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Rain_Alert'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])

    <span class="hljs-comment"># check % Snow column</span>
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &gt;= SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">1</span>  
    json_df.loc[json_df[<span class="hljs-string">'Percent_Snow'</span>] &lt; SNOW_THRESHOLD, <span class="hljs-string">'Snow_Alert'</span>] = <span class="hljs-number">0</span>
    <span class="hljs-keyword">if</span> (sum(json_df[<span class="hljs-string">'Snow_Alert'</span>]) &gt; <span class="hljs-number">0</span>):
        snow_msg = <span class="hljs-string">'There is '</span> \
                    +str(json_df[<span class="hljs-string">'Percent_Snow'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>]) \
                    +<span class="hljs-string">' % chance of snow'</span> \
                    +<span class="hljs-string">' at '</span> \
                    +str(json_df[<span class="hljs-string">'Time'</span>][json_df[<span class="hljs-string">'Percent_Snow'</span>]==<span class="hljs-number">1</span>][<span class="hljs-number">0</span>])

    alert_msg =<span class="hljs-string">""</span>
    <span class="hljs-keyword">if</span>(len(rain_msg)|len(snow_msg)!=<span class="hljs-number">0</span>):
         alert_msg = rain_msg +<span class="hljs-string">" "</span>+snow_msg

    link_for_click = json_df[<span class="hljs-string">'MobileLink'</span>][<span class="hljs-number">0</span>]

    <span class="hljs-keyword">return</span>(current_retrieved_datetime,alert_msg,link_for_click)
</code></pre>
<h4 id="heading-4-automated-loop">4. Automated loop</h4>
<p>Finally, for the last part, we will automate the process by using loops. The scripts below are putting the number of loops as ‘num_repeat = 999’.</p>
<pre><code class="lang-py">num_repeat = <span class="hljs-number">999</span> <span class="hljs-comment"># number of loops to repeat</span>
previous_alert_msg = <span class="hljs-string">""</span> <span class="hljs-comment"># initialize alert msg</span>
</code></pre>
<p>Use try and except to overcome errors (just in case something goes wrong with connections). Call ‘func_get_weather’ function and assign to variables.</p>
<pre><code class="lang-py"><span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(num_repeat):
    <span class="hljs-keyword">try</span>:
        current_retrieved_datetime,alert_msg,link_for_click = func_get_weather(url_page)
    <span class="hljs-keyword">except</span> (RuntimeError, TypeError, NameError, ValueError, urllib.error.URLError):
        print(<span class="hljs-string">'error catched'</span>)
</code></pre>
<p>Then, check if there are any changes in the weather. If nothing has changed, print the message to the screen. No chat message will be sent.</p>
<pre><code class="lang-py">    <span class="hljs-comment">#if the weather forecast has not changed, no alert msg will be sent</span>
    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg == alert_msg:
        print(i, current_retrieved_datetime, <span class="hljs-string">'no changes in weather forecast'</span>)
</code></pre>
<p>The message will be sent only if there is any change in the weather.</p>
<p>We can finalize our message at this point. Fetch your user id of your friends and store in ‘friend_list’. Loop to send the message to each friend one-by-one. We put sleep time = 2 seconds between each message and logout after finish.</p>
<pre><code class="lang-py">    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg != alert_msg:    
        <span class="hljs-comment"># login and send facebook msg </span>
        client = Client(FB_USERNAME,FB_PASSWORD)
        users = client.fetchAllUsers()
        friend_list=[user.uid <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users <span class="hljs-keyword">if</span> user.uid!=<span class="hljs-string">"0"</span>]
        <span class="hljs-comment"># loop though all friends</span>
        <span class="hljs-keyword">for</span> id <span class="hljs-keyword">in</span> friend_list: 
            client.send(Message(text=current_retrieved_datetime+<span class="hljs-string">' '</span>+<span class="hljs-string">'12-hr Weather Forecast'</span> +<span class="hljs-string">' '</span>+ alert_msg +<span class="hljs-string">' '</span>+link_for_click),thread_id=id, thread_type=ThreadType.USER)
            <span class="hljs-comment">#sleep for 2 secs between each msg</span>
            time.sleep(<span class="hljs-number">2</span>) 
        <span class="hljs-comment">#logout after sent</span>
        client.logout()
</code></pre>
<p>Execute delay time for the next message. Already defined in <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat/blob/master/scripts/params.py">params.py</a> file — in this case, it is 4 hours. And another one for AccuWeather request delay is 1 hour.</p>
<pre><code class="lang-py">    time.sleep(delay_time_sec)                         
time.sleep(update_interval_sec)
</code></pre>
<p>Again, don’t worry if you get lost. I have put the complete loop together below.</p>
<pre><code class="lang-py"><span class="hljs-comment"># Execute functions, retrieve data and send facebook msg</span>
num_repeat = <span class="hljs-number">999</span> <span class="hljs-comment"># number of loops to repeat</span>
previous_alert_msg = <span class="hljs-string">""</span> <span class="hljs-comment"># initialize alert msg</span>
<span class="hljs-keyword">for</span> i <span class="hljs-keyword">in</span> range(num_repeat):

    <span class="hljs-keyword">try</span>:
        current_retrieved_datetime,alert_msg,link_for_click = func_get_weather(url_page)
    <span class="hljs-keyword">except</span> (RuntimeError, TypeError, NameError, ValueError, urllib.error.URLError):
        print(<span class="hljs-string">'error catched'</span>)

    <span class="hljs-comment">#if the weather forecast has not changed, no alert msg will be sent</span>
    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg == alert_msg:
        print(i, current_retrieved_datetime, <span class="hljs-string">'no changes in weather forecast'</span>)
    <span class="hljs-comment">#if there is any changes in weather       </span>
    <span class="hljs-keyword">if</span> len(alert_msg) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">and</span> previous_alert_msg != alert_msg:    
        <span class="hljs-comment"># login and send facebook msg </span>
        client = Client(FB_USERNAME,FB_PASSWORD)
        users = client.fetchAllUsers()
        friend_list=[user.uid <span class="hljs-keyword">for</span> user <span class="hljs-keyword">in</span> users <span class="hljs-keyword">if</span> user.uid!=<span class="hljs-string">"0"</span>]
        <span class="hljs-comment"># loop though all friends</span>
        <span class="hljs-keyword">for</span> id <span class="hljs-keyword">in</span> friend_list: 
            client.send(Message(text=current_retrieved_datetime+<span class="hljs-string">' '</span>+<span class="hljs-string">'12-hr Weather Forecast'</span> +<span class="hljs-string">' '</span>+ alert_msg +<span class="hljs-string">' '</span>+link_for_click),thread_id=id, thread_type=ThreadType.USER)
            <span class="hljs-comment">#sleep for 2 secs between each msg</span>
            time.sleep(<span class="hljs-number">2</span>) 
        <span class="hljs-comment">#logout after sent</span>
        client.logout()    
        time.sleep(delay_time_sec)                         
    time.sleep(update_interval_sec)
print(current_retrieved_datetime,<span class="hljs-string">'Run Completed'</span>)
</code></pre>
<p>Ta-da! After all our hard work, here is a snapshot of the message we will get.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*34jxVzSyVzO86-OKGlzVHw.png" alt="Image" width="431" height="503" loading="lazy">
<em>Facebook chatbox msg. The location id in this example is 135564.</em></p>
<p>In case we need to know more detail, we can directly click on the link. It will navigate to the AccuWeather mobile website.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*sZ6uWgjnGDF1rVBoKy44Ng.png" alt="Image" width="603" height="586" loading="lazy">
<em>AccuWeather Link</em></p>
<p>The completed script for this how-to is also <a target="_blank" href="https://github.com/ekapope/Weather_Alert_Notification_Facebook_Chat">documented on GitHub</a>.</p>
<p>Thank you for reading. Please give it a try, have fun and let me know your feedback!</p>
<p>If you like what I did, consider following me on <a target="_blank" href="https://ekapope.github.io/">GitHub</a>, <a target="_blank" href="https://medium.com/@ekapope.v">Medium</a>, and <a target="_blank" href="https://twitter.com/EkapopeV">Twitter</a>. Make sure <a target="_blank" href="https://github.com/Ekapope">to star it on GitHub</a> :D</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Learn to build your first bot in Telegram with Python ]]>
                </title>
                <description>
                    <![CDATA[ By Dzaky Widya Putra Imagine this, there is a message bot that will send you a random cute dog image whenever you want, sounds cool right? Let’s make one! For this tutorial, we are going to use Python 3, python-telegram-bot, and public API RandomDog.... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/learn-to-build-your-first-bot-in-telegram-with-python-4c99526765e4/</link>
                <guid isPermaLink="false">66d45e414a7504b7409c3388</guid>
                
                    <category>
                        <![CDATA[ api ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Software Engineering ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 13 Dec 2018 14:40:49 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*vUK3nWPkSEJVAFRLLJbxHA.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Dzaky Widya Putra</p>
<p>Imagine this, there is a message bot that will send you a random cute dog image whenever you want, sounds cool right? Let’s make one!</p>
<p>For this tutorial, we are going to use <strong>Python 3, <a target="_blank" href="https://github.com/python-telegram-bot/python-telegram-bot">python-telegram-bot</a>,</strong> and public API <a target="_blank" href="https://random.dog/"><strong>RandomDog</strong></a><strong>.</strong></p>
<p>At the end of this tutorial, you will have a stress relieving bot that will send you cute dog images every time you need it, yay!</p>
<h3 id="heading-getting-started">Getting started</h3>
<p>Before we start to write the program, we need to generate a token for our bot. The token is needed to access the Telegram API, and install the necessary dependencies.</p>
<h4 id="heading-1-create-a-new-bot-in-botfather">1. Create a new bot in BotFather</h4>
<p>If you want to make a bot in Telegram, you have to “register” your bot first before using it. When we “register” our bot, we will get the token to access the Telegram API.</p>
<p>Go to the <a target="_blank" href="https://telegram.me/BotFather">BotFather</a> (if you open it in desktop, make sure you have the Telegram app), then create new bot by sending the <code>/newbot</code> command. Follow the steps until you get the username and token for your bot. You can go to your bot by accessing this URL: <code>[https://telegram.me/YOUR_BOT_USERNAME](https://telegram.me/YOUR_BOT_USERNAMEa)</code> and your token should looks like this.</p>
<pre><code><span class="hljs-number">704418931</span>:AAEtcZ*************
</code></pre><h4 id="heading-2-install-the-library">2. Install the library</h4>
<p>Since we are going to use a library for this tutorial, install it using this command.</p>
<pre><code>pip3 install python-telegram-bot
</code></pre><p>If the library is successfully installed, then we are good to go.</p>
<h3 id="heading-write-the-program">Write the program</h3>
<p>Let’s make our first bot. This bot should return a dog image when we send the <code>/bop</code> command. To be able to do this, we can use the public API from <a target="_blank" href="https://random.dog/"><strong>RandomDog</strong></a> to help us generate random dog images.</p>
<p>The workflow of our bot is as simple as this:</p>
<blockquote>
<p>access the API -&gt; get the image URL -&gt; send the image</p>
</blockquote>
<h4 id="heading-1-import-the-libraries">1. Import the libraries</h4>
<p>First, import all the libraries we need.</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Updater, CommandHandler
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> re
</code></pre>
<h4 id="heading-2-access-the-api-and-get-the-image-url">2. Access the API and get the image URL</h4>
<p>Let’s create a function to get the URL. Using the requests library, we can access the API and get the json data.</p>
<pre><code>contents = requests.get(<span class="hljs-string">'https://random.dog/woof.json'</span>).json()
</code></pre><p>You can check the json data by accessing that URL: <code>https://random.dog/woof.json</code> in your browser. You will see something like this on your screen:</p>
<pre><code>{“url<span class="hljs-string">":"</span>https:<span class="hljs-comment">//random.dog/*****.JPG"}</span>
</code></pre><p>Get the image URL since we need that parameter to be able to send the image.</p>
<pre><code>image_url = contents[<span class="hljs-string">'url'</span>]
</code></pre><p>Wrap this code into a function called <code>get_url()</code> .</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_url</span>():</span>
    contents = requests.get(<span class="hljs-string">'https://random.dog/woof.json'</span>).json()    
    url = contents[<span class="hljs-string">'url'</span>]
    <span class="hljs-keyword">return</span> url
</code></pre>
<h4 id="heading-3-send-the-image">3. Send the image</h4>
<p>To send a message/image we need two parameters, the image URL and the recipient’s ID — this can be group ID or user ID.</p>
<p>We can get the image URL by calling our <code>get_url()</code> function.</p>
<pre><code>url = get_url()
</code></pre><p>Get the recipient’s ID using this code:</p>
<pre><code>chat_id = update.message.chat_id
</code></pre><p>After we get the image URL and the recipient’s ID, it’s time to send the message, which is an image.</p>
<pre><code>bot.send_photo(chat_id=chat_id, photo=url)
</code></pre><p>Wrap that code in a function called <code>bop</code> , and make sure your code looks like this:</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bop</span>(<span class="hljs-params">bot, update</span>):</span>
    url = get_url()
    chat_id = update.message.chat_id
    bot.send_photo(chat_id=chat_id, photo=url)
</code></pre>
<h4 id="heading-4-main-program">4. <code>Main program</code></h4>
<p>Lastly, create another function called <code>main</code> to run our program. <strong>Don’t forget to change</strong> <code>YOUR_TOKEN</code> with the token that we generated earlier in this tutorial.</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    updater = Updater(<span class="hljs-string">'YOUR_TOKEN'</span>)
    dp = updater.dispatcher
    dp.add_handler(CommandHandler(<span class="hljs-string">'bop'</span>,bop))
    updater.start_polling()
    updater.idle()

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    main()
</code></pre>
<p>At the end your code should look like this:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Updater, InlineQueryHandler, CommandHandler
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> re

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_url</span>():</span>
    contents = requests.get(<span class="hljs-string">'https://random.dog/woof.json'</span>).json()    
    url = contents[<span class="hljs-string">'url'</span>]
    <span class="hljs-keyword">return</span> url

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bop</span>(<span class="hljs-params">bot, update</span>):</span>
    url = get_url()
    chat_id = update.message.chat_id
    bot.send_photo(chat_id=chat_id, photo=url)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    updater = Updater(<span class="hljs-string">'YOUR_TOKEN'</span>)
    dp = updater.dispatcher
    dp.add_handler(CommandHandler(<span class="hljs-string">'bop'</span>,bop))
    updater.start_polling()
    updater.idle()

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    main()
</code></pre>
<h4 id="heading-5-run-the-program">5. Run the program</h4>
<p>Awesome! You finished your first program. Now let’s check if it works. Save the file, name it <code>main.py</code> , then run it using this command.</p>
<pre><code>python3 main.py
</code></pre><p>Go to your telegram bot by accessing this URL: <code>https://telegram.me/YOUR_BOT_USERNAME</code>. Send the <code>/bop</code> command. If everything runs perfectly the bot will reply with a random dog image. Cute right?</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/cgojJGcVwIVamFkYrpcRzrOOBJ0xFB0cTkTP" alt="Image" width="600" height="400" loading="lazy">
<em>One random generatedly image</em></p>
<h3 id="heading-handling-errors">Handling errors</h3>
<p>Great! Now you have a bot that will send you a cute dog image whenever you want.</p>
<p>There is more! The <a target="_blank" href="https://random.dog/"><strong>RandomDog</strong></a> API not only generates images, but also videos and GIFs. If we access the API and we get a video or GIF, there is an error and the bot won’t send it to you.</p>
<p>Let’s fix this so the bot will only send a message with an image attachment. If we get a video or GIF then we will call the API again until we get an image.</p>
<h4 id="heading-1-match-the-file-extension-using-regex">1. Match the file extension using regex</h4>
<p>We are going to use a regex to solve this problem.</p>
<p>To distinguish an image from video or GIF, we can take a look at the file extension. We only need the last part of our URL.</p>
<pre><code>https:<span class="hljs-comment">//random.dog/*****.JPG</span>
</code></pre><p>We need to define, first, what file extensions are allowed in our program.</p>
<pre><code>allowed_extension = [<span class="hljs-string">'jpg'</span>,<span class="hljs-string">'jpeg'</span>,<span class="hljs-string">'png'</span>]
</code></pre><p>Then use the regex to extract the file extension from the URL.</p>
<pre><code>file_extension = re.search(<span class="hljs-string">"([^.]*)$"</span>,url).group(<span class="hljs-number">1</span>).lower()
</code></pre><p>Using that code, make a function called <code>get_image_url()</code> to iterate the URL until we get the file extension that we want (jpg,jpeg,png).</p>
<pre><code class="lang-py"><span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_image_url</span>():</span>
    allowed_extension = [<span class="hljs-string">'jpg'</span>,<span class="hljs-string">'jpeg'</span>,<span class="hljs-string">'png'</span>]
    file_extension = <span class="hljs-string">''</span>
    <span class="hljs-keyword">while</span> file_extension <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> allowed_extension:
        url = get_url()
        file_extension = re.search(<span class="hljs-string">"([^.]*)$"</span>,url).group(<span class="hljs-number">1</span>).lower()
    <span class="hljs-keyword">return</span> url
</code></pre>
<h4 id="heading-2-modify-your-code">2. Modify your code</h4>
<p>Great! Now for the last part, replace the <code>url = get_url()</code> line in the <code>bop()</code> function with <code>url = get_image_url()</code> , and your code should look like this:</p>
<pre><code class="lang-py"><span class="hljs-keyword">from</span> telegram.ext <span class="hljs-keyword">import</span> Updater, InlineQueryHandler, CommandHandler
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> re

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_url</span>():</span>
    contents = requests.get(<span class="hljs-string">'https://random.dog/woof.json'</span>).json()    
    url = contents[<span class="hljs-string">'url'</span>]
    <span class="hljs-keyword">return</span> url

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_image_url</span>():</span>
    allowed_extension = [<span class="hljs-string">'jpg'</span>,<span class="hljs-string">'jpeg'</span>,<span class="hljs-string">'png'</span>]
    file_extension = <span class="hljs-string">''</span>
    <span class="hljs-keyword">while</span> file_extension <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> allowed_extension:
        url = get_url()
        file_extension = re.search(<span class="hljs-string">"([^.]*)$"</span>,url).group(<span class="hljs-number">1</span>).lower()
    <span class="hljs-keyword">return</span> url

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">bop</span>(<span class="hljs-params">bot, update</span>):</span>
    url = get_image_url()
    chat_id = update.message.chat_id
    bot.send_photo(chat_id=chat_id, photo=url)

<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">main</span>():</span>
    updater = Updater(<span class="hljs-string">'YOUR_TOKEN'</span>)
    dp = updater.dispatcher
    dp.add_handler(CommandHandler(<span class="hljs-string">'bop'</span>,bop))
    updater.start_polling()
    updater.idle()

<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:
    main()
</code></pre>
<p>Nice! Everything should run perfectly. You can also check out my <a target="_blank" href="https://github.com/dzakyputra/doggobot">GitHub</a> account to get the code.</p>
<p>Finally, congratulations for finishing this tutorial, plus you have a cool Telegram bot now.</p>
<p>Please leave a comment if you think there are any errors in my code or writing, because I’m still learning and I want to get better.</p>
<p>Thank you and good luck practicing! :)</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to build the ultimate AI chatbot by following these steps ]]>
                </title>
                <description>
                    <![CDATA[ By Paul Pinard A quick guide that helps you avoid common pitfalls Building a bot is a rewarding experience: creating your own artificial intelligence is amazing! However, it can be a challenge, and there are mistakes to avoid. In this piece, we’re g... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-the-ultimate-ai-chatbot-by-following-these-steps-e0abe77a2b20/</link>
                <guid isPermaLink="false">66c350525ced6d98e4bd3333</guid>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ #chatbots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Wed, 26 Sep 2018 12:58:35 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*mDFiewb7C0vXSWvbZ6VodQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Paul Pinard</p>
<h4 id="heading-a-quick-guide-that-helps-you-avoid-common-pitfalls">A quick guide that helps you avoid common pitfalls</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/Na8oklNT0d3X7Nix1l0-mfV3mGJUv6BjNiKx" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Building a bot is a rewarding experience: creating your own artificial intelligence is amazing!</p>
<p>However, it can be a challenge, and there are mistakes to avoid. In this piece, we’re going to walk you through the most common or damaging mistakes new bot builders make in each phase of bot building: conception, training, building, connection, user experience and maintenance. Let’s roll!</p>
<h3 id="heading-conception"><strong>Conception</strong></h3>
<p>Building a bot doesn’t start at the first line of code. It starts much earlier, during the conception.</p>
<p>During that first step, it’s important to define the use case of your chatbot. What is the problem you want it to solve? What are your business needs?</p>
<p>We often see people saying “<em>I want a bot that does this</em>”, but when we dig deeper, we realize a different bot would actually fix the issue much more efficiently. If you want to build a chatbot that manages customers’ questions on return policies, but later realize that these queries are less than 2% of your global volume, you might want to switch to another topic. So, start from the business problem and build from there.</p>
<p>Now that you’ve established the real-life business need, how should the bot conversation flow go to solve it? What we usually do is take out a drawing board and draw all the conversation flows, from start to finish. Modeling all possibilities allows you to make sure every topic is covered and gives the developer a good overview of what needs to be done. It is also the first step of creating your user experience, which we’ll talk about later. For now, simply keep in mind that each conversation should be about 3 or 4 exchanges, no more.</p>
<p>During this step, remember your audience: who are the end-users that will be talking to your bot? You have to make something that works for them.</p>
<p>Do not forget to include small talk in your conception. All chatbots are expected to understand and reply to a number of topics unrelated to their mission: jokes, weather questions, “how are you doing”, and even remarks like “will you marry me” or other off-topic stuff. Be sure to plan for those if you want the user to be satisfied with the experience. But no worries, we provide pre-trained small-talk Skills on <a target="_blank" href="https://cai.tools.sap/?utm_source=medium&amp;utm_medium=article&amp;utm_campaign=UA2019">SAP Conversational AI</a>.</p>
<p><strong>What not to do when building a bot:</strong></p>
<blockquote>
<p><strong>1. Disregard it as a non-important step</strong></p>
<p><strong>2. Start from what you want and not from what you need</strong></p>
<p><strong>3. Incorrectly understand who the final users of the bot will be and design an experience they won’t appreciate</strong></p>
<p><strong>4. Not include small talk and other commonly asked questions</strong></p>
</blockquote>
<h3 id="heading-training"><strong>Training</strong></h3>
<p>Training the bot is the most important factor in determining its performance. Bad training will inevitably lead to a poor performing chatbot and frustrated users.</p>
<p>Based on the flow you’ve created during conception, training consists of creating intents and filling them with expressions. If you’re not comfortable with the concept of intents and expressions, <a target="_blank" href="https://cai.tools.sap/docs/concepts/intent">this article</a> should help you. But here are some things that make for good training.</p>
<p>The number of expressions in each intent is crucial. Five doesn’t cut it, you should go for 50+. SAP Conversational AI works very well on small datasets, but we still need a bit of information. These sentences should be varied and should come from end users. Never train your bot only with the development and project team: they know the technical slang too well to accurately represent the people that will actually use the bot.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/-Yycb-a6fPKt0xLNWMfceZVVoZYgLeJ33k2a" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Tagging entities has a few rules as well. Entities are keywords that you need to detect in a sentence to extract information (the key point here being “to extract information”). You don’t need to tag every noun, adjective or word per sentence just because you can! The point of entities is to extract relevant information that you can use in your code. Only tag those.</p>
<p>However, avoid having sentences that are only made of one word that is an entity (e.g. “Paris” as a complete sentence). This entity can be detected by any intent, which can lead to detection issues.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/MZHlf-J9SBKS9FC3zq8JzZGTgbxvBgpdBSRw" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A common best practice for big bots is to use intents and entities hand in hand. It is better to create a global intent and use entities to specify the user request, than to create very specific intents that the classifier will confuse as they overlap.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/EQsnPzNr1wkFVldSUSEuYeXKpbaAXO5840Ek" alt="Image" width="600" height="400" loading="lazy">
<em>Here, the global intent is troubleshooting, but entities detect which product isn’t functioning.</em></p>
<p><strong>What not to do when building a bot:</strong></p>
<blockquote>
<p><strong>5. Have less than 50 expressions per intent</strong></p>
<p><strong>6. Train your bot by people who are not the end-user</strong></p>
<p><strong>7. Tag every word in a sentence as an entity</strong></p>
<p><strong>8. Tag words as entities when you don’t use the extracted information</strong></p>
<p><strong>9. Have expressions that are only entities (i.e “Paris”)</strong></p>
<p><strong>10. Create very specific intents instead of using entities to understand the topic</strong></p>
</blockquote>
<h3 id="heading-building"><strong>Building</strong></h3>
<p>Building a bot is often assumed to involve just building the conversation flow. It is the fun part! It’s when everything comes to life. However, it can be a scary process.</p>
<p>The first thing to understand is that it’s ok to use multiple skills to complete one task. One skill doesn’t have to equal one full process. It can be a good solution to create one “mega-skill” whose job is to dispatch the user input to the correct skill.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/AziHdCyg92iqRr0HQ8RtCK0DrlIpJs-Nd9vB" alt="Image" width="600" height="400" loading="lazy">
<em>In our troubleshooting (ts) example, a mega-skill redirects to the different skills that manage the specific procedures</em></p>
<p>That is also a solution if you have skills with triggers that overlap each other. And if something doesn’t work, be sure to use the logs in the debug console to understand where the problem is coming from.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/ZjjNiSVhAlfy9E7Oq30-EFZsQdQyXhFLNJ07" alt="Image" width="600" height="400" loading="lazy"></p>
<p><strong>What not to do when building a bot:</strong></p>
<blockquote>
<p><strong>11. Insist on the “one skill = one task” philosophy</strong></p>
<p><strong>12. Not leverage the debugging tools of the platform</strong></p>
</blockquote>
<h3 id="heading-connecting-and-user-experience"><strong>Connecting and User Experience</strong></h3>
<p>When connecting your bot, you have to decide where it is going to be available to your users, and therefore work on a user experience. There are a few things to know to provide an enjoyable UX, the first one being: your bot has to look pretty. An attractive bot with plenty of buttons, graphic elements, HD pictures, colors, and a good personality makes all the difference.</p>
<p>But how do you get that?</p>
<p>First, think of your audience when you choose your channel. If you’re targeting the 50 to 65 age demographics, you’re probably not going to put your bot on Kik! Don’t try to attract your audience to a channel they don’t use, even if it’s better. Instead, integrate the bot where your users already are.</p>
<p>Then, keep in mind a chatbot is a conversational interface. Conversations are interactive exchanges; therefore, your bot should never reply with long-winded blocks of texts (more than 60 characters is getting long).</p>
<p>Separate replies into different messages, use images, buttons, lists and other UX components based on the channel you use to make it lively. It’s also important to create a rewarding conversation: your bot isn’t an FBI agent. Nobody wants to be asked 20 questions before getting an answer. Instead, create your flow and UX to provide an answer every 3 or 4 exchanges to keep the user engaged.</p>
<p>Since we provide a powerful natural language processing API with our bot building tool, our users tend to want to do everything through language. While that’s admirable, we still advise to diversify: offer cards, buttons and other graphic elements for interactivity and ease of use, but still make sure the entire flow can be done using natural language. That’s when users know your bot is the real deal.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/rl5eyf2CDstgMMB-NdSjzDt5tjYWSRNiDUlO" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Giving a personality to your chatbot is essential, however you have to find the right balance. We always advise to let your users know they’re talking to a bot. It’s simply expectation management!</p>
<p>A human talking to another human is going to expect the highest level of interaction, whereas a human talking to a bot is going to know that they can’t ask about anything and everything. However, don’t make it too robotic: give it a name, an image, and use smileys and tone of voice to make it memorable.</p>
<p><strong>What not to do when building a bot:</strong></p>
<blockquote>
<p><strong>13. Mis-identify the channel your audience is using</strong></p>
<p><strong>14. Create conversations where the user has to answer 4+ questions to get a first answer</strong></p>
<p><strong>15. Send blocks of texts as replies (more than 60 characters is too much)</strong></p>
<p><strong>16. Discard all UX elements (buttons, cards, lists, etc) just to focus on text</strong></p>
<p><strong>17. Pass your bot as a human person</strong></p>
<p><strong>18. Not giving your bot a personality that attracts your audience</strong></p>
</blockquote>
<h3 id="heading-maintenance"><strong>Maintenance</strong></h3>
<p>Once your bot is in production, your job is not done! Maintaining your bot is an essential part of its long-lasting success. That mainly consists of fine-tuning your training and monitoring what your users are saying to adapt your flow or create new use cases.</p>
<p>When training, proceed with caution. While it is important to add new user sentences via the log feed, you do not want to unbalance the training you’ve created that already works. Don’t swamp your intents by adding all the new expressions, only add what’s necessary. Keep in mind that all intents have to be trained the same amount! If one intent has 100 expressions and the other has 10, that’s no good. Therefore, check regularly when assigning new expressions. Our training analytics are your best friend when it comes to improving your training data!</p>
<p>Finally, your log feed is the place where you can see what users are talking about. Do you see a topic that your users are raising frequently that your bot doesn’t yet manage? Why not integrate it into your flow? That’s the best way to show your community that the bot they’re using is always striving to provide a great experience.</p>
<p><strong>What not to do when building a bot:</strong></p>
<blockquote>
<p><strong>19. Thinking that once the bot is in production, your job is done</strong></p>
<p><strong>20. Overflow intents with new user expression and mess up your existing training</strong></p>
<p><strong>21. Create inequalities in your intent size</strong></p>
<p><strong>22. Not pay attention to how people are using your bot</strong></p>
</blockquote>
<p>With all this in mind, you are fully on-board to build a first kick-ass bot! If you’re ready to go further, this <a target="_blank" href="https://medium.freecodecamp.org/how-to-build-your-first-chatbot-with-the-sap-conversational-ai-9a1a2bd44e3c">step-by-step tutorial</a> can guide you through the actual process to build an awesome joke-telling chatbot!</p>
<p>Happy building!</p>
<p><em>Originally published on <a target="_blank" href="https://cai.tools.sap/blog/building-a-bot-22-rules/">SAP Conversational AI Blog</a>.</em></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I built an HR Slack Bot with Node and Botkit ]]>
                </title>
                <description>
                    <![CDATA[ By Alexandre Robin Why create a Slack Bot ? I am an HR professional. More specifically I am a Human Resources Information System (HRIS) Consultant. I work with Application Tracking Systems, Learning Management Systems, and Core HR. But I have never h... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-built-an-hr-slack-bot-with-node-and-botkit-6b23b81531bb/</link>
                <guid isPermaLink="false">66d45d5933b83c4378a517ac</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Human Resources ]]>
                    </category>
                
                    <category>
                        <![CDATA[ slack ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Web Development ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Mon, 17 Sep 2018 16:18:30 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*3qx-9q-OBdf0zcge4Ca8yw.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Alexandre Robin</p>
<h3 id="heading-why-create-a-slack-bot">Why create a Slack Bot ?</h3>
<p>I am an HR professional. More specifically I am a Human Resources Information System (HRIS) Consultant. I work with Application Tracking Systems, Learning Management Systems, and Core HR. But I have never had the opportunity to work with an HR Bot. Which may be the Future of HR.</p>
<p>I read a lot about bots on Slack and Messenger, and used some of them in my daily life — Product Hunt, GitHub and Trello. But for HR purposes, I have never had the opportunity to work with a tool tailored for my needs.</p>
<p>That’s why I decided to work on my own bot.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*cyhNkd0jd7Zd3zBDLaKpSg.png" alt="Image" width="455" height="290" loading="lazy">
<em>Me starting to work</em></p>
<h3 id="heading-my-goals">My Goals</h3>
<p>My bot should be able to manage all the needs a small company could have on Slack:</p>
<ul>
<li>Onboarding</li>
<li>Putting people in touch</li>
<li>Reminders</li>
<li>Announcements</li>
<li>Birthdays /Anniversary</li>
<li>And many more</li>
</ul>
<h3 id="heading-reviewing-the-basics">Reviewing the basics</h3>
<p>For this program, I’ll use:</p>
<ul>
<li>Botkit</li>
<li>Node JS</li>
<li>Express Server</li>
<li>MongoDB</li>
<li>Slack API &amp; of course</li>
</ul>
<p>Botkit is:</p>
<blockquote>
<p>One easy way to build bot users, especially if you already work with <a target="_blank" href="https://nodejs.org/">Node.js</a>, is Howdy’s <a target="_blank" href="https://howdy.ai/botkit/"><strong>Botkit</strong></a>. Botkit is a framework that takes care of most these API gymnastics, so you can focus on your bot’s behavior.</p>
</blockquote>
<p>Exactly what I was looking for :-)</p>
<p>Botkit provides a boilerplate for Slack. But I have chosen to start from scratch to have a better understanding of my bot. However, it’s a good idea to train yourself with a bot created on <a target="_blank" href="https://glitch.com/botkit">Glitch</a>.</p>
<h4 id="heading-how-do-slack-bots-work">How do Slack bots work?</h4>
<p>I am not an expert. I have read again and again Slack and Botkit’s official documentation. I’m still not sure I understood everything. Here is my understanding of a Slack bot’s behavior:</p>
<p>Every App on Slack has a “scope” which is a perimeter on which an app can read or perform actions. A bot is part of an application created and installed on Slack.</p>
<p>Therefore, when you install an app on Slack, you give access to some information and permissions to it. For your bot, you want it to be, at least, able to send and reply to messages of other users.</p>
<p>There are then two cases:</p>
<ol>
<li>You want your bot to react to events happening <strong>directly in Slack</strong></li>
<li>You want your bot to react to events happening <strong>on your server</strong></li>
</ol>
<p>We will view both of them in this post!</p>
<h3 id="heading-getting-started">Getting Started</h3>
<p>Before anything else, you will need a server. In my case, Express.</p>
<p>Below you’ll find my server.js file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> express = <span class="hljs-built_in">require</span>(<span class="hljs-string">'express'</span>);
<span class="hljs-keyword">var</span> app = express();
<span class="hljs-keyword">var</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>).Server(app);
<span class="hljs-keyword">var</span> dotenv = <span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>);

<span class="hljs-comment">// configuration ===========================================</span>
<span class="hljs-comment">//load environment variables,</span>
dotenv.load();

<span class="hljs-comment">// public folder for images, css,...</span>
app.use(express.static(__dirname + <span class="hljs-string">'/public'</span>))

<span class="hljs-comment">//parsing</span>
app.use(bodyParser.json()); <span class="hljs-comment">// for parsing application/json</span>
app.use(bodyParser.urlencoded({
    <span class="hljs-attr">extended</span>: <span class="hljs-literal">true</span>
})); <span class="hljs-comment">//for parsing url encoded</span>

<span class="hljs-comment">// view engine ejs</span>
app.set(<span class="hljs-string">'view engine'</span>, <span class="hljs-string">'ejs'</span>);

<span class="hljs-comment">// routes</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./routes/routes'</span>)(app);

<span class="hljs-comment">//botkit</span>
<span class="hljs-built_in">require</span>(<span class="hljs-string">'./controllers/botkit'</span>)


<span class="hljs-comment">//START ===================================================</span>
http.listen(app.get(<span class="hljs-string">'port'</span>), <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'listening on port '</span> + app.get(<span class="hljs-string">'port'</span>));
});
</code></pre>
<p>This port must be public and accessible, not just on a localhost.</p>
<p>For the moment, this server is a blank page, showing and processing nothing.</p>
<p>You’ll then need a Slack App: just follow this <a target="_blank" href="https://api.slack.com/apps">link</a> to create one.</p>
<p>Then, you’ll have to configure your controller. The controller is the brain of your bot. It contains every skill and configuration. Below is my botkit.js file. It has almost the same content found in Botkit’s Starter kit available here: <a target="_blank" href="https://github.com/howdyai/botkit-starter-slack">https://github.com/howdyai/botkit-starter-slack</a></p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> mongoUri = <span class="hljs-string">'mongodb://localhost:27017/nameofyourDB'</span>
<span class="hljs-keyword">var</span> database = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../config/database'</span>)({
    <span class="hljs-attr">mongoUri</span>: mongoUri
})
<span class="hljs-keyword">var</span> request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>)

<span class="hljs-keyword">if</span> (!process.env.SLACK_ID || !process.env.SLACK_SECRET || !process.env.PORT) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error: Specify SLACK_ID SLACK_SECRET and PORT in environment'</span>);
    process.exit(<span class="hljs-number">1</span>);
}

<span class="hljs-keyword">var</span> controller = Botkit.slackbot({
    <span class="hljs-attr">storage</span>: database,
    <span class="hljs-attr">clientVerificationToken</span>: process.env.SLACK_TOKEN
})

<span class="hljs-built_in">exports</span>.controller = controller

<span class="hljs-comment">//CONNECTION FUNCTIONS=====================================================</span>

<span class="hljs-built_in">exports</span>.connect = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">team_config</span>) </span>{
        <span class="hljs-keyword">var</span> bot = controller.spawn(team_config);
        controller.trigger(<span class="hljs-string">'create_bot'</span>, [bot, team_config]);
    }
    <span class="hljs-comment">// just a simple way to make sure we don't</span>
    <span class="hljs-comment">// connect to the RTM twice for the same team</span>
<span class="hljs-keyword">var</span> _bots = {};

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">trackBot</span>(<span class="hljs-params">bot</span>) </span>{
    _bots[bot.config.token] = bot;
}

controller.on(<span class="hljs-string">'create_bot'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot, team</span>) </span>{
    <span class="hljs-keyword">if</span> (_bots[bot.config.token]) {
        <span class="hljs-comment">// already online! do nothing.</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"already online! do nothing."</span>)
    } <span class="hljs-keyword">else</span> {
        bot.startRTM(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>) </span>{
            <span class="hljs-keyword">if</span> (!err) {
                trackBot(bot);
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"RTM ok"</span>)
                controller.saveTeam(team, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, id</span>) </span>{
                    <span class="hljs-keyword">if</span> (err) {
                        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Error saving team"</span>)
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Team "</span> + team.name + <span class="hljs-string">" saved"</span>)
                    }
                })
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"RTM failed"</span>)
            }
            bot.startPrivateConversation({
                <span class="hljs-attr">user</span>: team.createdBy
            }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, convo</span>) </span>{
                <span class="hljs-keyword">if</span> (err) {
                    <span class="hljs-built_in">console</span>.log(err);
                } <span class="hljs-keyword">else</span> {
                    convo.say(<span class="hljs-string">'I am a bot that has just joined your team'</span>);
                    convo.say(<span class="hljs-string">'You must now /invite me to a channel so that I can be of use!'</span>);
                }
            });
        });
    }
});

<span class="hljs-comment">//REACTIONS TO EVENTS==========================================================</span>
<span class="hljs-comment">// Handle events related to the websocket connection to Slack</span>

controller.on(<span class="hljs-string">'rtm_open'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'** The RTM api just connected!'</span>)
});

controller.on(<span class="hljs-string">'rtm_close'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'** The RTM api just closed'</span>);
    <span class="hljs-comment">// you may want to attempt to re-open</span>
});
</code></pre>
<h4 id="heading-unlocking-the-first-case-react-to-the-events-happening-on-slack">Unlocking the first case: react to the events happening on Slack</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*51uJInZFmmF_0gD3ICFr9Q.jpeg" alt="Image" width="510" height="339" loading="lazy"></p>
<p>When you give the right permissions to your app, every time a message is sent on a channel, Slacks sends a request to your server with some information — the channel ID, the user, the timestamp and most importantly, the content of the message.</p>
<p>If we want our bot to react to a simple message like “Hi”, we have to give Slack an address to send the information to.</p>
<p>In a routes.js file write:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> Request = <span class="hljs-built_in">require</span>(<span class="hljs-string">'request'</span>)
<span class="hljs-keyword">var</span> slack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../controllers/botkit'</span>)
<span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">app</span>) </span>{
 app.post(<span class="hljs-string">'/slack/receive'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req,res</span>)</span>{
<span class="hljs-comment">//respond to Slack that the webhook has been received.</span>
    res.status(<span class="hljs-number">200</span>);
<span class="hljs-comment">// Now, pass the webhook into be processed</span>
    slack.controller.handleWebhookPayload(req, res)
  })
}
</code></pre>
<p>We now have a webhook : <a target="_blank" href="http://your-ip-or-domain:port/slack/receive">http://your-ip-or-domain:port/slack/receive</a></p>
<p>Once Slack is informed of this route via the Event Subscriptions page of your Slack App, it will be able to send it JSON. You will be able to receive it thanks to the parsing part of the server.js file above.</p>
<p>Here is a (simple) schema to explain the process behind it:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*c-Km3PgTIihfbthJC0BFfw.png" alt="Image" width="800" height="312" loading="lazy"></p>
<p>1- SLACK « Here is a JSON file with the latest event on your Slack Channel »</p>
<p>2- SERVER « Okay well received, I send it to Botkit»</p>
<p>3- BOTKIT «Here is a temporary answer, wait a second»</p>
<p>4- BOTKIT « Yeah! I hear a keyword, here is a JSON object with the action to perform »</p>
<p>If we want our bot to react every time it hears “Hello”, we can simply add this .hears() function to our controller:</p>
<pre><code class="lang-javascript">controller.hears([<span class="hljs-string">'hello'</span>, <span class="hljs-string">'hi'</span>], <span class="hljs-string">'direct_message,direct_mention,mention'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">bot, message</span>) </span>{
controller.storage.users.get(message.user, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, user</span>) </span>{
        <span class="hljs-keyword">if</span> (user &amp;&amp; user.name) {
            bot.reply(message, <span class="hljs-string">'Hello '</span> + user.name + <span class="hljs-string">'!!'</span>);
        } <span class="hljs-keyword">else</span> {
            bot.reply(message, <span class="hljs-string">'Hello.'</span>);
        }
    });
});
</code></pre>
<p>Notice the <code>storage.users.get()</code> part in this snippet. Botkit is compatible with almost all the database systems available on the market. I have decided to use MongoDB because it was on my learning list for a long time. Plus the documentation with Botkit is detailed.</p>
<p>Now, we have to let our imagination do the work and find some fun features to create.</p>
<h4 id="heading-second-case-initiate-a-conversation-with-your-bot">Second Case: initiate a conversation with your bot</h4>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*pEUkhsJtGgYrzGvq0xf57Q.jpeg" alt="Image" width="800" height="450" loading="lazy"></p>
<p>For this feature, I wanted my bot to react to events which were not initiated on Slack. For example, do a daily routine. If it’s someone’s anniversary in the company, send them a survey asking their feelings about their first months/weeks.</p>
<p>I have decided to use node-cron: <a target="_blank" href="https://github.com/kelektiv/node-cron">https://github.com/kelektiv/node-cron</a> to manage the daily check.</p>
<p>Here is below a cronjob firing every weekday at 9:00 am. Thanks to the Date() method, the bot gets today’s date and can compare it to the “joinedDate” of the user.</p>
<p>To get only the right users and avoid a forEach loop, we can use a query on our Database:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">var</span> dailyCheck = <span class="hljs-keyword">new</span> CronJob(<span class="hljs-string">'00 00 9 * * 1-5'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">/*
         * Runs every weekday (Monday through Friday)
         * at 09:00:00 AM. It does not run on Saturday
         * or Sunday.
         */</span>
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`DailyCheck triggered <span class="hljs-subst">${<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()}</span>`</span>)

        <span class="hljs-comment">//Gets today's date</span>
        <span class="hljs-keyword">let</span> d = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
        d.setUTCHours(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)

        <span class="hljs-keyword">let</span> threeMonthsAgo = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
        threeMonthsAgo.setUTCMonth(d.getUTCMonth() - <span class="hljs-number">3</span>)
        threeMonthsAgo.setUTCHours(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)


        <span class="hljs-keyword">let</span> sevenDaysAgo = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()
        sevenDaysAgo.setUTCDate(d.getUTCDate() - <span class="hljs-number">7</span>)
        sevenDaysAgo.setUTCHours(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>)


        controller.storage.users.find({
            <span class="hljs-string">"joinedDate"</span>: {
                <span class="hljs-string">"$eq"</span>: +sevenDaysAgo
            }
        }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, user</span>) </span>{
            user.forEach(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">member</span>) </span>{
                <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`Message was sent to <span class="hljs-subst">${member.name}</span>(<span class="hljs-subst">${member.id}</span>)`</span>)
                bot.startPrivateConversation({
                    <span class="hljs-attr">user</span>: member.id
                }, Conversations.sendSurvey7)
            })
        })
    }, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{
        <span class="hljs-comment">/* This function is executed when the job stops */</span>
    }, <span class="hljs-literal">true</span>,
    <span class="hljs-comment">/* Start the job right now */</span>
    timeZone = <span class="hljs-string">'Europe/Paris'</span> <span class="hljs-comment">/* Time zone of this job. */</span> )
</code></pre>
<p>And… Tada!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*MSZ0zXebVZ_wwCKP." alt="Image" width="800" height="533" loading="lazy">
_“A robot named Pepper holding an iPad” by [Unsplash](https://unsplash.com/@agkdesign?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="_blank" title=""&gt;Alex Knight on &lt;a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral" rel="noopener" target="<em>blank" title=")</em></p>
<h3 id="heading-conclusion">Conclusion</h3>
<p>After more than a year of being a camper and learning to code, I am really happy to be able to start and finish a project like this one. I now have a bot working and performing almost all the actions I had in mind at the design phase. And I still have a lot of ideas!</p>
<p>I am still working on this bot. The GitHub repository is available here: <a target="_blank" href="https://github.com/alexandrobin/hrbot">https://github.com/alexandrobin/hrbot</a>. Some of the commits are in French, but the codebase is commented in English. :-)</p>
<p>Besides, it’s quite easy to deploy it on Heroku with a Mongolab database if you don’t have a server!</p>
<p>If you have some suggestions or are interested by this article and project, feel free to leave a comment ! I would be happy to discuss with you.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I built a Twitter bot that generates song lyrics ]]>
                </title>
                <description>
                    <![CDATA[ By Shawn Toubeau In this article, I will go over how I built my Twitter lyric bot and how you’ll be able to set up your very own. Procedure Here’s a list of the components we have to set up. Twitter Account Text Editor/IDE The bot Heroku Automation ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/building-a-twitter-lyric-bot-12468255a4ee/</link>
                <guid isPermaLink="false">66c346b6c577a44239cd7b36</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Heroku ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Node.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ General Programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 30 Aug 2018 22:41:15 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/1*znQJ9QO7vwypsv2mZPiUxQ.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By Shawn Toubeau</p>
<p>In this article, I will go over how I built my Twitter lyric bot and how you’ll be able to set up your very own.</p>
<h3 id="heading-procedure">Procedure</h3>
<p>Here’s a list of the components we have to set up.</p>
<ol>
<li>Twitter Account</li>
<li>Text Editor/IDE</li>
<li>The bot</li>
<li>Heroku Automation</li>
</ol>
<h3 id="heading-setup-new-twitter-application"><strong>Setup New Twitter Application</strong></h3>
<p>To create a new Twitter application, go <a target="_blank" href="https://developer.twitter.com/en/apps">here</a>. You will need to apply for developer access. After you submit the application, it may take a while but Twitter will notify you when you get accepted.</p>
<h3 id="heading-setup-editoride"><strong>Setup Editor/IDE</strong></h3>
<p>Now you’re going to want to set up your editor. My preference is VS Code, so that’s what I’ll be using.</p>
<p>Make sure you have Git and Node installed.</p>
<p>Go ahead clone the following git repository to your computer.</p>
<pre><code>git clone https:<span class="hljs-comment">//github.com/ShawnToubeau/lyric-bot.git</span>
</code></pre><h3 id="heading-code-walk-trough">Code Walk Trough</h3>
<p>The 3 main files that the bot consists of are bot.js, lyrics.txt, and .env.</p>
<p><strong>SideNote</strong>: Your clone of the project will not contain a .env file because of the .gitignore, thus we will be creating our own later down in the article!</p>
<h4 id="heading-botjs">bot.js</h4>
<p>Starting in bot.js, we start by importing Twit, fs, and dotenv.</p>
<p>Twit is a module that supports the Twitter Developer API.</p>
<p>Fs, or file system, is a file I/O nodule that allows us to interact with our lyrics.txt file.</p>
<p>Dotenv is a module that reads in environment variables stored in our .env file.</p>
<pre><code><span class="hljs-keyword">const</span> Twit = <span class="hljs-built_in">require</span>(<span class="hljs-string">'twit'</span>);
</code></pre><pre><code><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);
</code></pre><pre><code><span class="hljs-built_in">require</span>(<span class="hljs-string">'dotenv'</span>).config();
</code></pre><pre><code><span class="hljs-keyword">const</span> order = <span class="hljs-number">4</span>; <span class="hljs-comment">// length of each n-gram</span>
</code></pre><pre><code><span class="hljs-keyword">let</span> nGrams = {};
</code></pre><pre><code><span class="hljs-keyword">const</span> Bot = <span class="hljs-keyword">new</span> Twit({
</code></pre><pre><code>  consumer_key: process.env.TWITTER_CONSUMER_KEY,
</code></pre><pre><code>  consumer_secret: process.env.TWITTER_CONSUMER_SECRET,
</code></pre><pre><code>  access_token: process.env.TWITTER_ACCESS_TOKEN,
</code></pre><pre><code>  access_token_secret: process.env.TWITTER_ACCESS_TOKEN_SECRET
</code></pre><pre><code>});
</code></pre><p>nGrams is the object that keeps track of substring occurrences so that we are able to use a probability to generate our own lyrics randomly. You can read more about them <a target="_blank" href="https://en.wikipedia.org/wiki/N-gram">here</a>.</p>
<p>And Bot is…well...our Bot! We need to define some variables to make it work. Using the dotenv module, we can grab the values stored in our .env file.</p>
<p>Next we define a few functions as follows:</p>
<h4 id="heading-pickrandomstart">pickRandomStart()</h4>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">pickRandomStart</span>(<span class="hljs-params">lyrics</span>) </span>{
</code></pre><pre><code>  <span class="hljs-keyword">const</span> random = <span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random()*lyrics.length)
</code></pre><pre><code>  <span class="hljs-keyword">return</span> lyrics.substring(random, random + order)
</code></pre><pre><code>}
</code></pre><p>This selects the starting point for where we grab our first n-gram.</p>
<h4 id="heading-makeengrammodel">makeEngramModel()</h4>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">makeEngramModel</span>(<span class="hljs-params">lyrics</span>) </span>{
</code></pre><pre><code>  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i &lt; lyrics.length - order; i++) {
</code></pre><pre><code>    <span class="hljs-keyword">const</span> gram = lyrics.substring(i, i + order);
</code></pre><pre><code>    <span class="hljs-keyword">if</span> (!nGrams[gram]) {
</code></pre><pre><code>    nGrams[gram] = [];
</code></pre><pre><code>    }
</code></pre><pre><code>    nGrams[gram].push(lyrics.charAt(i + order));
</code></pre><pre><code>  }
</code></pre><pre><code>}
</code></pre><p>This creates the model that tracks the order and occurrences of all the n-grams parsed from the lyrics. By using the number of occurrences as the n-gram’s probability, it allows us to generate new lyrics in a random order.</p>
<h4 id="heading-tweet">tweet()</h4>
<pre><code><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">tweet</span>(<span class="hljs-params"></span>) </span>{
</code></pre><pre><code>  fs.readFile(<span class="hljs-string">'lyrics.txt'</span>, <span class="hljs-string">'utf8'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error, lyrics</span>) </span>{
</code></pre><pre><code>  <span class="hljs-keyword">if</span> (error) {
</code></pre><pre><code>  <span class="hljs-built_in">console</span>.log(error.message);
</code></pre><pre><code>  } <span class="hljs-keyword">else</span> {
</code></pre><pre><code>    makeEngramModel(lyrics);
</code></pre><pre><code>    <span class="hljs-keyword">let</span> currentGram = pickRandomStart(lyrics);
</code></pre><pre><code>      <span class="hljs-comment">// checks to see if the start of the tweet doesn't start</span>
</code></pre><pre><code>      <span class="hljs-comment">// with punctuation or special characters and ends with a space</span>
</code></pre><pre><code>      <span class="hljs-keyword">while</span> (!currentGram.match(<span class="hljs-regexp">/^[0-9a-zA-Z]+$/</span>)) {
</code></pre><pre><code>        currentGram = pickRandomStart(lyrics);
</code></pre><pre><code>      }
</code></pre><pre><code>      <span class="hljs-keyword">let</span> tweet = currentGram;
</code></pre><pre><code>      <span class="hljs-comment">// runs until char limit is reached while finishing the last word it was on</span>
</code></pre><pre><code>      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> j = <span class="hljs-number">0</span>; (j &lt; <span class="hljs-number">150</span>) || (tweet.charAt(j).match(<span class="hljs-regexp">/^[0-9a-zA-Z]+$/</span>)); j++) {
</code></pre><pre><code>        <span class="hljs-keyword">const</span> possibilities = nGrams[currentGram];
</code></pre><pre><code>        <span class="hljs-keyword">const</span> next = possibilities[<span class="hljs-built_in">Math</span>.floor(<span class="hljs-built_in">Math</span>.random()*possibilities.length)];
</code></pre><pre><code>        tweet += next;
</code></pre><pre><code>        <span class="hljs-keyword">const</span> len = tweet.length;
</code></pre><pre><code>        currentGram = tweet.substring(len-order, len);
</code></pre><pre><code>      }
</code></pre><pre><code>      <span class="hljs-built_in">console</span>.log(tweet)
</code></pre><pre><code>      Bot.post(<span class="hljs-string">'statuses/update'</span>, {<span class="hljs-attr">status</span>: tweet}, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">error, tweet, response</span>) </span>{
</code></pre><pre><code>        <span class="hljs-keyword">if</span> (error) {
</code></pre><pre><code>          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Error making post. "</span>, error.message);
</code></pre><pre><code>        };
</code></pre><pre><code>      });
</code></pre><pre><code>    }
</code></pre><pre><code>  });
</code></pre><pre><code>}
</code></pre><p>Last but not least, this is the part that interacts with the bot. It starts by reading in the lyrics using the fs module and then it create the n-gram model using the lyrics variable. It selects a random starting point to use as the first n-gram which will be the start of the new lyrics. It performs a check to see whether the first n-gram contains only alphanumeric characters, because then it has a higher chance to start somewhat more sensical.</p>
<p>It then chains randomly selected n-grams that pair with the last n-gram added to the tweet variable. It does this for at least 150 characters and like before, performs a check to see if it will end on an alphanumeric n-gram. If it does not end on a alphanumeric n-gram, it then will keep chaining from the model until it does.</p>
<p>And finally the bot makes a post request with the lyrics as the tweet payload.</p>
<p>Now that we have a good idea how how our code works, run the following command:</p>
<pre><code>npm install
</code></pre><p>in the lyric-bot project folder.</p>
<p>This installs the necessary modules () // and explains what npm install does.</p>
<h3 id="heading-configuration">Configuration</h3>
<p>Now you’ll want to copy in a set of lyrics into the lyrics.txt file.</p>
<p>Then create a .env file. This will store your Twitter API tokens.</p>
<p>Inside the new file, paste the following:</p>
<pre><code>TWITTER_CONSUMER_KEY=
</code></pre><pre><code>TWITTER_CONSUMER_SECRET=
</code></pre><pre><code>TWITTER_ACCESS_TOKEN=
</code></pre><pre><code>TWITTER_ACCESS_TOKEN_SECRET=
</code></pre><p>and copy the respective tokens from your app’s developer console, once you get your developer application approved.</p>
<p>Time to test it!</p>
<p>Type</p>
<pre><code>node bot.js
</code></pre><p>And take a look at the console!</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/9n4D549XRgXATvMh5rHdoh6mz5KfSq4QSKS-" alt="Image" width="600" height="400" loading="lazy">
<em>Lyric sample created from Dailily by Movements</em></p>
<p>And check out the Twitter account too, of course:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/QlJ4o4NGqHCfCdLnporB-IcYc80I5zce9ZSN" alt="Image" width="600" height="400" loading="lazy">
<em>The tweet sent from the bot to the Twitter API</em></p>
<h3 id="heading-ta-da"><strong>TA DA!</strong></h3>
<p>You now have a working twitter bot that is able to post newly generated song lyrics! ?</p>
<p>Hold on though, we still have to automate it…</p>
<h3 id="heading-heroku-deployment">Heroku Deployment</h3>
<p>Go over to <a target="_blank" href="http://heroku.com">Heroku.com</a> and login. If you don’t have an account, you can create a free one! ?</p>
<p>Now, from the main dashboard, create a new app by clicking New-&gt;Create new app.</p>
<p>Enter an available name then click Create App.</p>
<p>Here is your app’s main control panel!</p>
<p>If you scroll down in the ‘Deploy’ tab, you will see instructions for ‘Deploy using Heroku Git’. Follow the steps here, and once you’ve successfully deployed your app continue on to the next step.</p>
<h3 id="heading-automation">Automation</h3>
<p>Go over to the ‘Overview’ tab and click ‘Configure Add-Ons’. Then, in the add-on search bar, type ‘ Heroku Scheduler’ and select it. A dialog will pop-up, hit ‘Provision’.</p>
<p>Once added, you can then click on the add-on and create these things called ‘jobs’. Jobs are essentially tasks that get executed by Heroku Scheduler.</p>
<p>You’ll want to click ‘Add new job’ and a configure window will appear. In the command option, type the following:</p>
<pre><code>node bot.js
</code></pre><p>and select the frequency you want to run the command. Once done click save.</p>
<p>And with that you have successfully deployed a Twitter lyric bot! ?</p>
<h3 id="heading-acknowledgments">Acknowledgments</h3>
<p>Most of the code for creating the custom text is credited to Daniel Shiffman. You can find his material <a target="_blank" href="https://shiffman.net/a2z/markov/">here</a>!</p>
<p>The lyric data I used in this article was sampled from a song called Daylily by Movements.</p>
<p>Big thank you to <a target="_blank" href="https://twitter.com/morgansah">Morgan Allgrove-Hodges</a> for giving feedback and fixing my silly grammar mistakes! ?</p>
<p>Connect with me on <a target="_blank" href="https://www.linkedin.com/in/shawn-toubeau/">LinkedIn</a> or follow me on <a target="_blank" href="https://twitter.com/shawntoubeau">Twitter</a>! I love making new connections ?</p>
<p>~ Shawn Toubeau</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How I made a LinkedIn contact adding bot - and actually got a few interviews with it ]]>
                </title>
                <description>
                    <![CDATA[ By YK Sugi On LinkedIn, there’s a section that’s titled, “People you may know.” It’s under the My Network tab. This is the page that suggests people you might want to connect with. You can click these Connect buttons to send connection requests to t... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-i-made-a-linkedin-contact-adding-bot-and-actually-got-a-few-interviews-with-it-37a6f5f85d4d/</link>
                <guid isPermaLink="false">66c34df2465d1b2f886ba3f1</guid>
                
                    <category>
                        <![CDATA[ bots ]]>
                    </category>
                
                    <category>
                        <![CDATA[ career advice ]]>
                    </category>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tech  ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ freeCodeCamp ]]>
                </dc:creator>
                <pubDate>Thu, 26 Jul 2018 15:33:06 +0000</pubDate>
                <media:content url="https://cdn-media-1.freecodecamp.org/images/0*TAOB-11BmEzHS-1b" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>By YK Sugi</p>
<p>On LinkedIn, there’s a section that’s titled, “People you may know.” It’s under the <strong>My Network</strong> tab.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/0*TAOB-11BmEzHS-1b" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is the page that suggests people you might want to connect with.</p>
<p>You can click these <strong>Connect</strong> buttons to send connection requests to the people in this list.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*OlQUSAu7j9W1waNDevacKg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>A few years ago, I found this page, and I started randomly adding people there. I would just click on the connect button on every single person I found on this page.</p>
<p>I just figured it might be useful to have a lot of connections on LinkedIn to get the kinds of jobs I wanted to get, for example, software engineer internships.</p>
<p>But after a while, it became a little bit cumbersome to keep clicking on these connect buttons manually.</p>
<p>So, I decided to make a little bot to click these buttons for me.</p>
<p>This is an article about how I made this bot, what happened as a result, and what I learned from it.</p>
<h3 id="heading-how-i-made-the-bot">How I made the bot</h3>
<h4 id="heading-the-tools-i-used">The tools I used</h4>
<p>I made this simple bot to add random people on LinkedIn with <strong>JavaScript</strong> and <a target="_blank" href="https://addons.mozilla.org/en-US/firefox/addon/greasemonkey/"><strong>Greasemonkey</strong></a>.</p>
<p>Greasemonkey is a Firefox add-on that helps you manage custom JavaScript code.</p>
<p>With it, you can set things up so that a certain set of code runs automatically when you open a certain URL.</p>
<p>You can also store some data in Greasemonkey. I used this feature to keep track of the number of people I added with this bot. That way, I was able to keep track of this number consistently even when I closed the browser or refreshed the page.</p>
<h4 id="heading-the-code-i-used">The code I used</h4>
<p>Unfortunately, I did not keep the code I used to create my bot after I used it.</p>
<p>So, in this article, I’ll do my best to recreate it as closely as possible.</p>
<p>Initially, to create this bit of code, I used Google Chrome. Later, I switched to Firefox to use Greasemonkey, which I mentioned earlier. I chose to use Chrome initially just because I was more used to it.</p>
<p>Now, let’s together go through how I would recreate this code today. In this article, just to keep it simple, I’m only going to show you the core functionality of this bot - adding people. So, I’m going to skip the part about using Greasemonkey to store data persistently here.</p>
<p>Please let me know in the comments if you’d like me to cover that part in a separate article.</p>
<h4 id="heading-step-0-javascript-basics">Step 0: JavaScript basics</h4>
<p>In case you’re not too familiar with JavaScript, let’s quickly go over some JavaScript basics here.</p>
<p>We’re going to use Google Chrome here, but you can use any browser you’d like to use.</p>
<p>First, open any website, let’s say, Google.com.</p>
<p>Then, you’ll need to open the browser’s JavaScript console there.</p>
<p>On Google Chrome, you can do it in a few different ways.</p>
<p>The way I usually do it is the following:</p>
<ul>
<li>Right click anywhere on the page.</li>
<li>Then, click <strong>Inspect</strong> out of the menu that’s popped up.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*RnSqub0Ljw3asxj5hi-UBg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>When you click it, a window like the following should show up.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EdFB6fMXV9FLv9x-TQZFRw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<ul>
<li>Then, click the <strong>Console</strong> tab there to show the JavaScript console.</li>
<li>Once you click the <strong>Console</strong> tab, you should see the JavaScript console.</li>
</ul>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*ExR9I9a7O6yfk4-5JB3Dxg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is where you can type in any JavaScript code to test it. You can use the code you enter to interact with the page that’s open in your browser.</p>
<p>For example, try typing in the following code in the console and press Enter.</p>
<pre><code>selected = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'body'</span>);
</code></pre><p>This selects the <strong>body</strong> tag in the page that’s open on the browser. Then, it assigns it to a new variable called <strong>selected</strong>.</p>
<p>In Chrome and Firefox, there is a shorthand for:</p>
<pre><code>selected = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'body'</span>);
</code></pre><p>Instead, you can just write:</p>
<pre><code>selected = $(<span class="hljs-string">'body'</span>);
</code></pre><p><a target="_blank" href="https://stackoverflow.com/questions/22244823/what-is-the-dollar-sign-in-javascript-if-not-jquery">This code is equivalent to the one above.</a></p>
<p>I’m going to use this shorthand notation with the dollar sign throughout this article to keep our code short and simple.</p>
<p>Also, don’t worry about it if you don’t know the basics of HTML and JavaScript yet. I’ll try my best to write this article so it’s easy to understand even for beginners.</p>
<p>If you’re not interested in the code I’m going to show you, you can also just skip to the sections about what happened and what I learned from this experience at the end.</p>
<p>Now, let’s walk through our bot’s code, step by step.</p>
<h4 id="heading-step-1-find-the-target-element">Step 1: Find the target element</h4>
<p>First, you’ll need to write the bit of code that finds the buttons that you want to click.</p>
<p>First, log in to LinkedIn. Then, go to the My Network tab. It’s currently at <a target="_blank" href="https://www.linkedin.com/mynetwork/">https://www.linkedin.com/mynetwork/</a> (July, 2018).</p>
<p>You should be able to find the <strong>People you may know</strong> section there.</p>
<p>Then, on Chrome, right click on the “connect” button on one of the recommended people there. Then, click <strong>Inspect</strong>.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*QNH8JeKcFlNNcSRDlJ7k3g.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Once you do so, the element that you just clicked on will be highlighted in the developer window.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*1s3975-NyQ4UEE4_RjGmvg.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>This is the HTML code that’s highlighted in blue here:</p>
<pre><code>&lt;span aria-hidden=”<span class="hljs-literal">true</span>”&gt;Connect&lt;/span&gt;
</code></pre><p>This is a <strong>span</strong> tab that shows the text: <strong>Connect</strong>. What we really want to click on is not this one, but its parent element, which is a button.</p>
<p>You can find it right above the span element that we selected.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*mFx3ou-b4BjE9iTQz00sCw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>Let’s now examine this button element:</p>
<pre><code>&lt;button data-control-name=”invite” <span class="hljs-class"><span class="hljs-keyword">class</span></span>=”button-secondary-small” data-ember-action=”” data-ember-action<span class="hljs-number">-1596</span>=”<span class="hljs-number">1596</span><span class="hljs-string">" data-is-animating-click=”true”&gt; &lt;span aria-hidden=”true”&gt;Connect&lt;/span&gt; &lt;span class=”visually-hidden”&gt; Invite Azul Pinochet Barros to connect &lt;/span&gt;&lt;/button&gt;</span>
</code></pre><p>There’s a bunch of stuff here, but here’s the important part:</p>
<pre><code>&lt;button data-control-name=”invite” ...&gt; <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">aria-hidden</span>=<span class="hljs-string">”true”</span>&gt;</span>Connect<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span> ...&lt;/button&gt;
</code></pre><p>Basically, this is a button element whose attribute, <strong>data-control-name</strong>, is “invite”.</p>
<p>In our script, all we need to do is select elements like this and click them.</p>
<p>You can select these elements with this piece of code:</p>
<pre><code>selected = $(“button[data-control-name=invite]”);
</code></pre><p>This reads as, select all the button elements whose data-control-name is “invite”.</p>
<blockquote>
<p><em>NOTE: It looks like LinkedIn’s website uses jQuery. So, the notation above is actually a jQuery selector, <a target="_blank" href="https://stackoverflow.com/questions/22244823/what-is-the-dollar-sign-in-javascript-if-not-jquery">not a helper function defined by Chrome</a>. Confusingly, their behaviours are slightly different ?</em></p>
</blockquote>
<p>Anyway, once you run this code in your Chrome console, you should be able to see that the correct elements have been selected.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*gkmjHPk7wCgX5BsoXIWb9w.png" alt="Image" width="600" height="400" loading="lazy">
<em>This is how you can make sure that the correct elements have been selected.</em></p>
<p>Now, with this piece of code - <code>selected = $("button[data-control-name=invite]");</code> - your browser finds multiple button elements and puts them in an array. To pick the first one, you can just select the first element in this array like so:</p>
<pre><code>toClick = $(<span class="hljs-string">"button[data-control-name=invite]"</span>)[<span class="hljs-number">0</span>];
</code></pre><p>Then, you can click it with this:</p>
<pre><code>toClick.click();
</code></pre><p>If it goes through, you should see a confirmation window popping up.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*mGV1IV56IA17NFDx6JfAEA.png" alt="Image" width="600" height="400" loading="lazy">
<em>A confirmation window that shows up when you click one of the <strong>connect</strong> buttons</em></p>
<h4 id="heading-step-2-loop-through-multiple-target-elements">Step 2: Loop through multiple target elements</h4>
<p>Now, the next step is to loop through multiple target elements to click so we can add multiple people.</p>
<p>After some experimentation, I realized that there’s a simpler way to select multiple buttons and loop through them than the one I showed earlier.</p>
<p>Here’s how I would do it.</p>
<p>First, use Inspect Element to analyze the structure of this page a bit more. Then, you should be able to see that the <strong>people you may know</strong> is just an unordered list.</p>
<p>You should be able to find code that looks like this:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*EgTc1y_-qQXvC_UcJw2UGQ.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The parent element is a <code>ul</code> (unordered list) element. Its children are <code>li</code> (list item) elements.</p>
<p>Each <code>li</code> element represents each of the <strong>people you may know</strong> cards you see on the screen.</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*XnIgeADO5OUKj3YFlQ1ePw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<p>By selecting these <code>li</code> elements instead of selecting the buttons directly, it actually becomes easier to go through multiple people.</p>
<p>You can select this <code>ul</code> element, the parent of the <code>li</code> elements, like this:</p>
<pre><code>ul = $(<span class="hljs-string">'ul.mn-pymk-list__cards'</span>)[<span class="hljs-number">0</span>];
</code></pre><p>This says, select the <code>ul</code> element with the class <code>ul.mn-pymk-list__cards</code>. We need to add <code>[0]</code> at the end because the raw result is an array containing a single element.</p>
<p>Then, you can select the first <code>li</code> element (the first person’s card) under the <code>ul</code> element like this:</p>
<pre><code>firstLi = ul.querySelector(<span class="hljs-string">'li'</span>);
</code></pre><p>We don’t need to add <code>[0]</code> at the end of this statement because the querySelector() function only returns one element.</p>
<p>Then, out of <code>firstLi</code>, you can select the button that we need to click like this:</p>
<pre><code>buttonToClick = firstLi.querySelector(<span class="hljs-string">"button[data-control-name=invite]"</span>);
</code></pre><p>After clicking this button with <code>buttonToClick.click()</code>, we should remove this <code>li</code> element so we can go to the next <code>li</code> element (the next person’s card). We can do that with this:</p>
<pre><code>ul.removeChild(firstLi);
</code></pre><p>Putting them all together, and putting everything in a while loop, you’ll get something like this:</p>
<pre><code>ul = $(<span class="hljs-string">'ul.mn-pymk-list__cards'</span>)[<span class="hljs-number">0</span>];firstLi = ul.querySelector(<span class="hljs-string">'li'</span>);<span class="hljs-keyword">while</span>(firstLi){ <span class="hljs-comment">// do this while firstLi still exists.  buttonToClick = firstLi.querySelector("button[data-control-name=invite]");  ul.removeChild(firstLi);  firstLi = ul.querySelector('li');}</span>
</code></pre><p>This code should work, but it has several issues.</p>
<ol>
<li>We add people <em>really</em> fast with this, so it’s going to be hard to know what’s going on when you run this code.</li>
<li>We are not keeping track of how many people we’ve added.</li>
<li>We are assuming that <code>buttonToClick</code> is always the correct button to click. Sometimes this button has the text “Invite” instead of “Connect”. We don’t want to click on too many of those “Invite” buttons.</li>
</ol>
<h4 id="heading-step-3-refine-our-code">Step 3: Refine our code</h4>
<p>I’ve fixed all of the issues I mentioned above and put together a relatively simple piece of code below.</p>
<p>It’s also <a target="_blank" href="https://gist.github.com/ykdojo/aea4cf27fec4bbb5a175e11bae39cb2d">here</a> on Gist. Perhaps it’s easier to read there.</p>
<pre><code><span class="hljs-comment">// this function allows us to stop our code for |ms| milliseconds.function sleep(ms) {  return new Promise(resolve =&gt; setTimeout(resolve, ms));}</span>
</code></pre><pre><code><span class="hljs-comment">// I've put our main code into this function.async function addPeople() {  ul = $('ul.mn-pymk-list__cards')[0];  firstLi = ul.querySelector('li');  count = 0; // this is the count of how many people you've added  while(firstLi &amp;&amp; count &lt; 100){ // stop after adding 100 people    buttonToClick = firstLi.querySelector("button[data-control-name=invite]");    // make sure that this button contains the text "Connect"    if (buttonToClick.innerText.includes("Connect")){      buttonToClick.click();      count += 1;      console.log("I have added " + count + " people so far.");    }    ul.removeChild(firstLi);    await sleep(1000); // stop this function for 1 second here.    firstLi = ul.querySelector('li');  }}</span>
</code></pre><pre><code>addPeople();
</code></pre><p>If you examine this code carefully, you should be able to notice the couple of changes I’ve made:</p>
<ol>
<li>I’ve put our code into an <em>async</em> function called addPeople(). In this function, every time we add someone, we pause for 1 second with the sleep() function. More about this pattern <a target="_blank" href="https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep">here</a>.</li>
<li>I added a <code>count</code> variable to keep track of how many people we’ve added.</li>
<li>I added this if statement: <code>if (buttonToClick.innerText.includes("Connect"){...}</code>. This way, we can make sure that the button we’re clicking contains the word “Connect” inside it.</li>
</ol>
<p>With these changes, when I run this code, it looks like this:</p>
<h4 id="heading-step-4-make-further-improvements">Step 4: Make further improvements!</h4>
<p>On top of what I showed above, I had a few more functionalities when I actually used my bot to add a bunch of people on LinkedIn.</p>
<p>First of all, I used Greasemonkey, which I mentioned earlier, to keep track of the total number of people I’ve added.</p>
<p>Also, to avoid being detected as a bot by LinkedIn, I added a few things:</p>
<ol>
<li>I randomized the order in which I added people.</li>
<li>I randomized the amount of time I waited every time I added a new person.</li>
</ol>
<p>I’ll leave all of these as exercise problems for you to solve in case you’re interested in solving them ?</p>
<h3 id="heading-what-happened">What happened</h3>
<p>With my script, I ended up adding 2000+ connections. Then, if I remember correctly, about 400 of them added me back.</p>
<p>As a result, I went from about 300 connections to 700+ connections within a week or so!</p>
<p>Then, after a while, I got banned by LinkedIn from adding any more people. I didn’t know that I could get banned! I was scared for a bit, but the ban lifted after 2 months or so.</p>
<p>More importantly, I was able to land a few interviews from those 400+ new connections. One of the interviews was with this company called Palantir.</p>
<p>Here’s a screenshot of the message I received from them:</p>
<p><img src="https://cdn-media-1.freecodecamp.org/images/1*u7fIRKAxBAk8rJTFeqyrXw.png" alt="Image" width="600" height="400" loading="lazy"></p>
<h3 id="heading-what-i-learned-from-this-experience">What I learned from this experience</h3>
<p>I thought what I was doing was pretty silly at the time, but I ended up learning a lot from this experience.</p>
<h4 id="heading-takeaway-1">Takeaway #1</h4>
<p>First of all, through this experience, I realized that LinkedIn actually works for getting jobs. I was able to get a few job interviews with my bot, after all.</p>
<p>Then, after a while, I also realized that adding thousands of random people was not the most efficient way to use LinkedIn. With that kind of approach, you end up adding a lot of people you don’t need to add.</p>
<p>So, after that experience, I changed my approach to a more focused one.</p>
<p>With my new approach, I would only add recruiters of the companies I wanted to work at. Then, I would only send messages to the people who added me back.</p>
<p>It turned out to be a much more focused, effective strategy to use LinkedIn. With this new strategy, I was able to get a few more job interviews with multiple tech companies, including Yelp and Xamarin. This time, I didn’t have to add thousands of new connections to achieve this result ?</p>
<p>NOTE: I talk more about this strategy in <a target="_blank" href="https://medium.freecodecamp.org/here-are-4-best-ways-to-apply-for-software-engineer-jobs-and-exactly-how-to-use-them-a644a88b2241">this article</a>, just in case you’re curious about it.</p>
<h4 id="heading-takeaway-2">Takeaway #2</h4>
<p>Having fun is the best way to hone your programming skills!</p>
<p>Through this particular project, I was able to hone my JavaScript skills. What I learned included:</p>
<ul>
<li>How to set a timed interval between function executions</li>
<li>How to select certain HTML elements with JavaScript</li>
<li>How to store data locally with Greasemonkey</li>
</ul>
<p>I learned these things through this project, and it didn’t feel like studying at all because it was so much fun.</p>
<h4 id="heading-takeaway-3">Takeaway #3</h4>
<p>From this experience, I’ve learned that it sometimes pays to do something weird. So, don’t be afraid of being a little bit mischievous and adventurous if you have any inclination to do so.</p>
<p>Even after this little experiment, I continued to do weird things for fun.</p>
<p>For example, when I was interning at Microsoft, I ran a little experiment where I “stole” a bunch of employee passwords. I did that by sending out a phishing email. It was supposed to be a huge give-away raffle with prizes like Xbox and Surface laptops. It was my hackathon project there.</p>
<p>I also started a <a target="_blank" href="https://www.youtube.com/csdojo">programming-education YouTube channel</a>, and eventually decided to <a target="_blank" href="https://medium.freecodecamp.org/why-i-left-my-100-000-job-at-google-60b5cf4ebefe">work on it full-time and quit my full-time software engineer job</a>.</p>
<p>Perhaps all of these things seemed a little bit weird to other people. But every time I went through each of these experiences, I learned something new, and I had tons of fun along the way. I would say the last one even made my career.</p>
<p>So again, don’t be afraid of trying something strange just for fun! You might learn something valuable along the way.</p>
<h4 id="heading-okay-thats-it-for-this-article">Okay, that’s it for this article.</h4>
<p>This was supposed to be sort of a fun article, but I usually write about more serious stuff.</p>
<p>For example, I have articles about <a target="_blank" href="https://medium.freecodecamp.org/heres-the-resume-i-used-to-get-a-job-at-google-as-a-software-engineer-26516526f29a">writing your software engineer resume</a>, <a target="_blank" href="https://medium.freecodecamp.org/here-are-4-best-ways-to-apply-for-software-engineer-jobs-and-exactly-how-to-use-them-a644a88b2241">the best ways to apply for software engineer jobs</a>, and <a target="_blank" href="https://medium.freecodecamp.org/how-to-get-a-software-engineer-job-at-google-and-other-top-tech-companies-efa235a33a6d">how to get a job at a top tech company</a>.</p>
<p>Feel free to check them out. They are all here on Medium.</p>
<p>Also, as always, if you have any questions about this or anything else, please feel free to let me know in a comment below or on <a target="_blank" href="https://www.instagram.com/ykdojo/">Instagram</a> or <a target="_blank" href="https://twitter.com/ykdojo">Twitter</a> (@ykdojo on both).</p>
<p>Thank you for reading this article!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
