<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ashley Tonkin</title>
    <description>The latest articles on DEV Community by Ashley Tonkin (@itsash).</description>
    <link>https://dev.to/itsash</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F884974%2F2850de3d-39bf-4547-9ae8-4f7bd6dff81d.jpg</url>
      <title>DEV Community: Ashley Tonkin</title>
      <link>https://dev.to/itsash</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itsash"/>
    <language>en</language>
    <item>
      <title>Meet Cozy Café</title>
      <dc:creator>Ashley Tonkin</dc:creator>
      <pubDate>Sun, 28 Jun 2026 22:02:26 +0000</pubDate>
      <link>https://dev.to/itsash/meet-cozy-cafe-19bh</link>
      <guid>https://dev.to/itsash/meet-cozy-cafe-19bh</guid>
      <description>&lt;h2&gt;
  
  
  Part 2: Meet Cozy Café (and Give Your Bot a Backbone)
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Part 2 of a beginner-friendly series on building a real Discord bot with JavaScript, discord.js, and Prisma. Part 1 got a bot online and replying to &lt;code&gt;/ping&lt;/code&gt;. Today we reveal what we're actually building — and lay the foundation for it.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Welcome back! In Part 1 you did something genuinely cool: you built a program that logs into Discord and answers a &lt;code&gt;/ping&lt;/code&gt; command with &lt;code&gt;Pong!&lt;/code&gt;. That's the entire skeleton of every Discord bot ever made — listen for an event, react to it.&lt;/p&gt;

&lt;p&gt;But &lt;code&gt;/ping&lt;/code&gt; is a bit lonely. So in this part, two things happen. First, &lt;strong&gt;the big reveal&lt;/strong&gt;: I'll show you the actual project we're building across this series. Second, we'll &lt;strong&gt;give your bot a backbone&lt;/strong&gt; — a tidy structure that lets it grow from one command to dozens without turning into an unreadable mess — and write the first two real commands of our game.&lt;/p&gt;

&lt;p&gt;Let's meet the café.&lt;/p&gt;




&lt;h2&gt;
  
  
  The reveal: we're building Cozy Café ☕
&lt;/h2&gt;

&lt;p&gt;Here's the project: &lt;strong&gt;Cozy Café — an idle café game you run entirely from inside Discord.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You open a little café with a slash command. From then on, your café quietly serves customers and earns coins &lt;strong&gt;in real time — even while you're offline.&lt;/strong&gt; You drop back in whenever you feel like it to collect your earnings, spend them on upgrades and new recipes, and slowly master your menu. Over time a humble coffee cart grows into a cozy corner the whole server knows.&lt;/p&gt;

&lt;p&gt;That's it. No twitch reflexes, no grinding, no pressure. Just a warm little thing that's &lt;em&gt;yours&lt;/em&gt; and is always gently ticking along in the background.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a moment of play feels like
&lt;/h3&gt;

&lt;p&gt;You open Discord in the morning, type &lt;code&gt;/collect&lt;/code&gt;, and your bot replies:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;☕ &lt;strong&gt;While you were away (9h 12m)&lt;/strong&gt; your café served &lt;strong&gt;74 customers&lt;/strong&gt; and earned &lt;strong&gt;184 coins.&lt;/strong&gt;&lt;br&gt;
You also discovered a new recipe: &lt;strong&gt;Cinnamon Roll&lt;/strong&gt; — add it to your menu with &lt;code&gt;/menu&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You spend thirty seconds deciding what to do — upgrade your espresso machine so you earn faster, or save up for that Matcha Latte you've been eyeing — then close Discord and get on with your day. The whole visit takes a minute, and it's completely optional.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why &lt;em&gt;this&lt;/em&gt; game? (the design philosophy)
&lt;/h3&gt;

&lt;p&gt;This is the part I really want you to understand, because it's a deliberate design choice, not an accident. You might know the feeling of a game or app that &lt;em&gt;punishes&lt;/em&gt; you for not showing up — a streak you'll lose, a pet that starves, daily quests that pile up into guilt. That's the opposite of cozy.&lt;/p&gt;

&lt;p&gt;Cozy Café is built on one rule:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Reward presence, never punish absence.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Everything follows from that sentence:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your café earns while you're away, so checking in once a day feels rewarding — there's always something waiting to collect.&lt;/li&gt;
&lt;li&gt;But earnings cap out at about a day's worth, so you never &lt;em&gt;need&lt;/em&gt; to check more than once a day. No compulsive refreshing.&lt;/li&gt;
&lt;li&gt;And nothing ever decays. Skip three days, skip a week — your coins, recipes, and progress are all exactly where you left them. You just collect a full batch when you're back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The thing that keeps pulling you back isn't fear of losing progress — it's the quiet satisfaction of &lt;strong&gt;collecting and mastering&lt;/strong&gt;: filling out a recipe book, leveling up your favourite drinks, watching your café grow. A carrot, never a stick.&lt;/p&gt;

&lt;p&gt;That's the game. Now let's build the foundation it needs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why we can't just keep piling code into one file
&lt;/h2&gt;

&lt;p&gt;In Part 1, everything lived in a single &lt;code&gt;index.js&lt;/code&gt;. That was perfect for one command. But look at where we're heading: &lt;code&gt;/open&lt;/code&gt;, &lt;code&gt;/cafe&lt;/code&gt;, &lt;code&gt;/collect&lt;/code&gt;, &lt;code&gt;/daily&lt;/code&gt;, &lt;code&gt;/shop&lt;/code&gt;, &lt;code&gt;/recipes&lt;/code&gt;, &lt;code&gt;/menu&lt;/code&gt;… If we cram all of those into &lt;code&gt;index.js&lt;/code&gt;, we'll end up with a 600-line monster where everything is tangled together and finding anything is a nightmare.&lt;/p&gt;

&lt;p&gt;So before we add features, we're going to reorganize. This is a habit real developers live by, and the "why" behind it has a name: &lt;strong&gt;separation of concerns.&lt;/strong&gt; The idea is simple — &lt;em&gt;each piece of your program should have one clear job and live in one obvious place.&lt;/em&gt; When every command is its own little file, you always know exactly where to look, and adding a new command never risks breaking an old one.&lt;/p&gt;

