How to Build a Discord AFK Bot: Auto-Reply When Users Are Away
Learn how to build a custom Discord AFK bot that auto-replies when users go offline or idle—step-by-step setup, code examples, and pro tips included.
August 27, 2026 · 2002 views
Discord AFK bots are a quiet superpower for community managers, moderators, and power users alike. Whether you’re running a gaming server, a study group, or a professional workspace, an AFK bot helps maintain clarity and reduce confusion when members step away—even for just five minutes. Unlike generic status indicators, a well-built AFK bot actively communicates absence with contextual auto-replies, logs activity, and respects user privacy. In this comprehensive guide, we’ll walk you through building your own lightweight, production-ready Discord AFK bot from scratch using Python and discord.py v2.3+. No prior bot dev experience? No problem—we’ll cover everything: setup, permissions, core logic, deployment, and even graceful error handling. 🤖✨
Why You Need an AFK Bot (and Why Pre-Made Bots Fall Short)
Many servers rely on third-party AFK bots like MEE6 or Dyno—but these often lack customization, throttle replies, or overcomplicate simple use cases. A custom AFK bot gives you full control over:
- ✅ When and how the AFK status is triggered (e.g., after 5 mins of inactivity, not just idle status)
- ✅ What message appears in chat (with user mention, timestamp, and optional reason)
- ✅ Where replies happen (only in allowed channels, DMs, or thread contexts)
- ✅ How long the AFK status persists before auto-clearing
- ✅ Integration with your moderation tools or logging systems
Plus—building it yourself teaches foundational concepts like event listeners, database persistence (we’ll use SQLite), and rate-limit awareness. It’s also a great first project for anyone learning Discord bot development.
Prerequisites & Setup Checklist
Before writing a single line of code, ensure you have:
- A Discord Developer Account (discord.com/developers)
- A test server where you have
Manage RolesandSend Messagespermissions - Python 3.10+ installed locally (or in your cloud environment)
- Basic command-line familiarity (
pip,git,venv) - Optional but recommended: DiscordCraft for free boilerplate templates and best-practice snippets
Once ready, create a new application in the Discord Developer Portal → Bot tab → Add Bot. Copy the token—you’ll need it soon. ⚠️ Never commit this token to GitHub!
Step-by-Step: Building Your AFK Bot
Step 1: Initialize Project & Install Dependencies
Create a clean project folder and set up a virtual environment:
mkdir discord-afk-bot && cd discord-afk-bot
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install discord.py==2.3.2 sqlite3 python-dotenv
Create .env to store your bot token securely:
DISCORD_TOKEN=your_bot_token_here
Step 2: Core Bot Structure
Start with main.py. This handles connection, events, and command registration:
# main.py
import discord
from discord.ext import commands, tasks
import asyncio
import sqlite3
import os
from datetime import datetime, timedelta
from dotenv import load_dotenv
load_dotenv()
TOKEN = os.getenv('DISCORD_TOKEN')
intents = discord.Intents.default()
intents.message_content = True
intents.members = True
intents.presences = True
bot = commands.Bot(command_prefix='!', intents=intents)
# Initialize DB on startup
@bot.event
async def on_ready():
print(f'✅ {bot.user} is online and ready!')
init_db()
afk_cleanup.start() # Start background cleanup task
def init_db():
conn = sqlite3.connect('afk.db')
c = conn.cursor()
c.execute('''
CREATE TABLE IF NOT EXISTS afk_users (
user_id INTEGER PRIMARY KEY,
reason TEXT DEFAULT 'AFK',
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
channel_id INTEGER
)
''')
conn.commit()
conn.close()
# Background task: clear stale AFK entries older than 24h
@tasks.loop(hours=1)
async def afk_cleanup():
conn = sqlite3.connect('afk.db')
c = conn.cursor()
cutoff = (datetime.utcnow() - timedelta(hours=24)).isoformat()
c.execute("DELETE FROM afk_users WHERE timestamp < ?", (cutoff,))
conn.commit()
conn.close()
bot.run(TOKEN)
Step 3: Add AFK Toggle Commands
Now let’s implement !afk and !back commands. Add this to main.py before bot.run():
@bot.command(name='afk')
async def set_afk(ctx, *, reason="AFK"):
"""Set yourself as AFK with optional reason"""
user_id = ctx.author.id
conn = sqlite3.connect('afk.db')
c = conn.cursor()
# Check if already AFK
c.execute("SELECT * FROM afk_users WHERE user_id = ?", (user_id,))
if c.fetchone():
await ctx.send("❌ You're already marked as AFK.")
conn.close()
return
# Insert AFK record
c.execute(
"INSERT OR REPLACE INTO afk_users (user_id, reason, channel_id) VALUES (?, ?, ?)",
(user_id, reason[:100], ctx.channel.id)
)
conn.commit()
conn.close()
await ctx.send(f"👋 {ctx.author.mention} is now AFK — *{reason}*.")
@bot.command(name='back')
async def clear_afk(ctx):
"""Clear your AFK status"""
user_id = ctx.author.id
conn = sqlite3.connect('afk.db')
c = conn.cursor()
c.execute("DELETE FROM afk_users WHERE user_id = ?", (user_id,))
rows = c.rowcount
conn.commit()
conn.close()
if rows > 0:
await ctx.send(f"✅ {ctx.author.mention} is back!")
else:
await ctx.send("ℹ️ You weren’t marked as AFK.")
Step 4: Auto-Reply Logic (The Magic Part!)
This is where your bot becomes truly useful. We’ll listen for messages mentioning AFK users—and reply only once per mention, with a clean, non-spammy response:
@bot.event
async def on_message(message):
if message.author.bot:
return
# Process commands first
await bot.process_commands(message)
# Skip if no mentions
if not message.mentions:
return
conn = sqlite3.connect('afk.db')
c = conn.cursor()
for mentioned in message.mentions:
c.execute("SELECT reason, timestamp FROM afk_users WHERE user_id = ?", (mentioned.id,))
row = c.fetchone()
if row:
reason, ts = row
# Format timestamp nicely
dt = datetime.fromisoformat(ts.replace('Z', '+00:00'))
ago = humanize_time(datetime.utcnow() - dt)
# Avoid duplicate replies in same channel within 5 mins
last_reply_key = f"{mentioned.id}_{message.channel.id}"
if hasattr(bot, 'last_afk_replies'):
if last_reply_key in bot.last_afk_replies:
if datetime.utcnow() - bot.last_afk_replies[last_reply_key] < timedelta(minutes=5):
continue
else:
bot.last_afk_replies = {}
bot.last_afk_replies[last_reply_key] = datetime.utcnow()
await message.reply(
f"🔖 {mentioned.mention} is currently AFK ({ago} ago) — *{reason}*."
)
conn.close()
# Helper for readable time deltas
def humanize_time(delta):
if delta.days >= 1:
return f"{delta.days}d"
hours = delta.seconds // 3600
if hours > 0:
return f"{hours}h"
minutes = (delta.seconds % 3600) // 60
return f"{minutes}m"
💡 Pro tip: This logic avoids spam by tracking recent replies per (user, channel) pair—and only triggers on mentions, not nickname matches or role pings.
Deploying Your AFK Bot Safely
For local testing, run python main.py. For 24/7 uptime, deploy to a service like:
- Railway.app (free tier, great for beginners)
- Render.com (free PostgreSQL + web services)
- Fly.io (great for SQLite + background tasks)
⚠️ Important: Never expose your .env file. Use platform-specific secret management (e.g., Railway’s Environment Variables section). Also, add restart: always and health checks in production.
[Image: dashboard showing Railway deployment settings for Discord bot]
Testing Your AFK Bot in Practice
- Invite your bot with minimal scope:
applications.commands+botscopes, and permissions:Send Messages,Read Message History,Use External Emojis - Run
!afk grabbing coffee ☕in any channel - Have a friend type
@YourName what's the deadline?→ expect auto-reply! - Confirm
!backclears status and stops replies - Verify old AFK entries vanish after 24h (thanks to
afk_cleanup)
Quick Tips
- 🛡️ Privacy-first design: Never log message content—only timestamps, user IDs, and channel IDs
- 📦 Scale smartly: Swap SQLite for PostgreSQL if your server exceeds 5K+ members
- 🌐 Multi-server support: The current DB schema works across servers—no changes needed
- 🧩 Extend easily: Add
!afk list(mod-only) or auto-AFK after X minutes of inactivity usingon_presence_update - 📊 Track adoption: Log how many times AFK was set/cleared per day—useful for engagement reports
FAQ
Q: Can this bot work in DMs?
A: Yes—but only if the user runs !afk in DMs and someone mentions them in a server where the bot is present. DM-triggered AFK statuses apply globally.
Q: Does it respect slow mode or channel permissions?
A: Absolutely. Replies only happen where the bot has Send Messages permission—and obey slow mode delays automatically via Discord’s API.
Q: How do I add emoji reactions to AFK replies?
A: Just append .add_reaction('💤') after await message.reply(...)—but test rate limits first! 💤
Final Thoughts
Building your own Discord AFK bot isn’t just about convenience—it’s about intentionality. You decide what absence means in your community: respectful silence, gentle reminders, or playful nudges. With under 150 lines of clean, documented Python, you’ve created something more reliable and transparent than most off-the-shelf alternatives. And once you’ve mastered this pattern, you’ll find it’s the perfect foundation for other utility bots—like welcome message customizers, reaction-role managers, or even lightweight ticketing systems.
Ready to go further? Check out DiscordCraft for open-source AFK bot extensions—including MongoDB sync, slash-command UIs, and audit logging modules. Happy coding! 🛠️🚀