Skip to main content

Dig Gmail emails and attachments into LLM-friendly markdown

Project description

maildigger — digging up your emails

maildigger

Dig your Gmail into structured, LLM-ready markdown in one command.

Extract emails and attachments from Gmail into clean markdown files with YAML frontmatter. PDFs, DOCX, XLSX, PPTX are auto-converted to text. Zero Google Cloud setup — just an App Password and you're in.

maildigger search -q "from:accountant@example.com after:2025/01/01 has:attachment"
 ╔══════════════════════════════════════════════╗
 ║        maildigger  v0.1.0                   ║
 ║  Digging up your emails for LLMs            ║
 ╚══════════════════════════════════════════════╝

 ✓ Found 57 emails

 Processing emails... ━━━━━━━━━━━━━━━━━━━━ 57/57

 ┌──────────── ✓ Extraction Complete ────────────┐
 │ Emails extracted    57                         │
 │ Attachments saved   176                        │
 │ Converted to text   23                         │
 └────────────────────────────────────────────────┘

Why?

You've got years of invoices, contracts, statements, and correspondence buried in Gmail. You want to feed them to an LLM — but Gmail gives you .eml blobs and nested MIME trees.

maildigger turns that mess into a clean folder of markdown files and converted attachments that any LLM can consume directly.

No Google Cloud project. No OAuth dance. No API keys. Just a 16-character App Password over IMAP.


Quickstart

1. Install

pip install -e .

2. Authenticate

maildigger auth

You'll need a Gmail App Password (requires 2-Step Verification). That's the only setup.

3. Extract

# Use Gmail's exact search syntax
maildigger search -q "from:alice@example.com after:2025/01/01"

# Or structured filters
maildigger search --sender boss@company.com --has-attachment --after 2025-06-01

# Dry run — see what matches without downloading
maildigger search -q "label:important" --dry-run

Search Examples

# Everything from a sender with attachments
maildigger search -q "from:invoices@vendor.com has:attachment"

# Subject search within a date range
maildigger search -q "subject:invoice after:2024/06/01 before:2024/12/31"

# Multiple people (finds emails involving any of them)
maildigger search -p alice@example.com -p bob@example.com --after 2025-01-01

# Label + starred
maildigger search -q "label:finance is:starred"

# Full-text body search
maildigger search -q "quarterly report budget"

# Cap results
maildigger search -q "from:reports@company.com" --limit 50

# Custom output directory
maildigger search -q "label:projects" -o ./my-exports

Output

Each run creates a timestamped, self-contained folder:

artifacts/
└── 2026-03-28_143052_from-alice-example-com/
    ├── manifest.json                              # Machine-readable index
    ├── manifest.md                                # Human-readable summary
    └── emails/
        ├── 0001_2025-06-15_q3-planning.md         # Email as markdown
        ├── 0001_..._attachments/
        │   ├── report.pdf                         # Original
        │   ├── report.pdf.txt                     # Extracted text
        │   ├── budget.xlsx                        # Original
        │   └── budget.xlsx.csv                    # Converted to CSV
        ├── 0002_2025-06-16_re-q3-planning.md
        └── ...

Email Format

Every email becomes a self-contained markdown file:

---
message_id: <abc123@mail.gmail.com>
from: Alice Smith <alice@example.com>
to: Bob Jones <bob@example.com>
date: 2025-06-15T10:30:00-07:00
subject: "Q3 Planning Document"
attachments: [report.pdf, budget.xlsx]
---

# Q3 Planning Document

Hi Bob,

Here's the Q3 planning document we discussed...

Metadata File (manifest.json)

Each extraction includes a machine-readable manifest with full metadata:

{
  "extraction_date": "2026-03-28T20:43:06.241721+00:00",
  "query": "from:invoices@vendor.com has:attachment",
  "total_emails": 57,
  "total_attachments": 176,
  "emails": [
    {
      "index": 1,
      "file": "emails/0001_2025-06-15_q3-planning.md",
      "message_id": "<abc123@mail.gmail.com>",
      "gmail_id": "98401",
      "from": "Alice Smith <alice@example.com>",
      "to": ["Bob Jones <bob@example.com>"],
      "cc": [],
      "date": "2025-06-15T10:30:00-07:00",
      "subject": "Q3 Planning Document",
      "labels": ["\\Important"],
      "attachments": [
        {
          "filename": "report.pdf",
          "content_type": "application/pdf",
          "size": 245120,
          "original": "emails/0001_..._attachments/report.pdf",
          "converted": "emails/0001_..._attachments/report.pdf.txt"
        },
        {
          "filename": "budget.xlsx",
          "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
          "size": 18432,
          "original": "emails/0001_..._attachments/budget.xlsx",
          "converted": "emails/0001_..._attachments/budget.xlsx.csv"
        }
      ],
      "word_count": 342
    }
  ]
}
Field Type Description
extraction_date string ISO 8601 timestamp (UTC) of when the extraction ran
query string Gmail search query used
total_emails int Number of emails extracted
total_attachments int Total attachments across all emails
emails[].index int Sequential 1-based index
emails[].file string Relative path to the email markdown file
emails[].message_id string RFC 822 Message-ID header
emails[].gmail_id string Gmail internal UID
emails[].from string Sender (display name + address)
emails[].to string[] Recipient addresses
emails[].cc string[] CC addresses (empty array if none)
emails[].date string|null ISO 8601 datetime, or null if unparseable
emails[].subject string Email subject line
emails[].labels string[] Gmail labels
emails[].attachments object[] Attachment records (see below)
emails[].word_count int Word count of the email body
attachments[].filename string Original filename
attachments[].content_type string MIME type
attachments[].size int Size in bytes
attachments[].original string Relative path to the saved file
attachments[].converted string? Relative path to converted text (only present if conversion succeeded)

Auto-Conversion

Format Converts To How
PDF .pdf.txt pdfplumber (page-by-page)
DOCX .docx.txt python-docx
XLSX .xlsx.csv openpyxl (per-sheet)
PPTX .pptx.txt python-pptx (per-slide)
HTML .html.md beautifulsoup4 + markdownify
Images kept as-is multimodal LLMs handle these directly

Conversion is best-effort — if it fails, the original is always preserved.


Feeding to LLMs

This tool was built specifically for LLM workflows:

  1. Start with the manifestmanifest.md gives the LLM a bird's-eye view of all emails
  2. Drill into individual emails — each .md file is self-contained with full metadata
  3. Converted attachments are ready to paste.txt and .csv files go straight into context
  4. Images work with multimodal models — Claude, GPT-4o, Gemini can read them directly

Example: Claude Code workflow

# Extract all emails from a vendor
maildigger search -q "from:invoices@vendor.com"

# Then in Claude Code, just point at the output
# "Study the emails in artifacts/ and build me a 2025 expense report"

Dependencies

All lightweight, well-maintained libraries. No Google API client libraries — just Python's built-in imaplib talking to Gmail's IMAP server.

Library Purpose
click CLI framework
rich Beautiful terminal output
pdfplumber PDF text extraction
python-docx DOCX text extraction
openpyxl XLSX to CSV conversion
python-pptx PPTX text extraction
beautifulsoup4 + markdownify HTML to Markdown

Troubleshooting

Problem Fix
"No saved credentials" Run maildigger auth
"Login failed" Use an App Password (16 chars), not your regular password
"Application-specific password required" Generate one at myaccount.google.com/apppasswords
Empty results Check the query works in Gmail's search bar first. Emails in Trash won't match.
Slow extraction IMAP is sequential. Use --limit for large mailboxes.
IMAP disabled Gmail Settings > Forwarding and POP/IMAP > Enable IMAP

License

MIT


Built for the age of LLMs. Stop copy-pasting from Gmail.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

maildigger-0.1.1.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

maildigger-0.1.1-py3-none-any.whl (20.8 kB view details)

Uploaded Python 3

File details

Details for the file maildigger-0.1.1.tar.gz.

File metadata

  • Download URL: maildigger-0.1.1.tar.gz
  • Upload date:
  • Size: 21.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for maildigger-0.1.1.tar.gz
Algorithm Hash digest
SHA256 ac002661e9c5cd92318bc0a57f2515ba29b5d823624ebbac089dc2e7dd911a43
MD5 010fe1a52d0696e668bfdc5585ad0200
BLAKE2b-256 e47c18c6fb8e7d617ab74653650bae079289704ad0e0b161cf2326e901bdbfee

See more details on using hashes here.

Provenance

The following attestation bundles were made for maildigger-0.1.1.tar.gz:

Publisher: release.yml on kotsaris/maildigger

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file maildigger-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: maildigger-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 20.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for maildigger-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 fc71ecab0c0a1cdb9bab4547b49a3714f936678b63dad33917290ab9d839e107
MD5 ade8ef38f84832cbebf656613f3e90be
BLAKE2b-256 ab5521847c912b3a186000d6882140c2e5bed2bd9613aff03aba7300892ab82c

See more details on using hashes here.

Provenance

The following attestation bundles were made for maildigger-0.1.1-py3-none-any.whl:

Publisher: release.yml on kotsaris/maildigger

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page