<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Development on 0x2142 | Networking Nonsense</title>
    <link>https://0x2142.com/tags/development/</link>
    <description>Recent content in Development on 0x2142 | Networking Nonsense</description>
    <image>
      <title>0x2142 | Networking Nonsense</title>
      <url>https://0x2142.com/logo.jpg</url>
      <link>https://0x2142.com/logo.jpg</link>
    </image>
    <generator>Hugo -- 0.143.1</generator>
    <language>en-us</language>
    <lastBuildDate>Wed, 19 Oct 2022 14:38:00 +0000</lastBuildDate>
    <atom:link href="https://0x2142.com/tags/development/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>[How To] Building a Simple Discord Bot using DiscordGo</title>
      <link>https://0x2142.com/how-to-discordgo-bot/</link>
      <pubDate>Wed, 19 Oct 2022 14:38:00 +0000</pubDate>
      <guid>https://0x2142.com/how-to-discordgo-bot/</guid>
      <description>A short tutorial for building a Discord chatbot with Golang.</description>
      <content:encoded><![CDATA[<div style="position: relative; padding-bottom: 56.25%; height: 0; overflow: hidden;">
      <iframe allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen="allowfullscreen" loading="eager" referrerpolicy="strict-origin-when-cross-origin" src="https://www.youtube.com/embed/G7A3nnMvfCk?autoplay=0&amp;controls=1&amp;end=0&amp;loop=0&amp;mute=0&amp;start=0" style="position: absolute; top: 0; left: 0; width: 100%; height: 100%; border:0;" title="YouTube video"></iframe>
    </div>

<p>Earlier this year I started trying out Golang as a new language to learn. Most of my prior experience is with Python, with a handful of other languages sprinkled in here &amp; there.</p>
<p>My experience with Go so far has been good &amp; I&rsquo;ve been having fun learning something new. It&rsquo;s been easy enough to pick up &amp; there have been quite a handful of things that I really appreciate about Go vs Python.</p>
<p>One of the best ways to learn something new is to find a good project to work on. Since I&rsquo;ve done quite a bit historically with Python &amp; building Webex chatbots - I figured one good way to learn Go would be trying to do something similar.</p>
<p>So in this blog post, I&rsquo;ll walk through how to build a simple Discord bot using a Golang module called <a href="https://github.com/bwmarrin/discordgo">DiscordGo</a>. We&rsquo;ll use the same project idea as my previous <a href="/how-to-building-a-basic-webex-chatbot/">Webex bot</a>, where the bot will leverage the OpenWeather APIs to retrieve weather information.</p>
<p>Since I&rsquo;ve been learning Go recently, I&rsquo;ll do my best to try to keep this simple - so that hopefully you can follow along if you&rsquo;re also new to Go 😊.</p>
<blockquote>
<p>Code repo for the examples below can be found <a href="https://github.com/0x2142/example-scripts/tree/master/simple-discord-chatbot">here</a></p></blockquote>
<hr>
<h1 id="setting-up-the-project">Setting up the Project</h1>
<p>So first we&rsquo;ll run through some quick setup. If you&rsquo;re familiar enough with Go already, you can likely skip this step &amp; just set up your project in the way you prefer.</p>
<p>I&rsquo;ve created a new project folder called <code>discord-weather-bot</code>. So first we&rsquo;ll initialize our Go module using the command <code>go mod init discord-weather-bot</code>.</p>
<p>Then, I&rsquo;ll create a <code>main.go</code> in this folder, as well as a sub-folder named <code>bot</code> which will contain a <code>bot.go</code> file.</p>
<p>Once we&rsquo;re done, our initial project layout should look like this:</p>
<p><img alt="project-layout" loading="lazy" src="/content/images/2022/08/project-layout.png#center"></p>
<p>We&rsquo;ll add on to this later, but this will work to start.</p>
<p>Next, we&rsquo;ll need to install the DiscordGo module, which we can do with one simple command: <code>go get github.com/bwmarrin/discordgo</code></p>
<h2 id="generating-api-keys">Generating API Keys</h2>
<p>Next we&rsquo;ll need to get our API keys for OpenWeather &amp; Discord.</p>
<p>OpenWeather is going to be the easier of the two - simply sign up for a free account <a href="https://openweathermap.org/price">here</a>. Then jump over to your account page &amp; create a new API key <a href="https://home.openweathermap.org/api_keys">here</a>. Free accounts are limited to only using their current weather API, which will be more than enough for our example.</p>
<p>Next, we&rsquo;ll look at the Discord side of things - which will be a few more hoops to jump though.</p>
<p>First we&rsquo;ll need to head over to the <a href="https://discord.com/developers/applications">Discord Applications</a> page &amp; create a new application/integration.</p>
<p><img alt="new-app" loading="lazy" src="/content/images/2022/08/new-app.png#center"></p>
<p>We&rsquo;ll be prompted to provide a name for our app (Note, this isn&rsquo;t the name of our bot - we&rsquo;ll get to that shortly):</p>
<p><img alt="new-app-name" loading="lazy" src="/content/images/2022/08/new-app-name.png#center"></p>
<p>Once we finish that, we&rsquo;ll be taken to a page to add additional information about our app - like tags, a description, an icon, or links to app resources. If you plan to publish your app publicly, you&rsquo;ll want to invest some time here. However, since we&rsquo;ll just be building the bot as a personal project, we don&rsquo;t need to do anything here.</p>
<p>Next, we&rsquo;ll want to jump down to the <strong>Bot</strong> section under <strong>Settings</strong>. This is where we can set up some basics for our bot &amp; get our bot API token.</p>
<p><img alt="side-menu" loading="lazy" src="/content/images/2022/08/side-menu.png#center"></p>
<p>We&rsquo;ll get a quick confirmation prompt, where we&rsquo;ll click on <strong>Yes, do it!</strong></p>
<p><img alt="add-bot-confirm" loading="lazy" src="/content/images/2022/08/add-bot-confirm.png#center"></p>
<p>Now we have our bot created! We can change the name of our bot here, to be separate from our app name if we want.</p>
<p><img alt="new-bot" loading="lazy" src="/content/images/2022/08/new-bot.png#center"></p>
<p>Scrolling down the page a bit - by default our bot is public, which means anyone could add it to their server. I&rsquo;ll switch this off for now, since this is intended to be a private example bot.</p>
<p><img alt="public-bot" loading="lazy" src="/content/images/2022/08/public-bot.png#center"></p>
<p>In order for our bot to receive &amp; process message content, we&rsquo;ll also need to enable the toggle for <strong>message content</strong>. Note that Discord says this is only allowed for servers under a certain size, after which they would need to be verified in order to use this intent. Since this bot will only be used as an example, we don&rsquo;t need to worry about that yet - but we will still need to enable the intent:</p>
<p><img alt="bot-intents" loading="lazy" src="/content/images/2022/09/bot-intents.png#center"></p>
<p>For now, this is all we need to set up on this page. Before we leave, we&rsquo;ll want to reset our bot access token - and save the token for later:</p>
<p><img alt="bot-token" loading="lazy" src="/content/images/2022/08/bot-token.png#center"></p>
<p>Last but not least, we&rsquo;ll need to add our Discord bot to a server. I have a server in Discord that I use for testing, and I&rsquo;ve created a private channel to experiment with this bot.</p>
<p>In order to do this, we&rsquo;ll need to create an OAuth authorization link that can be used to give the bot access.</p>
<p>Under our <strong>Settings</strong>, we&rsquo;ll click on <strong>OAuth2</strong> &gt; <strong>URL Generator</strong>.</p>
<p><img alt="oauth-url-gen-1" loading="lazy" src="/content/images/2022/08/oauth-url-gen-1.png#center"></p>
<p>In the screenshot above, I&rsquo;ve selected the <strong>Scope</strong> as <strong>bot</strong>. And checked off permissions for <strong>Read Messages</strong>, <strong>Send Messages</strong>, and <strong>Send Messages in Threads</strong>. For what we&rsquo;ll be doing in this example, that should be more than enough.</p>
<p>At the bottom of the screenshot, you can see that Discord will auto-generate a URL which we can now use to add the bot to our Discord Server.</p>
<p>If we visit that link while signed into Discord, we&rsquo;ll be prompted to add it to our server:</p>
<p><img alt="add-bot-to-server" loading="lazy" src="/content/images/2022/08/add-bot-to-server.png#center"></p>
<p>We&rsquo;ll also be prompted to confirm the permissions the bot will have:</p>
<p><img alt="confirm-bot-perms" loading="lazy" src="/content/images/2022/08/confirm-bot-perms.png#center"></p>
<p>Now our bot has been successfully added to our Discord server, but will show as offline since we haven&rsquo;t built it yet! Let&rsquo;s get started with that.</p>
<h1 id="building-the-bot---basic-interaction">Building the Bot - Basic Interaction</h1>
<p>So we&rsquo;ll start off by building out just enough of our bot code for it to send messages to our server.</p>
<h2 id="collecting-api-keys-via-environment-variables">Collecting API Keys via Environment Variables</h2>
<p>For the purpose of this project, I&rsquo;ll be storing both the Discord &amp; OpenWeather API keys as environment variables. So well keep our <code>main.go</code> simple &amp; just handle importing our API keys and kicking off the bot:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: main.go</span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;discord-weather-bot/bot&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;log&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Load environment variables</span>
</span></span><span class="line"><span class="cl"> <span class="nx">botToken</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">LookupEnv</span><span class="p">(</span><span class="s">&#34;BOT_TOKEN&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="s">&#34;Must set Discord token as env variable: BOT_TOKEN&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">openWeatherToken</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">LookupEnv</span><span class="p">(</span><span class="s">&#34;OPENWEATHER_TOKEN&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="s">&#34;Must set Open Weather token as env variable: OPENWEATHER_TOKEN&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> 
</span></span><span class="line"><span class="cl">    <span class="c1">// Start the bot</span>
</span></span><span class="line"><span class="cl"> <span class="nx">bot</span><span class="p">.</span><span class="nx">BotToken</span> <span class="p">=</span> <span class="nx">botToken</span>
</span></span><span class="line"><span class="cl"> <span class="nx">bot</span><span class="p">.</span><span class="nx">OpenWeatherToken</span> <span class="p">=</span> <span class="nx">openWeatherToken</span>
</span></span><span class="line"><span class="cl"> <span class="nx">bot</span><span class="p">.</span><span class="nf">Run</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In the code above, we&rsquo;ll import <code>os</code> and <code>log</code> which we&rsquo;ll use to handle importing our environment variables &amp; generating errors if this fails. We&rsquo;ll also import our <code>bot</code> folder with <code>discord-weather-bot/bot</code>.</p>
<p>For our <code>main</code> function, we&rsquo;ll start by trying to find our environment variables. For each of the variables, we&rsquo;ll use a format of <code>&lt;varname&gt;, ok := os.LookupEnv(&quot;&lt;env name&gt;&quot;)</code>.</p>
<p>Using <code>os.LookupEnv</code>, we&rsquo;ll try and search for the environment variable by name - so in the first example looking for <code>BOT_TOKEN</code>. If the environment variable exists, it will be assigned to <code>botToken</code> - and <code>ok</code> will be <code>True</code>. Otherwise, we&rsquo;ll get no value for <code>botToken</code>, and <code>ok</code> will be <code>False</code>.</p>
<p>If either environment variable doesn&rsquo;t exist, we&rsquo;ll just print a log message reminding the user to set the variable. <code>log.Fatal</code> will also quit the program after displaying the log message.</p>
<p>Finally, assuming we have both of those API keys - we&rsquo;ll kick those keys over to the bot module &amp; start the bot with <code>bot.Run()</code>. Let&rsquo;s hop over &amp; build that piece now.</p>
<h2 id="connecting-to-discord">Connecting to Discord</h2>
<p>Next, we&rsquo;ll start out bot code with enough to receive the variables being passed by <code>main.go</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/bot.go</span>
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">bot</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;log&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;os&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;os/signal&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;strings&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/bwmarrin/discordgo&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">OpenWeatherToken</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">BotToken</span>         <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Not implemented yet</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>In the snippet above, we&rsquo;ve defined two variables - <code>OpenWeatherToken</code> and <code>BotToken</code>. Note that both start with a capital letter, meaning that they have been <a href="https://go.dev/tour/basics/3">exported</a> &amp; available to code outside this module - which is how we can update these values via <code>main.go</code> by setting <code>bot.BotToken = botToken</code>.</p>
<p>Okay - Let&rsquo;s get started with connecting our bot to Discord. We&rsquo;ll update our <code>Run</code> function to the following:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/bot.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Run</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Create new Discord Session</span>
</span></span><span class="line"><span class="cl"> <span class="nx">discord</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">discordgo</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">&#34;Bot &#34;</span> <span class="o">+</span> <span class="nx">BotToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">log</span><span class="p">.</span><span class="nf">Fatal</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Add event handler</span>
</span></span><span class="line"><span class="cl"> <span class="nx">discord</span><span class="p">.</span><span class="nf">AddHandler</span><span class="p">(</span><span class="nx">newMessage</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Open session</span>
</span></span><span class="line"><span class="cl"> <span class="nx">discord</span><span class="p">.</span><span class="nf">Open</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">discord</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Run until code is terminated</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">&#34;Bot running...&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nb">make</span><span class="p">(</span><span class="kd">chan</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Signal</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">signal</span><span class="p">.</span><span class="nf">Notify</span><span class="p">(</span><span class="nx">c</span><span class="p">,</span> <span class="nx">os</span><span class="p">.</span><span class="nx">Interrupt</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="o">&lt;-</span><span class="nx">c</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>To begin with, we create a new discord session with <code>discordgo.New()</code> &amp; passing our <code>BotToken</code>. Discord requires an HTTP <code>Authorization</code> header that contains <code>Bot &lt;token&gt;</code> or <code>Bearer &lt;token&gt;</code> for OAuth. The <code>discordgo.New()</code> function is just setting this header for us.</p>
<p>Next we&rsquo;ll register an event handler to our Discord client. In a moment, we&rsquo;ll create a new function called <code>newMessage</code> that will receive &amp; process any new messages that are created in our channel. More to come in just a moment!</p>
<p>Now we can open our websocket tunnel to Discord via <code>discord.Open()</code>. If you&rsquo;re not familiar with websockets, I wrote a little about them when I built a Webex bot in <a href="/how-to-building-a-basic-webex-chatbot/#webhook-vs-websocket-which-to-use">this post</a>. We&rsquo;ll also include a <code>defer discord.Close()</code>, which will ensure that we gracefully close the Discord session whenever the <code>Run()</code> function exits.</p>
<p>Lastly, we&rsquo;ll use Go&rsquo;s <code>os/signal</code> package to listen for any interrupt/process kill signals. This will allow our Discord bot to run in the background until we stop it. We do this by creating a new channel of type <code>os.Signal</code> where we will listen for termination signals. Then using <code>signal.Notify</code>, we tell the signal package where to send termination signals (our <code>c</code> channel) - and what signals we want to listen for (<code>os.Interrupt</code>). Then, using <code>&lt;-c</code>, our program will hold until something is received on the channel.</p>
<p>An operator like <code>&lt;-</code> is typically used for concurrency, where we have one function passing data to another via a channel. The <code>&lt;-</code> or <code>-&gt;</code> operator indicates which direction that data is being sent. So in this example, we&rsquo;re receiving data from channel <code>c</code> using the statement <code>&lt;-c</code>. Now the tricky thing here, is that we&rsquo;re not assigning that input to another variable (for example, <code>incomingSignal &lt;- c</code>), since we don&rsquo;t care what signal was sent - just that there was a signal received.</p>
<p>After we receive any termination signal, the code will continue to the end of the <code>Run()</code> function. This will then automatically execute our <code>defer discord.Close()</code> from earlier, which will tear down our Discord session.</p>
<h2 id="processing-incoming-messages">Processing Incoming Messages</h2>
<p>Okay, now that we have our Discord session establishment handled, let&rsquo;s build out our <code>newMessage()</code> function to handle receiving messages.</p>
<p>So for now, our code for this function will look like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/bot.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">newMessage</span><span class="p">(</span><span class="nx">discord</span> <span class="o">*</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">Session</span><span class="p">,</span> <span class="nx">message</span> <span class="o">*</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageCreate</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Ignore bot messaage</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">message</span><span class="p">.</span><span class="nx">Author</span><span class="p">.</span><span class="nx">ID</span> <span class="o">==</span> <span class="nx">discord</span><span class="p">.</span><span class="nx">State</span><span class="p">.</span><span class="nx">User</span><span class="p">.</span><span class="nx">ID</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Respond to messages</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="s">&#34;weather&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="nx">discord</span><span class="p">.</span><span class="nf">ChannelMessageSend</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">ChannelID</span><span class="p">,</span> <span class="s">&#34;I can help with that!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="s">&#34;bot&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="nx">discord</span><span class="p">.</span><span class="nf">ChannelMessageSend</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">ChannelID</span><span class="p">,</span> <span class="s">&#34;Hi there!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Let&rsquo;s take a quick look at the code above.</p>
<p>The <code>newMessage()</code> function is our event handler that we are registering with our Discord client. It will need to receive two values - a <a href="https://go.dev/tour/moretypes/1">pointer</a> to our Discord session (<code>*discordgo.Session</code>), and the type of event we&rsquo;re listening for. In this case we&rsquo;re listening for new message creation events (<code>*discordgo.MessageCreate</code>). A full list of events that we could listen for are listed in the <a href="https://discord.com/developers/docs/topics/gateway#commands-and-events">Discord Gateway Documentation</a>.</p>
<p>First thing we&rsquo;ll do when processing the message is ensure that the bot ignores any message from itself. We wouldn&rsquo;t want our bot creating a loop by responding to it&rsquo;s own messages, which then generates a new message to respond to!</p>
<p>We accomplish this by checking the incoming message&rsquo;s Author ID (<code>message.Author.ID</code>) against our current Discord session&rsquo;s User ID (<code>discord.State.User.ID</code>). If these values match, then the incoming message was created by our bot code - so we&rsquo;ll just <code>return</code> and stop execution of this function.</p>
<p>If the incoming message is from another user, then we can go ahead and process it. There are a number of ways to do this depending on what you want to achieve. To keep things simple, I am using a quick <a href="https://go.dev/tour/flowcontrol/9">switch</a> statement to evaluate the message content.</p>
<p>Our bot then is listening for two keywords - &ldquo;weather&rdquo; and &ldquo;bot&rdquo;. If the incoming message content contains either of those two words, our bot will respond with a message by using <code>discord.ChannelMessageSend</code> - and passing the channel ID from the incoming message (<code>message.ChannelID</code>) and our message.</p>
<blockquote>
<p>A quick note: I used a switch/case here since it would be easier to add onto than a long list of if/else&hellip; but the example code here still isn&rsquo;t perfect. For instance, our second case is only looking for someone using the word <code>bot</code>. However this is a simple match, and would still catch on someone talking about a robot or a bottle - since both contain <code>bot</code> in them. If this was a production/public bot, we would want to clean that up a bit.</p></blockquote>
<h2 id="testing">Testing</h2>
<p>Okay, with that we can now give our bot a quick test.</p>
<p>Let&rsquo;s go ahead and start our bot with <code>go run main.go</code>. Don&rsquo;t forget to set your <code>BOT_TOKEN</code> &amp; <code>OPENWEATHER_TOKEN</code> environment variables first! (Or comment out the code for the <code>OPENWEATHER_TOKEN</code>, since we&rsquo;re not using it just yet)</p>
<p>Here&rsquo;s my test run:</p>
<p><img alt="basic-interaction" loading="lazy" src="/content/images/2022/08/basic-interaction.png#center"></p>
<p>As we can see, the bot responded just as we expected!</p>
<p>Another fun thing to point out, is that since we&rsquo;re using websockets for the connection (aka Discord Gateway) - our bot can also register presence events. So as long as our bot is connected, it will actually show as online:</p>
<p><img alt="bot-presence" loading="lazy" src="/content/images/2022/08/bot-presence.png#center"></p>
<h1 id="adding-basic-bot-commands">Adding Basic Bot Commands</h1>
<p>So far we&rsquo;ve gotten our bot connected to Discord, and responding to regular chat messages. Now we&rsquo;ll focus on actually adding functionality to our bot by leveraging the OpenWeather API.</p>
<p>For now, we&rsquo;ll implement this with very simple command matching. In a future blog post, I&rsquo;ll be showing how to accomplish this with Discord slash commands.</p>
<p>So first we&rsquo;ll modify our existing switch statement in the <code>newMessage()</code> function. We&rsquo;ll provide a little help text with our response to someone mentioning <code>weather</code> - and create a new command by catching messages with <code>!zip</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/bot.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Respond to messages</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="s">&#34;weather&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="nx">discord</span><span class="p">.</span><span class="nf">ChannelMessageSend</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">ChannelID</span><span class="p">,</span> <span class="s">&#34;I can help with that! Use &#39;!zip &lt;zip code&gt;&#39;&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="s">&#34;bot&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="nx">discord</span><span class="p">.</span><span class="nf">ChannelMessageSend</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">ChannelID</span><span class="p">,</span> <span class="s">&#34;Hi there!&#34;</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">strings</span><span class="p">.</span><span class="nf">Contains</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Content</span><span class="p">,</span> <span class="s">&#34;!zip&#34;</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">  <span class="nx">currentWeather</span> <span class="o">:=</span> <span class="nf">getCurrentWeather</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">Content</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="nx">discord</span><span class="p">.</span><span class="nf">ChannelMessageSendComplex</span><span class="p">(</span><span class="nx">message</span><span class="p">.</span><span class="nx">ChannelID</span><span class="p">,</span> <span class="nx">currentWeather</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></div><p>Under the <code>!zip</code> command, we&rsquo;ll have a function called <code>getCurrentWeather()</code> which will return a Discord message. However, in this case we&rsquo;ll change things up a little - and use an embedded message so we can include some formatting. This also means that we&rsquo;ll change our response function to <code>discord.ChannelMessageSendComplex()</code>.</p>
<p>Where <code>discord.ChannelMessageSend()</code> needs the channel ID &amp; a simple string message, <code>discord.ChannelMessageSendComplex()</code> will require data of the type <code>discordgo.MessageSend</code> instead of a string. In our <code>getCurrentWeather()</code> function, we&rsquo;ll see how to assemble our response using this structure.</p>
<h2 id="querying-weather">Querying Weather</h2>
<p>So to start with, we&rsquo;ll need to query the OpenWeather API &amp; retrieve the current weather information for a given US ZIP code.</p>
<p>In our project, I&rsquo;ve created a new Go file called <code>command-weather.go</code> in the same <code>bot</code> subfolder which already contains our <code>bot.go</code> file. This new file will also be part of <code>package bot</code>.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/command-weather.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">bot</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;encoding/json&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;fmt&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;io/ioutil&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;net/http&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;regexp&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;strconv&#34;</span>
</span></span><span class="line"><span class="cl"> <span class="s">&#34;time&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="s">&#34;github.com/bwmarrin/discordgo&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>I&rsquo;ve included the list of imports above, in case you&rsquo;re following along.</p>
<p>To start with, I&rsquo;ll define two items that we&rsquo;ll need for our `getCurentWeather() function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/command-weather.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">const</span> <span class="nx">URL</span> <span class="kt">string</span> <span class="p">=</span> <span class="s">&#34;https://api.openweathermap.org/data/2.5/weather?&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">WeatherData</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Weather</span> <span class="p">[]</span><span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Description</span> <span class="kt">string</span> <span class="s">`json:&#34;description&#34;`</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="s">`json:&#34;weather&#34;`</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Main</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Temp</span>     <span class="kt">float64</span> <span class="s">`json:&#34;temp&#34;`</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Humidity</span> <span class="kt">int</span>     <span class="s">`json:&#34;humidity&#34;`</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="s">`json:&#34;main&#34;`</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Wind</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Speed</span> <span class="kt">float64</span> <span class="s">`json:&#34;speed&#34;`</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="s">`json:&#34;wind&#34;`</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Name</span> <span class="kt">string</span> <span class="s">`json:&#34;name&#34;`</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The first item is a constant for the base URL for OpenWeather&rsquo;s API. This should never need to change, which is why we use a constant here.</p>
<p>In addition, I&rsquo;ve defined a new <a href="https://go.dev/tour/moretypes/2">struct</a> called <code>WeatherData</code> that will be used to unpack our JSON response from OpenWeather&rsquo;s API. They list a current sample response payload on their <a href="https://openweathermap.org/current#current_JSON">documentation</a> page, which we can use to build our data structure. Since we won&rsquo;t be using all of the data provided in the response, I also don&rsquo;t need to define every value in our struct.</p>
<blockquote>
<p>Note: Need a quicker way to convert a sample JSON payload to a Golang struct? There are a handful of awesome websites that will do this conversion automatically for you. I like to use <a href="https://transform.tools/json-to-go">this one</a>, but that&rsquo;s only one of a handful of options!</p></blockquote>
<p>Next, we&rsquo;ll start building out our <code>getCurrentWeather()</code> function:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/command-weather.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">getCurrentWeather</span><span class="p">(</span><span class="nx">message</span> <span class="kt">string</span><span class="p">)</span> <span class="o">*</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageSend</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Match 5-digit US ZIP code</span>
</span></span><span class="line"><span class="cl"> <span class="nx">r</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">regexp</span><span class="p">.</span><span class="nf">Compile</span><span class="p">(</span><span class="s">`\d{5}`</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">zip</span> <span class="o">:=</span> <span class="nx">r</span><span class="p">.</span><span class="nf">FindString</span><span class="p">(</span><span class="nx">message</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// If ZIP not found, return an error</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">zip</span> <span class="o">==</span> <span class="s">&#34;&#34;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&amp;</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageSend</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">Content</span><span class="p">:</span> <span class="s">&#34;Sorry that ZIP code doesn&#39;t look right&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></div><p>In our function definition, we&rsquo;ll be expecting a string input containing the user&rsquo;s message - for example: &ldquo;!zip 12345&rdquo; - and we&rsquo;ll be returning a <code>discordgo.MessageSend</code> object like I mentioned earlier.</p>
<p>Next, we&rsquo;ll do a quick (and very lazy) regular expression match against the incoming message &amp; attempt to pull out the ZIP code. This regex is only searching for a pattern of 5 digits. If the <code>r.FindString()</code> call finds a match, it will return the 5-digit ZIP code. If it doesn&rsquo;t match, it will return an empty string.</p>
<p>In case we don&rsquo;t match a ZIP code, we&rsquo;ll check to see if the <code>zip</code> variable is empty. If it is, we&rsquo;ll have Discord send a message back to the user saying it&rsquo;s invalid.</p>
<p>Here&rsquo;s where we&rsquo;ll see a simple example of using our <code>discordgo.MessageSend</code> object. This object is just a Go struct, so we can use it to store data in the format of <code>Key: Value</code>. In this case, we can still respond with a simple plain-text message - by using the <code>Content</code> key and supplying a string message. The full definition &amp; possible fields for this object are in the <a href="https://pkg.go.dev/github.com/bwmarrin/discordgo#MessageSend">discordgo docs</a>.</p>
<p>Next, we&rsquo;ll handle making our HTTP request to the OpenWeather API:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/command-weather.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">        <span class="nx">weatherURL</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">&#34;%szip=%s&amp;units=imperial&amp;appid=%s&#34;</span><span class="p">,</span> <span class="nx">URL</span><span class="p">,</span> <span class="nx">zip</span><span class="p">,</span> <span class="nx">OpenWeatherToken</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Create new HTTP client &amp; set timeout</span>
</span></span><span class="line"><span class="cl"> <span class="nx">client</span> <span class="o">:=</span> <span class="nx">http</span><span class="p">.</span><span class="nx">Client</span><span class="p">{</span><span class="nx">Timeout</span><span class="p">:</span> <span class="mi">5</span> <span class="o">*</span> <span class="nx">time</span><span class="p">.</span><span class="nx">Second</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Query OpenWeather API</span>
</span></span><span class="line"><span class="cl"> <span class="nx">response</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">client</span><span class="p">.</span><span class="nf">Get</span><span class="p">(</span><span class="nx">weatherURL</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="o">&amp;</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageSend</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">Content</span><span class="p">:</span> <span class="s">&#34;Sorry, there was an error trying to get the weather&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span></code></pre></div><p>We&rsquo;ll start by generating the complete API URL. We&rsquo;ll use <code>fmt.Sprintf</code> to generate a formatted string &amp; inject variable values. So in this case, we&rsquo;re combining the base URL with the required parameters: zip, units, and appid. The <code>zip</code> value will contain the ZIP code we just collected from our user. <code>appid</code> will be our OpenWeather API key. Using <code>fmt.Sprintf</code>, we can inject variables using the <code>%s</code> placeholder for string values - then provide those values as inputs.</p>
<p>So for example, a full query URL for ZIP code 12345 may look similar to this: <code>https://api.openweathermap.org/data/2.5/weather?zip=12345&amp;units=imperial&amp;appid=ABCDEF12345</code></p>
<p>Then we create a new instance of an <code>http.Client</code> - just so we can modify the timeout value to 5 seconds. We then use this client to issue a HTTP GET request to the full <code>weatherURL</code>.</p>
<p>Assuming the call succeeds, we&rsquo;ll have our JSON response stored in the <code>response</code> variable. If not, we&rsquo;ll quickly check for an error &amp; send a Discord message back to the user.</p>
<h2 id="generating-a-discord-embed-message">Generating a Discord Embed Message</h2>
<p>Now we can parse our JSON response:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/command-weather.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Open HTTP response body</span>
</span></span><span class="line"><span class="cl"> <span class="nx">body</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">ioutil</span><span class="p">.</span><span class="nf">ReadAll</span><span class="p">(</span><span class="nx">response</span><span class="p">.</span><span class="nx">Body</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="nx">response</span><span class="p">.</span><span class="nx">Body</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Convert JSON</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">data</span> <span class="nx">WeatherData</span>
</span></span><span class="line"><span class="cl"> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">body</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">data</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Pull out desired weather info &amp; Convert to string if necessary</span>
</span></span><span class="line"><span class="cl"> <span class="nx">city</span> <span class="o">:=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">Name</span>
</span></span><span class="line"><span class="cl"> <span class="nx">conditions</span> <span class="o">:=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">Weather</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">Description</span>
</span></span><span class="line"><span class="cl"> <span class="nx">temperature</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">FormatFloat</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">Main</span><span class="p">.</span><span class="nx">Temp</span><span class="p">,</span> <span class="sc">&#39;f&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">64</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">humidity</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">Itoa</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">Main</span><span class="p">.</span><span class="nx">Humidity</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">wind</span> <span class="o">:=</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">FormatFloat</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">Wind</span><span class="p">.</span><span class="nx">Speed</span><span class="p">,</span> <span class="sc">&#39;f&#39;</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">64</span><span class="p">)</span>
</span></span></code></pre></div><p>We&rsquo;ll read in the HTTP response body using <code>ioutil.ReadAll()</code>. We&rsquo;ll need to close this object, so we&rsquo;ll immediately follow that read with a <code>defer response.Body.Close()</code> to make sure that happens.</p>
<p>Next we&rsquo;ll create a new variable called <code>data</code>, which will be of type <code>WeatherData</code> - our struct that we created earlier to store the JSON data. We can then convert that JSON response to our <code>data</code> object using <code>json.Unmarshal()</code> and passing our HTTP body &amp; the target variable to store the data in.</p>
<p>Lastly, we can start pulling out the information we need to use later. For clarity, I&rsquo;ve chosen to pull out each individual value here &amp; assign them to their own variable names. Since our Discord response can only be a string, we also handle type conversions here - from integer or float64 to a string.</p>
<p>Finally, we can generate our Discord <code>MessageSend</code> object:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// snippet from: bot/command-weather.go</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="c1">// Build Discord embed response</span>
</span></span><span class="line"><span class="cl"> <span class="nx">embed</span> <span class="o">:=</span> <span class="o">&amp;</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageSend</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">Embeds</span><span class="p">:</span> <span class="p">[]</span><span class="o">*</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageEmbed</span><span class="p">{{</span>
</span></span><span class="line"><span class="cl">   <span class="nx">Type</span><span class="p">:</span>        <span class="nx">discordgo</span><span class="p">.</span><span class="nx">EmbedTypeRich</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nx">Title</span><span class="p">:</span>       <span class="s">&#34;Current Weather&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nx">Description</span><span class="p">:</span> <span class="s">&#34;Weather for &#34;</span> <span class="o">+</span> <span class="nx">city</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">   <span class="nx">Fields</span><span class="p">:</span> <span class="p">[]</span><span class="o">*</span><span class="nx">discordgo</span><span class="p">.</span><span class="nx">MessageEmbedField</span><span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Name</span><span class="p">:</span>   <span class="s">&#34;Conditions&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Value</span><span class="p">:</span>  <span class="nx">conditions</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Inline</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Name</span><span class="p">:</span>   <span class="s">&#34;Temperature&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Value</span><span class="p">:</span>  <span class="nx">temperature</span> <span class="o">+</span> <span class="s">&#34;°F&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Inline</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Name</span><span class="p">:</span>   <span class="s">&#34;Humidity&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Value</span><span class="p">:</span>  <span class="nx">humidity</span> <span class="o">+</span> <span class="s">&#34;%&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Inline</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">    <span class="p">{</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Name</span><span class="p">:</span>   <span class="s">&#34;Wind&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Value</span><span class="p">:</span>  <span class="nx">wind</span> <span class="o">+</span> <span class="s">&#34; mph&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">     <span class="nx">Inline</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="p">},</span>
</span></span><span class="line"><span class="cl">   <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">},</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">embed</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>We&rsquo;ll create a new variable called <code>embed</code>, which will store a <code>discordgo.MessageSend</code> struct.</p>
<p>Earlier we used this to send a <code>Content:</code> value, but this time we&rsquo;ll be leveraging the <code>Embeds</code> key. <code>Embeds</code> is of the type <code>[]*discordgo.MessageEmbed</code>, which is again just a struct to store data. For reference, the format &amp; possible fields of the <code>MessageEmbed</code> object are listed <a href="https://pkg.go.dev/github.com/bwmarrin/discordgo#MessageEmbed">here</a></p>
<p>Within this <code>MessageEmbed</code> object, we can start filling in data about our message. So we fill in our message <code>Title</code> and <code>Description</code> - and tell Discord that this is going to be a richtext type of response.</p>
<p>We&rsquo;ll be displaying a box in discord that has a number of key/value pairs - which we&rsquo;ll use to show the measurement title &amp; value. To do this, we&rsquo;ll fill in the <code>Fields:</code> key - which takes in a list of structs of the type <code>[]*discordgo.MessageEmbedField</code>.</p>
<p>For each of the weather measurements we want to display, we&rsquo;ll provide a <code>Name:</code>, the <code>Value:</code>, and specify <code>Inline: true</code>. The <code>Inline</code> options tells Discord to try and list each key/value inline with eachother, rather than placing each pair on a new line.</p>
<p>Last, we&rsquo;ll return our <code>discordgo.MessageSend</code> object via <code>return embed</code>.</p>
<h2 id="testing-1">Testing</h2>
<p>Let&rsquo;s test this out. We can stop our currently running bot with <code>Ctrl+C</code>, then restart with <code>go run main.go</code>.</p>
<p>We should be able to ask our bot for the weather using our new <code>!zip</code> command:</p>
<p><img alt="basic-bot-command" loading="lazy" src="/content/images/2022/09/basic-bot-command.png#center"></p>
<p>Look at that! The bot quickly responds with the current weather, and formatted in an embedded message. There&rsquo;s a lot more we could do with embedded messages, but this is just an example to get started.</p>
<hr>
<p>Okay - I think that wraps up this blog post. I know it was a long one, so if you&rsquo;re still with me - Thank you! I hope this was helpful.</p>
<p>I am planning on a follow up post shortly, where I&rsquo;ll cover how to add Discord slash commands to our example bot. Check back soon or subscribe to the Blog or YouTube if you&rsquo;re interested!</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
