A simple Express.js website and API for Discord bots. This project has been updated with modern security practices and best practices.
- Modern Express.js server with security middleware
- Rate limiting to prevent abuse
- Security headers via Helmet
- Environment variable support
- RESTful API endpoints
- Error handling and logging
- Node.js >= 18.0.0
- npm or yarn
- Clone the repository:
git clone https://github.com/JLDENSMORE/Simple-Discord-Bot-Website.git
cd Simple-Discord-Bot-Website- Install dependencies:
npm install-
Configure environment variables:
- Copy
.env.exampleto.env - Fill in your configuration values:
PORT=3000 NODE_ENV=development SUPPORT_SERVER=https://discord.gg/your-server-invite BOT_INVITE=https://discord.com/oauth2/authorize?client_id=YOUR_BOT_ID&scope=bot&permissions=YOUR_PERMISSIONS BOT_SERVER=http://localhost:19172
Alternatively, you can use
config.json(not recommended for production). - Copy
-
Start the server:
npm startGET /api- API information and available endpointsGET /api/logo- Get bot logo/avatar in various sizesGET /api/status- Get bot status, server count, ping, and online/offline statusGET /api/stats- Alias for/api/statusPOST /api/update-stats- Update bot stats (server count, ping)
The API allows your Discord bot to report its statistics (server count, ping, status) to the website, which will then be displayed on the homepage. The website automatically fetches stats from your bot server, but you can also manually update them via POST requests.
Endpoint: GET /api/status or GET /api/stats
Response:
{
"status": "online",
"serverCount": 1234,
"ping": 45,
"lastUpdate": "2024-01-15T10:30:00.000Z"
}Example (cURL):
curl https://yourwebsite.com/api/statusExample (JavaScript/Node.js):
const response = await fetch('https://yourwebsite.com/api/status');
const data = await response.json();
console.log(`Bot is ${data.status}, serving ${data.serverCount} servers`);Example (Python):
import requests
response = requests.get('https://yourwebsite.com/api/status')
data = response.json()
print(f"Bot is {data['status']}, serving {data['serverCount']} servers")Endpoint: POST /api/update-stats
Request Body:
{
"serverCount": 1234,
"ping": 45
}Both fields are optional - you can send just serverCount, just ping, or both.
Response:
{
"success": true,
"message": "Stats updated successfully",
"serverCount": 1234,
"ping": 45
}cURL:
curl -X POST https://yourwebsite.com/api/update-stats \
-H "Content-Type: application/json" \
-d '{"serverCount": 1234}'JavaScript/Node.js (Discord.js):
const { Client, GatewayIntentBits } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once('ready', async () => {
const serverCount = client.guilds.cache.size;
// Update server count on website
try {
const response = await fetch('https://yourwebsite.com/api/update-stats', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ serverCount: serverCount })
});
const data = await response.json();
console.log(`Updated website: ${data.message}`);
} catch (error) {
console.error('Failed to update website stats:', error);
}
});
// Update every 5 minutes
setInterval(async () => {
const serverCount = client.guilds.cache.size;
await fetch('https://yourwebsite.com/api/update-stats', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ serverCount: serverCount })
});
}, 5 * 60 * 1000);
client.login('YOUR_BOT_TOKEN');Python (discord.py):
import discord
import aiohttp
import asyncio
client = discord.Client(intents=discord.Intents.default())
async def update_stats():
server_count = len(client.guilds)
async with aiohttp.ClientSession() as session:
async with session.post(
'https://yourwebsite.com/api/update-stats',
json={'serverCount': server_count}
) as response:
data = await response.json()
print(f"Updated website: {data['message']}")
@client.event
async def on_ready():
print(f'Bot is ready! Serving {len(client.guilds)} servers')
await update_stats()
# Update every 5 minutes
while True:
await asyncio.sleep(5 * 60) # 5 minutes
await update_stats()
client.run('YOUR_BOT_TOKEN')JavaScript/Node.js (Discord.js):
const { Client, GatewayIntentBits } = require('discord.js');
const client = new Client({ intents: [GatewayIntentBits.Guilds] });
client.once('ready', async () => {
const updateStats = async () => {
const serverCount = client.guilds.cache.size;
const ping = client.ws.ping; // WebSocket ping in milliseconds
try {
const response = await fetch('https://yourwebsite.com/api/update-stats', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
serverCount: serverCount,
ping: ping
})
});
const data = await response.json();
console.log(`Updated: ${data.serverCount} servers, ${data.ping}ms ping`);
} catch (error) {
console.error('Failed to update stats:', error);
}
};
// Update immediately
await updateStats();
// Update every 5 minutes
setInterval(updateStats, 5 * 60 * 1000);
});
client.login('YOUR_BOT_TOKEN');Python (discord.py):
import discord
import aiohttp
import asyncio
client = discord.Client(intents=discord.Intents.default())
async def update_stats():
server_count = len(client.guilds)
ping = round(client.latency * 1000) # Convert to milliseconds
async with aiohttp.ClientSession() as session:
async with session.post(
'https://yourwebsite.com/api/update-stats',
json={
'serverCount': server_count,
'ping': ping
}
) as response:
data = await response.json()
print(f"Updated: {data['serverCount']} servers, {data['ping']}ms ping")
@client.event
async def on_ready():
print(f'Bot is ready! Serving {len(client.guilds)} servers')
await update_stats()
# Update every 5 minutes
while True:
await asyncio.sleep(5 * 60) # 5 minutes
await update_stats()
client.run('YOUR_BOT_TOKEN')cURL:
curl -X POST https://yourwebsite.com/api/update-stats \
-H "Content-Type: application/json" \
-d '{"ping": 45}'JavaScript:
const ping = 45; // Your bot's ping in milliseconds
await fetch('https://yourwebsite.com/api/update-stats', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ping: ping })
});400 Bad Request:
{
"error": "Invalid server count",
"message": "serverCount must be a non-negative number"
}500 Internal Server Error:
{
"error": "Internal server error",
"message": "An error occurred while updating stats"
}-
Update Frequency: Update stats every 5-10 minutes to avoid unnecessary load. The website automatically refreshes every 10 minutes.
-
Error Handling: Always wrap API calls in try-catch blocks and handle errors gracefully.
-
Partial Updates: You can update just
serverCountor justping- the API will preserve the other value. -
Automatic Status: The website automatically determines online/offline status based on whether it can reach your bot server. You don't need to send status manually.
-
Caching: If your bot server is temporarily unavailable, the website will show cached data until it can reconnect.
- Helmet.js: Sets various HTTP headers for security
- Rate Limiting: Prevents abuse with request rate limiting
- Environment Variables: Secure configuration management
- Error Handling: Proper error handling without exposing sensitive information
- Input Validation: Basic validation and sanitization
- Updated all dependencies to latest versions
- Removed deprecated
body-parser(using Express built-in) - Removed
node-fetch(using native fetch API) - Added security middleware (Helmet, rate limiting)
- Improved error handling throughout
- Added environment variable support
- Fixed timeout handling in API routes
- Updated to Node.js 18+ requirement
MIT
Feel free to make a pull request with any updates or changes you would like to see.