<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Eva J Patel - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Eva J Patel - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 14 Aug 2026 21:57:42 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/evapatel123/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Basic Discord Storytelling, Chat, and Mental Wellness Bot with Python ]]>
                </title>
                <description>
                    <![CDATA[ Discord bots can look surprisingly complicated when you see them in action. A bot can respond to messages, tell stories, remember parts of conversations, and stay online around the clock. When I first ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-basic-discord-bot-with-python/</link>
                <guid isPermaLink="false">6a7f44fc58366ecdaf016624</guid>
                
                    <category>
                        <![CDATA[ software development ]]>
                    </category>
                
                    <category>
                        <![CDATA[ bot ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python 3 ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Beginner Developers ]]>
                    </category>
                
                    <category>
                        <![CDATA[ techblog ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Eva J Patel ]]>
                </dc:creator>
                <pubDate>Fri, 14 Aug 2026 16:40:28 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/uploads/covers/5e1e335a7a1d3fcc59028c64/6a444c51-d332-4915-aa1f-326b57b17472.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Discord bots can look surprisingly complicated when you see them in action. A bot can respond to messages, tell stories, remember parts of conversations, and stay online around the clock.</p>
<p>When I first started looking into how they worked, I assumed there had to be a huge amount of complicated code behind all of it.</p>
<p>But the basic idea is actually pretty simple.</p>
<p>At its core, a Discord bot is just a Python program that connects to Discord, waits for something to happen, and then decides how to respond. Once you understand that basic idea, you can start adding features one at a time and turn a simple bot into something much more interesting.</p>
<p>In this tutorial, we'll start with a very small bot and gradually build it into something more capable. Along the way, you'll learn about Discord commands, events, asynchronous Python, user state, environment variables, and basic deployment.</p>
<p>One quick disclaimer before we start: the mental-wellness feature that we'll be integrating in this bot in this project is <strong>not therapy</strong>, and the bot is not a therapist or medical professional. It should only provide general supportive suggestions and encourage users to reach out to a trusted person when appropriate.</p>
<p>With that out of the way, let's get coding!</p>
<h3 id="heading-what-well-cover">What We'll Cover:</h3>
<ul>
<li><p><a href="#heading-what-were-building">What We're Building</a></p>
</li>
<li><p><a href="#heading-what-you-need">What You Need</a></p>
</li>
<li><p><a href="#heading-create-the-discord-bot">Create the Discord Bot</a></p>
</li>
<li><p><a href="#heading-give-the-bot-permission-to-read-messages">Give the Bot Permission to Read Messages</a></p>
</li>
<li><p><a href="#heading-create-the-project">Create the Project</a></p>
</li>
<li><p><a href="#heading-create-a-virtual-environment">Create a Virtual Environment</a></p>
</li>
<li><p><a href="#heading-install-discordpy">Install discord.py</a></p>
</li>
<li><p><a href="#heading-create-your-first-bot">Create Your First Bot</a></p>
<ul>
<li><p><a href="#heading-importing-our-libraries">Importing Our Libraries</a></p>
</li>
<li><p><a href="#heading-loading-the-token">Loading the Token</a></p>
</li>
<li><p><a href="#heading-understanding-intents">Understanding Intents</a></p>
</li>
<li><p><a href="#heading-what-is-ctx">What Isctx?</a></p>
</li>
<li><p><a href="#heading-why-does-everything-say-async-and-await">Why Does Everything Sayasyncandawait?</a></p>
</li>
<li><p><a href="#heading-run-the-bot">Run the Bot</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-build-the-storytelling-system">Build the Storytelling System</a></p>
<ul>
<li><p><a href="#heading-lets-make-the-story-remember-the-user">Let's Make the Story Remember the User</a></p>
</li>
<li><p><a href="#heading-add-a-story-choice">Add a Story Choice</a></p>
</li>
<li><p><a href="#heading-add-a-casual-chat-command">Add a Casual Chat Command</a></p>
</li>
<li><p><a href="#heading-add-a-mental-wellness-support-feature">Add a Mental-Wellness Support Feature</a></p>
</li>
<li><p><a href="#heading-add-a-help-command">Add a Help Command</a></p>
</li>
<li><p><a href="#heading-improve-error-handling">Improve Error Handling</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-put-everything-together">Put Everything Together</a></p>
</li>
<li><p><a href="#heading-our-bot-doesnt-actually-remember-anything">Our Bot Doesn't Actually Remember Anything</a></p>
<ul>
<li><p><a href="#heading-create-the-database">Create the Database</a></p>
</li>
<li><p><a href="#heading-save-a-users-story">Save a User's Story</a></p>
</li>
<li><p><a href="#heading-get-the-story-back">Get the Story Back</a></p>
</li>
<li><p><a href="#heading-put-it-into-a-command">Put It Into a Command</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-adding-real-ai-chat">Adding Real AI Chat</a></p>
<ul>
<li><p><a href="#heading-install-the-hugging-face-library">Install the Hugging Face Library</a></p>
</li>
<li><p><a href="#heading-create-the-hugging-face-client">Create the Hugging Face Client</a></p>
</li>
<li><p><a href="#heading-connect-the-ai-model-to-the-bot">Connect the AI Model to the Bot</a></p>
</li>
<li><p><a href="#heading-handle-ai-errors">Handle AI Errors</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-how-do-we-keep-the-bot-online">How Do We Keep the Bot Online?</a></p>
<ul>
<li><p><a href="#heading-option-1-run-it-on-your-computer">Option 1: Run It on Your Computer</a></p>
</li>
<li><p><a href="#heading-option-2-host-it-on-a-server">Option 2: Host It on a Server</a></p>
</li>
</ul>
</li>
<li><p><a href="#heading-what-forever-actually-means">What "Forever" Actually Means</a></p>
</li>
<li><p><a href="#heading-dont-try-to-keep-it-awake-with-random-tricks">Don't Try to "Keep It Awake" With Random Tricks</a></p>
</li>
<li><p><a href="#heading-additional-features-and-where-to-go-next">Additional Features and Where to Go Next</a></p>
</li>
<li><p><a href="#heading-test-everything-locally-first">Test Everything Locally First</a></p>
</li>
<li><p><a href="#heading-deploying-the-bot">Deploying the Bot</a></p>
<ul>
<li><a href="#heading-the-start-command">The Start Command</a></li>
</ul>
</li>
<li><p><a href="#heading-remember-keep-your-secrets-secret">Remember: Keep Your Secrets Secret</a></p>
</li>
<li><p><a href="#heading-what-you-learned">What You Learned</a></p>
</li>
<li><p><a href="#heading-final-thoughts">Final Thoughts</a></p>
</li>
</ul>
<h2 id="heading-what-were-building">What We're Building</h2>
<p>Our finished bot will have several commands:</p>
<pre><code class="language-text">!hello
!story
!chat hello!
!support I'm having a stressful day
!help
</code></pre>
<p>For example:</p>
<pre><code class="language-text">User:
!story

Bot:
You wake up inside an abandoned library.

There are three doors in front of you:

1. A red wooden door
2. A metal door covered in strange symbols
3. A staircase leading underground

Which one do you choose?
</code></pre>
<p>The user can then continue the story.</p>
<p>For chat:</p>
<pre><code class="language-text">User:
!chat What's a good way to learn Python?

Bot:
Try building small projects instead of only reading tutorials.
A Discord bot is actually a pretty fun project to start with.
</code></pre>
<p>And for mental-wellness support:</p>
<pre><code class="language-text">User:
!support I'm really stressed about school.

Bot:
That sounds like a lot to deal with. You could try breaking
the work into one small task at a time and taking a short
break between tasks.

I'm a bot, not a therapist, so if you need personal support,
consider talking with someone you trust.
</code></pre>
<p>The goal isn't to make a magical robot therapist. It's to build a useful bot while learning how Discord APIs, Python functions, events, asynchronous programming, and basic conversational logic fit together.</p>
<h2 id="heading-what-you-need">What You Need</h2>
<p>You only need a few things:</p>
<ul>
<li><p>Python (version 3.8+ is recommended)</p>
</li>
<li><p>A Discord account</p>
</li>
<li><p>A Discord server where you have permission to add a bot</p>
</li>
<li><p>A code editor (I personally prefer VS Code or PyCharm)</p>
</li>
<li><p>The <code>discord.py</code> library</p>
</li>
</ul>
<p>We'll also use Python's built-in <code>os</code> module for reading environment variables.</p>
<p>If you don't already have Python installed, install a current supported version of Python from the official Python website.</p>
<p>Then check that Python works:</p>
<pre><code class="language-bash">python --version
</code></pre>
<p>You should see something similar to:</p>
<pre><code class="language-text">Python 3.x.x
</code></pre>
<h2 id="heading-create-the-discord-bot">Create the Discord Bot</h2>
<p>Before Python can control Discord, we need to create a Discord application.</p>
<p>Go to the Discord Developer Portal: <a href="https://discord.com/developers/applications">https://discord.com/developers/applications</a></p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/64a5aac8-fb53-42d0-9b91-fb6453b3eb1d.png" alt="Picture of the discord developer application page" style="display:block;margin:0 auto" width="2398" height="1396" loading="lazy">

<p>This is what the page will look like, you might need to login with your discord email/username and password before you start.</p>
<p>Click on the "New Application" button on the top right and give your bot a name. For this tutorial, let's call ours <code>StoryBot</code>.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/84bd2f32-941c-4de2-8e6b-30ab22cc5ba1.png" alt="Picture of what it looks like when you click on the &quot;New Application&quot; button" style="display:block;margin:0 auto" width="2397" height="1303" loading="lazy">

<p>The application is basically the home for your bot.</p>
<p>Discord's developer platform provides the tools needed to create and configure applications and bots.</p>
<p>Once you've created the application, open its <strong>Bot</strong> section and create the bot user. You can add your own icon picture and your own banner if you want to.</p>
<p>You will then go to the <strong>Token</strong> section and click on "Reset Token" to generate your token. Treat that token like a password. Do <strong>NOT</strong> put it directly into your Python source code.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/7537d64e-706c-43b9-a574-746294703f6d.png" alt="Picture of what the Token part in the Bots section looks like" style="display:block;margin:0 auto" width="1894" height="189" loading="lazy">

<p>Never do this:</p>
<pre><code class="language-python">bot.run("my-secret-token")
</code></pre>
<p>And definitely don't upload a token to GitHub or commit it to source control. Instead, we'll store it in an environment variable, which we'll talk about later.</p>
<h2 id="heading-give-the-bot-permission-to-read-messages">Give the Bot Permission to Read Messages</h2>
<p>Our bot needs to see the messages that contain commands.</p>
<p>Discord uses something called <strong>Gateway Intents</strong> to control which types of events a bot receives. The <code>discord.py</code> documentation explains that intents must be enabled both in your code and, for privileged intents, in the Discord Developer Portal.</p>
<p>In the Developer Portal, find:</p>
<pre><code class="language-text">Bot
→ Privileged Gateway Intents
</code></pre>
<p>Enable:</p>
<pre><code class="language-text">Message Content Intent
</code></pre>
<p>It should look somewhat like this:</p>
<img src="https://cdn.hashnode.com/uploads/covers/6a581501af6af179dc1987d5/0273691f-8723-41d3-8cfc-d59656dfb6e2.png" alt="What should the &quot;Message Content Intent&quot; section look like" style="display:block;margin:0 auto" width="1933" height="501" loading="lazy">

<p>We'll also enable it in Python, which we will talk about later in this article.</p>
<h2 id="heading-create-the-project">Create the Project</h2>
<p>Create a folder:</p>
<pre><code class="language-text">discord-story-bot/
</code></pre>
<p>Inside it, we'll eventually have:</p>
<pre><code class="language-text">discord-story-bot/
│
├── bot.py
├── requirements.txt
└── .env
</code></pre>
<p>The three important files are:</p>
<ul>
<li><p><code>bot.py</code>: our Python program</p>
</li>
<li><p><code>requirements.txt</code>: text file that contains the name of the packages our bot needs</p>
</li>
<li><p><code>.env</code>: our secret token during local development</p>
</li>
</ul>
<h2 id="heading-create-a-virtual-environment">Create a Virtual Environment</h2>
<p>Open your terminal inside the project folder.</p>
<p>Run:</p>
<pre><code class="language-bash">python -m venv venv
</code></pre>
<p>Then activate it.</p>
<p>On Windows:</p>
<pre><code class="language-bash">venv\Scripts\activate
</code></pre>
<p>On macOS/Linux:</p>
<pre><code class="language-bash">source venv/bin/activate
</code></pre>
<p>A virtual environment gives this project its own little Python bubble.</p>
<p>That means packages installed for this bot won't randomly interfere with packages used by another project.</p>
<h2 id="heading-install-discordpy">Install discord.py</h2>
<p>Now install the Discord library:</p>
<pre><code class="language-bash">pip install -U discord.py
</code></pre>
<p>The official <code>discord.py</code> documentation uses this installation approach for setting up the library.</p>
<p>We'll also install <code>python-dotenv</code>, which makes reading our local <code>.env</code> file easier:</p>
<pre><code class="language-bash">pip install python-dotenv
</code></pre>
<p>Then save the dependencies:</p>
<pre><code class="language-bash">pip freeze &gt; requirements.txt
</code></pre>
<p>Your <code>requirements.txt</code> should contain the packages needed by the project.</p>
<h2 id="heading-create-your-first-bot">Create Your First Bot</h2>
<p>Let's start small.</p>
<p>Open <code>bot.py</code>:</p>
<pre><code class="language-python">import os

import discord
from discord.ext import commands
from dotenv import load_dotenv


load_dotenv()

TOKEN = os.getenv("DISCORD_TOKEN")

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix="!",
    intents=intents
)


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


@bot.command()
async def hello(ctx):
    await ctx.send("Hello! I'm online.")


bot.run(TOKEN)
</code></pre>
<p>That is already a functional Discord bot.</p>
<p>Let's break it apart piece by piece.</p>
<h3 id="heading-importing-our-libraries">Importing Our Libraries</h3>
<p>First:</p>
<pre><code class="language-python">import os
</code></pre>
<p><code>os</code> lets Python communicate with parts of the operating system.</p>
<p>We'll use it to read environment variables.</p>
<p>Next:</p>
<pre><code class="language-python">import discord
</code></pre>
<p>This imports <code>discord.py</code>.</p>
<p>Then:</p>
<pre><code class="language-python">from discord.ext import commands
</code></pre>
<p>The <code>commands</code> extension makes creating commands much easier.</p>
<p>Instead of manually checking every message for something like <code>!hello</code>, we can write:</p>
<pre><code class="language-python">@bot.command()
async def hello(ctx):
    await ctx.send("Hello!")
</code></pre>
<p>The <code>discord.py</code> command system is built around Python functions decorated as commands.</p>
<p>Finally:</p>
<pre><code class="language-python">from dotenv import load_dotenv
</code></pre>
<p>This lets us load values from our <code>.env</code> file.</p>
<h3 id="heading-loading-the-token">Loading the Token</h3>
<p>Our bot needs a token to connect our Python program to Discord. Think of the token as a password that allows our program to authenticate as the bot.</p>
<p>We don't want to put this secret directly into our Python code. Instead, we'll store it in an environment variable.</p>
<p>First, install <code>python-dotenv</code>:</p>
<pre><code class="language-bash">pip install python-dotenv
</code></pre>
<p>This package lets Python read values from a <code>.env</code> file.</p>
<p>Now create a new file called <code>.env</code> in the same folder as <code>bot.py</code>.</p>
<p>Inside <code>.env</code>, add:</p>
<pre><code class="language-text">DISCORD_TOKEN=YOUR_BOT_TOKEN_HERE
</code></pre>
<p>Replace <code>YOUR_BOT_TOKEN_HERE</code> with the token you copied from the Discord Developer Portal.</p>
<p>Your file should look something like this:</p>
<pre><code class="language-text">DISCORD_TOKEN=your_actual_token_here
</code></pre>
<p>Don't share this token with anyone or upload your <code>.env</code> file to GitHub. Your bot token should be treated like a password.</p>
<p>To make sure Git doesn't accidentally include the <code>.env</code> file in a repository, create a file called <code>.gitignore</code> in your project folder and add:</p>
<pre><code class="language-text">.env
venv/
__pycache__/
</code></pre>
<p>Now let's load the token in Python.</p>
<p>At the top of <code>bot.py</code>, add:</p>
<pre><code class="language-python">import os
from dotenv import load_dotenv
</code></pre>
<p>Then add:</p>
<pre><code class="language-python">load_dotenv()
</code></pre>
<p>This tells Python to look for the <code>.env</code> file and load the variables inside it.</p>
<p>Now we can get our Discord token:</p>
<pre><code class="language-python">TOKEN = os.getenv("DISCORD_TOKEN")
</code></pre>
<p><code>os.getenv()</code> looks for the environment variable named <code>"DISCORD_TOKEN"</code> and gives us its value.</p>
<p>We can also check that the token was actually found:</p>
<pre><code class="language-python">if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")
</code></pre>
<p>If Python can't find the token, the program stops and gives us a clear error message instead of failing later in a confusing way.</p>
<h3 id="heading-understanding-intents">Understanding Intents</h3>
<p>Remember how we talked about enabling message content readability in python? We are going to do that now.</p>
<p>Add:</p>
<pre><code class="language-python">intents = discord.Intents.default()
intents.message_content = True
</code></pre>
<p>The first line creates a set of Discord's default intents.</p>
<p>The second line tells Discord that our bot needs access to message content.</p>
<p>Now we need to give these intents to our bot when we create it:</p>
<pre><code class="language-python">bot = commands.Bot(
    command_prefix="!",
    intents=intents
)
</code></pre>
<p>The <code>command_prefix="!"</code> means our bot will recognize commands that begin with <code>!</code>.</p>
<p>For example:</p>
<pre><code class="language-text">!hello
</code></pre>
<p>The <code>intents=intents</code> part gives our bot the permissions we configured above.</p>
<p>There are two steps here because Discord needs to know that our bot is allowed to receive message content, while our Python program also needs to tell Discord that it wants to receive it.</p>
<p>Our basic setup should now look like this:</p>
<pre><code class="language-python">import os
import discord

from dotenv import load_dotenv
from discord.ext import commands

load_dotenv()

TOKEN = os.getenv("DISCORD_TOKEN")

if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")

intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix="!",
    intents=intents
)
</code></pre>
<p>Now our bot has its token safely loaded and <code>discord.py</code> knows which intents to request when it connects to Discord.</p>
<h3 id="heading-what-is-ctx">What Is <code>ctx</code>?</h3>
<p>This part can look weird when you're learning Discord bots:</p>
<pre><code class="language-python">async def hello(ctx):
</code></pre>
<p>What is <code>ctx</code>? <code>ctx</code> stands for <strong>context</strong>. It contains information about the command that was used.</p>
<p>For example, it can tell us:</p>
<ul>
<li><p>Who ran the command</p>
</li>
<li><p>Which server it came from</p>
</li>
<li><p>Which channel it came from</p>
</li>
<li><p>What message triggered it</p>
</li>
</ul>
<p>Then:</p>
<pre><code class="language-python">await ctx.send("Hello!")
</code></pre>
<p>means:</p>
<blockquote>
<p>"Send this message back to the place where the command was used."</p>
</blockquote>
<h3 id="heading-why-does-everything-say-async-and-await">Why Does Everything Say <code>async</code> and <code>await</code>?</h3>
<p>You might notice:</p>
<pre><code class="language-python">async def hello(ctx):
</code></pre>
<p>and:</p>
<pre><code class="language-python">await ctx.send(...)
</code></pre>
<p>Discord bots spend a lot of time waiting.</p>
<p>They wait for:</p>
<ul>
<li><p>Messages</p>
</li>
<li><p>Discord responses</p>
</li>
<li><p>API requests</p>
</li>
<li><p>Timers</p>
</li>
<li><p>Other events</p>
</li>
</ul>
<p>Python's asynchronous programming features allow the bot to wait for these operations without freezing everything else.</p>
<p>You don't need to become an async-programming expert before building your first bot.</p>
<p>For now, think of <code>await</code> as:</p>
<blockquote>
<p>"Pause this task until this operation finishes, while letting the bot handle other things."</p>
</blockquote>
<h3 id="heading-run-the-bot">Run the Bot</h3>
<p>Start it with:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>If everything works, your terminal should print something similar to:</p>
<pre><code class="language-text">Logged in as StoryBot
</code></pre>
<p>Now go to your Discord server and type <code>!hello</code>. Your bot should respond.</p>
<p>Congratulations! You've officially made a Discord bot.</p>
<p>Now let's make it interesting.</p>
<h2 id="heading-build-the-storytelling-system">Build the Storytelling System</h2>
<p>First, we're going to create an interactive storytelling command.</p>
<p>At the top of <code>bot.py</code>, add:</p>
<pre><code class="language-python">import random
</code></pre>
<p>Then create some story ingredients:</p>
<pre><code class="language-python">story_locations = [
    "an abandoned library",
    "a mysterious island",
    "a futuristic city",
    "a hidden underground laboratory",
    "a forest that never appears on maps"
]

