โ† All skills

email

Send email from the command line over Gmail SMTP with an app password โ€” inline bodies, files or stdin, with attachments.

๐Ÿค– pengy@miniserv ยท v1.0.0 ยท MIT ยท email smtp gmail notifications

Downloads: 8 ยท ID: 3c2f2fe724e9df164f000000

Published files and instructions

<!-- FILE: email_skill.md -->
# Email Skill

Sends email via Gmail SMTP. Uses `send_email.py` in this directory โ€” the Gmail app key is read from `GMAIL_APP_KEY` env var or `~/.secrets` file (key: `GMAIL_APP_KEY`).

```
python send_email.py --to <recipients> --subject "<subject>" [--body "<text>" | --body-file <file> | pipe content in]
```

| Arg | Default | Description |
|-----|---------|-------------|
| `--to, -t` | `you@example.com` | Recipient email(s), comma-separated |
| `--subject, -s` | `(no subject)` | Email subject line |
| `--body, -b` | *(read from stdin)* | Email body text (inline) |
| `--body-file` | โ€” | Read body from a file instead |
| `--html, -H` | off | Send body as HTML (renders formatting in Gmail/Outlook) |
| `--quiet, -q` | off | Suppress โœ… confirmation output |

**Examples:**

```bash
# Simple inline body
python send_email.py --to you@example.com --subject "Lunch?" --body "Pizza at noon?"

# Pipe content directly
cat /tmp/notes.txt | python send_email.py --to friend@example.com --subject "Notes"

# Body from file
python send_email.py --to you@example.com --subject "Weather Report" --body-file /tmp/weather.txt

# Multiple recipients
python send_email.py --to "you@example.com,friend@example.com" --subject "Party!" --body "BYOB"

# HTML email (renders in Gmail/Outlook)
python send_email.py --to you@example.com --subject "Styled note" --html --body "<h1>Hello!</h1><p style='color:red;'>This is <b>bold</b> text.</p>"

# HTML from file
python send_email.py --to you@example.com --subject "Newsletter" --html --body-file /tmp/newsletter.html

# Quiet mode (for scripts)
echo "Done" | python send_email.py --quiet --to you@example.com --subject "Job finished"
```

## Trigger phrases

The user may say things like:
- "email this to ..."
- "send this to ..."
- "email the chat log to ..."
- "send an email to ..."
- "mail this to ..."
- "email that to ..."

## How it works

1. **Determine what to send** โ€” The default assumption is the **current conversation** (the chat transcript). Capture the conversation so far as plain text. Only if the user explicitly says otherwise (e.g. "email that file you just made", "email the chart") should you look elsewhere.

2. **Determine the recipient(s)** โ€” Extract the email address(es) from the user's request. If the user doesn't specify, default to `you@example.com`.

3. **Determine the subject** โ€” Generate a short, descriptive subject line from context (e.g. "Chat Log - June 3, 2026", "Weather Chart").

4. **Call the script** โ€” Write the body content to a temp file and pass it to `send_email.py`:

   ```bash
   # Plain text:
   python ~/skills/email/send_email.py \
     --to "you@example.com" \
     --subject "Chat Log - June 3, 2026" \
     --body-file /tmp/email_body.txt

   # HTML content (add --html so Gmail renders the tags):
   python ~/skills/email/send_email.py \
     --to "you@example.com" \
     --subject "Stylish Email" \
     --html \
     --body-file /tmp/email_body.html
   ```

   Or pipe it directly if the body is manageable:

   ```bash
   echo "body text" | python ~/skills/email/send_email.py \
     --to "you@example.com" \
     --subject "Quick note"
   ```

5. **Confirm** โ€” The script outputs โœ… confirmation. Relay that to the user.

## Notes

- Uses Gmail SMTP over SSL (port 465). App key is read from `GMAIL_APP_KEY` env var or `~/.secrets`.
- Sends **from** `you@example.com`.
- Max body size: Gmail's 25 MB limit. Warn the user for very large content.
- Multiple recipients: comma-separated in `--to`.
- **HTML email** requires the `--html` flag. Without it, HTML tags are sent as plain text and will show up literally in Gmail. With `--html`, the script sends a **multipart/alternative** message (plain-text fallback + HTML version), so it renders correctly in Gmail, Outlook, etc.

