Building a Lightweight, Automated Email Sorting Robot for Gmail with Python and IMAP

Managing high-volume transactional emails, notifications, and alerts can quickly clutter any professional inbox. While built-in filters help, they often lack the dynamic flexibility needed to extract custom metadata, integrate with external logging systems, or gracefully handle custom naming conventions (like dynamic emojis) on the fly.

To solve this, I designed and deployed a lightweight, autonomous Mail Sorting Robot using Python and GitHub Actions. Here is a technical breakdown of its architecture, execution pipeline, and key IMAP implementation details.


1. Core Objectives & Solved Tasks

The system operates on an automated schedule to achieve three main results:

  • Dynamic Classification: Instead of hardcoded rules for every sender, the robot extracts the core brand identity directly from the email domain (e.g., communication.raiffeisen.ua becomes Raiffeisen) and dynamically creates isolated folders inside Gmail.
  • Urgency & Importance Assessment: It scans incoming subject lines using pre-compiled regular expressions to separate transactional noise from high-priority system alerts or critical business communications.
  • External Ledger Sync: Every successful execution batch aggregates metrics and logs the data to an external data warehouse/dashboard (via Google Apps Script HTTPS endpoints) for real-time tracking and volume analysis.

2. Technical Stack & Architecture

To maximize performance and keep the footprint minimal, the project completely avoids heavy frameworks, relying strictly on standard Python libraries and lightweight parsing utilities.

The Stack

  • Runtime: Python 3.13 (utilizing native standard libraries)
  • Protocols: IMAP (via standard imaplib)
  • Parsing: mail-parser (for safe, robust extraction of MIME headers and body payloads)
  • Automation: GitHub Actions (Runner environment with custom pip caching enabled)

Component Design & Tips

UNSEEN CheckBODY.PEEK[]Regex/DomainReportGmail INBOXIMAPConnectormail-parserSlack Notificationmain.pyFilters & RulesGoogle Sheets API

💡 Tip 1: The BODY.PEEK[] Pattern Standard IMAP fetch methods like (RFC822) automatically mark emails as “Read” on the server. To evaluate content without altering the user’s unread badges, the robot utilizes BODY.PEEK[]. This ensures that when an email is copied to its destination folder, it remains UNSEEN (unread) for the end-user.

Tip 2: Network Optimization via UIDs Iterating over volatile message sequence numbers often leads to concurrency race conditions. The robot strictly uses UID commands (uid('search'), uid('fetch'), uid('COPY')) to guarantee atomic execution during multi-step routing.

⚙️ Tip 3: Pre-Compiled Regular Expressions For high-throughput keyword scanning, regex patterns are compiled once at module initialization (re.compile(pattern, re.IGNORECASE)), preventing the runtime overhead of recompiling strings inside the email loops.


3. Implementation Specifics (IMAP & Gmail Quirks)

Working directly with IMAP protocols requires handling several platform-specific edge cases, especially regarding folder localization and session states.

UTF-7 Modified Encoding for Emojis

Gmail directories support rich visuals, but the IMAP specification requires folder names containing non-ASCII characters (like 📁_CHANNELS or ⚠️_IMPORTANT) to be encoded in Modified UTF-7. The script dynamically translates strings to ensure nested folders render natively across all mail clients without throwing structural server errors.

Atomic “Move” Lifecycle

Because native IMAP protocols do not have a dedicated atomic “MOVE” instruction that behaves consistently across all environments, the robot guarantees mail preservation through a rigorous three-step lifecycle:

  1. uid('COPY', msg_id, destination_folder): Securely replicates the message structure into the newly resolved label path.
  2. uid('STORE', msg_id, '+FLAGS', '\\Deleted'): Marks the original staging message in INBOX as redundant only after a verified successful copy status.
  3. expunge(): Issued exactly once at the end of the batch operation to instruct the Gmail server to purge the marked originals from the root folder globally.

4. Key Takeaways & Added Value

Building this system highlighted a few critical insights regarding enterprise automation workflows:

  • Fail-Safe Isolation (Unprocessed Folder): If an unpredictable parsing or network error occurs midway through processing an email, the script intercepts the exception, sends an immediate alert to Slack with a stack trace, and moves the problematic email to a safe Unprocessed directory. This prevents the pipeline from stalling or getting stuck in an infinite loop on corrupt messages.
  • Server-Side Efficiency: Keeping logical operations server-side via IMAP copies rather than downloading full raw payloads, mutating them locally, and uploading them back reduces bandwidth requirements significantly. This makes it ideal for a fast GitHub Actions cron job.
  • Decoupled Architecture: Separating the classification engine (filters.py), business criteria (rules.py), and transport layer (connection.py) makes the codebase highly maintainable. Adapting this robot to a different provider or scaling it up requires zero architectural modifications.

Logo

© 2026Oksana Gorbenko. Released under the MIT License.

v1.0.0Built with Astro & TailwindLinkedinGithub