&lt;p&gt;Here's the structure we're building toward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cozy-cafe-bot/
├─ commands/
│  ├─ open.js        ← one file per command
│  └─ cafe.js
├─ data/
│  └─ store.js       ← where café data lives (for now)
├─ .env              ← your secrets (from Part 1)
├─ deploy-commands.js
└─ index.js          ← the "brain" that ties it together
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(That top folder is simply your project from Part 1. I've named it &lt;code&gt;cozy-cafe-bot/&lt;/code&gt; here, but the name honestly doesn't matter — keep &lt;code&gt;my-discord-bot&lt;/code&gt; from Part 1 if you like. Only the files and folders **inside&lt;/em&gt;* it matter.)*&lt;/p&gt;

&lt;p&gt;The trick that makes this work is a &lt;strong&gt;command handler&lt;/strong&gt;: a small bit of code in &lt;code&gt;index.js&lt;/code&gt; that automatically &lt;em&gt;finds&lt;/em&gt; every file in the &lt;code&gt;commands/&lt;/code&gt; folder and loads it. Add a new command file, and the bot just picks it up — you never have to manually wire each one in. Let's build it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Give every command a consistent shape
&lt;/h2&gt;

&lt;p&gt;The handler can only auto-load our commands if they all look the same on the outside. So we'll agree on a simple &lt;strong&gt;contract&lt;/strong&gt;: every command file exports an object with exactly two things —&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; — the command's name and description (built with &lt;code&gt;SlashCommandBuilder&lt;/code&gt;, same as Part 1).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;execute&lt;/code&gt; — the function that runs when someone uses the command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the whole contract. &lt;code&gt;{ data, execute }&lt;/code&gt;. Once every command follows it, the handler can treat them all identically, without caring what any individual command actually does. That consistency is what makes the magic possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — A place to keep café data (just for today)
&lt;/h2&gt;

&lt;p&gt;Our commands need somewhere to store each player's café. For this part, we'll use the simplest storage that exists: a &lt;strong&gt;&lt;code&gt;Map&lt;/code&gt;&lt;/strong&gt; held in the bot's memory.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;code&gt;Map&lt;/code&gt; is a built-in JavaScript container that stores key → value pairs. We'll use each player's Discord user ID as the key, and their café as the value — so we can look up "the café belonging to &lt;em&gt;this&lt;/em&gt; person."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a folder called &lt;code&gt;data&lt;/code&gt;, and inside it a file &lt;code&gt;store.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// data/store.js&lt;/span&gt;

&lt;span class="c1"&gt;// A simple in-memory store: Discord user ID  -&amp;gt;  that user's café.&lt;/span&gt;
&lt;span class="c1"&gt;// ⚠️ Heads up: this lives in the bot's memory (RAM), which means it is&lt;/span&gt;
&lt;span class="c1"&gt;// wiped clean every time the bot restarts. We'll fix that in Part 3.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cafes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cafes&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why put this in its own file? Because &lt;strong&gt;both&lt;/strong&gt; of our commands (&lt;code&gt;/open&lt;/code&gt; and &lt;code&gt;/cafe&lt;/code&gt;) need to read and write the &lt;em&gt;same&lt;/em&gt; café data. By keeping the &lt;code&gt;Map&lt;/code&gt; in one shared module, every file that imports it gets the exact same &lt;code&gt;Map&lt;/code&gt; — when &lt;code&gt;/open&lt;/code&gt; adds a café, &lt;code&gt;/cafe&lt;/code&gt; can immediately see it. (Node.js loads a module once and reuses it everywhere, which is exactly the behaviour we want here.)&lt;/p&gt;

&lt;p&gt;That warning in the comment is the whole point of this part, by the way. Keep it in mind — we'll come back to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — The first command: &lt;code&gt;/open&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;commands/open.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// commands/open.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SlashCommandBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmbedBuilder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cafes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../data/store.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// The "data" half of our contract — name + description&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SlashCommandBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;open&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Open your very own café!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="c1"&gt;// The "execute" half — what actually happens&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Do they already have a café?&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cafes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;You already run a café! Check on it with `/cafe`.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Create a brand-new café for this player&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cafe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'s Café`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;            &lt;span class="c1"&gt;// a little starting cash&lt;/span&gt;
      &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;openedAt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="c1"&gt;// we'll use this timestamp properly in Part 3&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="nx"&gt;cafes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Reply with a nice-looking embed instead of plain text&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EmbedBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xc5774a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;☕ Your café is now open!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Welcome to **&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;**. Grab an apron — let's get brewing.`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Coins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFooter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check on your café anytime with /cafe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;embeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A few things worth understanding here:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;interaction.user.id&lt;/code&gt;&lt;/strong&gt; is Discord's unique ID for whoever ran the command. Using it as our key means each person gets their &lt;em&gt;own&lt;/em&gt; café — your café and your friend's café never get mixed up.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The "already have one?" check&lt;/strong&gt; is a small but important habit: think about what happens if someone runs a command twice. Here, we politely stop instead of wiping out their existing café.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;EmbedBuilder&lt;/code&gt;&lt;/strong&gt; creates those tidy, bordered message cards you've seen fancy bots use. It's purely about presentation — &lt;code&gt;setTitle&lt;/code&gt;, &lt;code&gt;setDescription&lt;/code&gt;, &lt;code&gt;addFields&lt;/code&gt; — but it instantly makes your bot feel polished instead of plain. (&lt;code&gt;0xc5774a&lt;/code&gt; is a warm coffee-brown colour, written in hex.)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Step 4 — The second command: &lt;code&gt;/cafe&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Create &lt;code&gt;commands/cafe.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// commands/cafe.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;SlashCommandBuilder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EmbedBuilder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cafes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../data/store.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SlashCommandBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cafe&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Check in on your café.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cafe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;cafes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// No café yet? Point them to /open.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You don't have a café yet! Open one with `/open`.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;embed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EmbedBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mh"&gt;0xc5774a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`☕ &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addFields&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Coins&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;coins&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;cafe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;inline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setFooter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Coming soon: collect earnings, unlock recipes…&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;embeds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;embed&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how this command &lt;em&gt;only reads&lt;/em&gt; the café — it looks one up with &lt;code&gt;cafes.get(...)&lt;/code&gt; and shows it. The matching pattern to &lt;code&gt;/open&lt;/code&gt;'s &lt;code&gt;cafes.set(...)&lt;/code&gt;. Read and write, sharing the same &lt;code&gt;Map&lt;/code&gt;. That symmetry is the payoff for putting our store in its own file.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — The command handler that ties it all together
&lt;/h2&gt;

