From d895f1697aab438f533a0a479107c9cfebe96e3e Mon Sep 17 00:00:00 2001 From: Samir Benmendil Date: Mon, 1 Jan 2018 23:14:38 +0000 Subject: [PATCH] isync: use mbsync instead of offlineimap --- isync/config | 68 ++++++++++++++++++++++++++++++++++++ isync/parse-mail.py | 69 +++++++++++++++++++++++++++++++++++++ isync/sync-mail | 60 ++++++++++++++++++++++++++++++++ systemd/user/mbsync.service | 7 ++++ systemd/user/mbsync.timer | 10 ++++++ 5 files changed, 214 insertions(+) create mode 100644 isync/config create mode 100755 isync/parse-mail.py create mode 100755 isync/sync-mail create mode 100644 systemd/user/mbsync.service create mode 100644 systemd/user/mbsync.timer diff --git a/isync/config b/isync/config new file mode 100644 index 0000000..a45fe55 --- /dev/null +++ b/isync/config @@ -0,0 +1,68 @@ +# write state file into each slaves mailbox +SyncState * +CopyArrivalDate yes + +IMAPAccount gmail +Host imap.gmail.com +User samir.benmendil@gmail.com +PassCmd "sed -rn '/imap.gmail.com/ s/.*password (.*)/\\1/p' ~/.netrc" +SSLType IMAPS +CertificateFile /etc/ssl/certs/ca-certificates.crt + +IMAPStore gmail-remote +Account gmail + +MaildirStore gmail-local +Path ~/mail/gmail/ +Inbox ~/mail/gmail/inbox/ +SubFolders Verbatim + +Channel gmail-default +Master :gmail-remote: +Slave :gmail-local: +Patterns "INBOX" + +Channel gmail-youtube +Master :gmail-remote: +Slave :gmail-local: +Patterns "youtube" + +Channel gmail-youtube-orig +Master :gmail-remote: +Slave :gmail-local: +Patterns "youtube-orig" + +Channel gmail-archive +Master :gmail-remote:"[Google Mail]/All Mail" +Slave :gmail-local:"archive" + +Channel gmail-drafts +Master :gmail-remote:"[Google Mail]/Drafts" +Slave :gmail-local:"drafts" + +Channel gmail-sent +Master :gmail-remote:"[Google Mail]/Sent Mail" +Slave :gmail-local:"sent" + +Channel gmail-spam +Master :gmail-remote:"[Google Mail]/Spam" +Slave :gmail-local:"spam" + +Channel gmail-flagged +Master :gmail-remote:"[Google Mail]/Starred" +Slave :gmail-local:"flagged" + +Channel gmail-bin +Master :gmail-remote:"[Google Mail]/Bin" +Slave :gmail-local:"bin" + +Group gmail +Channel gmail-default +Channel gmail-youtube +Channel gmail-youtube-orig +Channel gmail-archive +Channel gmail-drafts +Channel gmail-sent +Channel gmail-spam +Channel gmail-flagged +Channel gmail-bin diff --git a/isync/parse-mail.py b/isync/parse-mail.py new file mode 100755 index 0000000..a72e768 --- /dev/null +++ b/isync/parse-mail.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 + +from datetime import timedelta +from email import message_from_file +import re +import sys +import youtube_dl + + +FROM = "{uploader} " +SUBJECT = "{title}" +CONTENT = """\ +{uploader} just uploaded a video +{title} ({duration}) + +{webpage_url} + +Description +─────────────────────────────────────────────────────────────────────── + +{description} + +─────────────────────────────────────────────────────────────────────── + +Manage notifications: https://www.youtube.com/subscription_manager +""" + + +def parse_file(fd): + msg = message_from_file(fd) + + payload = "" + if msg.is_multipart(): + for part in msg.get_payload(): + if part.get_content_type() != 'text/plain': + continue + charset = part.get_content_charset() + payload = part.get_payload(decode=True).decode(charset) + else: + payload = msg.get_payload(decode=True).decode(charset) + + url = re.search(r'^https?://www.youtube.com/watch\?v=[\w-]{11}', payload, re.MULTILINE).group() + # print(url) + with youtube_dl.YoutubeDL({'quiet' : True}) as ytdl: + info = ytdl.extract_info(url, download=False, force_generic_extractor=True) + + if info is None: + return False + + info['duration'] = timedelta(seconds=info['duration']) + + del msg['From'] + msg['From'] = FROM.format(**info) + + del msg['Subject'] + msg['Subject'] = SUBJECT.format(**info) + + msg.set_payload(CONTENT.format(**info)) + + return msg + + +if __name__ == "__main__": + if len(sys.argv) == 1: + msg = parse_file(sys.stdin) + else: + with open(sys.argv[1], 'r') as fd: + msg = parse_file(fd) + print(msg) diff --git a/isync/sync-mail b/isync/sync-mail new file mode 100755 index 0000000..a3b8a7a --- /dev/null +++ b/isync/sync-mail @@ -0,0 +1,60 @@ +#!/usr/bin/env bash + +dir="$(dirname "${BASH_SOURCE[0]}")" +config=$dir/config + +### pre-sync {{{ + +### }}} + +### sync {{{ +mbsync -c "$config" -a +errno=$? + +if [[ $errno -ne 0 ]]; then + echo "mbsync failed, ignoring post-sync commands." >&2 + exit $errno +fi +### }}} + +### post-sync {{{ +yt_src_mb=youtube-orig +yt_dst_mb=youtube +yt_parser=$dir/parse-mail.py +maildir=$(sed -nr 's/^Path\s*(.*)$/\1/p' $config) + + +if [[ -z $maildir ]]; then + echo "Could not extract 'Path' from 'MaildirStore' in '$config'" >&2 + exit 1 +fi + +# expand tilde +maildir=${maildir/#~/$HOME} + +shopt -s extglob +shopt -s nullglob +for mail in "$maildir"/$yt_src_mb/new/* ; do + mangled_mail=${mail/$yt_src_mb/$yt_dst_mb} + # remove UID for mbsync to regenerate it + mangled_mail="${mangled_mail/,U=+([0-9])}" + + echo -n "Parsing new message '$(basename "$mail")'..." + $yt_parser <"$mail" >"$mangled_mail" + + if [[ $? -eq 0 ]]; then + echo " Success." + else + echo " Failure! Copying message as is." + cp "$mail" "$mangled_mail" + fi + + # sync {a,m}time + touch --reference "$mail" "$mangled_mail" + mv "$mail" "${mail/new/cur}S" +done +# resync new yt_dst_mb +mbsync -c "$config" gmail-$yt_src_mb gmail-$yt_dst_mb + +### }}} + diff --git a/systemd/user/mbsync.service b/systemd/user/mbsync.service new file mode 100644 index 0000000..27f04ca --- /dev/null +++ b/systemd/user/mbsync.service @@ -0,0 +1,7 @@ +[Unit] +Description=Mailbox synchronization service +After=network.target network-online.target dbus.socket + +[Service] +Type=oneshot +ExecStart=%h/.config/isync/sync-mail diff --git a/systemd/user/mbsync.timer b/systemd/user/mbsync.timer new file mode 100644 index 0000000..f07d76a --- /dev/null +++ b/systemd/user/mbsync.timer @@ -0,0 +1,10 @@ +[Unit] +Description=Mailbox synchronization timer + +[Timer] +# every 5 minutes +OnCalendar=*:00/5:00 +Persistent=true + +[Install] +WantedBy=timers.target -- 2.48.1