Overview
The Price Tracker Bot uses node-cron to schedule automated tasks. Two main cron jobs run continuously:
Daily Summary - Sends product list to all registered chats at 20:00
Price Checks - Scrapes Amazon prices every 2 hours and notifies on price drops
Both schedules use standard cron syntax and run based on the server’s system timezone.
Cron Job Implementation
The bot imports and uses the node-cron library:
import cron from "node-cron" ;
All cron schedules run in the bot’s process. If the bot is stopped, scheduled tasks won’t execute until it restarts.
Daily Summary Schedule
Schedule Configuration
The daily summary runs at 20:00 (8:00 PM) server time:
index.mjs (lines 719-730)
// Daily summary at 20:00 (server timezone)
cron . schedule ( "0 20 * * *" , async () => {
console . log ( "📄 Envío de resumen diario..." );
for ( const chatId of chats ) {
try {
await dailySummary ( chatId );
await new Promise ( r => setTimeout ( r , 400 ));
} catch ( err ) {
console . error ( `❌ Error enviando resumen a ${ chatId } :` , err . message );
}
}
});
Cron Syntax Breakdown
"0 20 * * *"
│ │ │ │ │
│ │ │ │ └─── Day of week (0-7, Sunday=0 or 7)
│ │ │ └───── Month (1-12)
│ │ └─────── Day of month (1-31)
│ └────────── Hour (0-23)
└──────────── Minute (0-59)
Translation : At minute 0 of hour 20, every day
Daily Summary Schedule
string
default: "0 20 * * *"
Cron expression for when to send daily product summaries to all registered chats.
What the Daily Summary Does
Iterates through all registered chats stored in the chats Set
Sends interactive product list with buttons for each tracked product
Adds 400ms delay between messages to avoid Telegram rate limits
Handles errors gracefully - failures for one chat don’t stop others
Users receive a message with inline buttons to view details, check charts, or manage their tracked products.
Price Check Schedule
Schedule Configuration
Price checks run every 2 hours:
index.mjs (lines 732-740)
// Check prices every 2 hours
cron . schedule ( "0 */2 * * *" , async () => {
console . log ( "🔄 Cron: revisión automática de precios..." );
try {
await checkPrices ();
} catch ( err ) {
console . error ( "❌ Error en cron checkPrices:" , err . message );
}
});
Cron Syntax Breakdown
"0 */2 * * *"
│ │ │ │ │
│ │ │ │ └─── Day of week (any)
│ │ │ └───── Month (any)
│ │ └─────── Day of month (any)
│ └─────────── Hour (every 2 hours)
└───────────── Minute (at minute 0)
Translation : At minute 0 of every 2nd hour (00:00, 02:00, 04:00, …, 22:00)
Price Check Schedule
string
default: "0 */2 * * *"
Cron expression for when to scrape product prices and check for changes.
Execution Times
With the */2 pattern, checks occur at:
Time Execution 00:00 ✅ Midnight check 02:00 ✅ Early morning 04:00 ✅ 06:00 ✅ 08:00 ✅ Morning 10:00 ✅ 12:00 ✅ Noon 14:00 ✅ Afternoon 16:00 ✅ 18:00 ✅ Evening 20:00 ✅ (+ Daily Summary) 22:00 ✅ Night
Total : 12 price checks per day (every 2 hours)
What Price Checks Do
Launch Chromium browser via Playwright
Visit each tracked product URL on Amazon
Scrape current price and compare to stored value
Detect price drops and calculate savings
Send notifications to all registered chats for price reductions
Update price history (keeping last 120 entries per product)
Save data to persistence file
Price checks can take several minutes if you’re tracking many products. Each product is scraped sequentially with 900ms delays between requests.
Modifying Schedules
Changing Check Frequency
To check prices more or less frequently, modify the cron expression:
Every Hour
Every 4 Hours
Every 30 Minutes
Twice Daily (6 AM and 6 PM)
cron . schedule ( "0 * * * *" , async () => {
console . log ( "🔄 Cron: revisión automática de precios..." );
try {
await checkPrices ();
} catch ( err ) {
console . error ( "❌ Error en cron checkPrices:" , err . message );
}
});
Recommended frequency : Every 2-4 hours balances timely notifications with server load and Amazon rate limiting.
Changing Daily Summary Time
To send summaries at a different time, modify the hour value:
Morning (8 AM)
Noon (12 PM)
Evening (18:00 / 6 PM)
cron . schedule ( "0 8 * * *" , async () => {
console . log ( "📄 Envío de resumen diario..." );
for ( const chatId of chats ) {
try {
await dailySummary ( chatId );
await new Promise ( r => setTimeout ( r , 400 ));
} catch ( err ) {
console . error ( `❌ Error enviando resumen a ${ chatId } :` , err . message );
}
}
});
Disabling Scheduled Tasks
To disable a cron job, comment it out or remove it:
// Disabled: Daily summary
// cron.schedule("0 20 * * *", async () => {
// console.log("📄 Envío de resumen diario...");
// ...
// });
// Price checks still active
cron . schedule ( "0 */2 * * *" , async () => {
console . log ( "🔄 Cron: revisión automática de precios..." );
try {
await checkPrices ();
} catch ( err ) {
console . error ( "❌ Error en cron checkPrices:" , err . message );
}
});
Users can still manually trigger price checks using the /check command or ”🔄 Revisar precios ahora” button.
Advanced Cron Patterns
Business Hours Only
Check prices only during business hours (9 AM - 6 PM, Monday-Friday):
cron . schedule ( "0 9-18 * * 1-5" , async () => {
console . log ( "🔄 Cron: revisión automática de precios..." );
try {
await checkPrices ();
} catch ( err ) {
console . error ( "❌ Error en cron checkPrices:" , err . message );
}
});
Pattern breakdown : 0 9-18 * * 1-5
Minute: 0 (top of the hour)
Hour: 9-18 (9 AM through 6 PM)
Day of month: * (any)
Month: * (any)
Day of week: 1-5 (Monday through Friday)
Weekend Only
Check prices only on weekends:
cron . schedule ( "0 */3 * * 0,6" , async () => {
console . log ( "🔄 Cron: revisión automática de precios (fin de semana)..." );
try {
await checkPrices ();
} catch ( err ) {
console . error ( "❌ Error en cron checkPrices:" , err . message );
}
});
Pattern : 0 */3 * * 0,6 - Every 3 hours on Sunday (0) and Saturday (6)
Multiple Daily Summaries
Send summaries twice per day:
cron . schedule ( "0 9,21 * * *" , async () => {
console . log ( "📄 Envío de resumen diario..." );
for ( const chatId of chats ) {
try {
await dailySummary ( chatId );
await new Promise ( r => setTimeout ( r , 400 ));
} catch ( err ) {
console . error ( `❌ Error enviando resumen a ${ chatId } :` , err . message );
}
}
});
Pattern : 0 9,21 * * * - At 9:00 AM and 9:00 PM daily
Timezone Considerations
Server Timezone
Cron jobs run based on the server’s system timezone , not user timezones.
Check your server timezone :
date
timedatectl # On Linux with systemd
Example output :
Mon Mar 10 14:30:00 PST 2026
Time zone: America/Los_Angeles (PST, -0800)
If your server is in UTC but you want schedules in Pacific Time, adjust the hour values accordingly. For example, 20:00 PST = 04:00 UTC the next day.
Setting Server Timezone
On Linux :
# Set to Pacific Time
sudo timedatectl set-timezone America/Los_Angeles
# Set to UTC
sudo timedatectl set-timezone UTC
# Set to Eastern Time
sudo timedatectl set-timezone America/New_York
On Docker :
services :
price-tracker :
image : price-tracker
environment :
- TZ=America/Los_Angeles
volumes :
- /etc/localtime:/etc/localtime:ro
Restart the bot after changing the system timezone for cron schedules to use the new timezone.
Rate Limiting
The bot includes delays to avoid overwhelming Amazon and Telegram:
Between products during price checks (index.mjs:214):
await new Promise (( r ) => setTimeout ( r , 900 ));
Between chat notifications (index.mjs:242):
await new Promise (( r ) => setTimeout ( r , 500 ));
Between daily summaries (index.mjs:725):
await new Promise ( r => setTimeout ( r , 400 ));
Adjusting Delays
If you encounter rate limiting, increase these delays:
// Slower scraping to avoid detection
await new Promise (( r ) => setTimeout ( r , 2000 )); // 2 seconds instead of 900ms
// More conservative Telegram messaging
await new Promise (( r ) => setTimeout ( r , 1000 )); // 1 second instead of 500ms
Longer delays mean slower price checks but reduced risk of being rate-limited or blocked by Amazon.
Memory Management
With many products, the history can grow large. The HISTORY_LIMIT prevents unbounded growth:
index.mjs (lines 69, 196)
const HISTORY_LIMIT = 120 ;
// In checkPrices function:
updated . history . push ({ date: new Date (). toISOString (), price: scraped . price });
if ( updated . history . length > HISTORY_LIMIT )
updated . history = updated . history . slice ( - HISTORY_LIMIT );
Memory impact :
10 products × 120 history entries × ~50 bytes = ~60 KB
100 products × 120 history entries × ~50 bytes = ~600 KB
For tracking 100+ products, consider reducing HISTORY_LIMIT to 60 (5 days of history) or increasing price check intervals to every 4 hours.
Monitoring and Debugging
Console Logging
The bot outputs cron execution logs:
🔄 Cron: revisión automática de precios...
⏳ Iniciando revisión de precios...
🎉 2 productos cambiados — notificando a 3 chats.
Verifying Cron Syntax
Use crontab.guru to validate and understand cron expressions:
Testing Schedules
To test without waiting for scheduled times, temporarily change to run every minute:
// TESTING ONLY - runs every minute
cron . schedule ( "* * * * *" , async () => {
console . log ( "🔄 Test: revisión automática de precios..." );
try {
await checkPrices ();
} catch ( err ) {
console . error ( "❌ Error en cron checkPrices:" , err . message );
}
});
Do not use * * * * * in production! It runs every minute and will quickly exhaust resources and trigger rate limits.
Manual Execution
Via Telegram Commands
Users don’t need to wait for scheduled checks:
/check - Manually trigger price check for all products
/list - View daily summary UI without waiting for 20:00
Via Code
Call the functions directly for testing:
// Manually invoke price check
await checkPrices ();
// Send summary to specific chat
await dailySummary ( 123456789 );
Troubleshooting
Cron Jobs Not Running
Symptom : Scheduled tasks never execute
Solutions :
Verify bot is running continuously (not restarting)
Check console for syntax errors in cron expressions
Ensure node-cron is installed: npm list node-cron
Wrong Execution Times
Symptom : Tasks run at unexpected hours
Solutions :
Check server timezone: date
Verify cron expression using crontab.guru
Remember: 20:00 server time may differ from your local time
Duplicate Executions
Symptom : Price checks run multiple times per hour
Solution : Ensure you haven’t defined the same cron schedule multiple times in the code
Rate Limiting
Symptom : Amazon blocks requests or Telegram rate limits messages
Solutions :
Increase delays between requests (see Performance Optimization)
Reduce check frequency (every 4 hours instead of 2)
Use residential proxies for Amazon requests (advanced)
Next Steps
Setup Guide Review initial setup and configuration
Environment Variables Configure Telegram bot token and secrets