&lt;p&gt;Now we upgrade &lt;code&gt;index.js&lt;/code&gt; so it automatically loads everything in &lt;code&gt;commands/&lt;/code&gt; and routes each slash command to the right file. Replace your &lt;code&gt;index.js&lt;/code&gt; with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GatewayIntentBits&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;intents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;GatewayIntentBits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Guilds&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// --- 1. Load every command file into a collection ---&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Collection&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commandsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commands&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commandFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandsPath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;commandFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandsPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;execute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`⚠️  Skipping &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;: missing "data" or "execute".`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// --- 2. Say hello when the bot connects ---&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ClientReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✅ Logged in as &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// --- 3. Run the matching command whenever someone uses a slash command ---&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InteractionCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isChatInputCommand&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Something went wrong running that command. 😬&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the heart of Part 2, so let's unpack the three numbered sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Loading the commands.&lt;/strong&gt; &lt;code&gt;fs.readdirSync&lt;/code&gt; reads the &lt;code&gt;commands/&lt;/code&gt; folder and gives us a list of filenames; we keep only the &lt;code&gt;.js&lt;/code&gt; ones. For each, we &lt;code&gt;require&lt;/code&gt; it (that runs the file and hands us its &lt;code&gt;{ data, execute }&lt;/code&gt; object) and store it in a &lt;strong&gt;&lt;code&gt;Collection&lt;/code&gt;&lt;/strong&gt; — discord.js's souped-up version of a &lt;code&gt;Map&lt;/code&gt; — keyed by the command's name. The little check makes sure we only load files that honour our contract. The beautiful part: &lt;strong&gt;drop a new file into &lt;code&gt;commands/&lt;/code&gt; and it's automatically loaded.&lt;/strong&gt; You never edit this section again.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The ready event&lt;/strong&gt; is unchanged from Part 1 — still using the future-proof &lt;code&gt;Events.ClientReady&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Dispatching.&lt;/strong&gt; When a slash command comes in, we look up the matching command by name (&lt;code&gt;client.commands.get(...)&lt;/code&gt;) and call its &lt;code&gt;execute&lt;/code&gt;. The &lt;code&gt;try/catch&lt;/code&gt; is a safety net: if a command crashes, we log the real error for ourselves and show the user a friendly message instead of the bot silently dying.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;See how the handler doesn't know or care that &lt;code&gt;/open&lt;/code&gt; makes a café and &lt;code&gt;/cafe&lt;/code&gt; shows one? It just finds commands and runs them. &lt;em&gt;That's&lt;/em&gt; separation of concerns paying off.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — Tell Discord about the new commands
&lt;/h2&gt;

&lt;p&gt;Slash commands have to be registered with Discord before they appear (remember this from Part 1?). We'll make our &lt;code&gt;deploy-commands.js&lt;/code&gt; read the &lt;code&gt;commands/&lt;/code&gt; folder too, so it always stays in sync. Replace it with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// deploy-commands.js&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:fs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;REST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commandsPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;commands&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commandFiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readdirSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandsPath&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;commandFiles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commandsPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;execute&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="c1"&gt;// convert to the format Discord wants&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;REST&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Registering &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; command(s)…`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applicationGuildCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GUILD_ID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commands&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Commands registered!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's the same idea as the handler: walk the folder, collect each command's &lt;code&gt;data&lt;/code&gt;, and send the batch to Discord. Run it now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node deploy-commands.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;Registering 2 command(s)…&lt;/code&gt; then &lt;code&gt;✅ Commands registered!&lt;/code&gt;. (Re-run this script whenever you &lt;em&gt;add or rename&lt;/em&gt; a command — not every time you start the bot.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 7 — Run it and open your café
&lt;/h2&gt;

&lt;p&gt;Start the bot:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Over in your test server, type &lt;code&gt;/open&lt;/code&gt;. Your bot welcomes you with a warm embed and your café springs to life. Now type &lt;code&gt;/cafe&lt;/code&gt; — there it is, coins and all. Run &lt;code&gt;/open&lt;/code&gt; again and it politely tells you that you already have one.&lt;/p&gt;

