A comprehensive### 🏆 Race Management System
- Enhanced Race Planning: Add upcoming races with road/trail categorization
- Smart Distance Presets: Quick selection for 5K, 10K, Half Marathon, Marathon
- Custom Distances: Flexible distance input for trail races and non-standard events
- Race Tracking: Monitor race status (registered, completed, cancelled, DNS, DNF)
- Team Calendar: View upcoming races for all team members
- Public Announcements: Race additions are shared with the entire team
- Weekly Summaries: Automatic posts every Monday at 8:00 AM UTC with the week's races
- Monthly Overviews: First-of-month announcements with all races for the month
- Smart Formatting: Races grouped by date with member names and race details
- Configurable Scheduling: Customizable timing and timezone settings
- Manual Triggers: Admin commands to test and manually trigger announcements bot that automatically posts Strava activities from your running team members to a dedicated Discord channel. Built with real-time webhooks, rich activity displays, complete team management functionality, and an advanced race management system for tracking your team's upcoming events.
- Automatically posts new activities from team members via Strava webhooks
- Rich Discord embeds with comprehensive activity information
- Supports all activity types (running, cycling, swimming, etc.)
- Posts both public and private activities from registered members
- Activity name and description
- Distance, time, and pace
- Grade Adjusted Pace (GAP) for hill-adjusted performance
- Average heart rate and elevation gain
- Route map visualization (with optional Google Maps integration)
- Direct links to Strava activities
- Activity type-specific styling and icons
- Support for 40+ team members with secure token management
- OAuth2 authentication flow with encrypted token storage
- Member registration, deactivation, and removal
- Discord slash commands for easy team management
- Web-based registration system
- Enhanced Race Planning: Add upcoming races with road/trail categorization
- Smart Distance Presets: Quick selection for 5K, 10K, Half Marathon, Marathon
- Custom Distances: Flexible distance input for trail races and non-standard events
- Race Tracking: Monitor race status (registered, completed, cancelled, DNS, DNF)
- Team Calendar: View upcoming races for all team members
- Public Announcements: Race additions are shared with the entire team
- Slash Commands:
/members,/register,/last,/botstatus,/my-races,/all-races - Member Management: Add, remove, activate/deactivate members
- Activity Lookup: View any member's latest activity on-demand
- Race Management: Complete race lifecycle management with team visibility
- Admin Controls: Permission-based management commands
- Autocomplete: Smart member name suggestions
- Encrypted token storage with AES-256 encryption
- Non-blocking asynchronous operations
- Graceful error handling and token refresh
- Health monitoring and status endpoints
- Docker-ready with security best practices
- Node.js 24 or higher (check package.json for the exact version as it's subject to change in the future)
- Discord bot token and server permissions
- Strava API application credentials
- Public domain/server for webhooks (production only)
-
Clone and Setup
git clone https://github.com/mmarquet/strava-running-bot.git cd strava-running-bot npm install -
Configure Environment
cp .env.example .env # Edit .env with your API credentials (see detailed setup guide below) -
Start Development
npm run dev
-
Deploy with Docker (Optional)
docker compose -f docker/docker-compose.yml up -d
💡 Need help getting credentials? Follow the Complete Setup Guide below for detailed instructions.
Need detailed instructions? Follow these step-by-step guides to get all required credentials and configure your bot.
- Visit Discord Developer Portal
- Click "New Application" button (top right)
- Enter a name for your bot (e.g., "Strava Running Bot")
- Click "Create"
- In the left sidebar, click "Bot"
- Click "Add Bot" (if not already created)
- Under "Token" section:
- Click "Copy" to get your bot token
⚠️ IMPORTANT: Keep this token secret! It gives full access to your bot- Paste this token in your
.envfile asDISCORD_TOKEN
- Still in the Bot section, scroll down to "Privileged Gateway Intents"
- Enable "Message Content Intent" (required for some features)
- In the left sidebar, click "OAuth2" → "URL Generator"
- Under "Scopes", select:
- ✅
bot - ✅
applications.commands
- ✅
- Under "Bot Permissions", select:
- ✅
Send Messages - ✅
Use Slash Commands - ✅
Embed Links - ✅
Attach Files - ✅
Read Message History - ✅
Use External Emojis
- ✅
- Copy the generated URL at the bottom
- Open the invite link in your browser
- Select your Discord server from the dropdown
- Click "Authorize"
- Complete the CAPTCHA if prompted
- In Discord, go to User Settings (gear icon ⚙️)
- Go to "Advanced" and enable "Developer Mode"
- Navigate to the channel where you want the bot to post activities
- Right-click on the channel name
- Select "Copy Channel ID"
- Paste this ID in your
.envfile asDISCORD_CHANNEL_ID
- Go to Strava API Settings
- Log in with your Strava account (create one if you don't have it)
- Click "Create App" button
- Application Name: Enter your bot name (e.g., "Strava Running Bot")
- Category: Select "Other"
- Club: Leave blank (unless you have a specific Strava club)
- Website: Enter your website URL or GitHub repository URL
- Application Description: Brief description of your bot
- Authorization Callback Domain:
- For development:
localhostor127.0.0.1 - For production: Your actual domain (e.g.,
yourdomain.com) ⚠️ Important: Don't includehttp://orhttps://, just the domain
- For development:
- After creating the app, you'll see your application details
- Copy the "Client ID" and paste it in your
.envfile asSTRAVA_CLIENT_ID - Copy the "Client Secret" and paste it in your
.envfile asSTRAVA_CLIENT_SECRET ⚠️ IMPORTANT: Keep the Client Secret confidential!
-
Generate a secure random token for webhook verification:
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))" -
Copy the generated token to your
.envfile asSTRAVA_WEBHOOK_VERIFY_TOKEN -
This token ensures that webhook requests are actually coming from Strava
- Strava API has rate limits: 100 requests per 15 minutes, 1000 requests per day
- The bot automatically handles these limits with a 20%/10% safety margin and proper throttling
cp .env.example .envEdit your .env file with the credentials obtained from the previous steps. The .env.example file contains detailed comments for each variable.
# Generate encryption key (required)
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Generate webhook verification token (if not done already)
node -e "console.log(require('crypto').randomBytes(16).toString('hex'))"
⚠️ Security: Never commit your.envfile to version control. Keep all tokens and keys secure.
- A public domain or server accessible from the internet
- HTTPS enabled (Strava requires HTTPS for webhooks)
- Your bot running on that server
- Deploy your bot to your production server
- Ensure it's running on a public domain with HTTPS
- Test that
https://yourdomain.com/healthreturns a health check response
# Create webhook subscription (replace with your actual domain)
node utils/setup.js create-webhook https://yourdomain.com/webhook/strava
# This will register your server to receive activity updates from Strava# List existing webhooks to confirm creation
node utils/setup.js list-webhooks
# You should see your webhook listed with the callback URL- Post a new activity on Strava (or use an existing member's activity)
- Check your bot's logs to see if the webhook is received
- Verify the activity appears in your Discord channel
# List all webhook subscriptions
node utils/setup.js list-webhooks
# Delete a specific webhook (get ID from list command)
node utils/setup.js delete-webhook SUBSCRIPTION_ID
# Validate webhook configuration
node utils/setup.js validate-webhook| Command | Description | Usage |
|---|---|---|
/register |
Register yourself with Strava | /register |
/last |
Show last activity from a member | /last member: John |
| Command | Description | Usage |
|---|---|---|
/my-races add |
Add an upcoming race | /my-races add name: "Marathon" date: 21-04-2025 race_type: road distance_preset: Marathon |
/my-races list |
List your races | /my-races list or /my-races list status: registered |
/my-races remove |
Remove a race | /my-races remove race_id: 5 |
/my-races update |
Update race details | /my-races update race_id: 5 status: completed |
/my-races upcoming |
Show upcoming races for all members | /my-races upcoming days: 30 |
| Command | Description | Usage |
|---|---|---|
/members list |
List all registered team members | /members list |
/members remove |
Remove a team member | /members remove user: @user |
/members deactivate |
Temporarily deactivate a member | /members deactivate user: @user |
/members reactivate |
Reactivate a deactivated member | /members reactivate user: @user |
/botstatus |
Show bot statistics and health | /botstatus |
/all-races list |
List all team races | /all-races list or /all-races list status: upcoming |
/all-races upcoming |
Show upcoming races for all members | /all-races upcoming days: 60 |
/scheduler weekly |
Manually trigger weekly race announcement | /scheduler weekly |
/scheduler monthly |
Manually trigger monthly race announcement | /scheduler monthly |
/scheduler status |
Show scheduler status and upcoming races | /scheduler status |
- Road Race: Traditional road running events with standard distance presets
- Trail Race: Off-road events with custom distance input
- 5K: 5 kilometers
- 10K: 10 kilometers
- Half Marathon: 21.1 kilometers
- Marathon: 42.2 kilometers
- Other: Custom distance input
- Registered: Signed up for the race
- Completed: Race finished successfully
- Cancelled: Race was cancelled
- DNS: Did Not Start
- DNF: Did Not Finish
- Schedule: Every Monday at 8:00 AM UTC
- Content: All registered races for the current week (Monday-Sunday)
- Format: Grouped by date with race details and participant names
- Smart Display: Shows "Today", "Tomorrow", or days remaining
- Schedule: First day of each month at 8:00 AM UTC
- Content: All registered races for the current month
- Format: Organized by week with comprehensive race information
- Statistics: Total races and participating members count
- Enable/Disable: Toggle weekly and monthly announcements independently
- Custom Scheduling: Modify timing using cron expressions
- Timezone Support: Configure announcements for your local timezone
- Manual Testing: Admin commands to trigger announcements on-demand
GET /health- Health check endpointGET /members- List all registered members (JSON)
POST /members/:athleteId/delete- Remove member by athlete IDPOST /members/discord/:discordId/delete- Remove member by Discord IDPOST /members/:athleteId/deactivate- Deactivate memberPOST /members/:athleteId/reactivate- Reactivate member
GET /webhook/strava- Webhook verification endpointPOST /webhook/strava- Webhook event receiverGET /auth/strava- Start OAuth flowGET /auth/strava/callback- OAuth callback handler
# List all members
curl http://localhost:3000/members
# Remove a member
curl -X POST http://localhost:3000/members/12345678/delete
# Check bot health
curl http://localhost:3000/healthstrava-running-bot/
├── src/
│ ├── database/
│ │ ├── connection.js # SQLite database connection
│ │ ├── DatabaseManager.js # Database operations manager
│ │ └── migrations/ # Database schema migrations
│ │ ├── 001_initial_schema.sql
│ │ └── 002_add_race_type_distance.sql
│ ├── discord/
│ │ ├── bot.js # Discord bot implementation
│ │ └── commands.js # Slash command handlers
│ ├── strava/
│ │ └── api.js # Strava API integration
│ ├── server/
│ │ └── webhook.js # Webhook server & API endpoints
│ ├── processors/
│ │ └── ActivityProcessor.js # Main activity processing logic
│ ├── managers/
│ │ ├── MemberManager.js # Team member management
│ │ └── RaceManager.js # Race management system
│ ├── utils/
│ │ ├── ActivityFormatter.js # Activity data formatting
│ │ ├── DiscordUtils.js # Discord utility functions
│ │ ├── EmbedBuilder.js # Discord embed creation
│ │ ├── Logger.js # Logging utilities
│ │ └── RateLimiter.js # Strava API rate limiting
│ └── index.js # Application entry point
├── config/
│ └── config.js # Configuration management
├── utils/
│ └── setup.js # Setup and management utilities
├── data/ # Database and member data storage
├── logs/ # Application logs
├── .env.example # Environment variables template
├── docker/
│ ├── docker-compose.yml # Docker deployment configuration
│ └── Dockerfile # Container image definition
└── README.md # This documentation
- Central orchestrator for all bot operations
- Handles activity processing pipeline with queuing system
- Manages Discord and Strava integrations
- Coordinates member management and activity filtering
- Processes webhook events with delayed posting
- Discord.js v14 client wrapper with full intent support
- Slash command registration and autocomplete handling
- Rich embed generation using modular EmbedBuilder
- Google Maps route visualization integration
- Error handling with graceful client destruction
- Complete Strava API wrapper with OAuth2 flow
- Automatic rate limiting (80/15min, 900/day with safety margins)
- Activity data processing with GAP calculations
- Token refresh and authentication management
- Webhook signature verification for security
- Secure member data storage with SQLite database
- JSON-to-SQLite migration support for existing installations
- Token management with automatic refresh
- Member lifecycle operations (register/deactivate/remove)
- Discord user mapping and profile integration
- Complete race lifecycle management system
- Road and trail race categorization with smart distance handling
- Standard distance presets (5K, 10K, Half Marathon, Marathon)
- Custom distance support for trail races and special events
- Race status tracking and team calendar functionality
- Public race announcements for team engagement
- Automated Race Announcements: Cron-based scheduling for weekly and monthly race summaries
- Configurable Timing: Customizable schedule patterns with timezone support
- Smart Race Grouping: Intelligent organization of races by date and week
- Discord Integration: Direct posting to team channel with rich embedded announcements
- Manual Triggers: Admin testing capabilities with instant announcement generation
- SQLite database with automatic migrations
- Snake_case field naming for SQL compatibility
- Atomic operations with proper transaction handling
- Schema evolution support with migration tracking
- Data integrity and foreign key constraints
- Express.js server with comprehensive middleware
- Strava webhook event processing with signature validation
- RESTful member management API endpoints
- OAuth callback handling with HTML responses
- Health monitoring and error handling with proper status codes
- RateLimiter: Strava API compliance with request queuing
- ActivityFormatter: Distance, time, and pace calculations
- EmbedBuilder: Modular Discord embed creation system
- DiscordUtils: User ID parsing and utility functions
- Logger: Structured logging with category-based output
| Variable | Required | Description | Default |
|---|---|---|---|
DISCORD_TOKEN |
✅ | Discord bot token | - |
DISCORD_CHANNEL_ID |
✅ | Target Discord channel ID | - |
STRAVA_CLIENT_ID |
✅ | Strava API client ID | - |
STRAVA_CLIENT_SECRET |
✅ | Strava API client secret | - |
STRAVA_WEBHOOK_VERIFY_TOKEN |
✅ | Webhook verification token | - |
ENCRYPTION_KEY |
✅ | 32-byte hex encryption key for member data | - |
BASE_URL |
✅* | Public URL for webhooks (production only) | http://localhost:3000 |
PORT |
❌ | Server port | 3000 |
NODE_ENV |
❌ | Environment mode | development |
LOG_LEVEL |
❌ | Logging level (DEBUG, INFO, WARN, ERROR) | INFO |
POST_DELAY_MINUTES |
❌ | Delay before posting activities (minutes) | 15 |
SCHEDULER_TIMEZONE |
❌ | Timezone for race announcements | UTC |
WEEKLY_RACE_ANNOUNCEMENTS |
❌ | Enable weekly race announcements | true |
MONTHLY_RACE_ANNOUNCEMENTS |
❌ | Enable monthly race announcements | true |
WEEKLY_SCHEDULE |
❌ | Cron pattern for weekly announcements | 0 8 * * 1 (Mon 8AM) |
MONTHLY_SCHEDULE |
❌ | Cron pattern for monthly announcements | 0 8 1 * * (1st 8AM) |
Note:
BASE_URLis required for production deployments but optional for local development.
The race announcement scheduler can be customized through environment variables:
# Enable/disable automated announcements
WEEKLY_RACE_ANNOUNCEMENTS=true
MONTHLY_RACE_ANNOUNCEMENTS=true
# Customize announcement timing (cron format)
WEEKLY_SCHEDULE="0 8 * * 1" # Every Monday at 8:00 AM
MONTHLY_SCHEDULE="0 8 1 * *" # 1st day of month at 8:00 AM
# Set timezone for announcements
SCHEDULER_TIMEZONE="America/New_York" # Default: UTC| Schedule | Cron Expression | Description |
|---|---|---|
| Every Monday 8 AM | 0 8 * * 1 |
Weekly announcements |
| 1st of month 8 AM | 0 8 1 * * |
Monthly overview |
| Every Friday 5 PM | 0 17 * * 5 |
Weekend race prep |
| Every Sunday 6 PM | 0 18 * * 0 |
Week ahead preview |
- Activity Filters: Automatically filters activities without distance (weight training, etc.) and processes recent activities
- Rate Limiting: Conservative Strava API limits (80 requests/15min, 900/day) with request queuing and 20%/10% safety margin
- Posting Delay: Configurable delay before posting activities (default: 15 minutes) to allow for activity completion
- Route Visualization: Google Maps integration for GPS route display in Discord embeds
- Token Management: Automatic OAuth2 token refresh with secure AES-256 encrypted storage
- Data Persistence: Atomic JSON file operations with backup and recovery mechanisms
- Error Handling: Comprehensive error recovery with graceful degradation and logging
# Start with Docker Compose
docker compose -f docker/docker-compose.yml up -d
# View logs
docker compose -f docker/docker-compose.yml logs -f
# Check status
docker compose -f docker/docker-compose.yml psFor production deployment on a NAS or server:
- Configure Environment: Set
NODE_ENV=productionin.env - Resource Limits: Adjust memory/CPU limits in
docker/docker-compose.yml - Domain Setup: Configure domain and HTTPS for webhooks
- Monitoring: Set up log monitoring and alerting
- Backups: Regular backup of member data volume
See docs/DOCKER_DEPLOYMENT.md for detailed Docker deployment instructions.
# Check application health
curl http://localhost:3000/health
# View bot statistics
curl http://localhost:3000/members
# Check Docker health
docker compose -f docker/docker-compose.yml ps# Development
npm run dev
# Docker
docker compose -f docker/docker-compose.yml logs -f
# Specific timeframe
docker compose -f docker/docker-compose.yml logs --since="1h"- Weekly: Check member token status and activity
- Monthly: Update dependencies and security patches
- Quarterly: Review and rotate encryption keys
- As needed: Monitor Strava API quota usage
npm start # Start production server
npm run dev # Start development server with nodemon
npm test # Run tests (when implemented)# Validate configuration
node utils/setup.js validate
# Generate encryption key
node utils/setup.js generate-key
# Manage webhooks (production)
node utils/setup.js list-webhooks- Node.js 24 - Latest LTS with improved performance and security
- Discord.js - Modern Discord API wrapper with slash commands
- Express - Web framework for webhook server and API endpoints
- better-sqlite3 - High-performance SQLite database driver
- chalk - Enhanced terminal colors with ESM support
- dotenv - Improved environment variable management
- node-cron - Advanced task scheduling capabilities
- axios - HTTP client for API requests
- nodemon - Development auto-restart utility
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
- Encryption: All sensitive member data encrypted with AES-256
- Token Security: Secure storage and automatic rotation of API tokens
- Access Control: Permission-based Discord command access
- Input Validation: Comprehensive input validation and sanitization
- HTTPS Required: Production deployment requires HTTPS
- Webhook Verification: Strava webhook signature validation
- Rate Limiting: API rate limiting and abuse prevention
- Container Security: Non-root user and minimal container surface
- Regular security updates and dependency management
- Secure environment variable management
- Audit logs for administrative actions
- Backup and recovery procedures
# Check bot status
curl http://localhost:3000/health
# View logs
docker compose -f docker/docker-compose.yml logs -f
# Verify Discord permissions
# Check bot invite URL and server permissions# Verify Strava credentials
node utils/setup.js validate
# Check redirect URI configuration
# Ensure domain matches Strava app settings# Check webhook subscription
node utils/setup.js list-webhooks
# Verify member tokens
curl http://localhost:3000/members
# Check webhook logs
docker compose -f docker/docker-compose.yml logs -f | grep webhook# Check member status
curl http://localhost:3000/members
# Manually refresh if needed
# Members may need to re-authorize- 403 Forbidden: Check that your
STRAVA_WEBHOOK_VERIFY_TOKENmatches - 404 Not Found: Verify your server is running and accessible
- SSL Certificate Error: Ensure your domain has valid HTTPS
- No webhook events: Check Strava API rate limits and webhook subscription status
Enable debug logging by setting LOG_LEVEL=DEBUG in your .env file.
For additional support:
- Check the logs for detailed error messages
- Verify all configuration settings
- Test individual components (Discord, Strava, webhooks)
- Review Strava API quota and rate limits
- Check network connectivity and firewall settings
This project is licensed under the MIT License - see the LICENSE file for details.
- Strava API for providing comprehensive fitness data access
- Discord.js for excellent Discord bot development framework
- Node.js Community for robust ecosystem and best practices
- Running Community for inspiration and testing
Built with ❤️ for the running community For questions, issues, or contributions, please visit our GitHub repository.