Smart Telegram bot for real-time monitoring of classified ads on list.am
Features β’ Quick Start β’ Architecture β’ Development β’ Support
π Documentation: Quick Start β’ Development Guide β’ CI/CD Setup β’ Contributing β’ Code of Conduct β’ Security Policy β’ Changelog
If you find this bot useful and want to support its development:
Give this project a star on GitHub β it helps others discover it!
Your donations help maintain and improve the bot:
| Method | Details |
|---|---|
| Buy Me a Coffee β | buymeacoffee.com/zinovev_space |
| USDT (TRC20) | TTxEjq3w2jy1bgRSsrvwXhZB2VaKfkureh |
| Armenian Visa (ID Bank) | 4318270001094190 |
| Russian MIR (T-Bank) | 2200700167792802 |
Interested in sponsoring this project? Let's talk!
- Regular feature updates
- Priority support
- Your logo in README
- Custom integrations
Contact via GitHub Discussions or Telegram
List.am Bot is a powerful Telegram bot that helps you never miss important deals on list.am, Armenia's largest classifieds platform. Set up custom search queries and receive instant notifications when matching listings appear.
- β‘ Real-time Notifications β Get instant alerts for new listings matching your criteria
- π― Smart Filtering β Advanced duplicate detection ensures you only see new ads
- π Automatic Monitoring β Scheduled scraping with configurable intervals
- πΎ Persistent Sessions β PostgreSQL-backed Telegram sessions for reliability
- π‘οΈ Production Ready β Built with clean architecture, error handling, and monitoring
- π Scalable β Queue-based task processing with priority management
- π³ Docker Support β Easy deployment with Docker Compose
-
π Subscription Management
- Create text-based search subscriptions
- Create URL-based subscriptions with custom filters
- Pause/resume notifications per user
- View active subscriptions with type indicators
- Delete individual or all subscriptions
- Named subscriptions for better organization
- User subscription limit: 10 subscriptions per user
- Automatic cleanup when user blocks the bot
-
π Intelligent Scraping
- HTML parsing with Cheerio
- CloudFlare bypass support via FlareSolverr
- Proxy rotation for reliability
- Configurable retry logic and delays
-
π¬ Smart Notifications
- Rich message formatting with listing details
- Inline buttons for quick actions
- Duplicate detection to avoid spam
- User-friendly error handling
-
ποΈ Advanced Bot Features
- Multi-step wizards for subscription creation
- Typed context with session support
- Global exception filter with admin notifications
- Webhook support for production deployments
-
π Monitoring & Metrics
- Real-time metrics collection and storage
- Scraping performance tracking (duration, success rate)
- Notification delivery statistics
- Queue size monitoring
- Active subscriptions tracking
- Daily automated reports to admin (24h, 7d, 30d)
- Clean Architecture with Domain-Driven Design principles
- TypeORM with migrations and repository pattern
- Zod validation for configuration
- Winston logging with daily rotation
- Cron-based scheduling with manual queue override
- Transaction support for data consistency
- Built-in metrics for performance monitoring
- Rate limiting for Telegram API and list.am scraping
- Health checks with automatic fallback mechanisms
The project follows a layered architecture separating concerns:
src/
βββ application/ # Application services & use cases
β βββ monitoring/ # Metrics collection & analysis
β βββ notification/ # Notification logic
β βββ scheduler/ # Job scheduling & queue management
β βββ subscription/ # Subscription business logic
β βββ user/ # User management
βββ domain/ # Domain entities & ports
β βββ delivery/ # Notification delivery tracking
β βββ metric/ # Performance metrics
β βββ seen-listing/ # Listing history
β βββ subscription/ # User subscriptions
β βββ user/ # User entities
βββ infrastructure/ # External implementations
β βββ database/ # TypeORM configuration & repositories
β βββ scheduler/ # NestJS Schedule implementation
β βββ scraper/ # Web scraping infrastructure
βββ interfaces/ # External interfaces
β βββ bot/ # Telegram bot handlers
β βββ actions/ # Button callbacks
β βββ keyboards/ # Inline keyboards
β βββ messages/ # Message templates
β βββ scenes/ # Multi-step conversations
βββ common/ # Shared utilities
βββ config/ # Configuration modules
βββ filters/ # Exception filters
βββ formatters/ # Message formatters
βββ keyboards/ # Keyboard factories
βββ utils/ # Helper functions
- Repository Pattern β Abstracts data access with port/adapter pattern
- Factory Pattern β Centralized creation of keyboards and messages
- Strategy Pattern β Pluggable scraper implementations
- Observer Pattern β Event-driven notification system
- Queue Pattern β Priority-based task processing
The bot includes a built-in metrics system that tracks key performance indicators:
| Metric Type | Description | Storage |
|---|---|---|
| Scrape Duration | Time taken to scrape each query (ms) | Database with query metadata |
| Notification Success | Successful notification deliveries | Database with user and listing IDs |
| Notification Failure | Failed notifications with error reasons | Database with failure details |
| Queue Size | Current scraping queue size | Database snapshots |
| Active Subscriptions | Total active subscriptions per cycle | Database records |
Metrics are automatically collected during bot operation:
- Scraping metrics β recorded after each scrape attempt
- Notification metrics β tracked on every delivery attempt
- Queue metrics β updated when tasks are added
- Subscription metrics β collected during each scrape cycle
All metrics are stored in PostgreSQL and can be queried for:
- Performance analysis and optimization
- Success rate calculation
- Capacity planning
- Troubleshooting and debugging
Note: Metrics do not impact bot performance as they are saved asynchronously with error handling.
If BOT_INCIDENTS_USER_ID is configured, the admin receives automated daily reports at 9:00 AM with metrics for:
- Last 24 hours β recent performance snapshot
- Last 7 days β weekly trends
- Last 30 days β monthly overview
Each report includes:
- Average scrape duration
- Total notifications sent and success rate
- Average queue size
- Average active subscriptions
The bot implements comprehensive rate limiting and health check mechanisms to ensure reliable operation and prevent API abuse.
To respect API limits and prevent throttling, the bot uses token bucket rate limiting:
| Service | Limit | Description |
|---|---|---|
| Telegram API | 25 msg/sec | Conservative limit (official: 30/sec) |
| List.am | 2 req/sec | Respectful scraping rate |
How it works:
- Each request consumes one token from the bucket
- Tokens refill at a constant rate
- Requests are queued when no tokens are available
- Automatic backpressure prevents API overload
Implementation:
// Telegram notifications automatically rate-limited
await notificationService.sendListingNotification(payload);
// Scraping requests automatically rate-limited
const html = await flaresolvrrService.fetchHtml(url);The scraping system includes multiple layers of resilience:
-
Health Checks
- Periodic connection tests (every 60 seconds)
- Automatic availability tracking
- Status logging for monitoring
-
Retry with Exponential Backoff
- Up to 3 retry attempts per request
- Increasing delays: 1s β 2s β 4s (max 5s)
- Prevents overwhelming failed services
-
Graceful Degradation
- Logs failures without stopping the bot
- Continues with available functionality
- Admin receives error notifications
Configuration:
# FlareSolverr connection settings
FLARESOLVERR_URL=http://listambot.flaresolverr:8191
FLARESOLVERR_PORT=8191
FLARESOLVERR_MAX_TIMEOUT=60000To ensure optimal performance and fair resource allocation, each user is limited to:
- Maximum 10 active subscriptions per user
When attempting to create an 11th subscription, users will receive a friendly message:
β ΠΠΎΡΡΠΈΠ³Π½ΡΡ Π»ΠΈΠΌΠΈΡ ΠΏΠΎΠ΄ΠΏΠΈΡΠΎΠΊ: 10
Π£Π΄Π°Π»ΠΈΡΠ΅ Π½Π΅Π½ΡΠΆΠ½ΡΠ΅ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠΈ, ΡΡΠΎΠ±Ρ Π΄ΠΎΠ±Π°Π²ΠΈΡΡ Π½ΠΎΠ²ΡΠ΅.
Why this limit?
- Prevents resource abuse
- Ensures consistent bot performance for all users
- Encourages focused, relevant subscriptions
- Maintains database efficiency
The bot implements smart cleanup to respect user privacy and maintain data hygiene:
What happens when a user blocks the bot:
- β Bot detects block during notification attempt (HTTP 403 error)
- β Automatically finds all user's subscriptions
- β Deletes all subscriptions for that user
- β Logs cleanup action for monitoring
- β No notifications will be sent to blocked users
Benefits:
- Privacy-friendly β Data is removed when relationship ends
- Resource efficient β No wasted processing for blocked users
- GDPR compliant β Automatic data removal on user action
- Clean database β No orphaned subscriptions
Implementation Details:
// When bot is blocked (403 error)
if (isTelegramBotBlocked(error)) {
// Find user and their subscriptions
const user = await userService.findByTelegramUserId(telegramUserId);
const count = await subscriptionService.count(user.id);
// Clean up all subscriptions
if (count > 0) {
await subscriptionService.deleteAll(user.id);
logger.debug(`Cleaned up ${count} subscription(s) for blocked user`);
}
}User Experience:
- Silent cleanup β no errors thrown
- Happens automatically on next notification attempt
- User can restart with
/startif they unblock the bot later - Fresh start β all previous subscriptions are removed
Note: Users will need to recreate their subscriptions if they unblock the bot in the future.
π For detailed step-by-step instructions, see QUICK_START.md
- Docker & Docker Compose
- Telegram Bot Token (get from @BotFather)
-
Clone the repository
git clone https://github.com/zombiQWERTY/list_am_bot.git cd listambot -
Configure environment
cp env.example .env # Edit .env with your settings -
Start the project
make up
This command will:
- Build Docker images
- Start all services (PostgreSQL, bot)
- Set up the database
-
First time setup: Create database schema
If this is your first time running the project, create the database schema manually:
# Connect to the database container docker exec -it listambot.app bash # Inside container, run psql psql $POSTGRES_BASE_URL -c "CREATE SCHEMA IF NOT EXISTS core;" # Exit container exit
# Rebuild and restart entire project
make rebuild-all
# Rebuild and restart only the bot service
make rebuild-one
# Stop all services
make down
# View logs
make logsWhen adding new npm packages:
-
Install locally (for IDE autocomplete)
npm install <package-name>
-
Rebuild container to install inside Docker
make rebuild-one
# Production environment
docker build -t "listambot_base:latest" -f ./infra/prod/Dockerfile.base
docker build -t "listambot_app:latest" -f ./infra/prod/Dockerfile
--build-arg="BASE_IMAGE=listambot_base:latest"
sed -i "s#app_image#listambot_app:latest#g" docker-compose.prod.yml
docker compose -f docker-compose.prod.yml pull
docker-compose -f docker-compose.prod.yml up -dNote: Migrations run automatically in production environment.
The bot supports two types of subscriptions:
Simple keyword search across all categories:
User: /start
Bot: Welcome! Choose an action:
User: [Click "β ΠΠΎΠ±Π°Π²ΠΈΡΡ ΡΠ΅ΠΊΡΡΠΎΠΌ"]
Bot: Send your search query
User: Chevrolet Tahoe
Bot: β
Subscription created!
Best for:
- Simple keyword searches
- Brand/model names
- Generic terms
Advanced filtering using list.am's filter system:
User: /start
Bot: Welcome! Choose an action:
User: [Click "π ΠΠΎΠ±Π°Π²ΠΈΡΡ ΠΏΠΎ URL"]
Bot: Send a list.am URL with filters
User: https://www.list.am/category/212?n=2&price1=10000&price2=50000&cid=0
Bot: Name your subscription (3-100 characters)
User: Tahoe under $50k
Bot: β
Subscription created: "Tahoe under $50k"
Best for:
- Category-specific searches
- Price range filtering
- Region/location filtering
- Combined filters
- Visit list.am
- Select your category (e.g., Cars & Motorcycles)
- Apply desired filters:
- Price range
- Region
- Condition
- etc.
- Copy the URL from browser's address bar
- Paste it into the bot
Example URLs:
Cars $10k-$50k in Yerevan:
https://www.list.am/category/212?n=2&price1=10000&price2=50000&cid=0
Electronics in Avan:
https://www.list.am/category/224?n=2&cid=14
Real Estate 2+ bedrooms:
https://www.list.am/category/0/rooms-2/
URL subscriptions are displayed with a π icon and their custom name:
π Your subscriptions (3):
1. Chevrolet Tahoe [π]
2. π Tahoe under $50k [π]
3. π Electronics in Avan [π]
Each user can create up to 10 subscriptions (text + URL combined):
π Your subscription usage: 7/10
β
3 more subscriptions available
β At limit β delete old subscriptions to add new ones
To free up slots:
- Use
/listor click "π Π‘ΠΏΠΈΡΠΎΠΊ ΠΎΡΡΠ»Π΅ΠΆΠΈΠ²Π°Π΅ΠΌΡΡ " - Click π next to unwanted subscription
- Create new subscriptions
- Maximum 10 subscriptions per user to ensure optimal performance
- URL subscriptions must be from
list.amdomain only - Names help identify URL subscriptions at a glance
- You can have both text and URL subscriptions simultaneously
- Notifications include the subscription name/query for context
- Subscriptions auto-delete if you block the bot β restart fresh by unblocking and using
/start
| Variable | Description | Default | Required |
|---|---|---|---|
BOT_TOKEN |
Telegram Bot API token | - | β |
BOT_INCIDENTS_USER_ID |
Admin Telegram ID for error notifications | - | β |
BOT_DOMAIN |
Domain for webhook (production) | - | β |
BOT_WEBHOOK_URL |
Webhook path | /telegram-webhook |
β |
POSTGRES_HOST |
PostgreSQL host | localhost |
β |
POSTGRES_PORT |
PostgreSQL port | 5432 |
β |
POSTGRES_USERNAME |
Database user | - | β |
POSTGRES_PASSWORD |
Database password | - | β |
POSTGRES_NAME |
Database name | listambot |
β |
POSTGRES_TELEGRAF_SCHEMA |
Schema for Telegraf sessions | public |
β |
NODE_ENV |
Environment mode | development |
β |
FETCH_TIMEOUT_MS |
HTTP request timeout (ms) | 15000 |
β |
FLARESOLVERR_URL |
FlareSolverr service URL | http://localhost:8191 |
β |
FLARESOLVERR_PORT |
FlareSolverr service port | 8191 |
β |
FLARESOLVERR_MAX_TIMEOUT |
FlareSolverr timeout (ms) | 60000 |
β |
For bypassing CloudFlare protection:
FLARESOLVERR_URL=http://localhost:8191
FLARESOLVERR_MAX_TIMEOUT=60000Start FlareSolverr:
docker run -d \
--name=flaresolverr \
-p 8191:8191 \
ghcr.io/flaresolverr/flaresolverr:latestmake up # Start all services
make down # Stop all services
make rebuild-all # Rebuild and restart entire project
make rebuild-one # Rebuild and restart only bot service
make logs # View bot logs only
make shell # Access bot container shellMigrations in development must be created and run manually:
-
Generate a new migration
# Access the container docker exec -it listambot.app bash # Inside container /tmp/typeorm-generate.sh <migration-name> # Example /tmp/typeorm-generate.sh AddUserEmailField
-
Run migrations
# Inside container /tmp/typeorm-migrate.sh -
Revert last migration (if needed)
# Inside container /tmp/typeorm-revert.sh
Migrations run automatically when the container starts.
| Command | Description |
|---|---|
/start |
Initialize bot and show main menu |
/add |
Add new subscription |
/list |
View active subscriptions |
/last |
View last item directly from list.am |
/pause |
Pause all notifications |
/resume |
Resume notifications |
/delete |
Delete specific subscription |
/deleteall |
Delete all subscriptions |
/help |
Show help message |
Problem: Error about missing schema on first run.
Solution:
# Access container
docker exec -it listambot.core bash
# Create schemas
psql $POSTGRES_BASE_URL -c "CREATE SCHEMA IF NOT EXISTS core;"
# Run migrations
/tmp/typeorm-migrate.shProblem: docker exec commands fail with "No such container".
Solution: Check actual container names:
docker ps
# Use the actual container name from the outputProblem: New npm package not found after npm install.
Solution:
# Always rebuild container after installing dependencies
make rebuild-oneProblem: PostgreSQL port 5432/5032 already in use.
Solution:
# Stop conflicting service or change port in docker-compose.dev.yml
# Then restart
make down
make upProblem: Tables not created in database.
Solution:
# Check if migrations exist
docker exec -it listambot.core ls -la src/infrastructure/database/migrations/
# Run migrations manually
docker exec -it listambot.core /tmp/typeorm-migrate.sh- Check logs:
make logs - Database issues:
make db-shellto inspect database directly - Container shell:
make shellto debug inside container - View all containers:
docker ps -a - Rebuild from scratch:
make down && make rebuild-all
- NestJS β Progressive Node.js framework
- TypeScript β Typed JavaScript
- Telegraf β Telegram Bot API framework
- TypeORM β ORM for TypeScript
- PostgreSQL β Relational database
- @telegraf/session β PostgreSQL session storage
- Cheerio β HTML parsing
- Axios β HTTP client
- Zod β Schema validation
- Winston β Logging framework
- NestJS Schedule β Cron jobs
- ESLint + Prettier β Code quality
- BotModule β Telegram bot interface
- UserModule β User management
- SubscriptionModule β Subscription handling
- ScraperModule β Web scraping
- WorkerModule β Background jobs
- SchedulerModule β Task scheduling
Users β Store Telegram user data Subscriptions β User search queries SeenListings β Track viewed listings per subscription Deliveries β Notification delivery history
Contributions are welcome! Please read our Contributing Guidelines and Code of Conduct before getting started.
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Follow the existing code style
- Write meaningful commit messages
- Add tests for new features
- Update documentation as needed
- Run linter before committing
Found a security vulnerability? Please report it responsibly. See our Security Policy for details.
This project is licensed under the WTFPL (Do What The Fuck You Want To Public License) - see the LICENSE file for details.
TL;DR: You just DO WHAT THE FUCK YOU WANT TO. π
- Quick Start Guide β Get up and running in 5 minutes
- Development Workflow β Comprehensive development guide
- CI/CD Setup β GitHub Actions workflows and deployment
- Contributing Guidelines β How to contribute
- Changelog β Version history and release notes
- list.am β For providing the classifieds platform
- NestJS β For the amazing framework
- Telegraf β For the Telegram bot library
For questions, suggestions, or issues, please:
- Open an issue
- Start a discussion
Made with β€οΈ and TypeScript
If you find this project useful, please give it a βοΈ
Every contribution counts! Thank you for your support! π