diff --git a/bin/thunderbird-notify b/bin/thunderbird-notify new file mode 100755 index 0000000..d6603f7 --- /dev/null +++ b/bin/thunderbird-notify @@ -0,0 +1,111 @@ +#!/usr/bin/env python3 + +"""Watch a few mbox folders from Thunderbird and notify when there are +new messages. This should be a builtin feature in Thunderbird, but it +is not.""" + +# TODO: +# - handle emails received at the same second +# - add an action to Open TB (thunderbird mid:....) + +import sys +import argparse +import pyinotify +import os +import email.parser +import email.header +from pydbus import SessionBus + +parser = argparse.ArgumentParser(description=sys.modules[__name__].__doc__) +parser.add_argument("root", metavar="FILE", help="path to Thunderbird mail folder") +parser.add_argument("folder", metavar="FOLDER", help="folder to monitor", nargs="+") +options = parser.parse_args() + +watermarks = {} +notify = SessionBus().get(".Notifications") + + +class EventHandler(pyinotify.ProcessEvent): + def process_IN_CLOSE_WRITE(self, event): + watermark = process(event.pathname, watermarks[event.pathname]) + watermarks[event.pathname] = watermark + + +def process(path, watermark): + new_watermark = None + with open(path, "rb") as mbox: + marker = b"\nFrom " + chunk = 1024 + + mbox.seek(0, os.SEEK_END) + begin = mbox.tell() + while begin > 0: + while True: + begin -= 1024 + if begin < 0: + begin = 0 + break + mbox.seek(begin) + buffer = mbox.read(chunk) + idx = buffer.rfind(marker) + if idx == -1: + continue + begin = begin + idx + break + end = begin + 1 + while True: + mbox.seek(end) + buffer = mbox.read(chunk) + if len(buffer) < chunk: + end = end + len(buffer) + break + idx = buffer.find(marker) + if idx == -1: + end += chunk + continue + end = end + idx + break + mbox.seek(begin + 1) + message = mbox.read(end - begin).split(b"\n") + if message[0] == watermark: + return new_watermark + if new_watermark is None: + new_watermark = message[0] + if watermark is None: + return new_watermark + message = b"\n".join(message[1:]) + parsed = email.parser.BytesParser().parsebytes(message, headersonly=True) + subject = parsed.get("subject") + author = parsed.get("from") + if subject is not None and author is not None: + subject, charset = email.header.decode_header(subject) + if charset is not None: + subject = subject.decode(charset) + author, charset = email.header.decode_header(author) + if charset is not None: + author = author.decode(charset) + notify.Notify( + "Thunderbird", + 0, + "thunderbird", + f"Mail from {author}", + subject, + [], + {}, + 10000, + ) + return new_watermark + + +wm = pyinotify.WatchManager() +mask = pyinotify.IN_CLOSE_WRITE +handler = EventHandler() +notifier = pyinotify.Notifier(wm, handler) +for folder in options.folder: + folder = folder.split("/") + folder = [f"{f}.sbd" for f in folder[:-1]] + [folder[-1]] + folder = os.path.join(os.path.expanduser(options.root), *folder) + print(f"Watch {folder}...", flush=True) + watermarks[folder] = process(folder, None) + wm.add_watch(folder, mask) +notifier.loop() diff --git a/dotfiles/dunstrc b/dotfiles/dunstrc index 058a854..252536b 100644 --- a/dotfiles/dunstrc +++ b/dotfiles/dunstrc @@ -56,7 +56,7 @@ min_icon_size = 32 max_icon_size = 32 # echo /usr/share/icons/{Adwaita,gnome}/{512x512,256x256,48x48}/{devices,status}(N) | tr ' ' ':' - icon_path = /usr/share/icons/Adwaita/512x512/devices:/usr/share/icons/Adwaita/512x512/status:/usr/share/icons/Adwaita/256x256/status:/usr/share/icons/Adwaita/48x48/devices:/usr/share/icons/Adwaita/48x48/status:/usr/share/icons/gnome/256x256/devices:/usr/share/icons/gnome/256x256/status:/usr/share/icons/gnome/48x48/devices:/usr/share/icons/gnome/48x48/status + icon_path = /usr/share/icons/Adwaita/512x512/devices:/usr/share/icons/Adwaita/512x512/status:/usr/share/icons/Adwaita/256x256/status:/usr/share/icons/Adwaita/48x48/devices:/usr/share/icons/Adwaita/48x48/status:/usr/share/icons/gnome/256x256/devices:/usr/share/icons/gnome/256x256/status:/usr/share/icons/gnome/48x48/devices:/usr/share/icons/gnome/48x48/status:/home/bernat/.nix-profile/share/icons/hicolor/64x64/apps # History sticky_history = yes diff --git a/dotfiles/systemd/i3-session.target b/dotfiles/systemd/i3-session.target index 4448688..4098ad9 100644 --- a/dotfiles/systemd/i3-session.target +++ b/dotfiles/systemd/i3-session.target @@ -6,3 +6,4 @@ Wants=wallpaper.timer Wants=polybar.service Wants=i3-companion.service Wants=misc-x.service +Wants=thunderbird-notify.service diff --git a/dotfiles/systemd/thunderbird-notify.service b/dotfiles/systemd/thunderbird-notify.service new file mode 100644 index 0000000..956fc88 --- /dev/null +++ b/dotfiles/systemd/thunderbird-notify.service @@ -0,0 +1,9 @@ +[Unit] +Description=Display notifications when receiving new mails +PartOf=graphical-session.target +ConditionPathExists=%h/.config/thunderbird-notify.env + +[Service] +EnvironmentFile=%h/.config/thunderbird-notify.env +ExecStart=%h/.config/i3/bin/thunderbird-notify $ARGS +Restart=on-failure