Tech

Gmail Auto-Label & Triage: Email Teratur Tanpa Manual Sorting

Inbox rapi otomatis. Setup AI-based email triage yang label, prioritas, dan sort email tanpa kamu sentuh.
12
2 bulan lalu
Zainul Fanani
Gmail Auto-Label & Triage: Email Teratur Tanpa Manual Sorting
📅 11 Mar 2026🤍 0 👁 0 🔗 0

📎 Source:openclaw-sumopod — view on GitHub & star ⭐

📧 Gmail Auto-Label & Smart Triage Tutorial

Transform your inbox chaos into organized, prioritized workflows — automatically!


🎯 Before vs After


📂 Visual Label System

text
┌─────────────────────────────────────────────────────────┐
│  🏷️  YOUR SMART LABEL HIERARCHY                         │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  📄 Documents        → Contracts, PDFs, proposals       │
│  🏢 Clients          → Client communications            │
│  📊 Reports          → Analytics, dashboards, data      │
│  📋 Tasks            → Action items, to-do requests     │
│  🔥 Urgent           → High priority, needs attention   │
│  📰 Newsletters      → Subscriptions, updates           │
│  🗑️ Low Priority     → FYI only, read when free         │
│                                                         │
└─────────────────────────────────────────────────────────┘

Label Color Coding


🛠️ Step-by-Step Setup

Step 1: Create Gmail Labels

Go to Gmail → Left sidebar → Click "+" next to Labels

text
Create these labels:
├── 📄 Documents
├── 🏢 Clients  
├── 📊 Reports
├── 📋 Tasks
├── 🔥 Urgent
├── 📰 Newsletters
└── 🗑️ Low Priority

Step 2: Enable Gmail API Access

bash
# Install gog CLI (if not already installed)
curl -sSL https://openclaw.dev/install/gog | bash

# Authenticate with your Google account
gog auth login

# Verify access
gog gmail list --max=5

Step 3: Create Project Directory

bash
mkdir -p ~/automation/gmail-triage
cd ~/automation/gmail-triage

📋 Classification Rules

Rule Engine Logic

text
┌────────────────────────────────────────────────────────────┐
│                    EMAIL CLASSIFICATION FLOW               │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  📧 New Email Arrives                                      │
│         ↓                                                  │
│  ┌─────────────────┐                                       │
│  │ Check Keywords  │                                       │
│  └────────┬────────┘                                       │
│           ↓                                                │
│     ┌─────┴─────┬─────────────┬────────────┐              │
│     ↓         ↓             ↓            ↓                 │
│  🔥 Urgent  🏢 Clients   📄 Docs    📊 Reports            │
│  (contains: (from:        (subject:  (subject:            │
│   "URGENT"   @client.com)  "contract"  "report")          │
│   "ASAP")                  OR .pdf)                      │
│                                                            │
│     ┌─────┴─────┬─────────────┐                           │
│     ↓         ↓             ↓                              │
│  📋 Tasks   📰 Newsletters  🗑️ Low Priority               │
│  (subject:  (from:          (no match)                    │
│   "action"   newsletter)                                   │
│   "task")                                                  │
│                                                            │
└────────────────────────────────────────────────────────────┘

Keyword Dictionary


🔔 Notification Setup

Telegram Bot Configuration

bash
# 1. Create Telegram Bot via @BotFather
# 2. Get your Chat ID via @userinfobot
# 3. Set environment variables

export TELEGRAM_BOT_TOKEN="YOUR_BOT_TOKEN_HERE"
export TELEGRAM_CHAT_ID="YOUR_CHAT_ID_HERE"

Notification Triggers

text
┌─────────────────────────────────────────────────────────────┐
│  WHEN TO SEND TELEGRAM ALERTS                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✅ ALWAYS notify on:                                       │
│     • 🔥 Urgent emails                                      │
│     • 🏢 Client emails (high value)                         │
│     • 📋 Task emails (requires action)                      │
│                                                             │
│  ⚠️  SUMMARIZE only (daily digest):                         │
│     • 📄 Documents received                                 │
│     • 📊 Reports generated                                  │
│                                                             │
│  ❌ NO notification:                                        │
│     • 📰 Newsletters (check when convenient)                │
│     • 🗑️ Low Priority (batch review weekly)                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘

💻 Full Code Script

gmail-triage.py — Complete Auto-Label System

python
#!/usr/bin/env python3
"""
📧 Gmail Auto-Label & Smart Triage
Automated email classification with Telegram notifications
"""

import os
import re
import json
import base64
from datetime import datetime, timedelta
from typing import List, Dict, Optional
import requests

# ═══════════════════════════════════════════════════════════════
# CONFIGURATION
# ═══════════════════════════════════════════════════════════════

# Telegram Settings
TELEGRAM_BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN", "YOUR_BOT_TOKEN")
TELEGRAM_CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "YOUR_CHAT_ID")

# Label Definitions with Keywords
LABEL_RULES = {
    "🔥 Urgent": {
        "keywords": ["urgent", "asap", "emergency", "deadline today", "critical"],
        "senders": [],
        "notify": True,
        "priority": 1
    },
    "🏢 Clients": {
        "keywords": ["proposal", "contract", "invoice", "project", "quotation"],
        "senders": ["client", "customer"],  # Pattern matching
        "notify": True,
        "priority": 2
    },
    "📄 Documents": {
        "keywords": [".pdf", ".doc", "contract", "agreement", "document", "attachment"],
        "senders": [],
        "notify": False,
        "priority": 3
    },
    "📊 Reports": {
        "keywords": ["report", "analytics", "dashboard", "metrics", "stats", "performance"],
        "senders": ["analytics", "reports", "noreply"],
        "notify": False,
        "priority": 4
    },
    "📋 Tasks": {
        "keywords": ["action required", "task", "todo", "please review", "approve", "sign"],
        "senders": [],
        "notify": True,
        "priority": 2
    },
    "📰 Newsletters": {
        "keywords": ["newsletter", "weekly", "update", "digest", "roundup"],
        "senders": ["newsletter", "updates"],
        "notify": False,
        "priority": 5
    },
    "🗑️ Low Priority": {
        "keywords": ["fyi", "for your information", "no action needed"],
        "senders": ["no-reply", "noreply", "notifications"],
        "notify": False,
        "priority": 6
    }
}

# ═══════════════════════════════════════════════════════════════
# TELEGRAM NOTIFICATIONS
# ═══════════════════════════════════════════════════════════════

def send_telegram_message(message: str, priority: int = 3):
    """Send notification to Telegram with priority formatting"""
    
    # Priority emojis
    priority_emojis = {1: "🚨", 2: "⚡", 3: "📧", 4: "📎", 5: "📰", 6: "⚪"}
    emoji = priority_emojis.get(priority, "📧")
    
    # Format message with priority
    formatted = f"{emoji} *Gmail Triage Alert*\n\n{message}"
    
    url = f"https://api.telegram.org/bot{TELEGRAM_BOT_TOKEN}/sendMessage"
    payload = {
        "chat_id": TELEGRAM_CHAT_ID,
        "text": formatted,
        "parse_mode": "Markdown",
        "disable_notification": priority > 2  # Silent for low priority
    }
    
    try:
        response = requests.post(url, json=payload, timeout=10)
        return response.json().get("ok", False)
    except Exception as e:
        print(f"❌ Telegram error: {e}")
        return False


def send_daily_summary(stats: Dict):
    """Send daily digest of classified emails"""
    message = f"""
📊 *Daily Email Summary*

📧 Total Processed: `{stats['total']}`
🔥 Urgent: `{stats['urgent']}`
🏢 Clients: `{stats['clients']}`
📋 Tasks: `{stats['tasks']}`
📄 Documents: `{stats['documents']}`
📊 Reports: `{stats['reports']}`
📰 Newsletters: `{stats['newsletters']}`

✅ All emails have been auto-labeled!
"""
    send_telegram_message(message, priority=3)


