diff --git a/TELEGRAM_INTEGRATION_GUIDE.md b/TELEGRAM_INTEGRATION_GUIDE.md
new file mode 100644
index 0000000..1a1fbd0
--- /dev/null
+++ b/TELEGRAM_INTEGRATION_GUIDE.md
@@ -0,0 +1,394 @@
+# Telegram Notification Integration Guide
+
+This guide explains how to manually integrate the Telegram notification system into your existing `server.py` file.
+
+## Quick Setup
+
+If you want to quickly get started:
+
+1. **Install dependencies:**
+ ```bash
+ pip install -r requirements.txt
+ ```
+
+2. **Set up your Telegram bot** (see [setup_telegram.md](setup_telegram.md))
+
+3. **Set environment variables:**
+ ```bash
+ export TELEGRAM_BOT_TOKEN="your_bot_token_here"
+ export TELEGRAM_CHAT_ID="your_chat_id_here"
+ ```
+
+4. **Apply the integration** (see below)
+
+---
+
+## Manual Integration Steps
+
+### Step 1: Add Imports to server.py
+
+At the top of `server.py`, after the existing imports, add:
+
+```python
+# Telegram Notification System
+import asyncio
+from notification_system import init_notifier, send_notification_sync
+
+# Global notifier instance
+notifier = None
+```
+
+### Step 2: Modify classify_image() Function
+
+Find the `classify_image()` function. After this line:
+```python
+prediction_history.appendleft(result)
+```
+
+Add the following code:
+
+```python
+ # Send Telegram notification for concerning stages
+ if notifier and notifier.enabled:
+ stage = result.get('stage', 0)
+ if stage in getattr(config, 'TELEGRAM_ALERT_ON_STAGES', [2, 3]):
+ try:
+ send_notification_sync(
+ notifier,
+ notifier.send_freshness_alert(
+ stage=stage,
+ stage_name=result['stage_name'],
+ confidence=result['confidence'],
+ filename=result['filename'],
+ image_path=image_path if getattr(config, 'TELEGRAM_SEND_PHOTO', True) else None,
+ stage_probabilities=result.get('stage_probabilities'),
+ hex_colors=result.get('hex_colors')
+ )
+ )
+ print(f\" Telegram notification sent for {result['stage_name']}\")
+ except Exception as e:
+ print(f\" Telegram notification error: {e}\")
+```
+
+### Step 3: Add API Endpoints
+
+Add these new API endpoints before the `main()` function:
+
+```python
+@app.route("/api/test-notification", methods=["POST"])
+def test_notification():
+ """Test Telegram notification system."""
+ if not notifier or not notifier.enabled:
+ return jsonify({
+ "error": "Telegram notifications not configured",
+ "message": "Set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID environment variables"
+ }), 503
+
+ try:
+ success = send_notification_sync(notifier, notifier.send_test_notification())
+ if success:
+ return jsonify({
+ "success": True,
+ "message": "Test notification sent successfully!"
+ })
+ else:
+ return jsonify({
+ "success": False,
+ "message": "Failed to send test notification"
+ }), 500
+ except Exception as e:
+ return jsonify({
+ "success": False,
+ "error": str(e)
+ }), 500
+
+
+@app.route("/api/notification-status", methods=["GET"])
+def notification_status():
+ """Get Telegram notification system status."""
+ if not notifier:
+ return jsonify({
+ "enabled": False,
+ "configured": False,
+ "message": "Notifier not initialized"
+ })
+
+ return jsonify({
+ "enabled": notifier.enabled,
+ "configured": bool(notifier.bot_token and notifier.chat_id),
+ "bot_token_set": bool(notifier.bot_token),
+ "chat_id_set": bool(notifier.chat_id),
+ "alert_stages": getattr(config, 'TELEGRAM_ALERT_ON_STAGES', [2, 3]),
+ "send_photos": getattr(config, 'TELEGRAM_SEND_PHOTO', True)
+ })
+```
+
+### Step 4: Initialize Notifier in main()
+
+Find the `main()` function. After the `load_model()` call, add:
+
+```python
+ # Initialize Telegram notifier
+ global notifier
+ if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+ if notifier and notifier.enabled:
+ print("✅ Telegram notifications: ENABLED")
+ print(f" Chat ID: {notifier.chat_id}")
+
+ # Send startup notification if configured
+ if getattr(config, 'TELEGRAM_NOTIFY_ON_STARTUP', True):
+ try:
+ send_notification_sync(
+ notifier,
+ notifier.send_system_health(
+ status="starting",
+ uptime_seconds=0,
+ total_predictions=0,
+ model_loaded=True
+ )
+ )
+ print(" Startup notification sent")
+ except Exception as e:
+ print(f" ⚠️ Failed to send startup notification: {e}")
+ else:
+ print("⚠️ Telegram notifications: DISABLED (check configuration)")
+ else:
+ print("Telegram notifications: DISABLED (via config)")
+```
+
+### Step 5: Initialize for Production (Gunicorn)
+
+Find the module-level initialization section (at the bottom of server.py, before `if __name__ == "__main__":`).
+
+After these lines:
+```python
+server_start_time = datetime.now()
+```
+
+Add:
+
+```python
+# Initialize Telegram notifier for gunicorn/production
+if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+```
+
+---
+
+## Testing the Integration
+
+### 1. Test Notification Status
+
+```bash
+curl http://localhost:5000/api/notification-status
+```
+
+Expected response:
+```json
+{
+ "enabled": true,
+ "configured": true,
+ "bot_token_set": true,
+ "chat_id_set": true,
+ "alert_stages": [2, 3],
+ "send_photos": true
+}
+```
+
+### 2. Send Test Notification
+
+```bash
+curl -X POST http://localhost:5000/api/test-notification
+```
+
+You should receive a test message in Telegram!
+
+### 3. Test with Real Image
+
+Upload an image that triggers Stage 3 or 4:
+
+```bash
+curl -X POST -F "image=@spoiled_sample.jpg" http://localhost:5000/barcode
+```
+
+Check your Telegram for the freshness alert!
+
+---
+
+## Troubleshooting
+
+### "Telegram notifications: DISABLED"
+
+**Problem:** Notifier is disabled even though configuration is set.
+
+**Solutions:**
+- Check environment variables are set: `echo $TELEGRAM_BOT_TOKEN`
+- Verify bot token format (should be like `1234567890:ABC...`)
+- Ensure `python-telegram-bot` is installed: `pip install python-telegram-bot`
+- Check config.py: `TELEGRAM_ENABLED` should be `True`
+
+### No Notifications Received
+
+**Problem:** Server runs but no Telegram messages arrive.
+
+**Solutions:**
+- Test with `/api/test-notification` endpoint first
+- Verify Chat ID is correct (use `@userinfobot` to get it)
+- Check server logs for error messages
+- Ensure bot is not blocked in Telegram
+- Verify stage threshold: only Stage 2 and 3 trigger notifications by default
+
+### Import Error
+
+**Problem:** `ModuleNotFoundError: No module named 'telegram'`
+
+**Solution:**
+```bash
+pip install python-telegram-bot==20.7
+```
+
+### Async Warnings
+
+**Problem:** Warnings about event loops in console.
+
+**Solution:** This is normal and can be ignored. Notifications will still work. To suppress warnings, upgrade to Python 3.9+.
+
+---
+
+## Configuration Options
+
+All settings are in `config.py`:
+
+```python
+# Basic Configuration
+TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", None)
+TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", None)
+TELEGRAM_ENABLED = True
+
+# Notification Triggers
+TELEGRAM_ALERT_ON_STAGES = [2, 3] # Which stages trigger notifications
+TELEGRAM_PHOTO_MIN_STAGE = 2 # Minimum stage to send photos
+TELEGRAM_SEND_PHOTO = True # Include photos in notifications
+
+# Advanced
+TELEGRAM_NOTIFY_ON_STARTUP = True # Send notification on server start
+TELEGRAM_DAILY_SUMMARY_ENABLED = True # Daily summary reports
+TELEGRAM_DAILY_SUMMARY_TIME = "18:00" # Time for daily summary
+TELEGRAM_HEALTH_CHECK_INTERVAL = 3600 # Health check interval (seconds)
+```
+
+---
+
+## Advanced Features
+
+### Daily Summaries
+
+To enable daily summaries, you can add a scheduled task. Install the `schedule` library:
+
+```bash
+pip install schedule
+```
+
+Then add this code to `server.py`:
+
+```python
+import schedule
+
+def send_daily_summary():
+ """Send daily summary of predictions."""
+ if notifier and notifier.enabled:
+ predictions = list(prediction_history)
+ send_notification_sync(notifier, notifier.send_daily_summary(predictions))
+
+# Schedule daily summary
+if getattr(config, 'TELEGRAM_DAILY_SUMMARY_ENABLED', True):
+ summary_time = getattr(config, 'TELEGRAM_DAILY_SUMMARY_TIME', "18:00")
+ schedule.every().day.at(summary_time).do(send_daily_summary)
+
+ # Run scheduler in background thread
+ def run_scheduler():
+ while True:
+ schedule.run_pending()
+ time.sleep(60)
+
+ scheduler_thread = threading.Thread(target=run_scheduler, daemon=True)
+ scheduler_thread.start()
+```
+
+### Custom Alert Rules
+
+Add custom logic in `classify_image()`:
+
+```python
+# Alert only during business hours
+import datetime
+current_hour = datetime.datetime.now().hour
+if 9 <= current_hour <= 17:
+ # Send notification
+ send_notification_sync(...)
+
+# Alert on specific confidence thresholds
+if result['confidence'] > 0.9 and result['stage'] >= 2:
+ # High confidence spoilage detected
+ send_notification_sync(...)
+```
+
+---
+
+## Security Best Practices
+
+1. **Never commit tokens:** Always use environment variables
+2. **Use `.env` files:** Add `.env` to `.gitignore`
+3. **Rotate tokens regularly:** Generate new tokens monthly via BotFather
+4. **Restrict bot permissions:** Your bot only needs to send messages
+5. **Monitor usage:** Check for unauthorized access in BotFather
+
+---
+
+## Complete Example
+
+Here's a minimal working example combining all steps:
+
+```python
+# At top of server.py
+import asyncio
+from notification_system import init_notifier, send_notification_sync
+notifier = None
+
+# In classify_image(), after prediction_history.appendleft(result)
+if notifier and notifier.enabled and result.get('stage', 0) in [2, 3]:
+ try:
+ send_notification_sync(notifier, notifier.send_freshness_alert(
+ stage=result['stage'],
+ stage_name=result['stage_name'],
+ confidence=result['confidence'],
+ filename=result['filename'],
+ image_path=image_path,
+ stage_probabilities=result.get('stage_probabilities'),
+ hex_colors=result.get('hex_colors')
+ ))
+ except Exception as e:
+ print(f\"Notification error: {e}\")
+
+# In main(), after load_model()
+global notifier
+notifier = init_notifier()
+if notifier and notifier.enabled:
+ print("✅ Telegram notifications enabled")
+
+# At module level
+if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+```
+
+---
+
+## Support
+
+For more information:
+- [Setup Guide](setup_telegram.md) - Bot creation instructions
+- [Architecture Documentation](ARCHITECTURE.md) - System design
+- [Telegram Bot API](https://core.telegram.org/bots/api) - Official API docs
+
+If you encounter issues, check the server logs and verify your configuration with the `/api/notification-status` endpoint.
diff --git a/config.py b/config.py
index ed01ebb..3a257a3 100644
--- a/config.py
+++ b/config.py
@@ -114,3 +114,24 @@ def hour_to_stage(hours: int) -> int:
HSV_BINS = (8, 8, 8) # histogram bins for H, S, V channels
RGB_BINS = (8, 8, 8) # histogram bins for R, G, B channels
N_DOMINANT_COLORS = 3 # k-means clusters for dominant color extraction
+
+# ─── Telegram Notification Settings ─────────────────────────────────
+# Telegram Bot Configuration
+# Get these values from BotFather (see setup_telegram.md for instructions)
+TELEGRAM_BOT_TOKEN = os.environ.get("TELEGRAM_BOT_TOKEN", None) # Your bot token from BotFather
+TELEGRAM_CHAT_ID = os.environ.get("TELEGRAM_CHAT_ID", None) # Your chat ID (can be user or group)
+
+# Notification Preferences
+TELEGRAM_ENABLED = True # Enable/disable Telegram notifications globally
+TELEGRAM_ALERT_ON_STAGES = [2, 3] # Send alerts for these stages (Early Spoilage, Spoiled)
+TELEGRAM_PHOTO_MIN_STAGE = 2 # Minimum stage to send photos (saves bandwidth)
+TELEGRAM_SEND_PHOTO = True # Include photo in notifications
+TELEGRAM_DAILY_SUMMARY_ENABLED = True # Send daily summary reports
+TELEGRAM_DAILY_SUMMARY_TIME = "18:00" # Time for daily summary (HH:MM format)
+TELEGRAM_HEALTH_CHECK_INTERVAL = 3600 # Health check interval in seconds (3600 = 1 hour)
+TELEGRAM_NOTIFY_ON_STARTUP = True # Send notification when server starts
+
+# Advanced Settings
+TELEGRAM_MAX_RETRIES = 3 # Maximum retry attempts for failed notifications
+TELEGRAM_RETRY_DELAY = 2 # Initial retry delay in seconds (exponential backoff)
+TELEGRAM_RATE_LIMIT_BUFFER = 1 # Buffer time between messages in seconds
diff --git a/notification_system.py b/notification_system.py
new file mode 100644
index 0000000..798c989
--- /dev/null
+++ b/notification_system.py
@@ -0,0 +1,463 @@
+"""
+Telegram Notification System for Freshness Monitor
+===================================================
+Provides real-time alerts for freshness stage changes, daily summaries,
+and system health monitoring via Telegram bot.
+
+Features:
+ - Async notification dispatch (non-blocking)
+ - Retry logic with exponential backoff
+ - Multiple notification types (alerts, summaries, health checks)
+ - Rich formatting with emojis and stage-colored indicators
+ - Error handling and logging
+
+Usage:
+ from notification_system import TelegramNotifier
+
+ notifier = TelegramNotifier(bot_token, chat_id)
+ await notifier.send_freshness_alert(stage, confidence, image_path)
+"""
+
+import asyncio
+import logging
+import os
+import time
+from datetime import datetime
+from typing import Optional, Dict, List
+from io import BytesIO
+
+try:
+ from telegram import Bot, InputFile
+ from telegram.error import TelegramError, RetryAfter, TimedOut
+ TELEGRAM_AVAILABLE = True
+except ImportError:
+ TELEGRAM_AVAILABLE = False
+ logging.warning("python-telegram-bot not installed. Telegram notifications disabled.")
+
+import config
+
+# Configure logging
+logging.basicConfig(
+ level=logging.INFO,
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
+)
+logger = logging.getLogger(__name__)
+
+
+class TelegramNotifier:
+ """
+ Handles all Telegram notifications for the Freshness Monitor system.
+ Supports async operations to prevent blocking the main server.
+ """
+
+ # Stage emojis for visual appeal
+ STAGE_EMOJIS = {
+ 0: "🟢", # Very Fresh
+ 1: "🟡", # Fresh
+ 2: "🟠", # Early Spoilage
+ 3: "🔴", # Spoiled
+ }
+
+ def __init__(self, bot_token: Optional[str] = None, chat_id: Optional[str] = None):
+ """
+ Initialize the Telegram notifier.
+
+ Args:
+ bot_token: Telegram bot token (optional, falls back to config)
+ chat_id: Telegram chat ID (optional, falls back to config)
+ """
+ if not TELEGRAM_AVAILABLE:
+ logger.error("Telegram bot library not available. Install with: pip install python-telegram-bot")
+ self.enabled = False
+ return
+
+ self.bot_token = bot_token or getattr(config, 'TELEGRAM_BOT_TOKEN', None)
+ self.chat_id = chat_id or getattr(config, 'TELEGRAM_CHAT_ID', None)
+
+ # Validate configuration
+ if not self.bot_token or not self.chat_id:
+ logger.warning("Telegram bot token or chat ID not configured. Notifications disabled.")
+ self.enabled = False
+ return
+
+ # Initialize bot
+ try:
+ self.bot = Bot(token=self.bot_token)
+ self.enabled = True
+ logger.info("Telegram notifier initialized successfully")
+ except Exception as e:
+ logger.error(f"Failed to initialize Telegram bot: {e}")
+ self.enabled = False
+
+ async def _send_message_with_retry(
+ self,
+ text: str,
+ max_retries: int = 3,
+ parse_mode: str = "HTML"
+ ) -> bool:
+ """
+ Send a message with retry logic and exponential backoff.
+
+ Args:
+ text: Message text to send
+ max_retries: Maximum number of retry attempts
+ parse_mode: Message formatting (HTML or Markdown)
+
+ Returns:
+ True if message sent successfully, False otherwise
+ """
+ if not self.enabled:
+ logger.debug("Telegram notifications disabled, skipping message")
+ return False
+
+ for attempt in range(max_retries):
+ try:
+ await self.bot.send_message(
+ chat_id=self.chat_id,
+ text=text,
+ parse_mode=parse_mode
+ )
+ logger.info("Telegram message sent successfully")
+ return True
+
+ except RetryAfter as e:
+ wait_time = e.retry_after
+ logger.warning(f"Rate limited. Retrying after {wait_time} seconds...")
+ await asyncio.sleep(wait_time)
+
+ except TimedOut:
+ wait_time = 2 ** attempt # Exponential backoff
+ logger.warning(f"Request timed out. Retrying in {wait_time} seconds...")
+ await asyncio.sleep(wait_time)
+
+ except TelegramError as e:
+ logger.error(f"Telegram API error: {e}")
+ if attempt < max_retries - 1:
+ await asyncio.sleep(2 ** attempt)
+ else:
+ return False
+
+ except Exception as e:
+ logger.error(f"Unexpected error sending Telegram message: {e}")
+ return False
+
+ return False
+
+ async def _send_photo_with_retry(
+ self,
+ photo_path: str,
+ caption: str,
+ max_retries: int = 3,
+ parse_mode: str = "HTML"
+ ) -> bool:
+ """
+ Send a photo with caption with retry logic.
+
+ Args:
+ photo_path: Path to the image file
+ caption: Photo caption text
+ max_retries: Maximum number of retry attempts
+ parse_mode: Caption formatting
+
+ Returns:
+ True if photo sent successfully, False otherwise
+ """
+ if not self.enabled:
+ return False
+
+ if not os.path.exists(photo_path):
+ logger.error(f"Image file not found: {photo_path}")
+ return False
+
+ for attempt in range(max_retries):
+ try:
+ with open(photo_path, 'rb') as photo_file:
+ await self.bot.send_photo(
+ chat_id=self.chat_id,
+ photo=InputFile(photo_file),
+ caption=caption,
+ parse_mode=parse_mode
+ )
+ logger.info("Telegram photo sent successfully")
+ return True
+
+ except RetryAfter as e:
+ wait_time = e.retry_after
+ logger.warning(f"Rate limited. Retrying after {wait_time} seconds...")
+ await asyncio.sleep(wait_time)
+
+ except TimedOut:
+ wait_time = 2 ** attempt
+ logger.warning(f"Request timed out. Retrying in {wait_time} seconds...")
+ await asyncio.sleep(wait_time)
+
+ except TelegramError as e:
+ logger.error(f"Telegram API error: {e}")
+ if attempt < max_retries - 1:
+ await asyncio.sleep(2 ** attempt)
+ else:
+ return False
+
+ except Exception as e:
+ logger.error(f"Unexpected error sending Telegram photo: {e}")
+ return False
+
+ return False
+
+ async def send_freshness_alert(
+ self,
+ stage: int,
+ stage_name: str,
+ confidence: float,
+ filename: str,
+ image_path: Optional[str] = None,
+ stage_probabilities: Optional[Dict] = None,
+ hex_colors: Optional[Dict] = None
+ ) -> bool:
+ """
+ Send a freshness stage alert with detailed information.
+
+ Args:
+ stage: Freshness stage number (0-3)
+ stage_name: Human-readable stage name
+ confidence: Prediction confidence (0-1)
+ filename: Name of the analyzed file
+ image_path: Optional path to the image file
+ stage_probabilities: Optional dict of stage probabilities
+ hex_colors: Optional dict of dominant hex colors
+
+ Returns:
+ True if alert sent successfully
+ """
+ if not self.enabled:
+ return False
+
+ emoji = self.STAGE_EMOJIS.get(stage, "⚪")
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
+
+ # Build message with rich formatting
+ message = f"{emoji} Freshness Alert {emoji}\n\n"
+ message += f"Stage: {stage_name}\n"
+ message += f"Confidence: {confidence * 100:.1f}%\n"
+ message += f"File: {filename}\n"
+ message += f"Time: {timestamp}\n"
+
+ # Add stage probabilities if available
+ if stage_probabilities:
+ message += "\nStage Probabilities:\n"
+ for stage_label, prob in stage_probabilities.items():
+ emoji_icon = self.STAGE_EMOJIS.get(
+ list(config.LABEL_NAMES.values()).index(stage_label)
+ if stage_label in config.LABEL_NAMES.values() else 0,
+ "⚪"
+ )
+ message += f"{emoji_icon} {stage_label}: {prob * 100:.1f}%\n"
+
+ # Add dominant colors if available
+ if hex_colors:
+ message += f"\nDominant Colors: "
+ message += ", ".join(hex_colors.values())
+
+ # Add action recommendations based on stage
+ if stage >= 2: # Early Spoilage or worse
+ message += f"\n\n⚠️ Action Required: "
+ if stage == 2:
+ message += "Product is entering early spoilage stage. Consider consumption soon."
+ elif stage == 3:
+ message += "Product has spoiled. Do not consume!"
+
+ # Send photo if available and stage is concerning
+ if image_path and stage >= getattr(config, 'TELEGRAM_PHOTO_MIN_STAGE', 2):
+ caption = f"{emoji} {stage_name} ({confidence * 100:.1f}% confidence)"
+ success = await self._send_photo_with_retry(image_path, caption)
+ if success:
+ # Send detailed message separately
+ return await self._send_message_with_retry(message)
+ else:
+ # Fallback to text-only if photo fails
+ return await self._send_message_with_retry(message)
+ else:
+ # Send text-only message
+ return await self._send_message_with_retry(message)
+
+ async def send_daily_summary(self, predictions: List[Dict]) -> bool:
+ """
+ Send a daily summary of all predictions.
+
+ Args:
+ predictions: List of prediction dictionaries
+
+ Returns:
+ True if summary sent successfully
+ """
+ if not self.enabled or not predictions:
+ return False
+
+ timestamp = datetime.now().strftime("%Y-%m-%d")
+
+ # Count predictions by stage
+ stage_counts = {0: 0, 1: 0, 2: 0, 3: 0}
+ for pred in predictions:
+ stage = pred.get('stage', 0)
+ stage_counts[stage] = stage_counts.get(stage, 0) + 1
+
+ # Build summary message
+ message = f"📊 Daily Summary - {timestamp}\n\n"
+ message += f"Total Predictions: {len(predictions)}\n\n"
+
+ message += "Breakdown by Stage:\n"
+ for stage, count in sorted(stage_counts.items()):
+ if count > 0:
+ emoji = self.STAGE_EMOJIS.get(stage, "⚪")
+ stage_name = config.LABEL_NAMES.get(stage, f"Stage {stage}")
+ percentage = (count / len(predictions)) * 100
+ message += f"{emoji} {stage_name}: {count} ({percentage:.1f}%)\n"
+
+ # Add warnings if needed
+ warning_count = stage_counts.get(2, 0) + stage_counts.get(3, 0)
+ if warning_count > 0:
+ message += f"\n⚠️ {warning_count} items require attention!"
+ else:
+ message += f"\n✅ All items are fresh!"
+
+ return await self._send_message_with_retry(message)
+
+ async def send_system_health(
+ self,
+ status: str,
+ uptime_seconds: int,
+ total_predictions: int,
+ model_loaded: bool,
+ last_prediction: Optional[Dict] = None
+ ) -> bool:
+ """
+ Send system health status notification.
+
+ Args:
+ status: System status (running, warning, error)
+ uptime_seconds: Server uptime in seconds
+ total_predictions: Total number of predictions made
+ model_loaded: Whether ML model is loaded
+ last_prediction: Last prediction data (optional)
+
+ Returns:
+ True if health report sent successfully
+ """
+ if not self.enabled:
+ return False
+
+ # Calculate uptime
+ uptime_hours = uptime_seconds // 3600
+ uptime_mins = (uptime_seconds % 3600) // 60
+
+ # Status emoji
+ status_emoji = {
+ 'running': '✅',
+ 'warning': '⚠️',
+ 'error': '❌',
+ 'starting': '🔄'
+ }.get(status.lower(), '❓')
+
+ # Build health message
+ message = f"{status_emoji} System Health Report\n\n"
+ message += f"Status: {status.upper()}\n"
+ message += f"Uptime: {uptime_hours}h {uptime_mins}m\n"
+ message += f"Total Predictions: {total_predictions}\n"
+ message += f"Model Status: {'✅ Loaded' if model_loaded else '❌ Not Loaded'}\n"
+
+ if last_prediction:
+ stage_name = last_prediction.get('stage_name', 'Unknown')
+ timestamp = last_prediction.get('timestamp', 'N/A')
+ emoji = self.STAGE_EMOJIS.get(last_prediction.get('stage', 0), "⚪")
+ message += f"\nLast Prediction:\n"
+ message += f"{emoji} {stage_name}\n"
+ message += f"Time: {timestamp}\n"
+
+ return await self._send_message_with_retry(message)
+
+ async def send_test_notification(self) -> bool:
+ """
+ Send a test notification to verify configuration.
+
+ Returns:
+ True if test message sent successfully
+ """
+ if not self.enabled:
+ return False
+
+ message = "🧪 Test Notification\n\n"
+ message += "Freshness Monitor Telegram notifications are working correctly!\n\n"
+ message += f"Bot Token: {self.bot_token[:10]}...\n"
+ message += f"Chat ID: {self.chat_id}\n"
+ message += f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
+
+ return await self._send_message_with_retry(message)
+
+ async def send_error_alert(self, error_type: str, error_message: str) -> bool:
+ """
+ Send an error alert notification.
+
+ Args:
+ error_type: Type of error (e.g., "Model Loading", "Prediction")
+ error_message: Detailed error message
+
+ Returns:
+ True if error alert sent successfully
+ """
+ if not self.enabled:
+ return False
+
+ message = f"❌ System Error Alert\n\n"
+ message += f"Error Type: {error_type}\n"
+ message += f"Message: {error_message}\n"
+ message += f"Time: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\n"
+ message += "⚠️ Please check the system logs for more details."
+
+ return await self._send_message_with_retry(message)
+
+
+# Convenience function for sync contexts
+def send_notification_sync(notifier: TelegramNotifier, coro):
+ """
+ Helper function to send notifications from synchronous code.
+ Creates a new event loop if needed.
+
+ Args:
+ notifier: TelegramNotifier instance
+ coro: Coroutine to execute
+
+ Returns:
+ Result of the coroutine
+ """
+ try:
+ loop = asyncio.get_event_loop()
+ if loop.is_running():
+ # If loop is already running, schedule the task
+ asyncio.create_task(coro)
+ return True
+ else:
+ return loop.run_until_complete(coro)
+ except RuntimeError:
+ # No event loop exists, create a new one
+ loop = asyncio.new_event_loop()
+ asyncio.set_event_loop(loop)
+ try:
+ return loop.run_until_complete(coro)
+ finally:
+ loop.close()
+
+
+# Global notifier instance (initialized in server.py)
+_global_notifier: Optional[TelegramNotifier] = None
+
+
+def get_notifier() -> Optional[TelegramNotifier]:
+ """Get the global notifier instance."""
+ return _global_notifier
+
+
+def init_notifier(bot_token: Optional[str] = None, chat_id: Optional[str] = None):
+ """Initialize the global notifier instance."""
+ global _global_notifier
+ _global_notifier = TelegramNotifier(bot_token, chat_id)
+ return _global_notifier
diff --git a/requirements.txt b/requirements.txt
index 2ec3cf5..a748e6b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -6,3 +6,4 @@ Pillow>=10.0
joblib>=1.3
qrcode>=7.4
gunicorn>=21.2
+python-telegram-bot>=20.7
diff --git a/server_telegram_patch.py b/server_telegram_patch.py
new file mode 100644
index 0000000..31d91b1
--- /dev/null
+++ b/server_telegram_patch.py
@@ -0,0 +1,190 @@
+"""
+Telegram Integration Patch for server.py
+==========================================
+
+Add these code blocks to your server.py file:
+
+1. At the top, after other imports:
+"""
+
+# ─── Telegram Notification Import ───────────────────────────────────
+import asyncio
+from notification_system import init_notifier, send_notification_sync
+
+# Initialize global notifier
+notifier = None
+
+"""
+2. Modify the classify_image function to add notification trigger.
+ Find the classify_image function and add this code after prediction_history.appendleft(result):
+"""
+
+ # Telegram notification for concerning stages
+ if notifier and notifier.enabled:
+ stage = result.get('stage', 0)
+ if stage in getattr(config, 'TELEGRAM_ALERT_ON_STAGES', [2, 3]):
+ # Send notification asynchronously (non-blocking)
+ try:
+ send_notification_sync(
+ notifier,
+ notifier.send_freshness_alert(
+ stage=stage,
+ stage_name=result['stage_name'],
+ confidence=result['confidence'],
+ filename=result['filename'],
+ image_path=image_path if getattr(config, 'TELEGRAM_SEND_PHOTO', True) else None,
+ stage_probabilities=result.get('stage_probabilities'),
+ hex_colors=result.get('hex_colors')
+ )
+ )
+ except Exception as e:
+ logger.warning(f"Failed to send Telegram notification: {e}")
+
+"""
+3. Add new API endpoint for testing notifications.
+ Add this before the main() function:
+"""
+
+@app.route("/api/test-notification", methods=["POST"])
+def test_notification():
+ """Test Telegram notification system."""
+ if not notifier or not notifier.enabled:
+ return jsonify({
+ "error": "Telegram notifications not configured",
+ "message": "Set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID environment variables"
+ }), 503
+
+ try:
+ success = send_notification_sync(notifier, notifier.send_test_notification())
+ if success:
+ return jsonify({
+ "success": True,
+ "message": "Test notification sent successfully!"
+ })
+ else:
+ return jsonify({
+ "success": False,
+ "message": "Failed to send test notification"
+ }), 500
+ except Exception as e:
+ return jsonify({
+ "success": False,
+ "error": str(e)
+ }), 500
+
+
+@app.route("/api/notification-status", methods=["GET"])
+def notification_status():
+ """Get Telegram notification system status."""
+ if not notifier:
+ return jsonify({
+ "enabled": False,
+ "configured": False,
+ "message": "Notifier not initialized"
+ })
+
+ return jsonify({
+ "enabled": notifier.enabled,
+ "configured": bool(notifier.bot_token and notifier.chat_id),
+ "bot_token_set": bool(notifier.bot_token),
+ "chat_id_set": bool(notifier.chat_id),
+ "alert_stages": getattr(config, 'TELEGRAM_ALERT_ON_STAGES', [2, 3]),
+ "send_photos": getattr(config, 'TELEGRAM_SEND_PHOTO', True)
+ })
+
+
+"""
+4. Modify the main() function to initialize the notifier.
+ Add this code at the beginning of main(), right after load_model():
+"""
+
+ # Initialize Telegram notifier
+ global notifier
+ if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+ if notifier and notifier.enabled:
+ print("Telegram notifications: ENABLED")
+ # Send startup notification if configured
+ if getattr(config, 'TELEGRAM_NOTIFY_ON_STARTUP', True):
+ try:
+ send_notification_sync(
+ notifier,
+ notifier.send_system_health(
+ status="starting",
+ uptime_seconds=0,
+ total_predictions=0,
+ model_loaded=True
+ )
+ )
+ except Exception as e:
+ print(f"Failed to send startup notification: {e}")
+ else:
+ print("Telegram notifications: DISABLED (check configuration)")
+ else:
+ print("Telegram notifications: DISABLED (via config)")
+
+
+"""
+5. Also initialize notifier at module level (for gunicorn).
+ Add this code after the module-level initialization section:
+"""
+
+# Initialize notifier for gunicorn/production
+if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+
+"""
+
+===========================================
+COMPLETE INTEGRATION INSTRUCTIONS:
+===========================================
+
+To integrate Telegram notifications into server.py:
+
+1. Import the notification system at the top:
+ ```python
+ import asyncio
+ from notification_system import init_notifier, send_notification_sync
+ notifier = None
+ ```
+
+2. In classify_image(), after `prediction_history.appendleft(result)`, add:
+ ```python
+ # Telegram notification
+ if notifier and notifier.enabled and result.get('stage', 0) in getattr(config, 'TELEGRAM_ALERT_ON_STAGES', [2, 3]):
+ try:
+ send_notification_sync(notifier, notifier.send_freshness_alert(
+ stage=result['stage'],
+ stage_name=result['stage_name'],
+ confidence=result['confidence'],
+ filename=result['filename'],
+ image_path=image_path,
+ stage_probabilities=result.get('stage_probabilities'),
+ hex_colors=result.get('hex_colors')
+ ))
+ except Exception as e:
+ print(f"Telegram notification error: {e}")
+ ```
+
+3. Add the test endpoint before main():
+ Copy the /api/test-notification and /api/notification-status endpoints from above.
+
+4. In main(), after load_model(), add:
+ ```python
+ global notifier
+ if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+ if notifier and notifier.enabled:
+ print("Telegram notifications: ENABLED")
+ else:
+ print("Telegram notifications: DISABLED")
+ ```
+
+5. At module level, after server_start_time initialization:
+ ```python
+ if getattr(config, 'TELEGRAM_ENABLED', True):
+ notifier = init_notifier()
+ ```
+
+That's it! The system will now send notifications automatically.
+"""
diff --git a/setup_telegram.md b/setup_telegram.md
new file mode 100644
index 0000000..92b27fe
--- /dev/null
+++ b/setup_telegram.md
@@ -0,0 +1,425 @@
+# Telegram Notification Setup Guide
+
+This guide walks you through setting up Telegram notifications for the Freshness Monitor system.
+
+## 📱 Overview
+
+The Telegram notification system provides real-time alerts for:
+- **Freshness Warnings**: Notifications when products reach Stage 3 (Early Spoilage) or Stage 4 (Spoiled)
+- **Daily Summaries**: End-of-day reports with statistics
+- **System Health**: Periodic health checks and startup notifications
+- **Error Alerts**: Critical system errors
+
+---
+
+## 🤖 Step 1: Create a Telegram Bot
+
+1. **Open Telegram** and search for `@BotFather` (the official bot creation tool)
+
+2. **Start a conversation** with BotFather by clicking "Start" or sending `/start`
+
+3. **Create a new bot** by sending the command:
+ ```
+ /newbot
+ ```
+
+4. **Choose a name** for your bot (this is the display name users will see):
+ ```
+ Example: Freshness Monitor Bot
+ ```
+
+5. **Choose a username** for your bot (must end in 'bot'):
+ ```
+ Example: freshness_monitor_bot
+ ```
+
+6. **Save your bot token** - BotFather will send you a message like:
+ ```
+ Done! Congratulations on your new bot. You will find it at t.me/freshness_monitor_bot.
+ You can now add a description...
+
+ Use this token to access the HTTP API:
+ 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz1234567890
+
+ Keep your token secure and store it safely, it can be used by anyone to control your bot.
+ ```
+
+ ⚠️ **Keep this token secure!** Anyone with this token can control your bot.
+
+---
+
+## 👤 Step 2: Get Your Chat ID
+
+You need your Chat ID to receive messages from the bot.
+
+### Option A: Using a Bot (Easiest)
+
+1. **Search for** `@userinfobot` or `@get_id_bot` in Telegram
+
+2. **Start the bot** and it will immediately send you your Chat ID:
+ ```
+ Your Chat ID: 123456789
+ ```
+
+### Option B: Manual Method
+
+1. **Send a message** to your newly created bot (search for it using the username from Step 1)
+
+2. **Visit this URL** in your browser (replace `YOUR_BOT_TOKEN` with your actual token):
+ ```
+ https://api.telegram.org/botYOUR_BOT_TOKEN/getUpdates
+ ```
+
+3. **Look for the Chat ID** in the JSON response:
+ ```json
+ {
+ "ok": true,
+ "result": [{
+ "message": {
+ "chat": {
+ "id": 123456789,
+ ...
+ }
+ }
+ }]
+ }
+ ```
+
+### For Group Notifications
+
+If you want notifications in a Telegram group:
+
+1. **Create a group** in Telegram
+2. **Add your bot** to the group as a member
+3. **Send a message** in the group (mention the bot with `@your_bot_username`)
+4. **Get the group Chat ID** using Option B above (group IDs are negative numbers like `-123456789`)
+
+---
+
+## ⚙️ Step 3: Configure the Freshness Monitor
+
+### Method 1: Environment Variables (Recommended for Production)
+
+Set environment variables on your system:
+
+**Linux/Mac:**
+```bash
+export TELEGRAM_BOT_TOKEN="1234567890:ABCdefGHIjklMNOpqrsTUVwxyz1234567890"
+export TELEGRAM_CHAT_ID="123456789"
+```
+
+**Windows (Command Prompt):**
+```cmd
+set TELEGRAM_BOT_TOKEN=1234567890:ABCdefGHIjklMNOpqrsTUVwxyz1234567890
+set TELEGRAM_CHAT_ID=123456789
+```
+
+**Windows (PowerShell):**
+```powershell
+$env:TELEGRAM_BOT_TOKEN="1234567890:ABCdefGHIjklMNOpqrsTUVwxyz1234567890"
+$env:TELEGRAM_CHAT_ID="123456789"
+```
+
+**For Render.com or other cloud platforms:**
+- Go to your service's environment variables settings
+- Add `TELEGRAM_BOT_TOKEN` and `TELEGRAM_CHAT_ID` as environment variables
+
+### Method 2: Direct Configuration (Development Only)
+
+⚠️ **Not recommended for production** - tokens will be visible in your code!
+
+Edit `config.py` and replace the values:
+```python
+TELEGRAM_BOT_TOKEN = "1234567890:ABCdefGHIjklMNOpqrsTUVwxyz1234567890"
+TELEGRAM_CHAT_ID = "123456789"
+```
+
+---
+
+## 📦 Step 4: Install Dependencies
+
+Install the required Python package:
+
+```bash
+pip install python-telegram-bot==20.7
+```
+
+Or install all dependencies:
+```bash
+pip install -r requirements.txt
+```
+
+---
+
+## 🧪 Step 5: Test Your Configuration
+
+### Using the API Endpoint
+
+Start your server:
+```bash
+python server.py
+```
+
+Then send a test request:
+```bash
+curl -X POST http://localhost:5000/api/test-notification
+```
+
+You should receive a test message in Telegram!
+
+### Using Python Directly
+
+Create a test script `test_telegram.py`:
+```python
+import asyncio
+from notification_system import TelegramNotifier
+
+async def test():
+ notifier = TelegramNotifier()
+ success = await notifier.send_test_notification()
+ print(f"Test notification sent: {success}")
+
+asyncio.run(test())
+```
+
+Run it:
+```bash
+python test_telegram.py
+```
+
+---
+
+## 🎛️ Step 6: Customize Notification Preferences
+
+Edit `config.py` to adjust notification settings:
+
+```python
+# Which stages trigger alerts? (0=Very Fresh, 1=Fresh, 2=Early Spoilage, 3=Spoiled)
+TELEGRAM_ALERT_ON_STAGES = [2, 3] # Only alert on concerning stages
+
+# Send photos with notifications? (uses more bandwidth)
+TELEGRAM_SEND_PHOTO = True
+
+# Minimum stage to include photos (saves bandwidth for fresh products)
+TELEGRAM_PHOTO_MIN_STAGE = 2
+
+# Daily summary report
+TELEGRAM_DAILY_SUMMARY_ENABLED = True
+TELEGRAM_DAILY_SUMMARY_TIME = "18:00" # 6 PM daily summary
+
+# Health checks every hour
+TELEGRAM_HEALTH_CHECK_INTERVAL = 3600 # seconds
+
+# Notify when server starts
+TELEGRAM_NOTIFY_ON_STARTUP = True
+```
+
+---
+
+## 📱 Step 7: Bot Commands (Optional)
+
+You can add custom commands to your bot via BotFather:
+
+1. Send `/setcommands` to BotFather
+2. Select your bot
+3. Add these commands:
+ ```
+ start - Start receiving notifications
+ status - Get system status
+ summary - Get today's summary
+ help - Show help message
+ ```
+
+Then implement these commands in your bot (see Advanced Features below).
+
+---
+
+## 🔒 Security Best Practices
+
+1. **Never commit tokens to Git**
+ - Always use environment variables
+ - Add `.env` files to `.gitignore`
+
+2. **Restrict bot permissions**
+ - Your bot only needs to send messages
+ - Disable unused privacy settings in BotFather
+
+3. **Use group IDs carefully**
+ - Only add your bot to trusted groups
+ - Monitor group members to prevent unauthorized access
+
+4. **Rotate tokens periodically**
+ - Use `/token` command in BotFather to generate new tokens
+ - Update your configuration when rotating
+
+---
+
+## 🐛 Troubleshooting
+
+### "Bot token not configured" Error
+
+**Problem:** Server starts but notifications don't work.
+
+**Solution:**
+- Check that environment variables are set correctly
+- Verify the token format (should be like `1234567890:ABC...`)
+- Make sure you're using the correct token from BotFather
+
+### "Chat not found" Error
+
+**Problem:** Bot sends messages but you don't receive them.
+
+**Solution:**
+- Verify your Chat ID is correct
+- Make sure you've started a conversation with the bot
+- For groups, ensure the bot is a member and has permission to send messages
+
+### Messages Not Appearing
+
+**Problem:** No errors but messages don't arrive.
+
+**Solution:**
+- Check if bot is blocked in Telegram
+- Verify internet connectivity on the server
+- Check server logs for rate limiting issues
+- Try the test notification endpoint
+
+### Import Error: "No module named 'telegram'"
+
+**Problem:** Python can't find the telegram library.
+
+**Solution:**
+```bash
+pip install python-telegram-bot==20.7
+```
+
+### Async Warnings in Logs
+
+**Problem:** Warnings about event loops.
+
+**Solution:** This is normal in some environments. Notifications will still work, but you can suppress warnings by upgrading to Python 3.9+.
+
+---
+
+## 🚀 Advanced Features
+
+### Multiple Chat IDs
+
+To send notifications to multiple users/groups, modify `config.py`:
+
+```python
+TELEGRAM_CHAT_ID = "123456789,987654321,-111222333" # Comma-separated
+```
+
+Then update `notification_system.py` to loop through all IDs.
+
+### Custom Alert Rules
+
+Create custom notification rules in `server.py`:
+
+```python
+# Alert only during business hours
+import datetime
+current_hour = datetime.datetime.now().hour
+if 9 <= current_hour <= 17 and stage >= 2:
+ send_notification_sync(notifier, notifier.send_freshness_alert(...))
+```
+
+### Scheduled Reports
+
+Use `schedule` library for timed reports:
+
+```bash
+pip install schedule
+```
+
+```python
+import schedule
+import time
+
+def send_daily_report():
+ # Send summary notification
+ pass
+
+schedule.every().day.at("18:00").do(send_daily_report)
+
+while True:
+ schedule.run_pending()
+ time.sleep(60)
+```
+
+---
+
+## 📊 Notification Examples
+
+### Freshness Alert
+
+```
+🟠 Freshness Alert 🟠
+
+Stage: Stage 3 - Early Spoilage
+Confidence: 87.3%
+File: sample_12h.jpg
+Time: 2026-03-04 16:45:22
+
+Stage Probabilities:
+🟢 Stage 1 - Very Fresh: 2.1%
+🟡 Stage 2 - Fresh: 8.4%
+🟠 Stage 3 - Early Spoilage: 87.3%
+🔴 Stage 4 - Spoiled: 2.2%
+
+Dominant Colors: #c5c7c1, #7d7764, #a4a598
+
+⚠️ Action Required: Product is entering early spoilage stage. Consider consumption soon.
+```
+
+### Daily Summary
+
+```
+📊 Daily Summary - 2026-03-04
+
+Total Predictions: 24
+
+Breakdown by Stage:
+🟢 Stage 1 - Very Fresh: 8 (33.3%)
+🟡 Stage 2 - Fresh: 10 (41.7%)
+🟠 Stage 3 - Early Spoilage: 5 (20.8%)
+🔴 Stage 4 - Spoiled: 1 (4.2%)
+
+⚠️ 6 items require attention!
+```
+
+### System Health
+
+```
+✅ System Health Report
+
+Status: RUNNING
+Uptime: 12h 34m
+Total Predictions: 156
+Model Status: ✅ Loaded
+
+Last Prediction:
+🟢 Stage 1 - Very Fresh
+Time: 2026-03-04T16:45:22
+```
+
+---
+
+## 📚 Additional Resources
+
+- [Telegram Bot API Documentation](https://core.telegram.org/bots/api)
+- [python-telegram-bot Library](https://github.com/python-telegram-bot/python-telegram-bot)
+- [BotFather Commands Reference](https://core.telegram.org/bots/features#botfather)
+
+---
+
+## 💬 Support
+
+If you encounter issues:
+1. Check the troubleshooting section above
+2. Review server logs for error messages
+3. Test with the `/api/test-notification` endpoint
+4. Verify your bot token and chat ID are correct
+
+For questions about the Freshness Monitor system, see the main [README.md](README.md).