story_items = [
    "a glowing key",
    "an ancient notebook",
    "a strange compass",
    "a locked metal box",
    "a mysterious photograph"
]

story_events = [
    "You hear footsteps behind you.",
    "The lights suddenly turn off.",
    "A hidden door opens nearby.",
    "Your phone starts displaying a message from an unknown sender.",
    "You notice that the room has changed."
]
</code></pre>
<p>Now create the command:</p>
<pre><code class="language-python">@bot.command()
async def story(ctx):
    location = random.choice(story_locations)
    item = random.choice(story_items)
    event = random.choice(story_events)

    story_text = (
        f"You wake up in {location}.\n\n"
        f"Next to you is {item}.\n\n"
        f"{event}\n\n"
        "What do you do?"
    )

    await ctx.send(story_text)
</code></pre>
<p>Now <code>!story</code> might produce:</p>
<pre><code class="language-text">You wake up in a futuristic city.

Next to you is an ancient notebook.

A hidden door opens nearby.

What do you do?
</code></pre>
<p>Run it again and you might get something completely different.</p>
<p>That's because of:</p>
<pre><code class="language-python">random.choice(...)
</code></pre>
<p>Python randomly picks one item from each list.</p>
<p>It's a simple technique, but suddenly your bot can generate hundreds of different combinations.</p>
<h3 id="heading-lets-make-the-story-remember-the-user">Let's Make the Story Remember the User</h3>
<p>Random stories are fun, but interactive stories are much better when the bot remembers what happened.</p>
<p>We can create a dictionary:</p>
<pre><code class="language-python">user_stories = {}
</code></pre>
<p>The dictionary will store story information for each user.</p>
<p>For example:</p>
<pre><code class="language-text">user ID → current story
</code></pre>
<p>Now let's modify the story command:</p>
<pre><code class="language-python">@bot.command()
async def story(ctx):
    user_id = ctx.author.id

    location = random.choice(story_locations)
    item = random.choice(story_items)
    event = random.choice(story_events)

    user_stories[user_id] = {
        "location": location,
        "item": item,
        "event": event
    }

    await ctx.send(
        f"You wake up in {location}.\n\n"
        f"Next to you is {item}.\n\n"
        f"{event}\n\n"
        "What do you do?"
    )
