DEV Community

Phil IPS
Phil IPS

Posted on

Building a Discord Bot with AI Responses: Complete Guide

Building a Discord Bot with AI Responses: Complete Guide

Create a Discord bot that uses free AI APIs to respond intelligently to messages.

Introduction

Discord bots with AI capabilities are popular for communities, support servers, and entertainment. This tutorial shows how to build one using free AI APIs — no paid tokens required.

Prerequisites

  • Node.js 18+
  • Discord Developer account
  • Free API key from OpenRouter, Groq, or Together.ai
  • discord.js library: npm install discord.js

Step 1: Discord Bot Setup

// bot.js
const { Client, GatewayIntentBits, Events } = require('discord.js');

const client = new Client({
    intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.MessageContent
    ]
});

client.once(Events.ClientReady, () => {
    console.log(`Logged in as ${client.user.tag}`);
});

client.login(process.env.DISCORD_TOKEN);
Enter fullscreen mode Exit fullscreen mode

Step 2: AI Integration (OpenRouter)

// ai.js
async function getAIResponse(prompt, provider = 'openrouter') {
    const configs = {
        openrouter: {
            url: 'https://openrouter.ai/api/v1/chat/completions',
            key: process.env.OPENROUTER_API_KEY,
            model: 'google/gemini-2.0-flash-exp:free'
        },
        groq: {
            url: 'https://api.groq.com/openai/v1/chat/completions',
            key: process.env.GROQ_API_KEY,
            model: 'llama-3.1-8b-instant'
        }
    };

    const config = configs[provider];
    if (!config.key) throw new Error(`No API key for ${provider}`);

    const response = await fetch(config.url, {
        method: 'POST',
        headers: {
            'Authorization': `Bearer ${config.key}`,
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            model: config.model,
            messages: [
                { role: 'system', content: 'You are a helpful Discord bot. Keep responses under 2000 characters.' },
                { role: 'user', content: prompt }
            ],
            temperature: 0.7,
            max_tokens: 500
        })
    });

    if (!response.ok) {
        const err = await response.json();
        throw new Error(`${provider}: ${err.error?.message || response.status}`);
    }

    const data = await response.json();
    return data.choices[0].message.content;
}

module.exports = { getAIResponse };
Enter fullscreen mode Exit fullscreen mode

Step 3: Message Handler with Fallback

// handler.js
const { getAIResponse } = require('./ai.js');

const PROVIDERS = ['groq', 'openrouter']; // Groq first for speed

async function handleMessage(message) {
    // Ignore bots and non-mentions
    if (message.author.bot) return;
    if (!message.mentions.has(client.user)) return;

    const prompt = message.content.replace(`<@${client.user.id}>`, '').trim();
    if (!prompt) return;

    await message.channel.sendTyping();

    // Try providers in order until one works
    for (const provider of PROVIDERS) {
        try {
            const response = await getAIResponse(prompt, provider);
            await message.reply(response);
            return; // Success
        } catch (error) {
            console.warn(`${provider} failed:`, error.message);
        }
    }

    // All providers failed
    await message.reply('🤖 All AI services are busy. Try again in a moment!');
}

module.exports = { handleMessage };
Enter fullscreen mode Exit fullscreen mode

Step 4: Rate Limiting & Cooldowns

// cooldown.js
const cooldowns = new Map();
const COOLDOWN_MS = 5000; // 5 seconds per user

function checkCooldown(userId) {
    const now = Date.now();
    const lastUsed = cooldowns.get(userId) || 0;

    if (now - lastUsed < COOLDOWN_MS) {
        return COOLDOWN_MS - (now - lastUsed);
    }

    cooldowns.set(userId, now);
    return 0;
}

// In handler.js:
async function handleMessage(message) {
    // ... existing checks ...

    const cooldown = checkCooldown(message.author.id);
    if (cooldown > 0) {
        await message.reply(`⏳ Please wait ${Math.ceil(cooldown/1000)}s before asking again.`);
        return;
    }

    // ... rest of handler ...
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Complete Bot (bot.js)

require('dotenv').config();
const { Client, GatewayIntentBits, Events } = require('discord.js');
const { handleMessage } = require('./handler.js');

const client = new Client({
    intents: [
        GatewayIntentBits.Guilds,
        GatewayIntentBits.GuildMessages,
        GatewayIntentBits.MessageContent
    ]
});

client.once(Events.ClientReady, () => {
    console.log(`✅ ${client.user.tag} is online!`);
    client.user.setActivity('AI powered | @me to chat');
});

client.on(Events.MessageCreate, handleMessage);

client.login(process.env.DISCORD_TOKEN);
Enter fullscreen mode Exit fullscreen mode

Step 6: Environment Variables (.env)

DISCORD_TOKEN=your_bot_token_here
OPENROUTER_API_KEY=your_openrouter_key
GROQ_API_KEY=your_groq_key
# TOGETHER_API_KEY=your_together_key
Enter fullscreen mode Exit fullscreen mode

Step 7: Deploy for Free

Option A: Railway (Free tier)

npm init -y
npm install discord.js dotenv
# Push to GitHub, connect to Railway
Enter fullscreen mode Exit fullscreen mode

Option B: Render (Free tier)

  • Connect GitHub repo
  • Build: npm install
  • Start: node bot.js

Option C: Fly.io (Free allowance)

flyctl launch
flyctl deploy
Enter fullscreen mode Exit fullscreen mode

Advanced Features

Slash Commands

const { REST, Routes } = require('discord.js');

const commands = [
    {
        name: 'ask',
        description: 'Ask AI a question',
        options: [{ name: 'question', type: 3, description: 'Your question', required: true }]
    }
];

// Register on startup
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_TOKEN);
await rest.put(Routes.applicationCommands(client.user.id), { body: commands });
Enter fullscreen mode Exit fullscreen mode

Conversation Memory

const conversations = new Map();

function getHistory(userId) {
    return conversations.get(userId) || [];
}

function addToHistory(userId, role, content) {
    const history = getHistory(userId);
    history.push({ role, content });
    if (history.length > 10) history.shift(); // Keep last 10
    conversations.set(userId, history);
}

// In getAIResponse:
messages: [
    { role: 'system', content: '...' },
    ...getHistory(userId),
    { role: 'user', content: prompt }
]

// After response:
addToHistory(userId, 'user', prompt);
addToHistory(userId, 'assistant', response);
Enter fullscreen mode Exit fullscreen mode

Cost: $0/month

Component Cost
Discord API Free
OpenRouter/Groq/Together Free tier
Hosting (Railway/Render/Fly) Free tier
Total $0

Troubleshooting

Issue Fix
"Missing Access" Enable Message Content Intent in Developer Portal
Rate limited Add cooldowns, use fallback providers
Bot offline Check hosting logs, verify token
Slow responses Use Groq for speed, add caching

Conclusion

You now have a production-ready AI Discord bot running entirely on free tiers. Customize the system prompt for personality, add slash commands, and deploy to your community server.

Next steps: Add image generation, music, or moderation features using the same free API pattern.


Part of the AI Bot Tutorials series on Dev.to

Top comments (0)