&lt;p&gt;You just played the first thirty seconds of Cozy Café. 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  The catch (and it's on purpose)
&lt;/h2&gt;

&lt;p&gt;Now do this little experiment. With the bot running and your café open, go back to your terminal, press &lt;strong&gt;Ctrl + C&lt;/strong&gt; to stop the bot, then start it again with &lt;code&gt;node index.js&lt;/code&gt;. Hop back to Discord and type &lt;code&gt;/cafe&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You don't have a café yet! Open one with &lt;code&gt;/open&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Gone. Your café vanished. 😟&lt;/p&gt;

&lt;p&gt;This isn't a bug — it's the lesson. Remember that warning we wrote in &lt;code&gt;store.js&lt;/code&gt;? Our &lt;code&gt;Map&lt;/code&gt; lives in the bot's &lt;strong&gt;memory (RAM)&lt;/strong&gt;, and memory is &lt;em&gt;temporary&lt;/em&gt; — the moment the program stops, everything in it evaporates. This is exactly the cliffhanger Part 1 ended on: &lt;strong&gt;a bot, on its own, has no lasting memory.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And here's why it matters so much for &lt;em&gt;this&lt;/em&gt; game specifically. Cozy Café's whole magic trick — "while you were away for 9 hours you earned 184 coins" — depends on the bot reliably remembering two things between visits: your café's state, and &lt;em&gt;when you last collected&lt;/em&gt;. An idle game that forgets everything on restart isn't an idle game at all. So our game doesn't just &lt;em&gt;want&lt;/em&gt; permanent memory; it literally &lt;strong&gt;cannot exist&lt;/strong&gt; without it.&lt;/p&gt;

&lt;p&gt;That's our cue.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you built, and what's next
&lt;/h2&gt;

&lt;p&gt;Look at how far you came in this part:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You learned what we're building and &lt;em&gt;why&lt;/em&gt; it's designed the way it is — &lt;strong&gt;reward presence, never punish absence.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;You gave your bot a real backbone: a &lt;code&gt;commands/&lt;/code&gt; folder, a consistent &lt;code&gt;{ data, execute }&lt;/code&gt; contract, and a handler that auto-loads everything. Your bot can now grow to dozens of commands without becoming a mess.&lt;/li&gt;
&lt;li&gt;You wrote &lt;code&gt;/open&lt;/code&gt; and &lt;code&gt;/cafe&lt;/code&gt;, your first real game commands, complete with polished embeds.&lt;/li&gt;
&lt;li&gt;And you &lt;em&gt;felt&lt;/em&gt;, first-hand, the problem that defines the next chapter.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In &lt;strong&gt;Part 3&lt;/strong&gt;, we give Cozy Café a permanent memory. We'll introduce a real database and meet &lt;strong&gt;Prisma&lt;/strong&gt; — the friendly tool that lets us save and load café data with clean, readable JavaScript. That &lt;code&gt;store.js&lt;/code&gt; &lt;code&gt;Map&lt;/code&gt; will become a real &lt;code&gt;Cafe&lt;/code&gt; table, your cafés will survive restarts, and we'll finally write &lt;code&gt;/collect&lt;/code&gt; with genuine "earn while you're away" math powered by that &lt;code&gt;openedAt&lt;/code&gt; timestamp we quietly planted today.&lt;/p&gt;

&lt;h3&gt;
  
  
  Try it yourself before Part 3
&lt;/h3&gt;

&lt;p&gt;The best way to make this stick is to tinker. A few gentle challenges:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the welcome message or the café's starting coins in &lt;code&gt;open.js&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a fun fact to the &lt;code&gt;/cafe&lt;/code&gt; embed — maybe show how long ago the café opened (hint: compare &lt;code&gt;Date.now()&lt;/code&gt; to the &lt;code&gt;openedAt&lt;/code&gt; you stored).&lt;/li&gt;
&lt;li&gt;Bolder: create a brand-new command file, &lt;code&gt;commands/rename.js&lt;/code&gt;, that lets a player rename their café. If you follow the &lt;code&gt;{ data, execute }&lt;/code&gt; contract, the handler will pick it up automatically — no wiring required. (That's the whole point!)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Break things, fix them, make the café yours. See you in Part 3, where it finally remembers. ☕&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Enjoying the series? Part 3 builds directly on the project you just structured, so keep this folder handy.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>codenewbie</category>
      <category>javascript</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Getting-Started</title>
      <dc:creator>Ashley Tonkin</dc:creator>
      <pubDate>Sat, 27 Jun 2026 23:32:15 +0000</pubDate>
      <link>https://dev.to/itsash/discord-bot-series-part-1-getting-started-cjh</link>
      <guid>https://dev.to/itsash/discord-bot-series-part-1-getting-started-cjh</guid>
      <description>&lt;h2&gt;
  
  
  Part 1: From Zero to "It's Alive!"
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Part 1 of a beginner-friendly series on building a real Discord bot with JavaScript, discord.js, and Prisma.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So you want to build a Discord bot. Maybe you've seen bots that play music, run polls, hand out roles, or track who's the most active in a server, and thought: &lt;em&gt;how do they even do that?&lt;/em&gt; By the end of this series, you'll have built one yourself — and understood every piece along the way.&lt;/p&gt;

&lt;p&gt;This first part is all about getting started. We're going to take you from "I've never written a line of code" to a bot that logs into Discord, shows up online, and responds to a command you type. No prior coding experience required. I'll explain not just &lt;em&gt;what&lt;/em&gt; to click and type, but &lt;em&gt;why&lt;/em&gt; — because once you understand the why, you can build anything, not just copy-paste your way through one tutorial.&lt;/p&gt;

&lt;p&gt;Let's go.&lt;/p&gt;




&lt;h2&gt;
  
  
  What we're building (the whole series)
&lt;/h2&gt;

&lt;p&gt;Before we dive in, here's the map so you know where we're headed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Part 1 (this one):&lt;/strong&gt; Understand what a bot actually is, set one up in Discord, and get it responding to a slash command.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 2:&lt;/strong&gt; Give your bot more commands and learn how to organize your code so it doesn't turn into spaghetti.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 3:&lt;/strong&gt; Give your bot a &lt;em&gt;memory&lt;/em&gt; using a database. This is where &lt;strong&gt;Prisma&lt;/strong&gt; comes in — a tool that lets your bot save and recall information (like user points, settings, or warnings) even after it restarts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Part 4 and beyond:&lt;/strong&gt; Build a real feature end to end — think a leveling system or a simple economy — using everything we've learned.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If the word "database" or "Prisma" sounds intimidating right now, don't worry. We won't touch it until Part 3, and by then it'll make complete sense. For now, just keep this in the back of your mind: &lt;strong&gt;a bot on its own has no memory.&lt;/strong&gt; Every time it restarts, it forgets everything. A database is how we fix that — and Prisma is the friendly translator between your JavaScript code and that database.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, what &lt;em&gt;is&lt;/em&gt; a Discord bot?
&lt;/h2&gt;

&lt;p&gt;Let's clear up the biggest source of confusion right away, because it trips up almost everyone at the start.&lt;/p&gt;

&lt;p&gt;A Discord bot is &lt;strong&gt;not&lt;/strong&gt; something that lives "inside" Discord. It's a program — code — that runs on a computer (your laptop now, a server later) and &lt;em&gt;logs into Discord over the internet&lt;/em&gt;, the same way you log in with the Discord app. Discord just treats it as a special kind of user.&lt;/p&gt;

&lt;p&gt;Here's the mental model that makes everything click:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Your bot is a program that connects to Discord, &lt;strong&gt;listens&lt;/strong&gt; for things happening (someone typed a command, someone joined the server), and &lt;strong&gt;reacts&lt;/strong&gt; by doing something (sending a message, giving a role).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's genuinely the whole idea. Listen for events, react to them. Everything else is detail.&lt;/p&gt;

&lt;p&gt;So building a bot involves two separate worlds, and you'll bounce between them:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Discord side&lt;/strong&gt; — where you register your bot, give it an identity, and get permission for it to join servers. You do this in a web dashboard.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The code side&lt;/strong&gt; — where you write the actual instructions for what your bot does. This is the JavaScript.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll set up the Discord side first (so our bot &lt;em&gt;exists&lt;/em&gt;), then write the code (so it &lt;em&gt;does something&lt;/em&gt;).&lt;/p&gt;




&lt;h2&gt;
  
  
  What you'll need
&lt;/h2&gt;

&lt;p&gt;Three things, and they're all free.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. A Discord account and a test server
&lt;/h3&gt;

&lt;p&gt;You almost certainly have an account already. You'll also want a &lt;strong&gt;server you own&lt;/strong&gt; to test in — never test on a real community server, because your bot will misbehave while you learn, and that's totally normal.&lt;/p&gt;

&lt;p&gt;Making a test server takes ten seconds: in the Discord app, click the &lt;strong&gt;+&lt;/strong&gt; button on the far-left server bar, choose &lt;strong&gt;Create My Own&lt;/strong&gt;, and name it whatever you like ("Bot Lab" works). Done. This is your sandbox.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Node.js
&lt;/h3&gt;

&lt;p&gt;Here's the first "why." Your bot is written in JavaScript — the same language that runs websites in your browser. But your bot doesn't run in a browser; it runs on a computer as a standalone program. &lt;strong&gt;Node.js is the thing that lets JavaScript run outside the browser.&lt;/strong&gt; Think of it as the engine that takes your &lt;code&gt;.js&lt;/code&gt; files and actually executes them.&lt;/p&gt;

&lt;p&gt;Go to &lt;a href="https://nodejs.org" rel="noopener noreferrer"&gt;nodejs.org&lt;/a&gt; and download the &lt;strong&gt;LTS&lt;/strong&gt; version (LTS means "Long-Term Support" — the stable, reliable one that most people use). Anything &lt;strong&gt;version 22.12.0 or newer&lt;/strong&gt; will work with the tools in this series, and the current LTS is comfortably past that.&lt;/p&gt;

&lt;p&gt;To check it worked, you'll use a &lt;strong&gt;terminal&lt;/strong&gt;. Don't be scared of it — a terminal is just a text box where you type commands to your computer instead of clicking buttons. On Windows, open &lt;strong&gt;PowerShell&lt;/strong&gt; (search for it in the Start menu); on Mac, open &lt;strong&gt;Terminal&lt;/strong&gt; (search with Cmd+Space). Type this and press Enter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see something like &lt;code&gt;v22.12.0&lt;/code&gt; (or higher), you're good. That number is Node telling you it's installed and ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A code editor
&lt;/h3&gt;

&lt;p&gt;You &lt;em&gt;could&lt;/em&gt; write code in Notepad, but a proper code editor makes life dramatically easier — it color-codes your code, catches typos, and has a built-in terminal. The standard choice (and what I'd recommend) is &lt;strong&gt;&lt;a href="https://code.visualstudio.com" rel="noopener noreferrer"&gt;Visual Studio Code&lt;/a&gt;&lt;/strong&gt;, usually just called "VS Code." It's free. Download it, install it, open it.&lt;/p&gt;

&lt;p&gt;That's the whole toolkit. Now let's make our bot exist.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 1 — Create your application in the Discord Developer Portal
&lt;/h2&gt;

&lt;p&gt;Everything on the Discord side happens in one place: the &lt;strong&gt;Developer Portal&lt;/strong&gt;. Head to &lt;a href="https://discord.com/developers/applications" rel="noopener noreferrer"&gt;discord.com/developers/applications&lt;/a&gt; and sign in with your normal Discord account.&lt;/p&gt;

&lt;p&gt;Click &lt;strong&gt;New Application&lt;/strong&gt; in the top right. Give it a name (this becomes your bot's name, but you can change it later), check the box to agree to Discord's terms, and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A quick note on vocabulary, because Discord uses two words that sound the same but aren't:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;application&lt;/strong&gt; is the overall &lt;em&gt;project&lt;/em&gt; — a container that holds your bot's identity, settings, and permissions.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;bot&lt;/strong&gt; is the actual &lt;em&gt;user account&lt;/em&gt; that joins servers and sends messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You just made the application. In the next step, you'll find the bot living &lt;em&gt;inside&lt;/em&gt; it. One application, one bot — that's the relationship.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 2 — Find your bot and get its token
&lt;/h2&gt;

&lt;p&gt;In the left-hand menu of your new application, click &lt;strong&gt;Bot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This page is your bot's identity. You can give it a profile picture and username here. But the important part is the &lt;strong&gt;token&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  What a token is (and why you must guard it)
&lt;/h3&gt;

&lt;p&gt;Scroll to the &lt;strong&gt;Token&lt;/strong&gt; section and click &lt;strong&gt;Reset Token&lt;/strong&gt; (you may need to confirm). Discord will show you a long string of random-looking characters. &lt;strong&gt;Copy it now and keep it somewhere safe for a minute&lt;/strong&gt; — Discord only shows it once, and you'll need it shortly.&lt;/p&gt;

&lt;p&gt;Now, the most important sentence in this entire article:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Your token is your bot's password. Anyone who has it can fully control your bot.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's not &lt;em&gt;like&lt;/em&gt; a password — it genuinely &lt;em&gt;is&lt;/em&gt; the credential that proves "I am this bot" to Discord. If someone gets your token, they can make your bot do anything: spam servers, delete messages, get you banned. There is no "I forgot my password" recovery that protects you here.&lt;/p&gt;

&lt;p&gt;So three rules, starting right now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Never&lt;/strong&gt; paste your token into a chat, screenshot, forum post, or a question to a stranger.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Never&lt;/strong&gt; put your token directly in your code where it could get shared.&lt;/li&gt;
&lt;li&gt;If a token ever leaks, click &lt;strong&gt;Reset Token&lt;/strong&gt; immediately — that instantly invalidates the old one.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We'll handle rule #2 properly in a few minutes using something called a &lt;code&gt;.env&lt;/code&gt; file. Keep that copied token handy.&lt;/p&gt;

&lt;h3&gt;
  
  
  While you're here: intents
&lt;/h3&gt;

&lt;p&gt;Scroll down a little to &lt;strong&gt;Privileged Gateway Intents&lt;/strong&gt;. You'll see toggles for &lt;strong&gt;Presence&lt;/strong&gt;, &lt;strong&gt;Server Members&lt;/strong&gt;, and &lt;strong&gt;Message Content&lt;/strong&gt;, all switched off.&lt;/p&gt;

&lt;p&gt;Here's the why. When your bot connects, it would be wasteful (and a privacy problem) for Discord to fire off &lt;em&gt;every single event&lt;/em&gt; happening in &lt;em&gt;every&lt;/em&gt; server to &lt;em&gt;every&lt;/em&gt; bot. So Discord uses &lt;strong&gt;intents&lt;/strong&gt; — think of them as subscriptions. Your bot says "I'm interested in &lt;em&gt;these&lt;/em&gt; kinds of events," and Discord only sends those. It keeps bots efficient and respects users' data.&lt;/p&gt;

&lt;p&gt;Three intents are &lt;strong&gt;privileged&lt;/strong&gt;, meaning they're sensitive enough to be off by default:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Presence&lt;/strong&gt; — seeing when users go online, idle, or offline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Members&lt;/strong&gt; — getting notified when people join or leave, and reading the full member list.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Message Content&lt;/strong&gt; — actually reading the &lt;em&gt;text&lt;/em&gt; of normal messages.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last one matters a lot in 2026: Discord now strictly enforces it. If your bot tries to read message text &lt;em&gt;without&lt;/em&gt; enabling Message Content (both here in the portal &lt;strong&gt;and&lt;/strong&gt; in your code), Discord just hands back an empty string. It's a deliberate privacy guardrail.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Good news for us:&lt;/strong&gt; for this entire first bot, you don't need to enable &lt;em&gt;any&lt;/em&gt; of these. We're going to use &lt;strong&gt;slash commands&lt;/strong&gt;, and as you'll see, slash commands deliver their data straight to your bot — so we never need to read raw message text. That's the modern, privacy-friendly way to build, and it means you can leave all three toggles off. Just know what they are for when a future bot needs them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 3 — Invite your bot to your server
&lt;/h2&gt;

&lt;p&gt;Right now your bot exists but isn't &lt;em&gt;in&lt;/em&gt; any server. Let's fix that.&lt;/p&gt;

&lt;p&gt;In the left menu, go to &lt;strong&gt;OAuth2&lt;/strong&gt;, then find the &lt;strong&gt;OAuth2 URL Generator&lt;/strong&gt;. This tool builds a special invite link for your bot. (OAuth2 is just the standard system websites use to grant apps permission to act on your behalf — it's the same machinery behind "Log in with Google.")&lt;/p&gt;

&lt;p&gt;Under &lt;strong&gt;Scopes&lt;/strong&gt;, check two boxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;bot&lt;/code&gt;&lt;/strong&gt; — this says "I want to add this as a bot to a server."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;applications.commands&lt;/code&gt;&lt;/strong&gt; — this says "allow this bot to register slash commands." Without it, the &lt;code&gt;/&lt;/code&gt; commands we're about to build won't appear. Easy to forget, so don't.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When you check &lt;code&gt;bot&lt;/code&gt;, a &lt;strong&gt;Bot Permissions&lt;/strong&gt; panel appears below. For now, tick &lt;strong&gt;Send Messages&lt;/strong&gt;. (Permissions control what your bot is &lt;em&gt;allowed&lt;/em&gt; to do once it's in a server — separate from intents, which control what it gets to &lt;em&gt;see&lt;/em&gt;.)&lt;/p&gt;

&lt;p&gt;Now scroll to the bottom, &lt;strong&gt;copy the generated URL&lt;/strong&gt;, and paste it into your browser. Discord will ask which server to add the bot to — pick your test server, click &lt;strong&gt;Authorize&lt;/strong&gt;, and complete the little human check.&lt;/p&gt;

&lt;p&gt;Pop back into Discord and look at your test server's member list. Your bot is there! It'll show as &lt;strong&gt;offline&lt;/strong&gt; for now — that's expected. A bot only appears "online" when its &lt;em&gt;code is actually running&lt;/em&gt;. Which is exactly what we're about to make happen.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 4 — Set up your project
&lt;/h2&gt;

&lt;p&gt;Time to switch to the code side. Open VS Code, then open its built-in terminal with &lt;strong&gt;Terminal → New Terminal&lt;/strong&gt; from the top menu. This terminal is where we'll run commands; the file area is where we'll write code.&lt;/p&gt;

&lt;p&gt;First, make a folder for your bot and move into it. In the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;my-discord-bot
&lt;span class="nb"&gt;cd &lt;/span&gt;my-discord-bot
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mkdir&lt;/code&gt; &lt;em&gt;makes a directory&lt;/em&gt; (a folder), and &lt;code&gt;cd&lt;/code&gt; &lt;em&gt;changes directory&lt;/em&gt; into it. Everything we do now lives in this folder.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the project
&lt;/h3&gt;

&lt;p&gt;Run this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm init &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; is the &lt;strong&gt;Node Package Manager&lt;/strong&gt; — it comes free with Node.js and its job is to install and manage code libraries other people have written (so you don't reinvent the wheel). This command creates a file called &lt;code&gt;package.json&lt;/code&gt;, which is basically your project's ID card: it records your project's name, version, and — importantly — the list of libraries it depends on. The &lt;code&gt;-y&lt;/code&gt; just says "yes to all the default settings."&lt;/p&gt;

&lt;h3&gt;
  
  
  Install the libraries we need
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;discord.js dotenv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This downloads two libraries into a &lt;code&gt;node_modules&lt;/code&gt; folder and records them in your &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;discord.js&lt;/strong&gt; — the star of the show. It's a library that wraps Discord's complicated raw API in friendly JavaScript, so instead of manually crafting web requests, you write things like &lt;code&gt;client.login()&lt;/code&gt;. We're using &lt;strong&gt;version 14&lt;/strong&gt;, the current line.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dotenv&lt;/strong&gt; — a tiny helper that loads secrets (like your token) from a separate file into your program. This is how we'll keep that token &lt;em&gt;out&lt;/em&gt; of our code.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Protect your secrets
&lt;/h3&gt;

&lt;p&gt;Create two new files in your project folder (in VS Code, click the "new file" icon in the file panel).&lt;/p&gt;

&lt;p&gt;First, a file named exactly &lt;strong&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt; (yes, it starts with a dot). This is where your secrets live. Paste in the following, replacing the placeholder with the token you copied earlier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;paste-your-bot-token-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second — and this is the part people forget — create a file named &lt;strong&gt;&lt;code&gt;.gitignore&lt;/code&gt;&lt;/strong&gt; with this inside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node_modules/
.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's the why, and it ties straight back to our token rule. Later, you'll likely use &lt;strong&gt;Git&lt;/strong&gt; to back up your code or share it (for example on GitHub). A &lt;code&gt;.gitignore&lt;/code&gt; file tells Git "never include these." We're excluding &lt;code&gt;.env&lt;/code&gt; so your secret token never gets uploaded anywhere public, and &lt;code&gt;node_modules&lt;/code&gt; because it's huge and anyone can re-download it with &lt;code&gt;npm install&lt;/code&gt;. Setting this up &lt;em&gt;now&lt;/em&gt;, before you ever touch Git, means you can never accidentally leak your token. That's not paranoia — leaked bot tokens are one of the most common beginner mistakes, and bots get scanned for constantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 5 — Write the bot and bring it online
&lt;/h2&gt;

&lt;p&gt;Create one more file, named &lt;strong&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/strong&gt;. This is your bot's brain. Type (don't just paste — typing it helps it sink in) the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Load the secrets from our .env file into the program&lt;/span&gt;
&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Pull the tools we need out of the discord.js library&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GatewayIntentBits&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Create our bot ("client") and tell Discord which events we care about&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;intents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;GatewayIntentBits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Guilds&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// This runs ONCE, the moment the bot successfully logs in&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;once&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ClientReady&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;readyClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`✅ Logged in as &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;readyClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// Finally: log in using the token from our .env file&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's walk through &lt;em&gt;why&lt;/em&gt; each piece is there, top to bottom:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;require('dotenv').config()&lt;/code&gt;&lt;/strong&gt; runs first so that our secret token is loaded and ready before anything else needs it. &lt;code&gt;require(...)&lt;/code&gt; is simply how Node.js pulls in a library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;const { Client, Events, GatewayIntentBits } = require('discord.js')&lt;/code&gt;&lt;/strong&gt; grabs three specific tools out of discord.js: &lt;code&gt;Client&lt;/code&gt; (your bot), &lt;code&gt;Events&lt;/code&gt; (a tidy list of event names), and &lt;code&gt;GatewayIntentBits&lt;/code&gt; (those intent subscriptions from Step 2).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;new Client({ intents: [GatewayIntentBits.Guilds] })&lt;/code&gt;&lt;/strong&gt; creates your bot and subscribes it to basic server-related events. Notice we only ask for &lt;code&gt;Guilds&lt;/code&gt; ("guild" is Discord's internal word for "server") — no privileged intents needed, exactly as promised.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;client.once(Events.ClientReady, ...)&lt;/code&gt;&lt;/strong&gt; registers a reaction to the "I'm connected!" event. &lt;code&gt;once&lt;/code&gt; means it only fires a single time. (Small but useful detail: discord.js is in the middle of renaming this event from &lt;code&gt;ready&lt;/code&gt; to &lt;code&gt;clientReady&lt;/code&gt;. By using the &lt;code&gt;Events.ClientReady&lt;/code&gt; &lt;em&gt;enum&lt;/em&gt; instead of typing the name as text, you're shielded from that change — your code keeps working across versions. A nice example of why using provided constants beats hardcoding strings.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;client.login(process.env.DISCORD_TOKEN)&lt;/code&gt;&lt;/strong&gt; is the moment of truth — it connects to Discord using your token. &lt;code&gt;process.env.DISCORD_TOKEN&lt;/code&gt; is how we read the value &lt;code&gt;dotenv&lt;/code&gt; loaded from &lt;code&gt;.env&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Run it
&lt;/h3&gt;

&lt;p&gt;Back in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything's right, you'll see &lt;code&gt;✅ Logged in as YourBotName#0000&lt;/code&gt; in the terminal — and if you flip over to Discord, &lt;strong&gt;your bot is now showing as online.&lt;/strong&gt; That little green dot is your code, running, connected to Discord. Take a second to enjoy it. You built that.&lt;/p&gt;

&lt;p&gt;To stop the bot, click the terminal and press &lt;strong&gt;Ctrl + C&lt;/strong&gt;. (And yes — when you stop it, the bot goes offline. It's only alive while the program runs. Keeping it online 24/7 means keeping the program running somewhere, which is a later-part topic.)&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 6 — Your first slash command
&lt;/h2&gt;

&lt;p&gt;An online bot that does nothing is a bit anticlimactic. Let's make it respond to &lt;code&gt;/ping&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why slash commands?&lt;/strong&gt; A few years ago, bots worked by reading every message and checking if it started with something like &lt;code&gt;!ping&lt;/code&gt;. That required reading all message text (that privileged Message Content intent), and it was clunky. &lt;strong&gt;Slash commands&lt;/strong&gt; are Discord's modern, built-in system: users type &lt;code&gt;/&lt;/code&gt;, Discord shows them your bot's available commands, and when they pick one, Discord sends your bot a neat package of exactly what was requested. Cleaner for users, and no need to snoop on message content. Everybody wins.&lt;/p&gt;

&lt;p&gt;There are two stages: first you &lt;strong&gt;register&lt;/strong&gt; the command with Discord (tell it the command exists), then you &lt;strong&gt;handle&lt;/strong&gt; it in your bot (decide what to do when someone uses it).&lt;/p&gt;

&lt;h3&gt;
  
  
  Register the command
&lt;/h3&gt;

&lt;p&gt;Slash commands have to be registered with Discord before they show up. Create a new file called &lt;strong&gt;&lt;code&gt;deploy-commands.js&lt;/code&gt;&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;REST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;SlashCommandBuilder&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;discord.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Define our commands&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commands&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;SlashCommandBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setDescription&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Replies with Pong!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toJSON&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Set up a connection to Discord's API using our token&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;REST&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;setToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Send the commands to Discord&lt;/span&gt;
&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Registering slash commands...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;rest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;applicationGuildCommands&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GUILD_ID&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;commands&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Slash commands registered!&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This needs two new pieces of info — your application's ID and your server's ID — so add them to your &lt;strong&gt;&lt;code&gt;.env&lt;/code&gt;&lt;/strong&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DISCORD_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;paste-your-bot-token-here
&lt;span class="nv"&gt;CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;paste-your-application-id-here
&lt;span class="nv"&gt;GUILD_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;paste-your-server-id-here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where to find them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CLIENT_ID&lt;/strong&gt; — in the Developer Portal, open your app, go to &lt;strong&gt;General Information&lt;/strong&gt;, and copy the &lt;strong&gt;Application ID&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GUILD_ID&lt;/strong&gt; — in the Discord app, you first need &lt;strong&gt;Developer Mode&lt;/strong&gt; on: &lt;strong&gt;User Settings → Advanced → Developer Mode&lt;/strong&gt;. Then right-click your test server's icon and choose &lt;strong&gt;Copy Server ID&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now register the command by running the file once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node deploy-commands.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see &lt;code&gt;✅ Slash commands registered!&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;One worthwhile "why" here: we registered to a specific &lt;em&gt;guild&lt;/em&gt; (&lt;code&gt;applicationGuildCommands&lt;/code&gt;). Guild commands appear &lt;strong&gt;instantly&lt;/strong&gt;, which is perfect while testing. You &lt;em&gt;can&lt;/em&gt; register commands globally for all servers, but those can take up to an hour to roll out — so we'll stick with guild commands for now. You only need to re-run this script when you &lt;em&gt;add or change&lt;/em&gt; commands, not every time you start the bot.&lt;/p&gt;

&lt;h3&gt;
  
  
  Handle the command
&lt;/h3&gt;

&lt;p&gt;Now teach the running bot what to &lt;em&gt;do&lt;/em&gt; when &lt;code&gt;/ping&lt;/code&gt; is used. Back in &lt;strong&gt;&lt;code&gt;index.js&lt;/code&gt;&lt;/strong&gt;, add this new block just below your &lt;code&gt;ClientReady&lt;/code&gt; block (above the &lt;code&gt;client.login&lt;/code&gt; line):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This runs EVERY time someone uses a slash command&lt;/span&gt;
&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;InteractionCreate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Ignore anything that isn't a slash command&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isChatInputCommand&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;commandName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ping&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;interaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Pong! 🏓&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why it's written this way:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;client.on(Events.InteractionCreate, ...)&lt;/code&gt;&lt;/strong&gt; uses &lt;code&gt;on&lt;/code&gt; (not &lt;code&gt;once&lt;/code&gt;) because we want to react &lt;em&gt;every&lt;/em&gt; time, not just the first. An "interaction" is Discord's word for a user engaging with your bot — a slash command is one kind.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;if (!interaction.isChatInputCommand()) return&lt;/code&gt;&lt;/strong&gt; is a safety guard. Interactions include buttons, menus, and more; this line says "if it's not a typed slash command, stop here and ignore it."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;interaction.commandName === 'ping'&lt;/code&gt;&lt;/strong&gt; checks &lt;em&gt;which&lt;/em&gt; command was used, so you can branch to different behavior per command later.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;await interaction.reply('Pong! 🏓')&lt;/code&gt;&lt;/strong&gt; sends the response. The &lt;code&gt;await&lt;/code&gt; matters: talking to Discord happens over the internet and takes a tiny moment, so &lt;code&gt;await&lt;/code&gt; says "wait for that to finish before moving on." (That's also why the function is marked &lt;code&gt;async&lt;/code&gt; — the two always travel together.)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  See it work
&lt;/h3&gt;

&lt;p&gt;Start the bot again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hop into your test server, type &lt;code&gt;/&lt;/code&gt; in any channel, pick &lt;strong&gt;ping&lt;/strong&gt; from the menu Discord shows you, and hit Enter. Your bot replies &lt;strong&gt;Pong! 🏓&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's a complete, working, modern Discord bot. 🎉&lt;/p&gt;




&lt;h2&gt;
  
  
  When things go wrong (they will — that's normal)
&lt;/h2&gt;

&lt;p&gt;A few of the most common snags, and what they usually mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bot stays offline / "An invalid token was provided."&lt;/strong&gt; Your &lt;code&gt;DISCORD_TOKEN&lt;/code&gt; in &lt;code&gt;.env&lt;/code&gt; is wrong or has extra spaces/quotes. Re-copy it from the portal (Reset Token if needed). Tokens go in &lt;code&gt;.env&lt;/code&gt; with no quotes around them.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/ping&lt;/code&gt; doesn't appear in Discord.&lt;/strong&gt; Either you didn't run &lt;code&gt;node deploy-commands.js&lt;/code&gt;, or your &lt;code&gt;CLIENT_ID&lt;/code&gt; / &lt;code&gt;GUILD_ID&lt;/code&gt; are off, or you forgot the &lt;code&gt;applications.commands&lt;/code&gt; scope when inviting the bot. Re-invite with that scope checked if so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Cannot find module 'discord.js'&lt;/code&gt;.&lt;/strong&gt; You're not in the right folder, or you skipped &lt;code&gt;npm install&lt;/code&gt;. Make sure your terminal is inside &lt;code&gt;my-discord-bot&lt;/code&gt; and run &lt;code&gt;npm install discord.js dotenv&lt;/code&gt; again.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing happens when you run a command.&lt;/strong&gt; Make sure the bot is &lt;em&gt;actually running&lt;/em&gt; (&lt;code&gt;node index.js&lt;/code&gt; in a terminal you haven't closed) at the moment you use the command.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Reading error messages is a real skill, not a sign you're bad at this. Every developer Googles their errors constantly — that's the job, not a failure.&lt;/p&gt;




&lt;h2&gt;
  
  
  What you just learned
&lt;/h2&gt;

&lt;p&gt;Step back and notice how much you now understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A bot is just a &lt;strong&gt;program that connects to Discord, listens for events, and reacts&lt;/strong&gt; — not magic, not something hidden inside Discord.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;Developer Portal&lt;/strong&gt; is where your bot gets its identity, and its &lt;strong&gt;token is a password&lt;/strong&gt; you protect with a &lt;code&gt;.env&lt;/code&gt; file and &lt;code&gt;.gitignore&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Intents&lt;/strong&gt; are event subscriptions, and &lt;strong&gt;slash commands&lt;/strong&gt; let you build powerful bots without ever reading raw messages.&lt;/li&gt;
&lt;li&gt;You wired up &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;discord.js&lt;/code&gt;, and &lt;code&gt;dotenv&lt;/code&gt;, and got a real command responding in your server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That foundation carries the whole rest of the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Coming up in Part 2
&lt;/h2&gt;

&lt;p&gt;Right now everything lives in one file, and our bot knows exactly one command. In Part 2, we'll add more commands and reorganize the project so each command lives in its own tidy file — the structure real bots use, so things stay manageable as they grow.&lt;/p&gt;

&lt;p&gt;And then in &lt;strong&gt;Part 3&lt;/strong&gt;, we tackle the big one: &lt;strong&gt;memory&lt;/strong&gt;. Notice that if you stop and restart your bot, it remembers nothing — there's nowhere for it to store information. We'll introduce a &lt;strong&gt;database&lt;/strong&gt; to fix that, and use &lt;strong&gt;Prisma&lt;/strong&gt; to talk to it in clean, readable JavaScript instead of raw database commands. That's when your bot stops being a toy and starts being able to &lt;em&gt;remember&lt;/em&gt; things about your server.&lt;/p&gt;

&lt;p&gt;See you in Part 2. Now go change your bot's reply to something other than "Pong!" — seriously, go break it and fix it. That's how this sticks. 🚀&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Found this helpful? Part 2 builds directly on the project you just made, so keep this folder around.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>discord</category>
      <category>prisma</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