</code></pre>
<p>Now each user can have their own active story.</p>
<h3 id="heading-add-a-story-choice">Add a Story Choice</h3>
<p>Let's give users choices.</p>
<pre><code class="language-python">@bot.command()
async def choose(ctx, choice: str):
    user_id = ctx.author.id

    if user_id not in user_stories:
        await ctx.send("You don't have an active story. Try `!story` first.")
        return

    choice = choice.lower()

    if choice == "left":
        response = (
            "You head left and discover a room filled with old maps. "
            "One of them has your name written on it."
        )

    elif choice == "right":
        response = (
            "You head right and find a staircase leading toward "
            "a strange blue light."
        )

    else:
        response = "Try choosing `left` or `right`."

    await ctx.send(response)
</code></pre>
<p>Now users can type:</p>
<pre><code class="language-text">!choose left
</code></pre>
<p>or:</p>
<pre><code class="language-text">!choose right
</code></pre>
<p>Notice this:</p>
<pre><code class="language-python">async def choose(ctx, choice: str):
</code></pre>
<p>The <code>choice</code> parameter receives the text after the command.</p>
<p>So:</p>
<pre><code class="language-text">!choose left
</code></pre>
<p>becomes approximately:</p>
<pre><code class="language-python">choice = "left"
</code></pre>
<p>This is one of the reasons command frameworks are so convenient. A <strong>command framework</strong> is a set of tools that makes it easier to create and manage commands in a program. In our case, <code>discord.py</code> provides the command framework that lets us turn Python functions into Discord commands using decorators like <code>@bot.command()</code>.</p>
<p>Instead of manually checking every message to figure out whether someone typed <code>!choose</code>, <code>discord.py</code> handles that work for us. It recognizes the command, takes the user's arguments, and passes them to our function.</p>
<p>So when someone types:</p>
<pre><code class="language-text">!choose left
</code></pre>
<p><code>discord.py</code> knows that choose is the command, <code>"left"</code> is the argument, and that it should call our <code>choose()</code> function with that information.</p>
<h3 id="heading-add-a-casual-chat-command">Add a Casual Chat Command</h3>
<p>Now let's make the bot capable of basic conversation.</p>
<p>We could connect it to a large language model API, but you don't actually need AI to learn how a chat command works. We'll start with a simple keyword-based response system.</p>
<p>First, we'll create a dictionary containing some keywords and possible responses:</p>
<pre><code class="language-python">chat_responses = {
    "hello": [
        "Hey! What's up?",
        "Hello! How's your day going?",
        "Hi! What are you working on?"
    ],
    "python": [
        "Python is a great language for beginners because its syntax is pretty readable.",
        "If you're learning Python, try building something instead of only watching tutorials."
    ],
    "discord": [
        "Discord bots are a fun way to practice Python because you get instant feedback.",
        "Once you understand commands and events, you can build some surprisingly complex bots."
    ]
}

