Every dev Discord server needs a code roast bot. Someone pastes a code snippet, the bot tears it apart with the energy of a senior developer who hasn't had coffee yet. It's educational, it's entertaining, and it's surprisingly easy to build.
In this tutorial, we'll build a Discord bot that watches for code blocks in messages, reviews them with AI in a roast-style tone, and replies with brutally honest feedback. Think code review meets comedy.
What we're building
User:
!roast
javascript
function getUser(id) {
var user = null;
fetch('/api/users/' + id).then(res => {
user = res.json();
});
return user;
}
Bot:
🔥 CODE ROAST 🔥
Oh no. Oh no no no.
1. You're returning `user` before the fetch completes.
This function returns `null` every single time. You've
built a function whose only job is to return null. Impressive.
2. `var` in 2026? Did you find this code in a time capsule?
3. `res.json()` returns a Promise too, which you're not
awaiting. So even if the timing worked, `user` would
be a Promise object, not actual data.
Here's what you probably meant:
javascript
async function getUser(id) {
const res = await fetch(/api/users/${id});
return res.json();
}
Rating: 2/10 — The indentation was nice though. 💀
Prerequisites
- Node.js 20+
- A Discord account
- An Anthropic API key
- A Discord server where you have admin permissions
Step 1: Create the Discord bot
- Go to discord.com/developers/applications
- Click "New Application" → name it "Code Roaster"
- Go to "Bot" tab → click "Add Bot"
- Copy the bot token
- Under "Privileged Gateway Intents," enable Message Content Intent
- Go to "OAuth2" → "URL Generator" → select
botscope andSend Messages+Read Message Historypermissions - Copy the generated URL and open it to invite the bot to your server
Step 2: Set up the project
mkdir roast-bot && cd roast-bot
npm init -y
npm install discord.js @anthropic-ai/sdk
Step 3: Build the bot
Create index.js:
import { Client, GatewayIntentBits } from 'discord.js';
import Anthropic from '@anthropic-ai/sdk';
const client = new Client({
intents: [
GatewayIntentBits.Guilds,
GatewayIntentBits.GuildMessages,
GatewayIntentBits.MessageContent,
],
});
const anthropic = new Anthropic();
const ROAST_PROMPT = `You are a code review bot with the personality of a brutally honest senior developer who's seen too much bad code. Your job is to roast the code — point out every issue in a funny, sarcastic way. But always be educational: explain WHY something is wrong and show the fix.
Rules:
- Be funny and sarcastic, but never mean-spirited or personal
- Always explain the actual technical issue behind each roast
- End with a corrected version of the code if possible
- Give a rating out of 10
- Keep it under 300 words
- Use emojis sparingly for effect
Roast this code:`;
// Listen for !roast command
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
if (!message.content.startsWith('!roast')) return;
// Extract code block
const codeMatch = message.content.match(/```
{% endraw %}
(\w*)\n?([\s\S]*?)
{% raw %}
```/);
if (!codeMatch) {
message.reply('Paste a code block with your message. Example:\n!roast\n\\`\\`\\`js\nyour code here\n\\`\\`\\`');
return;
}
const language = codeMatch[1] || 'unknown';
const code = codeMatch[2].trim();
if (code.length < 10) {
message.reply("That's barely code. Give me something to work with. 😤");
return;
}
if (code.length > 3000) {
message.reply("I'm not reading all that. Keep it under 100 lines. 📏");
return;
}
// Show typing indicator
message.channel.sendTyping();
try {
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 800,
messages: [{
role: 'user',
content: `${ROAST_PROMPT}\n\nLanguage: ${language}\n\`\`\`${language}\n${code}\n\`\`\``
}],
});
const roast = response.content[0].text;
// Discord has a 2000 char limit
if (roast.length > 1900) {
const parts = roast.match(/[\s\S]{1,1900}/g) || [];
for (const part of parts) {
await message.reply(part);
}
} else {
message.reply(`🔥 **CODE ROAST** 🔥\n\n${roast}`);
}
} catch (err) {
console.error(err);
message.reply("My roasting circuits overheated. Try again. 🫠");
}
});
// Also support !review for a nicer tone
client.on('messageCreate', async (message) => {
if (message.author.bot) return;
if (!message.content.startsWith('!review')) return;
const codeMatch = message.content.match(/```
{% endraw %}
(\w*)\n?([\s\S]*?)
{% raw %}
```/);
if (!codeMatch) {
message.reply('Paste a code block with your message.');
return;
}
message.channel.sendTyping();
try {
const response = await anthropic.messages.create({
model: 'claude-sonnet-4-20250514',
max_tokens: 800,
messages: [{
role: 'user',
content: `Review this code constructively. Point out bugs, improvements, and best practices. Be helpful and professional. Keep it concise.\n\nLanguage: ${codeMatch[1] || 'unknown'}\n\`\`\`\n${codeMatch[2].trim()}\n\`\`\``
}],
});
message.reply(`📝 **Code Review**\n\n${response.content[0].text}`);
} catch (err) {
message.reply("Something went wrong. Try again.");
}
});
client.on('ready', () => {
console.log(`🔥 Roast bot is online as ${client.user.tag}`);
});
client.login(process.env.DISCORD_TOKEN);
Step 4: Run it
export DISCORD_TOKEN=your-discord-bot-token
export ANTHROPIC_API_KEY=your-anthropic-key
node index.js
Go to your Discord server and try:
!roast
javascript
for (var i = 0; i < arr.length; i++) {
setTimeout(function() { console.log(arr[i]); }, 1000);
}
plaintext
The two modes
The bot has two commands:
-
!roast— brutal, funny, educational. For entertainment and learning. -
!review— professional, constructive. For when you actually want help.
Same AI, different personality. The prompt controls everything.
Tuning the roast level
Adjust the prompt to control intensity:
Mild roast:
Be gently sarcastic, like a patient mentor who's slightly disappointed.
plaintext
Medium roast (default):
Be brutally honest and funny, like a senior dev who's seen too much.
plaintext
Nuclear roast:
Channel the energy of a developer who just found production code
written by an intern with no code review. Hold nothing back.
javascript
Rate limiting
Prevent spam by adding a cooldown:
const cooldowns = new Map();
const COOLDOWN_MS = 30000; // 30 seconds
client.on('messageCreate', async (message) => {
if (!message.content.startsWith('!roast')) return;
const lastUsed = cooldowns.get(message.author.id) || 0;
if (Date.now() - lastUsed < COOLDOWN_MS) {
const remaining = Math.ceil((COOLDOWN_MS - (Date.now() - lastUsed)) / 1000);
message.reply(`Cooldown: ${remaining}s. Your code isn't going anywhere. 😏`);
return;
}
cooldowns.set(message.author.id, Date.now());
// ... rest of the handler
});
Deploying
Same as any Node.js bot — a VPS with pm2, a Docker container, or Railway/Fly.io:
# pm2
pm2 start index.js --name roast-bot
# Docker
docker build -t roast-bot .
docker run -d --env-file .env roast-bot
What you learned
- How to create a Discord bot with
discord.js - How to extract code blocks from Discord messages with regex
- How to use different AI prompts to control tone and personality
- How to handle Discord's message length limits
- How to add rate limiting to prevent abuse
The bot is about 80 lines of core logic. It's the kind of thing that makes a dev Discord server 10x more fun — and people actually learn from the roasts because every joke comes with a real explanation.
Related resources
- JavaScript complete guide — the language behind the bot
- npm complete guide — managing dependencies
- Docker complete guide — deploying the bot
🛠️ Free tools related to this article:
Originally published at https://aimadetools.com
Top comments (0)