← Back to Skills Marketplace
occupythemilkyway

Iris Lite — Inbox Intelligence (Free)

by OccupyTheMilkyWay · GitHub ↗ · v1.0.0 · MIT-0
cross-platform ✓ Security Clean
25
Downloads
0
Stars
0
Active Installs
1
Versions
Install in OpenClaw
/install iris-lite
Description
Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste...
README (SKILL.md)

Iris Lite — Free Inbox Triage

Scan your last 25 emails and get a quick priority list with 2 draft replies.

Free vs Pro

Feature Iris Lite (Free) Iris Pro
Emails scanned 25 Up to 200
Draft replies 2 Every actionable email
Email categories ❌ ✅ Sales, HR, Legal, Finance
Reply tones ❌ Professional / Friendly / Brief
Weekly analytics ❌ ✅
JSON export ❌ ✅

👉 Upgrade: openclaw skills install iris-pro — get your key at ko-fi.com/occupythemilkyway


Step 1 — Install

pip3 install rich --break-system-packages --quiet

Step 2 — Quick inbox scan (Lite)

import os, imaplib, email, re
from email.header import decode_header
from email.utils import parsedate_to_datetime
from datetime import datetime, timezone
from rich.console import Console
from rich.table import Table
from rich.panel import Panel
from rich import box

console = Console()

GMAIL_ADDR = os.environ.get("GMAIL_ADDRESS", "").strip()
GMAIL_PASS = os.environ.get("GMAIL_APP_PASSWORD", "").strip()
YOUR_NAME  = os.environ.get("YOUR_NAME", "").strip()

# Lite hard limits
SCAN_LIMIT   = 25
DRAFT_LIMIT  = 2

if not GMAIL_ADDR or not GMAIL_PASS:
    console.print(Panel(
        "[red]GMAIL_ADDRESS and GMAIL_APP_PASSWORD are required.[/red]\
\
"
        "App password: myaccount.google.com/apppasswords",
        title="Setup Required", border_style="red"))
    raise SystemExit(1)

URGENT_KEYWORDS = ["urgent","asap","deadline","immediately","overdue","payment","invoice","emergency"]
NOISE_PATTERNS  = [r"unsubscribe",r"newsletter",r"no-reply@",r"noreply@",r"marketing@"]

def decode_str(s):
    if not s: return ""
    parts = decode_header(s)
    result = []
    for part, enc in parts:
        if isinstance(part, bytes):
            result.append(part.decode(enc or "utf-8", errors="replace"))
        else:
            result.append(str(part))
    return " ".join(result)

def is_noise(sender, subject):
    return any(re.search(p, (sender+subject).lower()) for p in NOISE_PATTERNS)

def score_email(subject, snippet, sender, age_hours, has_replied):
    score = 50
    text  = (subject + " " + snippet).lower()
    if any(k in text for k in URGENT_KEYWORDS): score += 20
    if "?" in text: score += 10
    if age_hours > 48: score -= 10
    if has_replied: score -= 15
    return max(0, min(score, 100))

console.print(Panel.fit(
    f"[bold cyan]🌈 Iris Lite — Quick Inbox Scan[/bold cyan]\
"
    f"Scanning last [yellow]{SCAN_LIMIT}[/yellow] emails from [green]{GMAIL_ADDR}[/green]\
"
    f"[dim]Lite: 25 emails, 2 drafts — upgrade to Pro for full inbox coverage[/dim]",
    border_style="cyan"
))

try:
    mail = imaplib.IMAP4_SSL("imap.gmail.com", 993)
    mail.login(GMAIL_ADDR, GMAIL_PASS)
    mail.select("INBOX", readonly=True)
except Exception as e:
    console.print(f"[red]❌ Login failed: {e}\
Check GMAIL_ADDRESS and GMAIL_APP_PASSWORD.[/red]")
    raise SystemExit(1)

