IMAP4-Mailordner pro Monat erstellen (Python)

Gepostet am 06. Dezember 2017  (Zuletzt geändert am 13. Dezember 2022 )
4 Minuten  • 747 Wörter  • Andere Sprachen:  English

Vor kurzem hat mich das Mail-Archiv einer Firma in den Wahnsinn getrieben – dort laufen alle möglichen Anfrage- und Statusmails ein, die dann aus rechtlichen Gründen archiviert werden. Bislang hat ein Mitarbeiter jeden Monat die ca. 40.000 Mails per Hand in einen Monatsordner verschoben. Dass Firefox keine Probleme mit diesen Mailmassen hat, finde ich zwar beeindruckend, aber die Arbeit ist dröge, nervig und dann doch irgendwie lästig.

Also habe ich nach diversen Anleitungen ein kleines Python-Skript geschrieben, dass sich per IMAP auf dem Mailserver einloggt und neue Mails automatisch aus der Inbox in einen passenden Monatsordner verschiebt. Eine kleine Anleitung dazu hier.

Konfigurationsdatei

Zunächst erstellt man dazu eine Konfigurationsdatei, z.B. mail_domain.de.ini mit folgendem Inhalt:

[server]
hostname: imap.server.de
 
[account]
username: login
password: passwort

Hostname enthält den Namen des IMAP-Servers, username den Login-Namen und Password eben das Passwort. Das Skript unten meldet sich per TLS/SSL an. Wer das nicht will, muss hier wohl noch Hand anlegen. Für Kommentare und Feedback bin ich dankbar!

Das Skript

Das Skript imap_folder_per_month.py selbst folgt hier:

#!/usr/bin/python3

import configparser
import datetime
import email.utils
import imaplib
import os
import re
import sys

list_response_pattern = re.compile(r'\((?P<flags>.*?)\) "(?P<delimiter>.*)" (?P<name>.*)')


def parse_list_response(line):
    flags, delimiter, mailbox_name = list_response_pattern.match(line.decode()).groups()
    mailbox_name = mailbox_name.strip('"')
    return (flags, delimiter, mailbox_name)


def open_connection(config_file, verbose=False):
    # Read the config file
    config = configparser.ConfigParser()
    config.read([os.path.expanduser(config_file)])

    # Connect to the server
    hostname = config.get('server', 'hostname')
    if verbose:
        print('Connecting to', hostname)
    connection = imaplib.IMAP4_SSL(hostname)

    # Login to our account
    username = config.get('account', 'username')
    password = config.get('account', 'password')
    if verbose:
        print('Logging in as', username)
    connection.login(username, password)
    return connection


if __name__ == '__main__':
    if len(sys.argv) < 2:
        print('Pass the name of the config file as first parameter, please!')
        sys.exit(-1)
    c = open_connection(sys.argv[1], verbose=True)
    try:
        # List of Mailboxes
        mailboxes = []
        typ, data = c.list()
        for line in data:
            flags, delimiter, mailbox_name = parse_list_response(line)
            mailboxes.append(mailbox_name)
            # print 'Parsed response:', (flags, delimiter, mailbox_name)

        # pprint.pprint(mailboxes)
        print(mailboxes)

        # get all messages from inbox
        typ, data = c.select('INBOX')
        num_msgs = int(data[0])
        print('There are %d messages in INBOX' % num_msgs)

        typ, msg_ids = c.search(None, 'ALL')
        msg_ids = msg_ids[0].decode()
        if msg_ids == '':
            msg_ids = []
        else:
            msg_ids = msg_ids.split(' ')[::-1]

        for msg_id in msg_ids:
            typ, msg_data = c.fetch(msg_id, '(RFC822)')
            msg = email.message_from_string(msg_data[0][1].decode())
            date_tuple = email.utils.parsedate_tz(msg['Date'])
            # pprint.pprint(date_tuple)

            # Convert to date string
            local_date = datetime.datetime.fromtimestamp(email.utils.mktime_tz(date_tuple))
            to_mailbox = 'INBOX/' + local_date.strftime("%Y-%m")

            print('Parsing #' + msg_id + ' ' + local_date.strftime("%Y-%m-%d"))

            if to_mailbox not in mailboxes:
                print(to_mailbox + ' created')
                typ, create_response = c.create(to_mailbox)
                mailboxes.append(to_mailbox)

            # Move message to mailbox
            c.copy(msg_id, to_mailbox)
            c.store(msg_id, '+FLAGS', r'(\Deleted)')

        # Clear
        c.expunge()
    finally:
        c.close()
        c.logout()

Das Skript liest den Namen der Konfigurationsdatei als Parameter von der Kommandozeile und holt sich daraus die Anmeldedaten. Es erfolgt ein Loginversuch (bei Fehlern wird das Skript abgebrochen).

Im Anschluss holt sich das Skript die Mailboxen in der INBOX, um diese für später zu kennen (wir suchen ja dann Ordner in der Art 2017-10, 2017-11, 2017-12 usw). Die Hauptarbeit beginnt danach, denn nun werden alle Mails der INBOX gelesen. Die Liste wird umgedreht, um bei der letzten Mail anzufangen (die mit der höchsten Nummer, dann haben wir keine Probleme, falls jetzt noch E-Mails reinkommen).

Die Mails werden danach aufgerufen und gelesen. Sie wird zunächst per IMAP geholt, geparst und das lokale Datum der Mail herausgelesen. Mails kommen normalerweise aus diversen Zeitzonen und müssen normalisiert werden auf die lokale Zeit (alternativ könnte man hier natürlich statt local_time, einfach die UTC-Zeit verwenden). Daraus ergibt sich für jede Mail ein lokales Datum, also Jahr und Monat und damit der Ordner, in den die Mail verschoben werden soll.

Zunächst prüft das Skript, ob der Ordner bereits angelegt wurde und holt sich gegebenenfalls nach. Schließlich wird die Mail kopiert und aus der INBOX gelöscht. Nachdem alle Mails durchlaufen wurden, wird das Postfach tatsächlich geleert (Expunge-Befehl bei IMAP).

Ein Aufruf von python3 imap_folder_per_month.py mail_domain.de.ini sollte genügen, um das Skript zu starten. Es kann sein, dass Python-Module fehlen. Diese müssen per pip nachinstalliert werden, z.B. mit pip install imaplib – hier bitte an die entsprechenden Python-Tutorials des jeweiligen Betriebssystems halten.

Automatisches Verschieben der Mails mit Cron

Läuft das Ganze, kann man schließlich einen Cronjob einrichten, um die Mailkonten regelmäßig aufzuräumen. Je nach Mailaufkommen kann man das z.B. stündlich oder sogar alle paar Minuten machen. Dazu ruft man auf dem Rechner, das das Skript regelmäßig ausführen soll (z.B. ein Server) crontab -e auf und ergänzt Folgendes:

*/15 * * * * /path/to/imap_folder_per_month.py /path/to/mail_domain.de.ini > /dev/null

Hier wird das Skript (Pfade müssen natürlich angepasst werden) alle 15 Minuten aufgerufen.

Endlich Ruhe vor den monatlichen Monsteraktionen!

Durch die Einloggen bei den Kommentaren werden zwei Cookies gesetzt. Mehr Informationen im Impressum.
Follow me