Discord has evolved from a gaming platform into a powerful ecosystem for communities of all kinds, and its API empowers developers to build custom bots that automate tasks, provide support, and handle natural-language conversations. In this comprehensive tutorial, we’ll guide you through creating a Discord bot using Node.js and Python, then enhance it with AI by connecting to ChatGPT or a self-hosted model.
Looking for a ready-made solution? Check out our step-by-step installation guide for a complete AI assistant.
Setting Up Your Discord Application
Before writing any code, you need to register your bot in the Discord Developer Portal:
Step 1: Create Your Application
- Visit the Discord Developer Portal
- Click New Application
- Name your app (e.g., “AI Bot Demo”)
- Navigate to the Bot tab and click Add Bot
- Copy the bot token - this is your bot’s authentication key
Step 2: Configure Bot Permissions
- Under OAuth2 → URL Generator, select the
bot
scope - Choose permissions:
- Read Messages/View Channels
- Send Messages
- Read Message History
- Copy the generated URL and use it to invite your bot to your server
For more detailed setup instructions, see our complete installation guide.
Creating a Basic Bot in Node.js
Let’s start with a simple bot that responds to commands.
Installation
# Initialize your project
npm init -y
# Install discord.js library
npm install discord.js
Basic Bot Setup
Create index.js
:
const { Client, GatewayIntentBits } = require('discord.js');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
],
});
client.on('ready', () => {
console.log(`✅ Logged in as ${client.user.tag}!`);
});
client.on('messageCreate', async (message) => {
// Ignore bot messages
if (message.author.bot) return;
// Simple ping command
if (message.content.toLowerCase() === '!ping') {
await message.reply('🏓 Pong!');
}
});
// Login with your bot token
client.login(process.env.DISCORD_BOT_TOKEN);
Running Your Bot
# Set your token as environment variable
export DISCORD_BOT_TOKEN="your_token_here"
# Run the bot
node index.js
Your bot will come online and respond to !ping
with “Pong!”
Adding AI with ChatGPT API
Now let’s give your bot conversational intelligence using OpenAI’s ChatGPT.
Installation
npm install openai
AI-Enhanced Bot
Update index.js
:
const { Client, GatewayIntentBits } = require('discord.js');
const { OpenAI } = require('openai');
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent
],
});
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
client.on('ready', () => {
console.log(`✅ Logged in as ${client.user.tag}!`);
});
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
// Ping command
if (message.content.toLowerCase() === '!ping') {
await message.reply('🏓 Pong!');
return;
}
// AI command
if (message.content.startsWith('!ask')) {
const userPrompt = message.content.slice(4).trim();
if (!userPrompt) {
await message.reply('❌ Please provide a question. Usage: `!ask Your question here`');
return;
}
// Show typing indicator
await message.channel.sendTyping();
try {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{
role: 'system',
content: 'You are a helpful assistant on Discord. Keep responses concise and friendly.'
},
{
role: 'user',
content: userPrompt
},
],
max_tokens: 500,
temperature: 0.7,
});
const aiReply = response.choices[0]?.message?.content.trim();
// Discord has a 2000 character limit
if (aiReply.length > 1900) {
await message.reply(aiReply.substring(0, 1900) + '...\n\n_Response truncated_');
} else {
await message.reply(aiReply || '❌ Sorry, I couldn\'t generate a response.');
}
} catch (err) {
console.error('OpenAI Error:', err);
await message.reply('❌ There was an error contacting the AI service. Please try again later.');
}
}
});
client.login(process.env.DISCORD_BOT_TOKEN);
Environment Variables
Create a .env
file:
DISCORD_BOT_TOKEN=your_discord_token_here
OPENAI_API_KEY=your_openai_api_key_here
Install and use dotenv:
npm install dotenv
Add to the top of index.js
:
require('dotenv').config();
Your bot now answers questions like “!ask How do I set up roles in Discord?” using ChatGPT!
Connecting to a Self-Hosted AI Model
For complete privacy and control, you can run a language model on your own server. Here’s how to connect to a local AI endpoint:
Installation
npm install axios
Local AI Integration
const axios = require('axios');
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
if (message.content.startsWith('!local')) {
const userPrompt = message.content.slice(6).trim();
if (!userPrompt) {
await message.reply('❌ Please provide a question. Usage: `!local Your question here`');
return;
}
await message.channel.sendTyping();
try {
const response = await axios.post('http://localhost:5000/api/chat', {
prompt: userPrompt,
max_tokens: 500,
}, {
timeout: 30000, // 30 second timeout
});
const aiReply = response.data.reply || '❌ No response from the local AI.';
await message.reply(aiReply);
} catch (err) {
console.error('Local AI Error:', err.message);
await message.reply('❌ Could not reach the local AI service.');
}
}
});
This approach aligns with our privacy-first philosophy: your data and documents never leave your own servers.
Creating the Bot in Python
Prefer Python? Here’s how to build the same bot using discord.py
.
Installation
pip install discord.py openai python-dotenv
Basic Python Bot
Create bot.py
:
import discord
import openai
import os
from dotenv import load_dotenv
# Load environment variables
load_dotenv()
# Configure Discord client
intents = discord.Intents.default()
intents.message_content = True
client = discord.Client(intents=intents)
# Configure OpenAI
openai.api_key = os.getenv('OPENAI_API_KEY')
@client.event
async def on_ready():
print(f'✅ Logged in as {client.user}')
@client.event
async def on_message(message):
# Ignore bot messages
if message.author.bot:
return
# Ping command
if message.content.lower() == '!ping':
await message.channel.send('🏓 Pong!')
return
# AI command
if message.content.startswith('!ask'):
user_prompt = message.content[4:].strip()
if not user_prompt:
await message.channel.send('❌ Please provide a question. Usage: `!ask Your question here`')
return
# Show typing indicator
async with message.channel.typing():
try:
response = openai.ChatCompletion.create(
model='gpt-4',
messages=[
{
'role': 'system',
'content': 'You are a helpful assistant on Discord. Keep responses concise and friendly.'
},
{
'role': 'user',
'content': user_prompt
}
],
max_tokens=500,
temperature=0.7,
)
reply_text = response.choices[0].message.content.strip()
# Handle Discord's 2000 character limit
if len(reply_text) > 1900:
reply_text = reply_text[:1900] + '...\n\n_Response truncated_'
await message.channel.send(reply_text)
except Exception as e:
print(f'Error: {e}')
await message.channel.send('❌ Error fetching AI response.')
# Run the bot
client.run(os.getenv('DISCORD_BOT_TOKEN'))
Python with Local AI
For self-hosted models, use requests
:
import requests
@client.event
async def on_message(message):
if message.author.bot:
return
if message.content.startswith('!local'):
user_prompt = message.content[6:].strip()
if not user_prompt:
await message.channel.send('❌ Please provide a question.')
return
async with message.channel.typing():
try:
response = requests.post(
'http://localhost:5000/api/chat',
json={'prompt': user_prompt, 'max_tokens': 500},
timeout=30
)
response.raise_for_status()
reply_text = response.json().get('reply', 'No response')
await message.channel.send(reply_text)
except Exception as e:
print(f'Local AI Error: {e}')
await message.channel.send('❌ Could not reach local AI service.')
Best Practices for AI-Powered Bots
1. Protect Your Keys
Never commit tokens or API keys to version control:
// ❌ BAD
const token = "MTk4NjIyNDgzNDc...";
// ✅ GOOD
require('dotenv').config();
const token = process.env.DISCORD_BOT_TOKEN;
2. Handle Rate Limits
Both Discord and AI services have rate limits:
const rateLimiter = new Map();
client.on('messageCreate', async (message) => {
const userId = message.author.id;
const now = Date.now();
// Check rate limit (5 requests per minute)
if (rateLimiter.has(userId)) {
const userData = rateLimiter.get(userId);
if (now - userData.lastRequest < 12000) { // 12 seconds between requests
await message.reply('⏰ Please wait a moment before asking again.');
return;
}
}
rateLimiter.set(userId, { lastRequest: now });
// Process command...
});
3. Implement Error Handling
Always wrap API calls in try-catch:
try {
const response = await openai.chat.completions.create({...});
// Process response
} catch (error) {
if (error.status === 429) {
await message.reply('⏰ Rate limit reached. Please try again in a moment.');
} else if (error.status === 401) {
console.error('Invalid API key');
await message.reply('❌ Bot configuration error.');
} else {
await message.reply('❌ An error occurred. Please try again.');
}
}
4. Log Interactions
Maintain logs for debugging and improvement:
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [
new winston.transports.File({ filename: 'bot.log' }),
new winston.transports.Console()
],
});
client.on('messageCreate', async (message) => {
logger.info({
user: message.author.tag,
content: message.content,
channel: message.channel.name,
timestamp: new Date().toISOString()
});
});
5. Limit Bot Permissions
Grant only necessary Discord permissions:
- ✅ Read Messages/View Channels
- ✅ Send Messages
- ✅ Read Message History
- ❌ Administrator
- ❌ Manage Server
- ❌ Manage Roles
For more on security and configuration, explore why private hosting and granular permissions matter for your community.
Advanced Features
Slash Commands
Modern Discord bots use slash commands for better UX:
const { REST, Routes, SlashCommandBuilder } = require('discord.js');
const commands = [
new SlashCommandBuilder()
.setName('ask')
.setDescription('Ask the AI assistant a question')
.addStringOption(option =>
option.setName('question')
.setDescription('Your question')
.setRequired(true)
),
].map(command => command.toJSON());
const rest = new REST({ version: '10' }).setToken(process.env.DISCORD_BOT_TOKEN);
// Register slash commands
(async () => {
try {
await rest.put(
Routes.applicationCommands(process.env.CLIENT_ID),
{ body: commands },
);
console.log('✅ Slash commands registered!');
} catch (error) {
console.error(error);
}
})();
// Handle slash commands
client.on('interactionCreate', async interaction => {
if (!interaction.isChatInputCommand()) return;
if (interaction.commandName === 'ask') {
const question = interaction.options.getString('question');
await interaction.deferReply();
try {
const response = await openai.chat.completions.create({...});
await interaction.editReply(response.choices[0].message.content);
} catch (error) {
await interaction.editReply('❌ Error processing your request.');
}
}
});
Context Memory
Keep track of conversation history:
const conversationHistory = new Map();
client.on('messageCreate', async (message) => {
if (!message.content.startsWith('!ask')) return;
const userId = message.author.id;
// Get or create conversation history
if (!conversationHistory.has(userId)) {
conversationHistory.set(userId, []);
}
const history = conversationHistory.get(userId);
const userPrompt = message.content.slice(4).trim();
// Add user message to history
history.push({ role: 'user', content: userPrompt });
// Keep only last 10 messages to avoid token limits
if (history.length > 10) {
history.shift();
}
try {
const response = await openai.chat.completions.create({
model: 'gpt-4',
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
...history
],
});
const aiReply = response.choices[0].message.content;
// Add AI response to history
history.push({ role: 'assistant', content: aiReply });
await message.reply(aiReply);
} catch (error) {
console.error(error);
await message.reply('❌ Error processing your request.');
}
});
Conclusion
By following this comprehensive guide, you’ve learned how to:
✅ Register and configure a Discord bot
✅ Build a command handler in Node.js and Python
✅ Integrate ChatGPT for AI-powered responses
✅ Connect to self-hosted AI models for privacy
✅ Implement best practices for security and reliability
An AI-powered bot can transform your Discord server by answering questions 24/7, processing documents, and interacting naturally with your community. Whether you choose OpenAI’s ChatGPT or a self-hosted solution, the possibilities are endless.
Ready to take it further? Explore our complete AI Discord bot guide or discover the latest AI trends shaping digital transformation in 2025.
Want a fully-featured AI assistant without the coding? Compare our free and premium plans and get started in minutes!
Start building smarter Discord communities today. Get Started with AIDiscord.bot →