FOR GAMERS, WITH GAMERS
Discord Bot for managing voice channels and messaging with event-driven RabbitMQ architecture.
- Event-driven microservice architecture with RabbitMQ
- Multi-guild support - One bot instance can manage multiple Discord servers
- Resilient RabbitMQ connection - Bot continues operating even when RabbitMQ is down
- Slash commands -
/authorto show bot author,/statusto check RabbitMQ status - Message sending to Discord channels
- Contest invitations - Send contest notifications with user mentions
- Voice channel member management
- Channel information queries
- Asynchronous request/response pattern
- Automatic RabbitMQ reconnection
- Docker support for easy deployment
- Designed for horizontal scaling
- Structured JSON logging
This bot is designed as part of a microservice architecture (MSA). It consumes events from RabbitMQ queues and publishes results back to response queues.
┌─────────┐ publish event ┌──────────────┐ consume ┌──────────────┐ Discord API ┌─────────┐
│ WAS │ ─────────────────► │ RabbitMQ │ ──────────► │ Discord Bot │ ────────────────► │ Discord │
│ │ │ (Request Q) │ │ (Consumer) │ │ Server │
└─────────┘ └──────────────┘ └──────────────┘ └─────────┘
▲ │
│ ┌──────────────┐ │
└───────────────────────── │ RabbitMQ │ ◄───────────────────┘
consume result │ (Response Q) │ publish result
└──────────────┘
GAMERS-BOT/
├── cmd/
│ └── main.go # Application entry point
├── internal/
│ ├── bot/
│ │ └── discord.go # Discord bot client and handlers
│ ├── rabbitmq/
│ │ ├── consumer.go # RabbitMQ consumer logic
│ │ ├── publisher.go # RabbitMQ publisher for responses
│ │ └── messages.go # Message type definitions
│ ├── handlers/
│ │ ├── handler.go # Handler interface
│ │ ├── message.go # Message send handler
│ │ ├── voice.go # Voice channel operations handler
│ │ └── channel.go # Channel info query handler
│ ├── config/
│ │ └── config.go # Configuration management
│ └── models/
│ └── models.go # Data models
├── docker/
│ └── Dockerfile
├── .env.example
├── go.mod
└── README.md
# 1. Clone the repository
git clone https://github.com/yourusername/GAMERS-BOT.git
cd GAMERS-BOT
# 2. Initial setup (creates .env, installs dependencies)
make setup
# 3. Edit .env with your Discord bot token
nano .env # or vim .env
# 4. Start RabbitMQ
make rabbitmq-start
# 5. Run the bot
make runThat's it! Your bot is now running. Use make help to see all available commands.
- Go 1.23 or higher
- Docker and Docker Compose (for containerized deployment)
- Make (for using Makefile commands)
- Discord Bot Token
- Go to Discord Developer Portal
- Click "New Application" and give it a name
- Go to "Bot" section and click "Add Bot"
- Copy the bot token
- Enable the following Privileged Gateway Intents:
- SERVER MEMBERS INTENT
- MESSAGE CONTENT INTENT
- Go to "OAuth2" > "URL Generator"
- Select scopes:
botandapplications.commands - Select bot permissions:
- View Channels
- Send Messages
- Move Members
- Use Slash Commands
- Copy the generated URL and invite the bot to your server(s)
Using Docker:
docker run -d --name rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
rabbitmq:3-managementAccess RabbitMQ Management UI at http://localhost:15672 (default credentials: guest/guest)
cp .env.example .envEdit .env file:
DISCORD_TOKEN=your_discord_bot_token_here
RABBITMQ_URL=amqp://guest:guest@localhost:5672/
RABBITMQ_REQUEST_QUEUE=discord.commands
RABBITMQ_RESPONSE_QUEUE=discord.responses
RABBITMQ_PREFETCH_COUNT=1Note: The bot supports multiple Discord servers (guilds) dynamically. Each RabbitMQ message should include the guild_id field to specify which server to target.
# Initial setup (creates .env, downloads dependencies)
make setup
# Start RabbitMQ
make rabbitmq-start
# Run the bot
make run
# Or run in development mode with hot reload
make dev# Install dependencies
go mod download
# Build
go build -o bin/bot ./cmd
# Run
./bin/botOr run directly:
go run ./cmd/main.goRun make help to see all available commands:
make helpCommon commands:
make build- Build the applicationmake run- Run the applicationmake test- Run testsmake docker-build- Build Docker imagemake rabbitmq-start- Start RabbitMQmake clean- Clean build artifacts
The bot provides the following slash commands:
Shows the bot author information.
Usage: /author
Response: Author: **SONU**
Checks the current RabbitMQ connection status.
Usage: /status
Response:
RabbitMQ Status: 🟢 Connected- When RabbitMQ is connectedRabbitMQ Status: 🔴 Disconnected- When RabbitMQ is not connected
The bot supports the following event types. All events require a guild_id field to specify which Discord server to target.
Send a message to a Discord channel.
Request:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440000",
"guild_id": "999999999999999999",
"event_type": "SEND_MESSAGE",
"payload": {
"channel_id": "123456789012345678",
"content": "Hello, Discord!"
}
}Response:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440000",
"success": true,
"data": {
"message_id": "987654321098765432",
"timestamp": "2025-01-08T12:34:56Z"
}
}Move members between voice channels.
Request:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440001",
"guild_id": "999999999999999999",
"event_type": "MOVE_MEMBERS",
"payload": {
"from_channel_id": "111111111111111111",
"to_channel_id": "222222222222222222",
"user_ids": []
}
}Note: Empty user_ids array moves all users in the source channel.
Response:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440001",
"success": true,
"data": {
"moved_count": 5,
"failed_users": []
}
}Get all voice channels in the guild.
Request:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440002",
"guild_id": "999999999999999999",
"event_type": "GET_VOICE_CHANNELS",
"payload": {}
}Response:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440002",
"success": true,
"data": {
"channels": [
{"id": "111111111111111111", "name": "General Voice"},
{"id": "222222222222222222", "name": "Gaming"}
]
}
}Get all text channels in the guild.
Request:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440003",
"guild_id": "999999999999999999",
"event_type": "GET_TEXT_CHANNELS",
"payload": {}
}Response:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440003",
"success": true,
"data": {
"channels": [
{"id": "333333333333333333", "name": "general"},
{"id": "444444444444444444", "name": "announcements"}
]
}
}Send a contest invitation with user mentions.
Request:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440004",
"guild_id": "999999999999999999",
"event_type": "SEND_CONTEST_INVITATION",
"payload": {
"channel_id": "333333333333333333",
"user_ids": ["111111111111111111", "222222222222222222"],
"contest_name": "Algorithm Challenge 2025",
"message": "Get ready for the most exciting coding challenge!"
}
}Note: The message field is optional. If not provided, a default message will be used.
Response:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440004",
"success": true,
"data": {
"message_id": "987654321098765432",
"notified_users": ["111111111111111111", "222222222222222222"],
"timestamp": "2025-01-08T12:34:56Z"
}
}When an error occurs:
{
"correlation_id": "550e8400-e29b-41d4-a716-446655440000",
"success": false,
"error": "channel not found: 123456789012345678"
}- Go to http://localhost:15672
- Login with credentials (default: guest/guest)
- Navigate to "Queues" tab
- Click on
discord.commandsqueue - Expand "Publish message" section
- Set "Delivery mode" to "2 - Persistent"
- Paste your JSON payload
- Click "Publish message"
import pika
import json
import uuid
# Connect to RabbitMQ
connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost')
)
channel = connection.channel()
# Declare queues
channel.queue_declare(queue='discord.commands', durable=True)
channel.queue_declare(queue='discord.responses', durable=True)
# Publish event
event = {
"correlation_id": str(uuid.uuid4()),
"guild_id": "999999999999999999",
"event_type": "SEND_MESSAGE",
"payload": {
"channel_id": "123456789012345678",
"content": "Hello from Python!"
}
}
channel.basic_publish(
exchange='',
routing_key='discord.commands',
body=json.dumps(event),
properties=pika.BasicProperties(
delivery_mode=2, # make message persistent
)
)
print(f"Published event with correlation_id: {event['correlation_id']}")
# Consume response
def callback(ch, method, properties, body):
response = json.loads(body)
if response['correlation_id'] == event['correlation_id']:
print(f"Received response: {response}")
ch.stop_consuming()
channel.basic_consume(
queue='discord.responses',
on_message_callback=callback,
auto_ack=True
)
print("Waiting for response...")
channel.start_consuming()
connection.close()package main
import (
"context"
"encoding/json"
"log"
"time"
"github.com/google/uuid"
amqp "github.com/rabbitmq/amqp091-go"
)
func main() {
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
if err != nil {
log.Fatal(err)
}
defer conn.Close()
ch, err := conn.Channel()
if err != nil {
log.Fatal(err)
}
defer ch.Close()
// Publish event
correlationID := uuid.New().String()
event := map[string]interface{}{
"correlation_id": correlationID,
"guild_id": "999999999999999999",
"event_type": "SEND_MESSAGE",
"payload": map[string]interface{}{
"channel_id": "123456789012345678",
"content": "Hello from Go!",
},
}
body, _ := json.Marshal(event)
err = ch.PublishWithContext(
context.Background(),
"", // exchange
"discord.commands", // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
DeliveryMode: amqp.Persistent,
ContentType: "application/json",
Body: body,
},
)
if err != nil {
log.Fatal(err)
}
log.Printf("Published event with correlation_id: %s", correlationID)
}# Setup project
make setup
# Start RabbitMQ
make rabbitmq-start
# Run in development mode (with hot reload)
make dev
# In another terminal, check RabbitMQ status
make rabbitmq-ui# Using Make
make build
# Or manually
go build -o bin/bot ./cmd# Using Make
make test
# With coverage
make test-coverage
# Or manually
go test ./...# Format code
make fmt
# Run all checks (format, vet, lint)
make check
# Or individually
go fmt ./...
golangci-lint run# Install golangci-lint and air (hot reload)
make install-toolsdocker build -f docker/Dockerfile -t gamers-discord-bot .docker run -d \
--name gamers-bot \
-e DISCORD_TOKEN=your_token \
-e RABBITMQ_URL=amqp://guest:guest@rabbitmq:5672/ \
--link rabbitmq \
gamers-discord-bot# Start all services (RabbitMQ + Bot)
make compose-up
# View logs
make compose-logs
# Stop all services
make compose-down
# Rebuild and start
make compose-build# Start all services
docker-compose up -d
# View logs
docker-compose logs -f
# Stop services
docker-compose down
# Rebuild
docker-compose up -d --buildThe included docker-compose.yml provides:
- RabbitMQ with management UI
- Discord Bot with automatic reconnection
- Health checks
- Persistent volumes for RabbitMQ data
- Automatic restart on failure
- Check if the bot is online in your Discord server
- Verify the
DISCORD_TOKENis correct - Ensure the bot has proper permissions
- Check the logs:
docker-compose logs -f discord-bot
Important: The bot will continue to operate even if RabbitMQ is not available. It will automatically attempt to reconnect every 10 seconds.
- Verify RabbitMQ is running:
docker ps | grep rabbitmq - Check the
RABBITMQ_URLis correct - Ensure the bot can reach RabbitMQ (network connectivity)
- Use
/statusslash command in Discord to check connection status - Check bot logs for reconnection attempts
- Check if events are published to the correct queue (
discord.commands) - Verify the event format matches the examples
- Check bot logs for errors
- Verify the correlation_id is a valid string
- Ensure the bot has "Move Members" permission
- Verify that users are actually in the source voice channel
- Check if the destination voice channel exists
- Bot cannot move users who have higher roles than the bot
The bot is designed to handle multiple Discord servers (guilds) dynamically. To use this feature:
- Invite the bot to multiple servers using the OAuth2 URL from the Discord Developer Portal
- Include guild_id in each RabbitMQ message to specify which server to target
- Get Guild ID: Enable Developer Mode in Discord (Settings > Advanced > Developer Mode), then right-click your server and select "Copy ID"
The bot will automatically handle requests for any guild it has been invited to, without requiring configuration changes or restarts.
The bot is designed to be resilient to RabbitMQ connection issues:
- The bot starts even if RabbitMQ is unavailable
- Automatically attempts to reconnect every 10 seconds
- No manual intervention required
- Use
/statusslash command to check current RabbitMQ connection status - Logs all connection attempts and failures
- Status updates are logged in real-time
- Discord slash commands (
/author,/status) work independently of RabbitMQ - Bot remains responsive to Discord events
- WAS integration is automatically restored when RabbitMQ comes back online
- Monitor bot logs for connection status
- Use the
/statuscommand to verify connectivity - Ensure RabbitMQ has proper health checks in production
- Consider setting up alerts for prolonged disconnections
- Web dashboard for monitoring
- Webhook support for event notifications
- Metrics and logging integration (Prometheus, Grafana)
- Guild whitelist/blacklist for security
- Dead letter queue for failed events
- Contest management with leaderboards
- Scheduled contest reminders
- User participation tracking
This project is licensed under the MIT License.
For issues and questions, please open an issue on GitHub.