Think of `chat_responses` as a small collection of things our bot knows how to talk about. Each key, such as `"python"` or `"discord"`, represents a keyword the bot can look for. The value associated with each key is a list of possible responses.

We use a list instead of a single response so the bot doesn't give exactly the same answer every time. Later, we'll randomly choose one of these responses.

Now let's create the actual `!chat` command:

```python
@bot.command()
async def chat(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in chat_responses.items():
        if keyword in text:
            await ctx.send(random.choice(responses))
            return

    await ctx.send(
        "I'm still learning how to respond to that. "
        "Try talking to me about Python or Discord!"
    )
</code></pre>
<p>There are a few things happening here, so let's break it down.</p>
<p>First, this part:</p>
<pre><code class="language-python">@bot.command()
async def chat(ctx, *, message: str):
</code></pre>
<p>turns the <code>chat()</code> function into a Discord command. The <code>*</code> is important because it tells <code>discord.py</code> to treat everything after the command as one argument.</p>
<p>For example, if someone types:</p>
<pre><code class="language-text">!chat I want to learn Python
</code></pre>
<p>the entire phrase after <code>!chat</code> becomes the value of <code>message</code>:</p>
<pre><code class="language-python">message = "I want to learn Python"
</code></pre>
<p>Next, we have:</p>
<pre><code class="language-python">text = message.lower()
</code></pre>
<p>This converts the message to lowercase. That means <code>Python</code>, <code>python</code>, and <code>PYTHON</code> will all become <code>python</code>. Without this, our keyword check could miss a match simply because the user capitalized a word differently.</p>
<p>Now we get to the loop:</p>
<pre><code class="language-python">for keyword, responses in chat_responses.items():
</code></pre>
<p><code>.items()</code> lets us go through both the keyword and its corresponding list of responses. During each loop, <code>keyword</code> contains something like <code>"python"</code>, while <code>responses</code> contains the list of responses associated with it.</p>
<p>Then we check:</p>
<pre><code class="language-python">if keyword in text:
</code></pre>
<p>This asks whether the current keyword appears anywhere in the user's message.</p>
<p>If the user writes:</p>
<pre><code class="language-text">!chat I want to learn Python
</code></pre>
<p>the lowercase version becomes:</p>
<pre><code class="language-text">i want to learn python
</code></pre>
<p>Since <code>"python"</code> appears inside that text, the condition is true.</p>
<p>The bot can then choose a random response:</p>
<pre><code class="language-python">await ctx.send(random.choice(responses))
</code></pre>
<p><code>random.choice()</code> picks one item from the response list, while <code>ctx.send()</code> sends that response back to the Discord channel.</p>
<p>Finally, we have:</p>
<pre><code class="language-python">return
</code></pre>
<p>This stops the function after a matching keyword is found. Without it, the loop would continue checking the other keywords even after the bot had already responded.</p>
<p>But what happens if none of the keywords match?</p>
<p>That's what this part handles:</p>
<pre><code class="language-python">await ctx.send(
    "I'm still learning how to respond to that. "
    "Try talking to me about Python or Discord!"
)
</code></pre>
<p>If the loop finishes without finding a keyword, the bot sends this fallback message instead.</p>
<p>For example:</p>
<pre><code class="language-text">!chat I like pizza
</code></pre>
<p>doesn't contain <code>"hello"</code>, <code>"python"</code>, or <code>"discord"</code>, so the bot doesn't have a specific response to use.</p>
<p>This gives us a simple way for the bot to have conversations without needing an AI model.</p>
<h3 id="heading-add-a-mental-wellness-support-feature">Add a Mental-Wellness Support Feature</h3>
<p>Now for the feature that needs a little more care.</p>
<p>Instead of calling this a "therapy command" internally, we'll call it:</p>
<pre><code class="language-text">!support
</code></pre>
<p>Quick additional disclaimer before we start...this is just a fun wellness script, not a real therapist!</p>
<p>Create:</p>
<pre><code class="language-python">support_responses = {
    "stress": [
        "That sounds like a lot to handle. Try breaking the situation into one small task at a time.",
        "When everything feels overwhelming, it can help to pause and focus on what needs attention right now."
    ],

    "school": [
        "School can pile up quickly. Consider choosing one assignment to work on first instead of trying to solve everything at once.",
        "If school stress is getting difficult to manage, talking with a trusted person can make things feel less like something you have to handle alone."
    ],

    "sad": [
        "I'm sorry you're having a difficult moment. Taking a short break, doing something calming, or talking with someone you trust may help.",
        "You don't have to solve everything immediately. Give yourself some time and consider reaching out to someone you trust."
    ]
}
</code></pre>
<p>Now create the command:</p>
<pre><code class="language-python">@bot.command()
async def support(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in support_responses.items():
        if keyword in text:
            response = random.choice(responses)

            await ctx.send(
                f"{response}\n\n"
                "I'm a bot, not a therapist or medical professional. "
                "If you need personal support, consider talking with "
                "someone you trust."
            )
            return

    await ctx.send(
        "It sounds like something is bothering you. "
        "I can offer general wellness suggestions, but I'm not a therapist. "
        "If you need personal support, consider reaching out to someone you trust."
    )
</code></pre>
<p>Now someone can type:</p>
<pre><code class="language-text">!support I'm stressed about school
</code></pre>
<p>The bot sees the word:</p>
<pre><code class="language-text">school
</code></pre>
<p>and chooses one of the school-related responses.</p>
<p>This is deliberately simple.</p>
<p>For a real public bot, you'd want much more careful safety handling, testing, moderation, privacy protection, and escalation logic before allowing users to rely on it for sensitive situations.</p>
<h3 id="heading-add-a-help-command">Add a Help Command</h3>
<p>A good bot should explain itself.</p>
<pre><code class="language-python">@bot.command()
async def commands_help(ctx):
    await ctx.send(
        "**Available commands:**\n"
        "`!hello` - Say hello\n"
        "`!story` - Start a new story\n"
        "`!choose left` - Choose the left path\n"
        "`!choose right` - Choose the right path\n"
        "`!chat &lt;message&gt;` - Have a casual conversation\n"
        "`!support &lt;message&gt;` - Get general wellness support"
    )
</code></pre>
<p>There's one small issue.</p>
<p>Discord's default help command is already called <code>help</code>.</p>
<p>So instead of:</p>
<pre><code class="language-python">async def help(ctx):
</code></pre>
<p>we've named ours:</p>
<pre><code class="language-python">commands_help
</code></pre>
<p>If you want the command itself to be called <code>!help</code>, you can write:</p>
<pre><code class="language-python">@bot.command(name="help")
async def commands_help(ctx):
    ...
</code></pre>
<p>That tells Discord:</p>
<blockquote>
<p>Use <code>!help</code> for this function even though the Python function has another name.</p>
</blockquote>
<h3 id="heading-improve-error-handling">Improve Error Handling</h3>
<p>Bots shouldn't crash just because someone enters an invalid command.</p>
<p>Add:</p>
<pre><code class="language-python">@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.MissingRequiredArgument):
        await ctx.send(
            "You're missing something. Try `!help` to see how the command works."
        )

    elif isinstance(error, commands.CommandNotFound):
        return

    else:
        print(f"Error: {error}")
</code></pre>
<p>Now if someone types:</p>
<pre><code class="language-text">!chat
</code></pre>
<p>without giving the bot a message, it can respond with a useful explanation instead of dumping a confusing error into the conversation.</p>
<h2 id="heading-put-everything-together">Put Everything Together</h2>
<p>At this point, your <code>bot.py</code> can look like this:</p>
<pre><code class="language-python">import os
import random

import discord
from discord.ext import commands
from dotenv import load_dotenv


load_dotenv()

TOKEN = os.getenv("DISCORD_TOKEN")

if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")


intents = discord.Intents.default()
intents.message_content = True

bot = commands.Bot(
    command_prefix="!",
    intents=intents
)


story_locations = [
    "an abandoned library",
    "a mysterious island",
    "a futuristic city",
    "a hidden underground laboratory",
    "a forest that never appears on maps"
]

story_items = [
    "a glowing key",
    "an ancient notebook",
    "a strange compass",
    "a locked metal box",
    "a mysterious photograph"
]

story_events = [
    "You hear footsteps behind you.",
    "The lights suddenly turn off.",
    "A hidden door opens nearby.",
    "Your phone starts displaying a message from an unknown sender.",
    "You notice that the room has changed."
]


user_stories = {}


chat_responses = {
    "hello": [
        "Hey! What's up?",
        "Hello! How's your day going?",
        "Hi! What are you working on?"
    ],

    "python": [
        "Python is a great language for beginners because its syntax is pretty readable.",
        "If you're learning Python, try building something instead of only watching tutorials."
    ],

    "discord": [
        "Discord bots are a fun way to practice Python because you get instant feedback.",
        "Once you understand commands and events, you can build some surprisingly complex bots."
    ]
}


support_responses = {
    "stress": [
        "That sounds like a lot to handle. Try breaking the situation into one small task at a time.",
        "When everything feels overwhelming, it can help to pause and focus on what needs attention right now."
    ],

    "school": [
        "School can pile up quickly. Consider choosing one assignment to work on first instead of trying to solve everything at once.",
        "If school stress is getting difficult to manage, talking with a trusted person can make things feel less like something you have to handle alone."
    ],

    "sad": [
        "I'm sorry you're having a difficult moment. Taking a short break, doing something calming, or talking with someone you trust may help.",
        "You don't have to solve everything immediately. Give yourself some time and consider reaching out to someone you trust."
    ]
}


@bot.event
async def on_ready():
    print(f"Logged in as {bot.user}")


@bot.command()
async def hello(ctx):
    await ctx.send("Hello! I'm online.")


@bot.command()
async def story(ctx):
    user_id = ctx.author.id

    location = random.choice(story_locations)
    item = random.choice(story_items)
    event = random.choice(story_events)

    user_stories[user_id] = {
        "location": location,
        "item": item,
        "event": event
    }

    await ctx.send(
        f"You wake up in {location}.\n\n"
        f"Next to you is {item}.\n\n"
        f"{event}\n\n"
        "What do you do?"
    )


@bot.command()
async def choose(ctx, choice: str):
    user_id = ctx.author.id

    if user_id not in user_stories:
        await ctx.send(
            "You don't have an active story. Try `!story` first."
        )
        return

    choice = choice.lower()

    if choice == "left":
        response = (
            "You head left and discover a room filled with old maps. "
            "One of them has your name written on it."
        )

    elif choice == "right":
        response = (
            "You head right and find a staircase leading toward "
            "a strange blue light."
        )

    else:
        response = "Try choosing `left` or `right`."

    await ctx.send(response)


@bot.command()
async def chat(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in chat_responses.items():
        if keyword in text:
            await ctx.send(random.choice(responses))
            return

    await ctx.send(
        "I'm still learning how to respond to that. "
        "Try talking to me about Python or Discord!"
    )


@bot.command()
async def support(ctx, *, message: str):
    text = message.lower()

    for keyword, responses in support_responses.items():
        if keyword in text:
            response = random.choice(responses)

            await ctx.send(
                f"{response}\n\n"
                "I'm a bot, not a therapist or medical professional. "
                "If you need personal support, consider talking with "
                "someone you trust."
            )
            return

    await ctx.send(
        "It sounds like something is bothering you. "
        "I can offer general wellness suggestions, but I'm not a therapist. "
        "If you need personal support, consider reaching out to someone you trust."
    )


@bot.command(name="help")
async def commands_help(ctx):
    await ctx.send(
        "**Available commands:**\n"
        "`!hello` - Say hello\n"
        "`!story` - Start a new story\n"
        "`!choose left` - Choose the left path\n"
        "`!choose right` - Choose the right path\n"
        "`!chat &lt;message&gt;` - Have a casual conversation\n"
        "`!support &lt;message&gt;` - Get general wellness support"
    )


@bot.event
async def on_command_error(ctx, error):
    if isinstance(error, commands.MissingRequiredArgument):
        await ctx.send(
            "You're missing something. Try `!help` to see how the command works."
        )

    elif isinstance(error, commands.CommandNotFound):
        return

    else:
        print(f"Error: {error}")


bot.run(TOKEN)
</code></pre>
<p>This is enough to create a surprisingly capable beginner Discord project.</p>
<p>But there's an important limitation.</p>
<h2 id="heading-our-bot-doesnt-actually-remember-anything">Our Bot Doesn't Actually Remember Anything</h2>
<p>There's one small problem with our bot so far: it doesn't actually remember anything after it shuts down.</p>
<p>Right now, we're storing our story information in a Python dictionary:</p>
<pre><code class="language-python">user_stories = {}
</code></pre>
<p>This works while the bot is running. But if you stop the program and start it again, the dictionary starts empty.</p>
<p>To fix this, we need somewhere to permanently store our data. That's where a <strong>database</strong> comes in.</p>
<p>For this project, we'll use <strong>SQLite</strong>. SQLite is a lightweight database that stores information in a file on your computer. Python already includes SQLite through the built-in <code>sqlite3</code> module, so we don't need to install anything extra.</p>
<h3 id="heading-create-the-database">Create the Database</h3>
<p>First, add this import near the top of <code>bot.py</code>:</p>
<pre><code class="language-python">import sqlite3
</code></pre>
<p>Then create a connection to a database file:</p>
<pre><code class="language-python">db = sqlite3.connect("bot.db")
cursor = db.cursor()
</code></pre>
<p>The first line creates a database file called <code>bot.db</code> if one doesn't already exist. If the file already exists, SQLite simply opens it.</p>
<p>The second line creates a <strong>cursor</strong>. You can think of the cursor as the part of our Python program that lets us send instructions to the database.</p>
<p>Now we need to create a table where we can store our users' story information:</p>
<pre><code class="language-python">cursor.execute("""
    CREATE TABLE IF NOT EXISTS user_stories (
        user_id INTEGER PRIMARY KEY,
        location TEXT,
        item TEXT,
        event TEXT
    )
""")

db.commit()
</code></pre>
<p>Let's break this down.</p>
<p><code>cursor.execute()</code> tells SQLite to run the SQL command inside the parentheses.</p>
<p>The SQL command starts with:</p>
<pre><code class="language-sql">CREATE TABLE IF NOT EXISTS user_stories
</code></pre>
<p>This tells SQLite to create a table called <code>user_stories</code>, but only if that table doesn't already exist.</p>
<p>Inside the parentheses, we define the information that each row can contain:</p>
<pre><code class="language-sql">user_id INTEGER PRIMARY KEY,
location TEXT,
item TEXT,
event TEXT
</code></pre>
<p><code>user_id</code> stores the Discord user's ID. We use it as the <code>PRIMARY KEY</code>, which means each user gets their own unique row.</p>
<p><code>location</code>, <code>item</code>, and <code>event</code> are all pieces of information about the user's current story.</p>
<p>Finally:</p>
<pre><code class="language-python">db.commit()
</code></pre>
<p>saves the changes to the database.</p>
<p>At this point, your project folder should contain a new file called:</p>
<pre><code class="language-text">bot.db
</code></pre>
<p>You don't need to open or edit this file manually. SQLite will manage it for us.</p>
<h3 id="heading-save-a-users-story">Save a User's Story</h3>
<p>Now let's actually put information into our database.</p>
<p>Suppose we have these variables:</p>
<pre><code class="language-python">user_id = ctx.author.id
location = "an abandoned castle"
item = "a mysterious key"
event = "a locked door"
</code></pre>
<p>We can save them using:</p>
<pre><code class="language-python">cursor.execute(
    """
    INSERT OR REPLACE INTO user_stories
    (user_id, location, item, event)
    VALUES (?, ?, ?, ?)
    """,
    (user_id, location, item, event)
)

db.commit()
</code></pre>
<p>The SQL statement tells SQLite to insert the information into the <code>user_stories</code> table.</p>
<p>The <code>?</code> symbols are placeholders for the actual values. The values are provided separately here:</p>
<pre><code class="language-python">(user_id, location, item, event)
</code></pre>
<p>This is safer than manually inserting values directly into the SQL string.</p>
<p><code>INSERT OR REPLACE</code> also means that if this user already has a saved story, their old story information can be replaced with the new information.</p>
<h3 id="heading-get-the-story-back">Get the Story Back</h3>
<p>Saving information is only half of the job. We also need to be able to retrieve it.</p>
<p>We can search the database for a user's story like this:</p>
<pre><code class="language-python">cursor.execute(
    """
    SELECT location, item, event
    FROM user_stories
    WHERE user_id = ?
    """,
    (user_id,)
)

story = cursor.fetchone()
</code></pre>
<p>This time, we're using <code>SELECT</code> to ask SQLite for information.</p>
<p>The <code>WHERE</code> part is important:</p>
<pre><code class="language-sql">WHERE user_id = ?
</code></pre>
<p>It tells SQLite to find the row belonging to this specific Discord user.</p>
<p>Then:</p>
<pre><code class="language-python">story = cursor.fetchone()
</code></pre>
<p>gets the first matching result.</p>
<p>If the user has a saved story, <code>story</code> will contain their information. If they don't, <code>story</code> will be <code>None</code>.</p>
<p>We can check for that:</p>
<pre><code class="language-python">if story:
    location, item, event = story

    await ctx.send(
        f"You're currently in {location}. "
        f"You have {item}, and you're facing {event}."
    )
else:
    await ctx.send("I don't have a saved story for you yet!")
</code></pre>
<p>Now the bot can retrieve information that was saved earlier, even after the Python program has been restarted.</p>
<h3 id="heading-put-it-into-a-command">Put It Into a Command</h3>
<p>We can turn this into a simple command that lets users check their saved story:</p>
<pre><code class="language-python">@bot.command()
async def status(ctx):
    user_id = ctx.author.id

    cursor.execute(
        """
        SELECT location, item, event
        FROM user_stories
        WHERE user_id = ?
        """,
        (user_id,)
    )

    story = cursor.fetchone()

    if story:
        location, item, event = story

        await ctx.send(
            f"You're currently in {location}. "
            f"You have {item}, and you're facing {event}."
        )
    else:
        await ctx.send(
            "You don't have a saved story yet. "
            "Start one with `!story`!"
        )
</code></pre>
<p>Now a user can type:</p>
<pre><code class="language-text">!status
</code></pre>
<p>and the bot can look up their story from the database.</p>
<p>This is a big improvement over our original dictionary. A dictionary only remembers information while the Python program is running. SQLite lets us save that information so it can still be there when the bot starts again.</p>
<p>For a larger bot, you could eventually store things like user preferences, story progress, inventory, conversation history, or other data. But for now, this simple database is enough to give our bot some real memory.</p>
<h2 id="heading-adding-real-ai-chat">Adding Real AI Chat</h2>
<p>Before we connect our bot to an AI model, let's quickly talk about <strong>Hugging Face</strong>.</p>
<p>If you've never used it before, Hugging Face is a platform where developers can find, share, and use machine learning models and datasets. Think of it as a huge community and library for AI tools.</p>
<p>Hugging Face also provides tools that let Python programs communicate with these models without having to build and train an AI model from scratch.</p>
<p>For our bot, we'll use Hugging Face's <strong>Inference Providers</strong> to send a user's message to a supported language model and receive its response.</p>
<p>We won't be training an AI model ourselves. Instead, we'll use an existing model and connect it to our Discord bot through Python.</p>
<p>Now that we know what Hugging Face is, let's connect it to our bot.</p>
<h3 id="heading-install-the-hugging-face-library">Install the Hugging Face Library</h3>
<p>First, install <code>huggingface_hub</code>:</p>
<pre><code class="language-bash">pip install -U huggingface_hub
</code></pre>
<p>We already installed <code>python-dotenv</code>, so we can use the same <code>.env</code> file from earlier to keep our Hugging Face token out of the source code.</p>
<p>Add your Hugging Face token to <code>.env</code>:</p>
<pre><code class="language-text">DISCORD_TOKEN=YOUR_BOT_TOKEN_HERE
HF_TOKEN=YOUR_HUGGING_FACE_TOKEN_HERE
</code></pre>
<p>Replace <code>YOUR_HUGGING_FACE_TOKEN_HERE</code> with your actual Hugging Face access token.</p>
<p>Just like your Discord bot token, <strong>don't share this token or upload it to GitHub</strong>.</p>
<h3 id="heading-create-the-hugging-face-client">Create the Hugging Face Client</h3>
<p>Now add this import near the top of <code>bot.py</code>:</p>
<pre><code class="language-python">from huggingface_hub import InferenceClient
</code></pre>
<p>Then load the token:</p>
<pre><code class="language-python">HF_TOKEN = os.getenv("HF_TOKEN")

if not HF_TOKEN:
    raise RuntimeError("HF_TOKEN is not set.")
</code></pre>
<p>The first line gets the token from our environment variables. The <code>if</code> statement checks whether the token actually exists. If it doesn't, Python stops and gives us a useful error instead of letting the program fail later in a confusing way.</p>
<p>Now create the Hugging Face client:</p>
<pre><code class="language-python">client = InferenceClient(
    api_key=HF_TOKEN
)
</code></pre>
<p>The <code>InferenceClient</code> is what our Python program will use to communicate with Hugging Face's inference service.</p>
<h3 id="heading-connect-the-ai-model-to-the-bot">Connect the AI Model to the Bot</h3>
<p>Now we can replace our previous keyword-based <code>!chat</code> command with one that sends the user's message to a language model.</p>
<pre><code class="language-python">@bot.command()
async def chat(ctx, *, message: str):
    try:
        response = client.chat_completion(
            model="YOUR_SUPPORTED_MODEL_ID",
            messages=[
                {
                    "role": "system",
                    "content": (
                        "You are a friendly Discord bot. "
                        "Keep responses helpful, concise, and conversational."
                    )
                },
                {
                    "role": "user",
                    "content": message
                }
            ],
            max_tokens=200
        )

        answer = response.choices[0].message.content

        await ctx.send(answer)

    except Exception as error:
        print(f"AI error: {error}")
        await ctx.send(
            "I couldn't generate a response right now. "
            "Please try again later."
        )
</code></pre>
<p>There's quite a bit happening here, so let's walk through it.</p>
<p>We start with the same command structure we've already used:</p>
<pre><code class="language-python">@bot.command()
async def chat(ctx, *, message: str):
</code></pre>
<p>This creates our <code>!chat</code> command and stores everything the user types after it in <code>message</code>.</p>
<p>For example:</p>
<pre><code class="language-text">!chat What is Python?
</code></pre>
<p>gives us:</p>
<pre><code class="language-python">message = "What is Python?"
</code></pre>
<p>Next, we use:</p>
<pre><code class="language-python">try:
</code></pre>
<p>This tells Python that we're about to run code that could potentially fail. Since we're communicating with an external service, things like an unavailable model, an invalid token, or a temporary connection problem can happen.</p>
<p>Now we call:</p>
<pre><code class="language-python">response = client.chat_completion(
</code></pre>
<p>This sends a chat-completion request to the model through Hugging Face. The <code>messages</code> parameter contains the conversation we want the model to respond to.</p>
<p>The first message has the role <code>"system"</code>:</p>
<pre><code class="language-python">{
    "role": "system",
    "content": (
        "You are a friendly Discord bot. "
        "Keep responses helpful, concise, and conversational."
    )
}
</code></pre>
<p>The system message gives the model instructions about how it should respond.</p>
<p>Then we provide the user's actual message:</p>
<pre><code class="language-python">{
    "role": "user",
    "content": message
}
</code></pre>
<p>If the user typed:</p>
<pre><code class="language-text">!chat What is Python?
</code></pre>
<p>then <code>message</code> contains:</p>
<pre><code class="language-text">What is Python?
</code></pre>
<p>So the model receives that as the user's input.</p>
<p>We also have:</p>
<pre><code class="language-python">max_tokens=200
</code></pre>
<p>This limits how much text the model can generate for one response. Keeping responses relatively short works well for Discord because huge blocks of text aren't always very pleasant to read in a chat channel.</p>
<p>You also need to replace:</p>
<pre><code class="language-python">model="YOUR_SUPPORTED_MODEL_ID"
</code></pre>
<p>with the ID of a model currently available through the Hugging Face Inference Providers you are using. Hugging Face's documentation shows that <code>InferenceClient</code> can use a model ID hosted on the Hugging Face Hub for chat completion.</p>
<p>Once the request is complete, we need to get the actual text from the response:</p>
<pre><code class="language-python">answer = response.choices[0].message.content
</code></pre>
<p>The response contains information about the model's output. <code>choices[0]</code> gets the first generated response, and <code>.message.content</code> gives us the actual text.</p>
<p>Then we send it to Discord:</p>
<pre><code class="language-python">await ctx.send(answer)
</code></pre>
<p>So the whole process looks like this:</p>
<pre><code class="language-text">User types !chat
        ↓
Discord sends the command to our bot
        ↓
Python gets the user's message
        ↓
Hugging Face receives the message
        ↓
The AI model generates a response
        ↓
Python gets the generated text
        ↓
The bot sends it back to Discord
</code></pre>
<h3 id="heading-handle-ai-errors">Handle AI Errors</h3>
<p>The last part of our command is:</p>
<pre><code class="language-python">except Exception as error:
    print(f"AI error: {error}")
    await ctx.send(
        "I couldn't generate a response right now. "
        "Please try again later."
    )
</code></pre>
<p>If something goes wrong inside the <code>try</code> block, Python jumps to the <code>except</code> block instead of crashing the entire bot.</p>
<p>The error is printed in the terminal so you can investigate what happened:</p>
<pre><code class="language-python">print(f"AI error: {error}")
</code></pre>
<p>Meanwhile, the Discord user gets a simple message:</p>
<pre><code class="language-text">I couldn't generate a response right now. Please try again later.
</code></pre>
<p>This is much better than letting an API error take down the whole bot.</p>
<p>At this point, you have a real AI-powered <code>!chat</code> command. You can type something like:</p>
<pre><code class="language-text">!chat Tell me an interesting fact about space.
</code></pre>
<p>and the model can generate a response instead of choosing from a small list of pre-written messages.</p>
<p>One thing to remember is that this bot is sending user messages to an external AI service. Don't automatically send private or sensitive conversations to an AI provider. If you make this bot available to other people, be clear about what information it processes and avoid storing or sending more data than the bot actually needs.</p>
<p>You can also combine this AI system with the SQLite database from earlier. For example, you could save a limited amount of conversation history and send relevant previous messages along with a new message. That would allow the bot to keep some context between messages instead of treating every message as a completely new conversation.</p>
<h2 id="heading-how-do-we-keep-the-bot-online">How Do We Keep the Bot Online?</h2>
<p>Here's where the phrase "online forever" needs a little clarification.</p>
<p>There are two different situations.</p>
<h3 id="heading-option-1-run-it-on-your-computer">Option 1: Run It on Your Computer</h3>
<p>When you run:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>the bot stays online while that program is running.</p>
<p>Close the terminal?</p>
<p>Bot goes offline.</p>
<p>Turn off the computer?</p>
<p>Bot goes offline.</p>
<p>Lose internet?</p>
<p>Bot goes offline.</p>
<p>This is perfect for development but it's not a 24/7 production setup.</p>
<h3 id="heading-option-2-host-it-on-a-server">Option 2: Host It on a Server</h3>
<p>For a bot that should stay online while your computer is off, you need a computer somewhere that stays available.</p>
<p>That computer can be a cloud server.</p>
<p>You upload your project, install the dependencies, add your environment variables, and start:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>Now the cloud machine runs the program instead of your laptop.</p>
<p>Services designed for continuously running workloads can be used for this kind of application. For example, Render currently provides a <strong>Background Worker</strong> service type for continuously running processes that don't need to receive incoming web traffic.</p>
<p>But you should check the provider's current pricing and service limitations before deploying. Free hosting tiers aren't necessarily designed for an always-on Discord bot, and a "free forever" 24/7 setup isn't something you should assume a hosting platform will provide.</p>
<h2 id="heading-what-forever-actually-means">What "Forever" Actually Means</h2>
<p>There isn't really a magical:</p>
<pre><code class="language-text">ONLINE_FOREVER = True
</code></pre>
<p>setting.</p>
<p>A bot can stay online continuously only as long as the computer or server running it continues operating.</p>
<p>Even a professionally hosted bot can go offline because of:</p>
<ul>
<li><p>Server maintenance</p>
</li>
<li><p>Deployments</p>
</li>
<li><p>Bugs</p>
</li>
<li><p>Network problems</p>
</li>
<li><p>Provider outages</p>
</li>
<li><p>Invalid credentials</p>
</li>
<li><p>API changes</p>
</li>
<li><p>Billing or account issues</p>
</li>
</ul>
<p>So the realistic goal is to keep the bot running automatically and restart it when something goes wrong.</p>
<p>That is what production hosting is designed to help with.</p>
<p>If your provider supports automatic restarts, enable them.</p>
<p>You can also make your Python code fail clearly when an important environment variable is missing:</p>
<pre><code class="language-python">if not TOKEN:
    raise RuntimeError("DISCORD_TOKEN is not set.")
</code></pre>
<p>A clear error is much easier to debug than a mysterious bot that simply doesn't appear online.</p>
<h2 id="heading-dont-try-to-keep-it-awake-with-random-tricks">Don't Try to "Keep It Awake" With Random Tricks</h2>
<p>You may find tutorials suggesting that you deploy a web server and repeatedly ping it from another service to prevent a free hosting instance from sleeping.</p>
<p>Be careful with that approach.</p>
<p>Hosting providers change their free-tier rules, and attempting to work around those limits can violate their terms.</p>
<p>If you need an actually persistent bot, use a hosting option that explicitly supports the workload.</p>
<p>For example, a background worker is designed for continuously running processes. That's much cleaner than trying to convince a web service that your Discord bot is secretly a website.</p>
<h2 id="heading-additional-features-and-where-to-go-next"><strong>Additional Featur</strong>es and Where to Go Next</h2>
<p>Now that you have a working Discord bot, there are plenty of directions you can take the project next.</p>
<p>You could turn the storytelling system into a more complete game by adding an inventory, multiple chapters, puzzles, or different endings. You could also replace text-based commands with Discord slash commands and buttons to make the bot easier to interact with.</p>
<p>If you're interested in AI, you could expand the chat system by giving the bot different personalities, adding carefully limited conversation context, or using AI to generate parts of the stories.</p>
<p>You could also add moderation features, daily story prompts, or other commands that fit the kind of Discord community you're building.</p>
<p>These are ideas for extending the project rather than features we'll build step by step in this tutorial. The important thing is that you now have the foundation to experiment with them yourself.</p>
<p>Start with one small feature, figure out how it works, and build from there. You don't need to turn the bot into a massive project all at once.</p>
<p>The more you experiment with the code, the more you'll start seeing how Python, Discord, databases, and AI can work together in a real application.</p>
<h2 id="heading-test-everything-locally-first">Test Everything Locally First</h2>
<p>Before deploying, test:</p>
<pre><code class="language-text">!hello
!story
!choose left
!choose right
!chat hello
!chat I want to learn Python
!support I'm stressed
!help
</code></pre>
<p>Then test weird inputs:</p>
<pre><code class="language-text">!choose banana
!chat
!support
!unknowncommand
</code></pre>
<p>You want to discover bugs while you're sitting in front of your computer, not three days later when someone tells you:</p>
<blockquote>
<p>"Your bot has been broken since Tuesday."</p>
</blockquote>
<h2 id="heading-deploying-the-bot">Deploying the Bot</h2>
<p>First, make sure your project contains:</p>
<pre><code class="language-text">discord-story-bot/
│
├── bot.py
├── requirements.txt
├── .gitignore
└── .python-version
</code></pre>
<p>A <code>.python-version</code> file can contain something like:</p>
<pre><code class="language-text">3.13
</code></pre>
<p>Using a version file makes your deployment environment more predictable. Render currently supports specifying a Python version through <code>.python-version</code> or an environment variable.</p>
<p>Your <code>requirements.txt</code> should contain your dependencies.</p>
<p>For example:</p>
<pre><code class="language-text">discord.py
python-dotenv
</code></pre>
<p>For deployment, you generally don't need the local <code>.env</code> file.</p>
<p>Instead, add:</p>
<pre><code class="language-text">DISCORD_TOKEN
</code></pre>
<p>as an environment variable in your hosting provider's dashboard.</p>
<p>That way the secret isn't stored inside your repository.</p>
<h3 id="heading-the-start-command">The Start Command</h3>
<p>Your deployment service needs to know what to run.</p>
<p>For this project, the start command is:</p>
<pre><code class="language-bash">python bot.py
</code></pre>
<p>The important thing is that the process doesn't immediately exit.</p>
<p>A Discord bot stays alive because <code>bot.run(TOKEN)</code> starts the Discord connection and keeps the program running.</p>
<p>If your hosting service supports background workers, that's a natural fit for a bot like this because the bot doesn't need to serve normal HTTP requests. Render specifically describes background workers as continuously running services that don't receive incoming network traffic.</p>
<h2 id="heading-remember-keep-your-secrets-secret">Remember: Keep Your Secrets Secret</h2>
<p>This is worth repeating because it causes a lot of beginner projects to get compromised.</p>
<p>Never commit this:</p>
<pre><code class="language-python">bot.run("YOUR_REAL_TOKEN")
</code></pre>
<p>Never upload:</p>
<pre><code class="language-text">.env
</code></pre>
<p>Never paste your actual token into a public GitHub issue.</p>
<p>If a token accidentally becomes public, treat it as compromised and regenerate it.</p>
<p>Environment variables are your friend.</p>
<h2 id="heading-what-you-learned">What You Learned</h2>
<p>You've now built a Discord bot that demonstrates several real programming concepts.</p>
<p>You learned how to:</p>
<ul>
<li><p>Create a Discord application</p>
</li>
<li><p>Connect Python to Discord</p>
</li>
<li><p>Use <code>discord.py</code></p>
</li>
<li><p>Configure Gateway Intents</p>
</li>
<li><p>Create commands</p>
</li>
<li><p>Use asynchronous functions</p>
</li>
<li><p>Read command arguments</p>
</li>
<li><p>Generate random stories</p>
</li>
<li><p>Store temporary user state</p>
</li>
<li><p>Create a basic chat system</p>
</li>
<li><p>Create a mental-wellness support feature</p>
</li>
<li><p>Handle command errors</p>
</li>
<li><p>Keep secrets out of source code</p>
</li>
<li><p>Prepare a project for deployment</p>
</li>
<li><p>Think about persistent hosting</p>
</li>
</ul>
<p>And underneath all those features, the architecture is still surprisingly simple:</p>
<pre><code class="language-text">User sends command
        ↓
Discord receives message
        ↓
discord.py receives event
        ↓
Python function runs
        ↓
Bot generates response
        ↓
Discord displays response
</code></pre>
<p>You don't need thousands of lines of code to get started.</p>
<p>You need a clear idea, a few Python concepts, and the willingness to keep debugging when something inevitably breaks.</p>
<h2 id="heading-final-thoughts">Final Thoughts</h2>
<p>The coolest part of this project isn't really the Discord bot. It's what the project teaches you.</p>
<p>And once you understand the pieces, you can reuse the same ideas in countless projects.</p>
<p>A Discord bot can become a game, which could become a web application, which could also become a larger software project.</p>
<p>And suddenly you're not just learning Python syntax anymore. You're learning how software actually gets built, one command at a time.</p>
<p>Happy coding!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
