Automated Monthly Developer Report Generator for Discord
DevLedger reads developer updates from Discord channels, extracts completed and planned tasks using AI (LLM), and generates comprehensive monthly reports with tables and insights.
- 📥 Automatic Message Fetching - Collects messages from multiple Discord channels
- 🤖 AI-Powered Task Extraction - Uses LLM (Groq/OpenAI) to extract completed and planned tasks
- 📊 Product-Grouped Reports - Clean markdown with work grouped by product/app
- 📋 Executive Summary - Auto-generated paragraph + bullet points for leadership
- 🏆 Key Achievements - Highlights major milestones and feature launches
- ⏰ Automated Scheduling - Runs on the last working day of each month (GitHub Actions)
- 💬 Slash Commands - On-demand reports via
/monthly-reportand/monthly-report-for - 🔄 Multi-API-Key Fallback - Automatically switches to backup API key on rate limit
- 🇮🇳 IST Timezone Support - Configured for Asia/Kolkata timezone
DevLedger/
├── devledger/
│ ├── __init__.py
│ ├── main.py # CLI entry point (scheduled runs)
│ ├── bot.py # Discord bot with slash commands
│ ├── bot_main.py # Bot entry point
│ ├── config/
│ │ ├── __init__.py
│ │ ├── config.yaml # Main configuration file
│ │ └── loader.py # Config loading with env overrides
│ ├── core/
│ │ ├── __init__.py
│ │ ├── fetcher.py # Discord message fetching
│ │ ├── preprocessor.py # Message cleaning and filtering
│ │ ├── extractor.py # AI task extraction
│ │ ├── formatter.py # Report formatting (tables)
│ │ └── poster.py # Discord posting
│ ├── services/
│ │ ├── __init__.py
│ │ ├── discord_client.py # Discord API wrapper
│ │ └── llm_client.py # LLM API client (Groq/OpenAI)
│ └── utils/
│ ├── __init__.py
│ ├── dates.py # Date utilities (IST, working days)
│ ├── logging.py # Logging setup
│ └── errors.py # Custom exceptions
├── tests/
│ ├── test_config.py
│ ├── test_dates.py
│ ├── test_extractor.py
│ ├── test_formatter.py
│ ├── test_llm_client.py
│ └── test_preprocessor.py
├── .github/
│ └── workflows/
│ └── monthly_report.yml # GitHub Actions workflow
├── config.yaml # Configuration template
├── requirements.txt
├── pytest.ini
└── README.md
git clone <repository-url>
cd DevLedger
# Create virtual environment
python3 -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
# Install dependencies
pip install -r requirements.txtEdit devledger/config/config.yaml:
discord:
bot_token: "" # Set via DISCORD_BOT_TOKEN env var
server_id: "YOUR_SERVER_ID"
source_channels:
- "general"
- "wp_plugins"
- "labeler"
- "shopify_product_addons"
- "discount_pricing"
- "llms_shopify_app"
- "shop_assistant_app"
report_channel: "monthly-report"
llm:
api_key: "" # Set via LLM_API_KEY env var
model_name: "llama-3.3-70b-versatile"
api_base_url: "https://api.groq.com/openai/v1"
max_tokens: 2048
temperature: 0.3
report:
timezone: "Asia/Kolkata"
format: "standard"
logging:
level: "INFO"Required:
export DISCORD_BOT_TOKEN="your-discord-bot-token"
export DISCORD_SERVER_ID="your-server-id"
export LLM_API_KEY="your-groq-or-openai-api-key"Optional:
export LLM_API_KEY_02="your-backup-api-key" # Fallback for rate limitsDry-run (preview without posting):
python -m devledger.main --dry-run --forceGenerate and post report:
python -m devledger.main --forceRun the slash command bot:
python -m devledger.bot_main- Go to Discord Developer Portal
- Click "New Application" → Name it "DevLedger"
- Go to "Bot" → Click "Add Bot"
- Enable these Privileged Gateway Intents:
- ✅ Message Content Intent
- ✅ Server Members Intent
- Copy the Bot Token (keep it secret!)
- Go to "OAuth2" → "URL Generator"
- Select scopes:
- ✅
bot - ✅
applications.commands
- ✅
- Select bot permissions:
- ✅ Read Messages/View Channels
- ✅ Send Messages
- ✅ Read Message History
- ✅ Use Slash Commands
- Copy the generated URL and open it to invite the bot
| Permission | Why Needed |
|---|---|
| Read Messages | Fetch messages from source channels |
| Send Messages | Post reports to report channel |
| Read Message History | Access historical messages |
| Use Slash Commands | Enable /monthly-report commands |
Once the bot is running, use these commands in Discord:
| Command | Description |
|---|---|
/monthly-report |
Generate report for current month (up to today) |
/monthly-report-for dec2025 |
Generate report for December 2025 |
/monthly-report-for january2026 |
Generate report for January 2026 |
/monthly-report-for 12-2025 |
Generate report for December 2025 |
The generated report includes:
📊 **Monthly Report - January 2026**
## 📋 Executive Summary
The team delivered significant product improvements and achieved key milestones. Focus areas included payment integrations, performance enhancements, and expanding pro features.
• Payment capabilities expanded across multiple products
• Achieved important user growth and review milestones
• Enhanced product stability and performance
## ✅ Completed Work
### 🛍️ Shop Assistant
• Integrated Dodo payment gateway _(Abhinav)_
• Improved email system _(Abhinav)_
### 🏷️ Labeler
• Launched yearly billing plans _(Sirajudheen)_
• Improved dashboard performance _(Vishnu)_
### 📦 WordPress Plugins
• Added deposit checkout feature _(Sanesh)_
## 🏆 Key Achievements
🎉 Reached 100+ 5-star reviews (Labeler)
🎉 Achieved 5,000+ active installs (Labeler)
🎉 Launched yearly billing plans (Labeler - Sirajudheen)
🎉 Integrated Dodo payments (Shop Assistant - Abhinav)These are sensitive and must be added as GitHub repository secrets:
| Secret | Description | Required |
|---|---|---|
DISCORD_BOT_TOKEN |
Discord bot authentication token | ✅ |
DISCORD_SERVER_ID |
Discord server (guild) ID | ✅ |
LLM_API_KEY |
Primary Groq or OpenAI API key | ✅ |
LLM_API_KEY_02 |
Backup API key (auto-switches on rate limit) | ❌ |
For multiple servers: Add DISCORD_SERVER_ID_2, DISCORD_SERVER_ID_3, etc.
These can be used to override config.yaml values (not secrets):
| Variable | Description | Use Case |
|---|---|---|
DISCORD_SOURCE_CHANNELS |
Comma-separated channel names | Override channels per workflow |
DISCORD_REPORT_CHANNEL |
Report channel name | Override report destination |
LLM_MODEL_NAME |
Override model name | Test different models |
LLM_API_BASE_URL |
Override API base URL | Switch LLM providers |
Groq (Recommended - Free tier available):
llm:
api_key: "gsk_..."
model_name: "llama-3.3-70b-versatile"
api_base_url: "https://api.groq.com/openai/v1"OpenAI:
llm:
api_key: "sk-..."
model_name: "gpt-4o-mini"
api_base_url: "https://api.openai.com/v1"The system maps channel names to friendly app names for the overview:
| Channel | App Name |
|---|---|
| wp_plugins | WordPress Plugins |
| labeler | Labeler |
| shopify_product_addons | Shopify Product Addons |
| discount_pricing | Discount Pricing |
| llms_shopify_app | LLMS Shopify App |
| shop_assistant_app | Shop Assistant |
| general | General |
The included workflow runs automatically on the last working day of each month.
Setup GitHub Secrets:
Go to: Repository → Settings → Secrets and variables → Actions → New repository secret
Add these secrets:
DISCORD_BOT_TOKEN- Your Discord bot tokenDISCORD_SERVER_ID- Your Discord server ID (first server)LLM_API_KEY- Primary Groq/OpenAI API keyLLM_API_KEY_02- (Optional) Backup API key for rate limit fallback
For multiple servers: Add DISCORD_SERVER_ID_2 for second server, etc.
Workflow files:
.github/workflows/monthly_report.yml- Main server workflow.github/workflows/monthly_report_server2.yml- Second server example (customize channels)
Scheduled to run at 11:59 PM IST on potential last working days (28th-31st).
# Clone repository
git clone <repository-url>
cd DevLedger
# Setup Python environment
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
# Set environment variables
export DISCORD_BOT_TOKEN="your-token"
export DISCORD_SERVER_ID="your-server-id"
export LLM_API_KEY="your-api-key"
export LLM_API_KEY_02="your-backup-key" # Optional
# Run the bot (keeps running for slash commands)
python -m devledger.bot_main
# Or use a process manager like PM2 or systemdUsing systemd (Linux):
Create /etc/systemd/system/devledger.service:
[Unit]
Description=DevLedger Discord Bot
After=network.target
[Service]
Type=simple
User=your-user
WorkingDirectory=/path/to/DevLedger
Environment="DISCORD_BOT_TOKEN=your-token"
Environment="LLM_API_KEY=your-api-key"
ExecStart=/path/to/DevLedger/.venv/bin/python -m devledger.bot_main
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetsudo systemctl daemon-reload
sudo systemctl enable devledger
sudo systemctl start devledger
sudo systemctl status devledgerCreate Dockerfile:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV DISCORD_BOT_TOKEN=""
ENV LLM_API_KEY=""
CMD ["python", "-m", "devledger.bot_main"]docker build -t devledger .
docker run -d \
--name devledger \
--restart unless-stopped \
-e DISCORD_BOT_TOKEN="your-token" \
-e LLM_API_KEY="your-api-key" \
devledger- Connect your GitHub repository
- Set environment variables in the dashboard:
DISCORD_BOT_TOKENLLM_API_KEY
- Set start command:
python -m devledger.bot_main
Railway (https://railway.app):
- Create new project → Deploy from GitHub
- Add environment variables
- It auto-detects Python and deploys
Render (https://render.com):
- New → Web Service → Connect repo
- Environment: Python
- Start Command:
python -m devledger.bot_main - Add environment variables
# Run all tests
python -m pytest tests/ -v
# Run with coverage
python -m pytest tests/ -v --cov=devledger
# Run specific test
python -m pytest tests/test_extractor.py -vpython -m devledger.main [OPTIONS]
Options:
--dry-run Preview report without posting to Discord
--force Run even if not the last working day
--config PATH Path to config file (default: devledger/config/config.yaml)
--month MM Month to generate report for (1-12)
--year YYYY Year to generate report for
--verbose, -v Enable debug logging- Fetch: Connects to Discord and fetches messages from configured channels for the month
- Preprocess: Cleans messages, removes noise (plans, questions, blockers)
- Extract: Sends messages to LLM to extract completed and planned tasks
- Overview: Generates a summary of key features, releases, and achievements
- Format: Creates markdown tables with Developer, Channel, Task columns
- Post: Sends the report to the configured Discord channel
- Ensure the bot has access to all source channels
- For private channels, explicitly add the bot
# macOS
/Applications/Python\ 3.13/Install\ Certificates.command- Switch to Groq (free tier with generous limits)
- Or reduce the number of source channels
- Wait a few minutes for commands to sync
- Try kicking and re-inviting the bot
- Check bot permissions
MIT License - see LICENSE file for details.
- Fork the repository
- Create a feature branch
- Make your changes
- Run tests:
python -m pytest tests/ -v - Submit a pull request