_, msg_ids = mail.search(None, "ALL")
all_ids    = msg_ids[0].split() if msg_ids and msg_ids[0] else []
recent_ids = list(reversed(all_ids[-SCAN_LIMIT:])) if len(all_ids) > SCAN_LIMIT else list(reversed(all_ids))

emails_data = []
now = datetime.now(timezone.utc)
for uid in recent_ids:
    try:
        _, raw = mail.fetch(uid, "(RFC822.HEADER FLAGS)")
        if not raw or not raw[0]: continue
        raw_header  = raw[0][1] if isinstance(raw[0], tuple) else raw[0]
        msg         = email.message_from_bytes(raw_header)
        subject     = decode_str(msg.get("Subject","(no subject)"))
        sender      = decode_str(msg.get("From",""))
        flags_raw   = raw[0][0] if isinstance(raw[0], tuple) else b""
        has_replied = b"\\Answered" in flags_raw
        try:
            sent_dt   = parsedate_to_datetime(msg.get("Date",""))
            if sent_dt.tzinfo is None: sent_dt = sent_dt.replace(tzinfo=timezone.utc)
            age_hours = (now - sent_dt).total_seconds() / 3600
        except Exception:
            age_hours = 0
        body_snippet = ""
        try:
            _, rb = mail.fetch(uid, "(BODY[TEXT]\x3C0.150>)")
            if rb and rb[0] and isinstance(rb[0], tuple) and rb[0][1]:
                body_snippet = rb[0][1].decode("utf-8", errors="replace").strip()[:100]
        except Exception:
            pass
        m = re.search(r'"?([^"\x3C]+)"?\s*\x3C([^>]+)>', sender)
        sender_name = m.group(1).strip() if m else sender
        noise   = is_noise(sender, subject)
        urgency = score_email(subject, body_snippet, sender, age_hours, has_replied)
        emails_data.append({
            "subject": subject, "sender": sender_name, "age_hours": age_hours,
            "urgency": urgency, "is_noise": noise, "replied": has_replied, "snippet": body_snippet,
        })
    except Exception:
        continue

mail.logout()

actionable = sorted([e for e in emails_data if not e["is_noise"]], key=lambda e: -e["urgency"])
noise_count = sum(1 for e in emails_data if e["is_noise"])

console.print()
tbl = Table(title=f"📬 Priority Inbox — Top {min(len(actionable), 15)} of {len(actionable)}",
            box=box.ROUNDED, border_style="cyan")
tbl.add_column("Score", width=7, justify="right")
tbl.add_column("From",  width=22, style="yellow")
tbl.add_column("Subject", width=42)
tbl.add_column("Age",   width=6, style="dim")

for e in actionable[:15]:
    sc  = "red" if e["urgency"] >= 70 else "yellow" if e["urgency"] >= 50 else "dim"
    age = f"{int(e['age_hours'])}h" if e["age_hours"] \x3C 48 else f"{int(e['age_hours']//24)}d"
    tbl.add_row(f"[{sc}]{e['urgency']}[/{sc}]", e["sender"][:20], e["subject"][:40], age)
console.print(tbl)

# 2 draft replies (Lite limit)
unreplied = [e for e in actionable if not e["replied"]]
console.print()
for e in unreplied[:DRAFT_LIMIT]:
    name   = e["sender"].split()[0]
    text   = (e["subject"] + " " + e["snippet"]).lower()
    if any(k in text for k in ["urgent","deadline","asap"]):
        body = "Thank you for flagging this — I'll look into it and get back to you shortly."
    elif "?" in text:
        body = "Thanks for your question. To answer: [your answer here]\
\
Let me know if you need more."
    else:
        body = "Thanks for reaching out. [Your response here]."
    sig  = f"\
\
— {YOUR_NAME}" if YOUR_NAME else ""
    console.print(Panel(
        f"Hi {name},\
\
{body}{sig}",
        title=f"[bold]📝 Draft — Re: {e['subject'][:45]}[/bold]",
        border_style="yellow"
    ))

