Skip to main content

Check Mail

Maybe you also prefer to just check if there’s new mail on the command line, before having to open yet another browser tab and downloading twelve kilograms of Javascript 🤷🏾‍♂️

#!/bin/env python
"""
Retrieve number and subjects of unread mail from Gmail.

External dependencies:
    Set a GMAIL_LOGIN environment variable as a json string with Gmail access credentials.
    An example in Zsh:
        export GMAIL_LOGIN='{"username":"you@gmail.com", "password":"app_password"}'

Usage example:
    In Zsh:
        $ chmod +x check_mail.py
        $ ./check_mail.py

        Or drop the .py and copy the script to $HOME/.local/bin, then:
        $ check_mail
"""

import os
import json
import imaplib
from socket import gaierror
from typing import Generator
from email import policy, message_from_bytes


def check_unread_email(
    username: str, password: str, gmail_handle: imaplib.IMAP4_SSL
) -> Generator[str, None, None]:
    """Check if there's unread mail in Gmail

    Args:
        username: Gmail email address.
        password: Google app password https://support.google.com/accounts/answer/185833
        gmail_handle: An IMAP SSL object connected to Gmail.

    Returns:
        A generator of string(s).

    Side effect:
        Prints a message if there's no unread mail.

    Raises:
        None
    """

    try:
        gmail_handle.login(username, password)
        gmail_handle.select("INBOX")

        _, response = gmail_handle.search(None, "(UNSEEN)")

        if unread := response[0].split():
            for email_number in reversed(unread):
                status, email_data = gmail_handle.fetch(
                    email_number, "(BODY.PEEK[HEADER.FIELDS (subject)])"
                )
                if status == "OK":
                    yield message_from_bytes(
                        email_data[0][1], policy=policy.default  # pyright: ignore
                    )["subject"].strip().replace("\r\n", "")
        else:
            print("Inbox zero!")
            exit(0)

    except imaplib.IMAP4.error as e:
        print(e)

    finally:
        gmail_handle.close()
        gmail_handle.logout()


if __name__ == "__main__":
    try:
        for num, subject in enumerate(
            iterable=check_unread_email(
                gmail_handle=imaplib.IMAP4_SSL("imap.gmail.com"),
                **json.loads(os.getenv("GMAIL_LOGIN")),  # pyright: ignore
            ),
            start=1,
        ):
            print(f"{num}. {subject}")

    except (gaierror, ConnectionResetError):
        print("Could not connect to Gmail")