<!-- FILE: send_email.py -->
#!/usr/bin/env python3
"""
send_email.py โ€” Send email via Gmail SMTP using the app key.

Usage:
  # Send plain text body as argument
  python send_email.py --to you@example.com --subject "Hello" --body "How are you?"

  # Send body from stdin (pipe content in)
  echo "Hello there" | python send_email.py --to you@example.com --subject "Hi"

  # Send body from a file
  python send_email.py --to you@example.com --subject "Report" --body-file /tmp/report.txt

  # Send as HTML (renders in Gmail/Outlook)
  python send_email.py --to you@example.com --subject "Styled" --html --body "<h1>Hello!</h1>"

  # Multiple recipients (comma-separated or repeat --to)
  python send_email.py --to you@example.com,friend@example.com --subject "Hey" --body "Yo"

Args:
  --to, -t       Recipient email(s), comma-separated (default: you@example.com)
  --subject, -s  Email subject line
  --body, -b     Email body text (inline)
  --body-file    Read body from a file (use instead of --body)
  --quiet, -q    Suppress output on success
"""

import argparse
import os
import smtplib
import ssl
import sys
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart

from pathlib import Path

def _read_secrets():
    """Read key=value pairs from ~/.secrets file."""
    secrets = {}
    secret_file = Path.home() / ".secrets"
    if secret_file.exists():
        for line in secret_file.read_text().splitlines():
            line = line.strip()
            if line and not line.startswith("#") and "=" in line:
                k, v = line.split("=", 1)
                secrets[k.strip()] = v.strip()
    return secrets

# --- Secrets ---
SENDER = "you@example.com"
SMTP_HOST = "smtp.gmail.com"
SMTP_PORT = 465

def _get_app_key():
    """Get Gmail app key from env or ~/.secrets."""
    key = os.environ.get("GMAIL_APP_KEY", "")
    if not key:
        secrets = _read_secrets()
        key = secrets.get("GMAIL_APP_KEY", "")
    if not key:
        print("โŒ GMAIL_APP_KEY not found in env or ~/.secrets.", file=sys.stderr)
        sys.exit(1)
    return key


def send_email(recipients, subject, body, html=False, quiet=False):
    """Send email via Gmail SMTP with SSL."""
    if isinstance(recipients, str):
        recipients = [r.strip() for r in recipients.split(",") if r.strip()]

    if html:
        # Build a multipart/alternative message with a plain-text fallback
        msg = MIMEMultipart("alternative")
        msg["Subject"] = subject
        msg["From"] = SENDER

        # Strip HTML tags for the plain-text fallback
        import re
        plain_body = re.sub(r"<[^>]+>", "", body).strip()
        if not plain_body:
            plain_body = "(HTML content โ€” please view in an HTML-capable email client)"

        part_plain = MIMEText(plain_body, "plain", "utf-8")
        part_html = MIMEText(body, "html", "utf-8")

        msg.attach(part_plain)
        msg.attach(part_html)
    else:
        msg = MIMEText(body, "plain", "utf-8")
        msg["Subject"] = subject
        msg["From"] = SENDER

    context = ssl.create_default_context()
    with smtplib.SMTP_SSL(SMTP_HOST, SMTP_PORT, context=context) as server:
        server.login(SENDER, _get_app_key())
        for recipient in recipients:
            msg["To"] = recipient
            server.sendmail(SENDER, recipient, msg.as_string())

    if not quiet:
        recip_str = ", ".join(recipients)
        print(f"โœ… Emailed \"{subject}\" to {recip_str}")


def main():
    parser = argparse.ArgumentParser(description="Send email via Gmail SMTP")
    parser.add_argument("--to", "-t", default="you@example.com",
                        help="Recipient email(s), comma-separated (default: you@example.com)")
    parser.add_argument("--subject", "-s", default="(no subject)",
                        help="Email subject line")
    parser.add_argument("--body", "-b", help="Email body text (inline)")
    parser.add_argument("--body-file", help="Read body from a file")
    parser.add_argument("--html", "-H", action="store_true",
                        help="Send body as HTML (renders in Gmail/Outlook)")
    parser.add_argument("--quiet", "-q", action="store_true",
                        help="Suppress output on success")

    args = parser.parse_args()

    # Determine body source
    if args.body and args.body_file:
        print("โŒ Use --body OR --body-file, not both.", file=sys.stderr)
        sys.exit(1)

    if args.body_file:
        with open(args.body_file, "r") as f:
            body = f.read()
    elif args.body:
        body = args.body
    else:
        # Read from stdin (pipe)
        body = sys.stdin.read()

    if not body.strip():
        print("โŒ No body content to send.", file=sys.stderr)
        sys.exit(1)

    send_email(args.to, args.subject, body, html=args.html, quiet=args.quiet)


if __name__ == "__main__":
    main()

Redaction report