# ═══════════════════════════════════════════════════════════════
# EMAIL CLASSIFICATION ENGINE
# ═══════════════════════════════════════════════════════════════

def classify_email(subject: str, sender: str, body: str = "") -> Optional[str]:
    """
    Classify email based on rules
    Returns label name or None
    """
    text = f"{subject} {body}".lower()
    sender_lower = sender.lower()
    
    best_match = None
    best_priority = 999
    
    for label, rules in LABEL_RULES.items():
        score = 0
        
        # Check keywords
        for keyword in rules["keywords"]:
            if keyword.lower() in text:
                score += 1
        
        # Check sender patterns
        for pattern in rules["senders"]:
            if pattern.lower() in sender_lower:
                score += 2  # Sender match is stronger
        
        # If matched and higher priority (lower number), update
        if score > 0 and rules["priority"] < best_priority:
            best_match = label
            best_priority = rules["priority"]
    
    return best_match


def get_label_id(label_name: str) -> Optional[str]:
    """Get Gmail label ID from name using gog CLI"""
    import subprocess
    
    try:
        result = subprocess.run(
            ["gog", "gmail", "labels", "list", "--format=json"],
            capture_output=True,
            text=True,
            timeout=30
        )
        
        if result.returncode != 0:
            print(f"❌ Error fetching labels: {result.stderr}")
            return None
        
        labels = json.loads(result.stdout)
        for label in labels:
            if label.get("name") == label_name:
                return label.get("id")
        
        return None
    except Exception as e:
        print(f"❌ Error: {e}")
        return None


def apply_label(message_id: str, label_id: str) -> bool:
    """Apply label to Gmail message"""
    import subprocess
    
    try:
        result = subprocess.run(
            ["gog", "gmail", "messages", "modify", message_id, 
             "--add-label", label_id],
            capture_output=True,
            text=True,
            timeout=30
        )
        return result.returncode == 0
    except Exception as e:
        print(f"❌ Error applying label: {e}")
        return False


# ═══════════════════════════════════════════════════════════════
# MAIN TRIAGE WORKFLOW
# ═══════════════════════════════════════════════════════════════

def fetch_unprocessed_emails(max_results: int = 50) -> List[Dict]:
    """Fetch recent unread emails using gog CLI"""
    import subprocess
    
    try:
        result = subprocess.run(
            ["gog", "gmail", "list", 
             "--query", "is:unread -in:📄* -in:🏢* -in:📊* -in:📋* -in:🔥* -in:📰* -in:🗑️*",
             "--max", str(max_results),
             "--format=json"],
            capture_output=True,
            text=True,
            timeout=60
        )
        
        if result.returncode != 0:
            print(f"❌ Error fetching emails: {result.stderr}")
            return []
        
        return json.loads(result.stdout) if result.stdout else []
    except Exception as e:
        print(f"❌ Error: {e}")
        return []


def get_email_content(message_id: str) -> Dict:
    """Get full email content"""
    import subprocess
    
    try:
        result = subprocess.run(
            ["gog", "gmail", "get", message_id, "--format=json"],
            capture_output=True,
            text=True,
            timeout=30
        )
        
        if result.returncode == 0 and result.stdout:
            return json.loads(result.stdout)
        return {}
    except Exception as e:
        print(f"❌ Error fetching email: {e}")
        return {}


