A WhatsApp bot that tracks beer consumption for a group of friends. Send a photo of your beer, and the bot will log it with optional AI-powered beer type classification (can, bottle, or draught).
- Automatic beer tracking via WhatsApp image submissions
- Optional AI beer classification using Google Gemini
- Leaderboard and statistics tracking
- Undo last submission (10-minute window)
- Admin commands for beer removal
- PostgreSQL database with Prisma ORM
- Duplicate detection via image hashing
- Structured logging with Pino
For comprehensive guides and documentation, visit the Wiki:
- Getting Started - Detailed setup instructions
- Configuration - Complete environment variable reference
- Architecture - System design and code structure
- Development Guide - Coding standards and contributing
- Commands - All available bot commands
- Docker (v20.10+)
- Docker Compose (v2.0+)
- Node.js v20.x or higher
- npm v10.x or higher
- PostgreSQL v16.x or higher
- Chromium browser (for WhatsApp Web interface)
-
Clone and configure
git clone <repository-url> cd 10000-beers cp .env.example .env
-
Edit
.envfile- Set
WHATSAPP_GROUP_ID(see Getting Group ID) - Set
ADMIN_IDS(comma-separated WhatsApp IDs) - Update
POSTGRES_PASSWORDfor production
- Set
-
Start services
docker compose up -d
-
Authenticate WhatsApp (first-time only)
docker compose logs -f bot
Scan the QR code with WhatsApp. Session will persist across restarts.
-
Access pgAdmin (optional) Navigate to
http://localhost:5050- Email:
admin@localhost.com - Password:
admin
- Email:
-
Clone and configure
git clone <repository-url> cd 10000-beers cp .env.example .env
-
Install dependencies
npm install
-
Set up PostgreSQL
# Create database createdb beers # Update .env with your database URL # DATABASE_URL=postgresql://username:password@localhost:5432/beers
-
Run database migrations
npm run prisma:migrate
-
Edit
.envfile- Set
WHATSAPP_GROUP_ID(see Getting Group ID) - Set
ADMIN_IDS(comma-separated WhatsApp IDs) - Set
IMAGE_STORAGE_PATH(defaults to/data/images) - Set
DATABASE_URLto your PostgreSQL connection string
- Set
-
Start development server
npm run dev
Scan the QR code with WhatsApp. Session persists in
.wwebjs_auth/session-10000-beers/. -
Build for production
npm run build npm start
| Variable | Description | Example |
|---|---|---|
DATABASE_URL |
PostgreSQL connection string | postgresql://user:pass@localhost:5432/beers |
WHATSAPP_GROUP_ID |
WhatsApp group ID to monitor | 1234567890@g.us |
| Variable | Default | Description |
|---|---|---|
NODE_ENV |
development |
Execution environment (development, production, test) |
LOG_LEVEL |
debug (dev) / info (prod) |
Log verbosity (debug, info, warn, error) |
ADMIN_IDS |
(empty) | Comma-separated admin WhatsApp IDs |
IMAGE_STORAGE_PATH |
/data/images |
Directory for storing beer images |
MAX_IMAGE_SIZE_MB |
10 |
Maximum image size in megabytes |
SUBMISSION_COOLDOWN_MINUTES |
0 |
Minimum time between submissions per user |
REPLY_ON_SUBMISSION |
true |
When false, suppress the AI rejection reply ("Doesn't look like a beer to me mate") |
AI_ENABLED |
false |
Enable AI beer classification |
AI_CONFIDENCE_THRESHOLD |
0.9 |
Minimum confidence for AI classification (0.0-1.0) |
GEMINI_API_KEY |
(required if AI enabled) | Google Gemini API key |
GEMINI_MODEL |
gemini-1.5-flash |
Gemini model to use |
GITHUB_REPO_OWNER |
gabrielg2020 |
GitHub repository owner for !release command |
GITHUB_REPO_NAME |
10000-beers |
GitHub repository name for !release command |
STARTUP_WAIT |
0 |
Seconds to wait before initialisation |
PUPPETEER_EXECUTABLE_PATH |
(auto-detected) | Path to Chromium executable |
| Variable | Default | Description |
|---|---|---|
POSTGRES_PASSWORD |
beers_dev_password |
PostgreSQL database password |
PGADMIN_EMAIL |
admin@localhost.com |
pgAdmin login email |
PGADMIN_PASSWORD |
admin |
pgAdmin login password |
- Start the bot in development mode
- Send a message in your group
- Check the logs for group ID in format
1234567890@g.usor1234567890@lid - Copy the ID to your
.envfile
| Command | Description | Access |
|---|---|---|
!undo |
Remove your last beer submission (10-minute window) | All users |
!leaderboard |
Display beer leaderboard | Admin only |
!removeLast @user |
Remove that user's last beer submission | Admin only |
!release [version] |
Fetch and display latest GitHub release | Admin only |
# Run all tests
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage# Generate Prisma client
npm run prisma:generate
# Create migration
npm run prisma:migrate
# Deploy migrations (production)
npm run prisma:migrate:prod
# Open Prisma Studio
npm run prisma:studiosrc/
├── index.ts # Application entry point
├── config/ # Configuration loader and validation
├── handlers/ # Message and command routing
├── commands/ # Command implementations
├── services/ # Business logic (beer, user, image, AI, statistics)
├── database/ # Prisma schema and client
├── types/ # TypeScript type definitions
├── utils/ # Logging, validation, file operations
└── whatsapp/ # WhatsApp client factory
tests/
└── unit/ # Unit tests with mocked dependencies
WhatsApp authentication persists across restarts:
- Development:
.wwebjs_auth/session-10000-beers/ - Docker: Bind mount ensures session survives container restarts
- First-time setup: Scan QR code once, session saved automatically
- Check logs:
docker compose logs -f bot(Docker) or console output (non-Docker) - Delete session:
rm -rf .wwebjs_auth/session-10000-beers/ - Restart bot and re-scan QR code
- Verify PostgreSQL is running:
docker compose psorpg_isready - Check
DATABASE_URLformat:postgresql://user:password@host:port/database - Ensure migrations are applied:
npm run prisma:migrate
- Verify
IMAGE_STORAGE_PATHdirectory exists and is writable - Check available disk space
- Docker: Ensure volume is properly mounted
- Docker: Chromium installed automatically in container
- Non-Docker: Install Chromium via package manager
# Debian/Ubuntu sudo apt-get install chromium # macOS brew install chromium
- Set
PUPPETEER_EXECUTABLE_PATHif auto-detection fails
This project is licensed under the MIT License - see the LICENSE file for details.
Built with 💻 by Gabriel Guimaraes