Back to Blog
TutorialFebruary 2, 202618 min

How to Make a Discord Bot with AI (Claude/GPT)

Build an intelligent Discord bot powered by Claude or GPT-4. Step-by-step tutorial with full code examples, rate limiting, and deployment options.

discordbotclaudeopenaitutorial

Molted Team

Molted.cloud

A Discord bot that actually understands context, answers questions intelligently, and maintains conversation history across messages. Not the keyword-matching bots of 2020, but a real AI that integrates with Claude or GPT-4. Building one from scratch teaches you API integration, rate limiting, and asynchronous programming. Or you can skip straight to using one.

What we are building

A Discord bot that:

  • Responds when mentioned or in DMs
  • Maintains conversation context within threads
  • Uses Claude or GPT-4 for responses
  • Handles rate limits gracefully
  • Can be deployed anywhere Node.js runs

The complete code is around 200 lines. Not a weekend project, but a solid afternoon.

Prerequisites

  • Node.js 18+ installed on your machine
  • A Discord account and a server where you have admin rights
  • An Anthropic or OpenAI API key (or your existing subscription—more on this below)
  • Basic JavaScript/TypeScript knowledge

About that API key: You don't necessarily need a pay-as-you-go API key. If you already have a Claude Pro, ChatGPT Plus, or similar subscription, you can often use that instead. Tools like OpenClaw support subscription-based authentication, so you're not stuck paying $100+/month in API fees on top of your existing $20 subscription. Those Reddit horror stories about "$700/month API bills"? Completely avoidable.

Creating a Discord application

Go to the Discord Developer Portal and click "New Application". Name it whatever you want.

Get your bot token

In your application, go to Bot → Reset Token. Copy this token and save it securely. This is your bot's password; never commit it to git or share it publicly.

Set permissions

Under Bot → Privileged Gateway Intents, enable:

  • Message Content Intent (required to read message text)
  • Server Members Intent (optional, for member-related features)

Generate invite link

Go to OAuth2 → URL Generator. Select:

  • Scopes: bot, applications.commands
  • Bot Permissions: Send Messages, Read Message History, View Channels

Copy the generated URL and open it in your browser to add the bot to your server.

Project setup

mkdir discord-ai-bot
cd discord-ai-bot
npm init -y
npm install discord.js @anthropic-ai/sdk dotenv

Create a .env file:

DISCORD_TOKEN=your-bot-token
ANTHROPIC_API_KEY=your-anthropic-key

Create .gitignore:

.env
node_modules/

Basic bot structure

Create index.js:

require('dotenv').config();
const { Client, GatewayIntentBits, Partials } = require('discord.js');
const Anthropic = require('@anthropic-ai/sdk');

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

const anthropic = new Anthropic({
  apiKey: process.env.ANTHROPIC_API_KEY,
});

// Store conversation history per channel
const conversations = new Map();

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

client.on('messageCreate', async (message) => {
  // Ignore bot messages
  if (message.author.bot) return;

  // Check if bot was mentioned or is a DM
  const isMentioned = message.mentions.has(client.user);
  const isDM = !message.guild;

  if (!isMentioned && !isDM) return;

  // Remove the mention from the message
  const content = message.content
    .replace(/<@!?\d+>/g, '')
    .trim();

  if (!content) return;

  // Get or create conversation history
  const channelId = message.channel.id;
  if (!conversations.has(channelId)) {
    conversations.set(channelId, []);
  }
  const history = conversations.get(channelId);

  // Add user message to history
  history.push({ role: 'user', content });

  // Keep only last 10 messages for context
  if (history.length > 20) {
    history.splice(0, history.length - 20);
  }

  try {
    // Show typing indicator
    await message.channel.sendTyping();

    // Call Claude
    const response = await anthropic.messages.create({
      model: 'claude-sonnet-4-20250514',
      max_tokens: 1024,
      system: 'You are a helpful assistant in a Discord server. Keep responses concise and friendly.',
      messages: history,
    });

    const reply = response.content[0].text;

    // Add assistant response to history
    history.push({ role: 'assistant', content: reply });

    // Send response (split if too long)
    if (reply.length > 2000) {
      const chunks = reply.match(/.{1,2000}/g);
      for (const chunk of chunks) {
        await message.reply(chunk);
      }
    } else {
      await message.reply(reply);
    }
  } catch (error) {
    console.error('Error:', error);
    await message.reply('Sorry, I encountered an error. Please try again.');
  }
});

client.login(process.env.DISCORD_TOKEN);

Run it:

node index.js

Mention your bot in Discord and it should respond.

Skip the coding?

OpenClaw includes Discord integration out of the box. Zero code needed.

Start free trial

Using OpenAI instead

If you prefer GPT-4, install the OpenAI SDK:

npm install openai

Replace the Anthropic setup:

const OpenAI = require('openai');

const openai = new OpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// In the message handler, replace the Claude call:
const response = await openai.chat.completions.create({
  model: 'gpt-4o',
  max_tokens: 1024,
  messages: [
    { role: 'system', content: 'You are a helpful assistant in a Discord server.' },
    ...history,
  ],
});

const reply = response.choices[0].message.content;

Conversation context management

The basic implementation stores all history in memory. For production:

// Better context management with expiry
const conversations = new Map();
const CONTEXT_EXPIRY = 30 * 60 * 1000; // 30 minutes
const MAX_CONTEXT_LENGTH = 20;

function getConversation(channelId) {
  const now = Date.now();

  if (!conversations.has(channelId)) {
    conversations.set(channelId, { messages: [], lastActivity: now });
  }

  const conv = conversations.get(channelId);

  // Reset if stale
  if (now - conv.lastActivity > CONTEXT_EXPIRY) {
    conv.messages = [];
  }

  conv.lastActivity = now;
  return conv.messages;
}

function addMessage(channelId, role, content) {
  const messages = getConversation(channelId);
  messages.push({ role, content });

  // Trim to max length
  while (messages.length > MAX_CONTEXT_LENGTH) {
    messages.shift();
  }
}

// Clean up old conversations periodically
setInterval(() => {
  const now = Date.now();
  for (const [id, conv] of conversations) {
    if (now - conv.lastActivity > CONTEXT_EXPIRY) {
      conversations.delete(id);
    }
  }
}, 60000);

Rate limiting

Both Discord and AI APIs have rate limits. Handle them gracefully:

const rateLimit = new Map();
const RATE_LIMIT_WINDOW = 60000; // 1 minute
const MAX_REQUESTS_PER_WINDOW = 10;

function isRateLimited(userId) {
  const now = Date.now();
  const userLimits = rateLimit.get(userId) || { count: 0, windowStart: now };

  // Reset window if expired
  if (now - userLimits.windowStart > RATE_LIMIT_WINDOW) {
    userLimits.count = 0;
    userLimits.windowStart = now;
  }

  if (userLimits.count >= MAX_REQUESTS_PER_WINDOW) {
    return true;
  }

  userLimits.count++;
  rateLimit.set(userId, userLimits);
  return false;
}

// In message handler:
if (isRateLimited(message.author.id)) {
  await message.reply('You are sending messages too quickly. Please wait a moment.');
  return;
}

Error handling

Production bots need robust error handling:

async function callAI(messages, retries = 3) {
  for (let i = 0; i < retries; i++) {
    try {
      const response = await anthropic.messages.create({
        model: 'claude-sonnet-4-20250514',
        max_tokens: 1024,
        messages,
      });
      return response.content[0].text;
    } catch (error) {
      if (error.status === 429) {
        // Rate limited, wait and retry
        await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        continue;
      }
      if (error.status >= 500) {
        // Server error, retry
        await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        continue;
      }
      throw error;
    }
  }
  throw new Error('Max retries exceeded');
}

Let us handle the bot

Molted deploys OpenClaw with Discord ready. 24-hour free trial.

Try free for 24 hours

Deployment options

VPS (DigitalOcean, Hetzner)

Cheapest long-term. Run with PM2 for process management:

npm install -g pm2
pm2 start index.js --name discord-bot
pm2 startup
pm2 save

Railway

Connect your GitHub repo, set environment variables, deploy. Pricing based on usage, typically $5-10/month for a bot.

Fly.io

Similar to Railway, good free tier for small bots.

Home server

If you have a machine that is always on, run it there. Same PM2 setup as VPS.

The simpler path

Building a Discord bot from scratch teaches valuable skills: API integration, async programming, error handling, deployment. It is worth doing once for the learning experience.

But if you want a working AI bot without the coding and maintenance, pre-built solutions exist. OpenClaw, for example, includes Discord integration that connects in minutes. You configure a bot token, enable the channel, and you have AI responses without writing code.

The tradeoff is customization versus convenience. A custom bot does exactly what you program. OpenClaw does what OpenClaw does, but it does it reliably without you managing servers or debugging at 2 AM.

For teams, the calculation usually favors managed solutions. Developer time spent maintaining a Discord bot is developer time not spent on your actual product. For personal projects and learning, building your own is rewarding.

Related guides

Free 24-hour trial

Discord AI bot in 60 seconds

OpenClaw on Molted includes full Discord integration. No coding, no hosting headaches.

Start free trial

24-hour free trial · No credit card required · Cancel anytime

Ready to try OpenClaw?

Deploy your AI personal assistant in 60 seconds. No coding required.

Start free trial

24-hour free trial · No credit card required