def triage_emails(dry_run: bool = False):
    """Main triage function"""
    
    print("🔍 Starting Gmail Triage...")
    print("━" * 50)
    
    # Statistics
    stats = {key: 0 for key in LABEL_RULES.keys()}
    stats["total"] = 0
    stats["unclassified"] = 0
    
    # Fetch emails
    emails = fetch_unprocessed_emails(max_results=100)
    
    if not emails:
        print("✅ No new emails to process!")
        return
    
    print(f"📧 Found {len(emails)} unprocessed emails\n")
    
    for email in emails:
        msg_id = email.get("id")
        subject = email.get("subject", "No Subject")
        sender = email.get("from", "Unknown")
        
        print(f"Processing: {subject[:50]}...")
        
        # Get full content for better classification
        full_email = get_email_content(msg_id)
        body = full_email.get("snippet", "")
        
        # Classify
        label = classify_email(subject, sender, body)
        
        if label:
            stats[label] += 1
            stats["total"] += 1
            
            print(f"  └─ 📌 Labeled: {label}")
            
            if not dry_run:
                # Apply label
                label_id = get_label_id(label)
                if label_id:
                    apply_label(msg_id, label_id)
                
                # Send notification if required
                rules = LABEL_RULES[label]
                if rules["notify"]:
                    message = f"""
*{label}*

*From:* `{sender}`
*Subject:* {subject}

_Priority Level: {rules['priority']}_
"""
                    send_telegram_message(message, rules["priority"])
                    print(f"  └─ 📱 Notification sent")
        else:
            stats["unclassified"] += 1
            print(f"  └─ ⚪ No match (skipped)")
    
    print("\n" + "━" * 50)
    print("📊 TRIAGE SUMMARY")
    print("━" * 50)
    for label, count in stats.items():
        if count > 0 and label in LABEL_RULES:
            print(f"  {label}: {count}")
    print(f"  Total: {stats['total']}")
    print(f"  Unclassified: {stats['unclassified']}")
    print("━" * 50)
    
    # Send daily summary if it's the last run of the day
    hour = datetime.now().hour
    if hour >= 18:  # After 6 PM
        send_daily_summary(stats)


# ═══════════════════════════════════════════════════════════════
# PRIORITY ROUTING
# ═══════════════════════════════════════════════════════════════

def move_to_inbox_top(message_id: str):
    """Move important emails to top of inbox"""
    import subprocess
    
    try:
        # Mark as important
        subprocess.run(
            ["gog", "gmail", "messages", "modify", message_id, "--add-label", "IMPORTANT"],
            capture_output=True,
            timeout=10
        )
    except Exception as e:
        print(f"⚠️ Could not prioritize: {e}")


def archive_low_priority():
    """Auto-archive low priority emails"""
    import subprocess
    
    try:
        result = subprocess.run(
            ["gog", "gmail", "list", 
             "--query", "in:🗑️* is:unread older_than:7d",
             "--format=json"],
            capture_output=True,
            text=True,
            timeout=30
        )
        
        if result.returncode == 0:
            emails = json.loads(result.stdout) if result.stdout else []
            for email in emails:
                msg_id = email.get("id")
                subprocess.run(
                    ["gog", "gmail", "messages", "modify", msg_id, 
                     "--remove-label", "INBOX"],
                    capture_output=True,
                    timeout=10
                )
            print(f"📦 Archived {len(emails)} old low-priority emails")
    except Exception as e:
        print(f"⚠️ Could not archive: {e}")


# ═══════════════════════════════════════════════════════════════
# CLI INTERFACE
# ═══════════════════════════════════════════════════════════════