console.print()
console.print(Panel(
    f"Scanned: [yellow]{len(emails_data)}[/yellow]  "
    f"Actionable: [cyan]{len(actionable)}[/cyan]  "
    f"Noise: [dim]{noise_count}[/dim]  "
    f"Drafts: [yellow]{min(len(unreplied), DRAFT_LIMIT)}/{DRAFT_LIMIT}[/yellow] (Lite limit)\
\
"
    f"[bold yellow]🔓 Want more?[/bold yellow]\
"
    f"Iris Pro scans [bold]200 emails[/bold], drafts [bold]every reply[/bold], classifies by category, "
    f"and gives you weekly analytics.\
\
"
    f"[bold cyan]openclaw skills install iris-pro[/bold cyan]\
"
    f"Get your key → [bold]ko-fi.com/occupythemilkyway[/bold]",
    title="Summary + Upgrade",
    border_style="cyan"
))
Usage Guidance
Before installing, make sure you are comfortable giving the skill a Gmail app password and allowing it to read recent inbox subjects and snippets. Consider using a revocable app password and installing Python dependencies in a virtual environment.
Capability Analysis
Type: OpenClaw Skill Name: iris-lite Version: 1.0.0 The iris-lite skill is a legitimate email triage tool that scans the last 25 Gmail messages via IMAP to generate urgency scores and draft replies. The code in SKILL.md uses standard Python libraries (imaplib, email) and follows its stated purpose without any evidence of data exfiltration, hidden network calls, or malicious instructions. It correctly uses a read-only IMAP connection and handles sensitive credentials via environment variables.
Capability Tags
cryptocan-make-purchases
Capability Assessment
Purpose & Capability
The stated purpose is to scan recent Gmail messages and create a priority list, and the provided code matches that purpose, but it necessarily handles sensitive email account access and email snippets.
Instruction Scope
The visible instructions bound the scan to the last 25 inbox messages, use readonly IMAP selection, and do not show sending, deleting, or modifying Gmail messages.
Install Mechanism
The skill is instruction-only but asks the user to install the unpinned PyPI package 'rich' with '--break-system-packages', which can affect the local Python environment.
Credentials
Requiring GMAIL_ADDRESS and GMAIL_APP_PASSWORD is proportionate for a Gmail IMAP skill, but a Gmail app password is still a sensitive credential.
Persistence & Privilege
The provided artifacts show no background persistence, no stored files, no protected-path writes, and the visible code logs out of IMAP after scanning.
How to Use
  1. Make sure OpenClaw is installed (local or Docker)
  2. Run the install command in chat: /install iris-lite
  3. After installation, invoke the skill by name or use /iris-lite
  4. Provide required inputs per the skill's parameter spec and get structured output
Version History
v1.0.0
Initial Lite release: 25 email scan, 2 drafts, priority list, Pro upsell
Metadata
Slug iris-lite
Version 1.0.0
License MIT-0
All-time Installs 0
Active Installs 0
Total Versions 1
Frequently Asked Questions

What is Iris Lite — Inbox Intelligence (Free)?

Iris Lite — Inbox Intelligence (Free). Scans your last 25 Gmail emails, shows urgency scores and a priority list, and drafts 2 quick replies. A free taste... It is an AI Agent Skill for Claude Code / OpenClaw, with 25 downloads so far.

How do I install Iris Lite — Inbox Intelligence (Free)?

Run "/install iris-lite" in the OpenClaw or Claude Code chat to install it in one step — no extra setup required.

Is Iris Lite — Inbox Intelligence (Free) free?

Yes, Iris Lite — Inbox Intelligence (Free) is completely free, licensed under MIT-0. You can download, install and use it at no cost.

Which platforms does Iris Lite — Inbox Intelligence (Free) support?

Iris Lite — Inbox Intelligence (Free) is cross-platform and runs anywhere OpenClaw / Claude Code is available (cross-platform).

Who created Iris Lite — Inbox Intelligence (Free)?

It is built and maintained by OccupyTheMilkyWay (@occupythemilkyway); the current version is v1.0.0.

💬 Comments