A simple Telegram bot that allows users to import workout plans into their Garmin Connect account. Built with Python and Pyrogram, and uses MongoDB for persistent user storage.
Try it live: HeyGarminBot on Telegram
- User Login Flow: Users authenticate with their Garmin Connect credentials via
/start. Credentials are kept in a short-lived in-memory session and immediately discarded after exchanging for a Garmin session token. - Workout Import: Once authorized, any text message or workout plan sent to the bot is uploaded to the user's Garmin Connect account.
- AI-Powered Workout Generation: Converts natural language workout descriptions into structured Garmin workouts using OpenAI GPT-4o-mini.
- Redis-Based Rate Limiting: Sliding window rate limiting with Redis (falls back to in-memory or disabled for local dev).
- Workout Logging: All prompts and results are logged to MongoDB with processing times and error tracking.
- Usage Statistics: Users can check their API usage with the
/statscommand. - Logout: Users can remove their Garmin authorization with the
/logoutcommand. - Session Management: Temporary credentials are stored in a TTL cache with a 5-minute expiration to avoid persisting raw passwords.
- Persistent Storage: User state, Garmin session tokens, and workout logs are stored in MongoDB via Motor (async MongoDB driver).
- Language: Python 3.11
- Telegram Framework: Pyrogram
- AI Model: OpenAI GPT-4o-mini
- Async MongoDB: Motor
- Redis: Async Redis client for rate limiting
- In-Memory Cache: cachetools
- Containerization: Docker & Docker Compose
├── bot.py # Main script for the Telegram bot
├── chatgpt.py # OpenAI integration for workout generation
├── garmin.py # Garmin Connect API integration
├── garmin_convert.py # Workout JSON to Garmin format converter
├── rate_limiter.py # Redis-based rate limiting with fallback
├── workout_log.py # MongoDB workout logging
├── user.py # User CRUD helpers for MongoDB
├── session.py # Temporary session storage
├── workout_schema.json # JSON Schema for workout validation
├── SYSTEM_PROMPT.md # AI prompt for workout parsing
├── Dockerfile # Docker image definition
├── docker-compose.yml # Compose file for bot + MongoDB + Redis
├── requirements.txt # Python dependencies
├── .env # Environment variables (not checked in)
└── mongo_data/ # Local MongoDB data volume
- Docker & Docker Compose installed
- Telegram Bot credentials:
TELEGRAM_API_ID,TELEGRAM_API_HASH,TELEGRAM_BOT_TOKEN
Create a .env file in the project root:
# Telegram Bot
TELEGRAM_API_ID=123456
TELEGRAM_API_HASH=abcdef1234567890
TELEGRAM_BOT_TOKEN=123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11
# OpenAI
OPENAI_API_KEY=sk-proj-...
# MongoDB
MONGODB_URI=mongodb://mongo:27017
# Redis (optional - leave empty to disable rate limiting for local dev)
REDIS_URL=redis://redis:6379/0
# Rate Limiting (optional, defaults shown)
RATE_LIMIT_HOURLY=10
RATE_LIMIT_DAILY=50
RATE_LIMIT_MONTHLY=200- Create a host folder for MongoDB data:
mkdir -p mongo_data
- Start the services:
docker-compose up -d --build
- Monitor logs:
docker-compose logs -f bot
- To stop:
docker-compose down
- Install dependencies:
pip install -r requirements.txt
- Ensure MongoDB is running locally on
mongodb://localhost:27017. - Run the bot:
python bot.py
The bot implements a Redis-based sliding window rate limiting system to protect your OpenAI API key from abuse:
- Redis Sliding Window: Uses sorted sets (ZSET) with timestamps for O(log N) performance
- Graceful Degradation: Falls back to in-memory rate limiting if Redis is unavailable
- Local Dev Mode: Rate limiting is disabled when
REDIS_URLis not set - Configurable Limits: Set via environment variables (hourly/daily/monthly)
- User Commands:
/stats- Check current usage statistics- Users receive clear error messages with time-to-retry when limits are exceeded
- Production: Redis ZSET with automatic cleanup and 31-day expiry
- Fallback: In-memory list of timestamps (survives Redis failures)
- Local Dev: Rate limiting completely disabled for development convenience
All workout generation requests are logged to MongoDB:
- Prompt Storage: Original user input text
- Result Tracking: Generated workout JSON and Garmin workout ID
- Performance Metrics: Processing time in milliseconds
- Error Logging: Full error details for failed requests
- User Analytics: Query history and statistics per user
- Never store raw passwords – credentials are only kept in memory and purged after authentication.
- API Key Protection – rate limiting prevents abuse of OpenAI API quota.
- Environment variables – do not commit your
.envor service-account keys to version control. Use volume mounts or secret management in production. - Non-root container user – consider adding a non-root user in your Dockerfile for additional security.
This project is licensed under the MIT License. Feel free to use and modify it for your own purposes!