if __name__ == "__main__":
    import argparse
    
    parser = argparse.ArgumentParser(
        description="📧 Gmail Auto-Label & Smart Triage",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
Examples:
  python gmail-triage.py              # Run triage
  python gmail-triage.py --dry-run    # Preview only
  python gmail-triage.py --archive    # Archive old low-priority
  python gmail-triage.py --summary    # Send daily summary
        """
    )
    
    parser.add_argument("--dry-run", action="store_true", 
                        help="Preview without applying labels")
    parser.add_argument("--archive", action="store_true",
                        help="Archive old low-priority emails")
    parser.add_argument("--summary", action="store_true",
                        help="Send daily summary now")
    
    args = parser.parse_args()
    
    if args.archive:
        archive_low_priority()
    elif args.summary:
        send_daily_summary({
            "total": 0, "urgent": 0, "clients": 0, "tasks": 0,
            "documents": 0, "reports": 0, "newsletters": 0
        })
    else:
        triage_emails(dry_run=args.dry_run)

⚡ Automation Setup

1. Make Script Executable

bash
chmod +x ~/automation/gmail-triage/gmail-triage.py

2. Create Environment File

bash
cat > ~/automation/gmail-triage/.env << 'EOF'
TELEGRAM_BOT_TOKEN=your_bot_token_here
TELEGRAM_CHAT_ID=your_chat_id_here
EOF

3. Add to Crontab (Auto-Run Every 15 Minutes)

bash
# Open crontab
crontab -e

# Add this line:
*/15 * * * * cd ~/automation/gmail-triage && source .env && python3 gmail-triage.py >> triage.log 2>&1

# Daily summary at 6 PM
0 18 * * * cd ~/automation/gmail-triage && source .env && python3 gmail-triage.py --summary >> triage.log 2>&1

🧪 Testing Your Setup

Dry Run (Safe Preview)

bash
cd ~/automation/gmail-triage
python3 gmail-triage.py --dry-run

Test Telegram Notifications

bash
python3 -c "
from gmail_triage import send_telegram_message
send_telegram_message('🧪 Test notification working!', priority=1)
"

Verify Gmail Labels

bash
gog gmail labels list

📊 Expected Results

After running for 1 week:

text
┌─────────────────────────────────────────────────────────────┐
│  📧 WEEKLY STATISTICS                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  Emails Processed:     ~500                                 │
│  Auto-Labeled:         ~450 (90%)                           │
│  Manual Review:        ~50 (10%)                            │
│  Notifications Sent:   ~25                                  │
│  Time Saved:           ~3 hours/week                        │
│                                                             │
│  🎯 Zero emails slip through the cracks!                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘

🔧 Customization Tips

Adding New Labels

  1. Create label in Gmail
  2. Add to LABEL_RULES dictionary
  3. Define keywords and priority

Adjusting Keywords

Edit the keywords list in LABEL_RULES:

python
"🏢 Clients": {
    "keywords": ["your", "custom", "keywords", "here"],
    "senders": ["@yourclient.com"],
    "notify": True,
    "priority": 2
}

Changing Notification Schedule

Modify the cron timing:

bash
# Every 5 minutes (more frequent)
*/5 * * * * python3 gmail-triage.py

# Hourly
0 * * * * python3 gmail-triage.py

# Business hours only
*/15 9-17 * * 1-5 python3 gmail-triage.py

✅ Quick Start Checklist

  • Create 7 Gmail labels (📄 🏢 📊 📋 🔥 📰 🗑️)
  • Install gog CLI and authenticate
  • Create Telegram bot (@BotFather)
  • Get Telegram Chat ID (@userinfobot)
  • Save script to ~/automation/gmail-triage/
  • Set environment variables
  • Test with --dry-run
  • Add to crontab
  • Monitor first few runs
  • Adjust keywords as needed

🆘 Troubleshooting


💡 Pro Tip: Review your labeled emails weekly to refine keywords and improve accuracy!

Happy Triage! 🚀

Ada Pertanyaan? Yuk Ngobrol!

Butuh bantuan setup OpenClaw, konsultasi IT, atau mau diskusi project engineering? Book a call langsung — gratis.

Book a Call — Gratis

via Cal.com • WITA (UTC+8)

📬 Subscribe Newsletter

Free

Dapat alert setiap ada artikel baru langsung ke inbox kamu. Free, no spam. 🚀

👥 Join 0+ engineers & tech enthusiasts

F

Zainul Fanani

Founder, Radian Group. Engineering & tech enthusiast.

💬 Komentar

Catatan Fanani

Ngutak-ngatik teknologi, nulis pengalaman.

Perusahaan

  • CV Radian Fokus Mandiri — Balikpapan
  • PT UNO Solusi Teknik — Balikpapan
  • PT Reka Formasi Elektrika — Jakarta
  • PT Raya Fokus Solusi — Sidoarjo
© 2026 Catatan Fanani. All rights reserved.