From bc79a9f65ba9f9fdb308b4a8eb75d0ca4e692463 Mon Sep 17 00:00:00 2001 From: Michael Hunteman Date: Thu, 31 Aug 2023 18:56:05 -0500 Subject: Add aerc filters --- .config/aerc/filters/calendar | 270 +++++++++++++++++++++++++++++++ .config/aerc/filters/colorize | 177 ++++++++++++++++++++ .config/aerc/filters/hldiff | 46 ++++++ .config/aerc/filters/html | 11 ++ .config/aerc/filters/html-unsafe | 17 ++ .config/aerc/filters/plaintext | 17 ++ .config/aerc/filters/show-ics-details.py | 89 ++++++++++ .config/aerc/filters/wrap | Bin 0 -> 2455008 bytes 8 files changed, 627 insertions(+) create mode 100755 .config/aerc/filters/calendar create mode 100755 .config/aerc/filters/colorize create mode 100755 .config/aerc/filters/hldiff create mode 100755 .config/aerc/filters/html create mode 100755 .config/aerc/filters/html-unsafe create mode 100755 .config/aerc/filters/plaintext create mode 100755 .config/aerc/filters/show-ics-details.py create mode 100755 .config/aerc/filters/wrap (limited to '.config') diff --git a/.config/aerc/filters/calendar b/.config/aerc/filters/calendar new file mode 100755 index 0000000..2808e13 --- /dev/null +++ b/.config/aerc/filters/calendar @@ -0,0 +1,270 @@ +#!/usr/bin/awk -f +# ex: ft=awk +# +# awk filter for aerc to parse text/calendar mime-types +# +# Based on the ical2org.awk script by Eric S Fraga and updated by Guide Van +# Hoecke. Adapted to aerc by Koni Marti +# + +BEGIN { + UIDS[0]; + people_attending[0]; + people_partstat[0]; + people_rsvp[0]; + + # use a colon to separate the type of data line from the actual contents + FS = ":"; +} + +{ + # remove carriage return from every line + gsub(/\r/, "") +} + +/^[ ]/ { + # this block deals with the continuation lines that start with a whitespace + # + line = $0 + # remove trailing whitespaces + gsub(/^[ ]/, "", line) + + # assumes continuation lines start with a space + if (indescription) { + entry = entry line + } else if (insummary) { + summary = summary line + } else if (inattendee) { + attendee = attendee line + } else if (inorganizer) { + organizer = organizer line + } else if (inlocation) { + location = location unescape(line, 0) + } +} + +/^BEGIN:VALARM/,/^END:VALARM/ { + next +} + +/^BEGIN:VEVENT/ { + # start of an event: initialize global values used for each event + start_date = ""; + end_date = ""; + entry = "" + id = "" + + indescription = 0; + insummary = 0 + inattendee = 0 + inorganizer = 0 + inlocation = 0 + + location = "" + status = "" + summary = "" + attendee = "" + organizer = "" + + rrend = "" + rcount = "" + intfreq = "" + idx = 0 + + delete people_attending; + delete people_partstat; + delete people_rsvp; +} + +/^[A-Z]/ { + if (attendee != "" && inattendee==1) + add_attendee(attendee) + + if (organizer != "" && inorganizer==1) + organizer = find_full_name(organizer) + + indescription = 0; + insummary = 0; + inattendee = 0; + inorganizer = 0; + inlocation = 0; +} + +/^DTSTART[:;]/ { + tz = get_value($0, "TZID=[^:;]*", "=") + start_date = datetimestring($2, tz); +} + +/^DTEND[:;]/ { + tz = get_value($0, "TZID=[^:;]*", "=") + end_date = datetimestring($2, tz); +} + +/^RRULE[:]/ { + freq = get_value($0, "FREQ=[^:;]*", "=") + interval = get_value($0, "INTERVAL=[^:;]*", "=") + rrend = get_value($0, "UNTIL=[^:;]*", "=") + rcount = get_value($0, "COUNT=[^:;]*", "=") + intfreq = tolower(freq) + if (interval != "") + intfreq = " +" interval intfreq +} + +/^METHOD/ { + method = $2 +} + +/^UID/ { + line = prepare($0) + id = line +} + +/^STATUS/ { + line = prepare($0) + status = line +} + +/^DESCRIPTION/ { + line = prepare($0) + entry = entry line + indescription = 1; +} + +/^SUMMARY/ { + line = prepare($0) + summary = line + insummary = 1; +} + +/^ORGANIZER/ { + organizer = $0 + inorganizer = 1; +} + +/^LOCATION/ { + line = prepare($0) + location = unescape(line, 0); + inlocation = 1; +} + +/^ATTENDEE/ { + attendee = $0 + inattendee = 1; +} + +/^END:VEVENT/ { + #output event + if (method != "") { + printf "\n This is a meeting %s\n\n", method + } + fmt = " %-14s%s\n" + is_duplicate = (id in UIDS); + if(is_duplicate == 0) { + printf fmt, "SUMMARY", unescape(summary, 0) + if(location != "") + printf fmt, "LOCATION", location + if(organizer != "") + printf fmt, "ORGANIZER", organizer + for (idx in people_attending) { + printf fmt, "ATTENDEE [" idx "]", people_attending[idx] + partstat = people_partstat[idx] + if (partstat != "") { + printf fmt, "", "STATUS\t" partstat + } + rsvp = people_rsvp[idx] + if (rsvp != "") { + printf fmt, "", "RSVP\t" rsvp + } + } + printf fmt, "START", start_date + printf fmt, "END", end_date + if (intfreq != "") { + printf "\n"fmt, "RECURRENCE", intfreq + if (rcount != "") + printf fmt, "COUNTS", rcount + if (rrend != "") + printf fmt, "END DATE", rrend + + } + if(entry != "") + print "\n" unescape(entry, 1); + UIDS[id] = 1; + } +} + +func prepare(line) { + gsub($1, "", line) + gsub(/^[: ]/, "", line) + return line +} + +function unescape(input, preserve_newlines) +{ + ret = input + gsub(/\\,/, ",", ret) + gsub(/\\;/, ";", ret) + if (preserve_newlines) + gsub(/\\n/, "\n", ret) + else + gsub(/\\n/, " ", ret) + return ret +} + + +function datetimestring(input, tzInput) +{ + timestr = input + pos = index(timestr, "T") + if (pos < 0) { + return timestr + } + + date = substr(timestr, 1, pos) + time = substr(timestr, pos+1, length(timestr)) + + year = substr(date, 1, 4) + month = substr(date, 5, 2) + day = substr(date, 7, 2) + + hour = substr(time, 1, 2) + min = substr(time, 3, 2) + sec = substr(time, 5, 2) + + return sprintf("%4d/%02d/%02d %02d:%02d:%02d %s", year, month, day, hour, min, sec, tzInput) +} + +function add_attendee(attendee) +{ + CN = find_full_name(attendee) + if (CN != "") { + idx = idx + 1 + people_attending[idx] = CN; + people_partstat[idx] = get_value(attendee, "PARTSTAT=[^;:]+", "=") + people_rsvp[idx] = get_value(attendee, "RSVP=[^;:]+", "=") + } +} + +function find_full_name(line) +{ + name = get_value(line, "CN=[^;:]+", "=") + gsub(/"[^"]*"/,"",line) + email = get_value(line, "(mailto|MAILTO):[^;]+", ":") + + if (name == "") { + return sprintf("<%s>", email) + } else { + return sprintf("%s <%s>", name, email) + } +} + +function get_value(line, regexp, sep) { + value = "" + match(line, regexp) + { + z = split(substr(line,RSTART,RLENGTH),data,sep) + if (z > 1) { + value = data[2] + } + } + return value +} diff --git a/.config/aerc/filters/colorize b/.config/aerc/filters/colorize new file mode 100755 index 0000000..9d2a736 --- /dev/null +++ b/.config/aerc/filters/colorize @@ -0,0 +1,177 @@ +#!/usr/bin/awk -f +# Copyright (c) 2022 Robin Jarry +# +# A filter for the aerc mail program to colorize messages / attachments. +# Basic colour themes are supported. To use a theme set the theme variable +# in your aerc.conf accordingly, for example: +# +# text/plain=colorize -v theme=solarized + +BEGIN { + if (theme == "solarized") { + # R;G;B colors + url = "\033[38;2;181;137;0m" # yellow + header = "\033[38;2;211;54;130m" # magenta + signature = "\033[38;2;211;54;130m" # magenta + diff_meta = "\033[1;38;2;131;148;150m" # bold brblue + diff_chunk = "\033[38;2;42;161;152m" # cyan + diff_add = "\033[38;2;133;153;0m" # green + diff_del = "\033[38;2;220;50;47m" # red + quote_1 = "\033[38;2;38;139;210m" # blue + quote_2 = "\033[38;2;203;75;22m" # brred + quote_3 = "\033[38;2;211;54;130m" # magenta + quote_4 = "\033[38;2;108;113;196m" # brmagenta + quote_x = "\033[38;2;147;161;161m" # brcyan + bold = "\033[1m" + reset = "\033[0m" + } else if (theme == "" || theme == "default") { + # R;G;B colors + url = "\033[38;2;255;255;175m" # yellow + header = "\033[38;2;175;135;255m" # purple + signature = "\033[38;2;175;135;255m" # purple + diff_meta = "\033[1;38;2;255;255;255m" # bold white + diff_chunk = "\033[38;2;0;205;205m" # cyan + diff_add = "\033[38;2;0;205;0m" # green + diff_del = "\033[38;2;205;0;0m" # red + quote_1 = "\033[38;2;95;175;255m" # blue + quote_2 = "\033[38;2;255;135;0m" # orange + quote_3 = "\033[38;2;175;135;255m" # purple + quote_4 = "\033[38;2;255;95;215m" # pink + quote_x = "\033[38;2;128;128;128m" # gray + bold = "\033[1m" + reset = "\033[0m" + } else if (theme == "terminal") { + # terminal respects the users configured terminal color theme + url = "\033[4;34m" # underline blue + header = "\033[35m" # magenta + signature = "\033[35m" # magenta + diff_meta = "\033[2m" # faint + diff_chunk = "\033[36m" # cyan + diff_add = "\033[32m" # green + diff_del = "\033[31m" # red + quote_1 = "\033[37m" # grey + quote_2 = "\033[34m" # blue + quote_3 = "\033[2;37m" # faint grey + quote_4 = "\033[2;34m" # faint blue + quote_x = "\033[2;37m" # faint grey + bold = "\033[1m" + reset = "\033[0m" + } else { + print "error: unknown theme " theme > "/dev/stderr" + exit 1 + } + # state + in_diff = 0 + in_signature = 0 + in_headers = 0 + in_body = 0 + # patterns + header_pattern = "^[A-Z][[:alnum:]-]+:" + url_pattern = "[[:lower:]]+://[[:graph:]]+|(mailto:)?[[:alnum:]_\\+\\.~/-]*[[:alnum:]_]@[[:lower:]][[:alnum:]\\.-]*[[:lower:]]" + meta_pattern = "^(diff --git|(new|deleted) file|similarity index|(rename|copy) (to|from)|index|---|\\+\\+\\+) " +} +function color_quote(line) { + level = 0 + quotes = "" + while (line ~ /^>/) { + level += 1 + quotes = quotes ">" + line = substr(line, 2) + while (line ~ /^ /) { + quotes = quotes " " + line = substr(line, 2) + } + } + if (level == 1) { + color = quote_1 + } else if (level == 2) { + color = quote_2 + } else if (level == 3) { + color = quote_3 + } else if (level == 4) { + color = quote_4 + } else { + color = quote_x + } + if (match(line, meta_pattern)) { + return color quotes bold line reset + } else if (line ~ /^\+/) { + return color quotes diff_add line reset + } else if (line ~ /^-/) { + return color quotes diff_del line reset + } + gsub(url_pattern, url "&" color, line) + return color quotes line reset +} +{ + # Strip carriage returns from line + sub(/\r$/, "") + + if (in_diff) { + if ($0 ~ /^-- ?$/) { + in_diff = 0 + in_signature = 1 + $0 = signature $0 reset + } else if ($0 ~ /^@@ /) { + gsub(/^@@[^@]+@@/, diff_chunk "&" reset) + } else if (match($0, meta_pattern)) { + $0 = diff_meta $0 reset + } else if ($0 ~ /^\+/) { + $0 = diff_add $0 reset + } else if ($0 ~ /^-/) { + $0 = diff_del $0 reset + } else if ($0 !~ /^ / && $0 !~ /^$/) { + in_diff = 0 + in_body = 1 + if ($0 ~ /^>/) { + $0 = color_quote($0) + } else { + gsub(url_pattern, url "&" reset) + } + } + } else if (in_signature) { + gsub(url_pattern, url "&" signature) + $0 = signature $0 reset + } else if (in_headers) { + if ($0 ~ /^$/) { + in_headers = 0 + in_body = 1 + } else { + sub(header_pattern, header "&" reset) + gsub(url_pattern, url "&" reset) + } + } else if (in_body) { + if ($0 ~ /^>/) { + $0 = color_quote($0) + } else if ($0 ~ /^diff --git /) { + in_body = 0 + in_diff = 1 + $0 = diff_meta $0 reset + } else if ($0 ~ /^-- ?$/) { + in_body = 0 + in_signature = 1 + $0 = signature $0 reset + } else { + gsub(url_pattern, url "&" reset) + } + } else if ($0 ~ /^diff --git /) { + in_diff = 1 + $0 = diff_meta $0 reset + } else if ($0 ~ /^-- ?$/) { + in_signature = 1 + $0 = signature $0 reset + } else if (match($0, header_pattern)) { + in_headers = 1 + sub(header_pattern, header "&" reset) + gsub(url_pattern, url "&" reset) + } else { + in_body = 1 + if ($0 ~ /^>/) { + $0 = color_quote($0) + } else { + gsub(url_pattern, url "&" reset) + } + } + + print +} diff --git a/.config/aerc/filters/hldiff b/.config/aerc/filters/hldiff new file mode 100755 index 0000000..dc8c727 --- /dev/null +++ b/.config/aerc/filters/hldiff @@ -0,0 +1,46 @@ +#!/usr/bin/awk -f + +BEGIN { + bright = "\x1B[1m" + red = "\x1B[31m" + green = "\x1B[32m" + cyan = "\x1B[36m" + reset = "\x1B[0m" + + hit_diff = 0 +} +{ + if (hit_diff == 0) { + # Strip carriage returns from line + gsub(/\r/, "", $0) + + if ($0 ~ /^diff /) { + hit_diff = 1; + print bright $0 reset + } else if ($0 ~ /^.*\|.*(\+|-)/) { + left = substr($0, 0, index($0, "|")-1) + right = substr($0, index($0, "|")) + gsub(/-+/, red "&" reset, right) + gsub(/\++/, green "&" reset, right) + print left right + } else { + print $0 + } + } else { + # Strip carriage returns from line + gsub(/\r/, "", $0) + + if ($0 ~ /^-/) { + print red $0 reset + } else if ($0 ~ /^\+/) { + print green $0 reset + } else if ($0 ~ /^ /) { + print $0 + } else if ($0 ~ /^@@ (-[0-9]+,[0-9]+ \+[0-9]+,[0-9]+) @@.*/) { + sub(/^@@ (-[0-9]+,[0-9]+ \+[0-9]+,[0-9]+) @@/, cyan "&" reset) + print $0 + } else { + print bright $0 reset + } + } +} diff --git a/.config/aerc/filters/html b/.config/aerc/filters/html new file mode 100755 index 0000000..5ceee40 --- /dev/null +++ b/.config/aerc/filters/html @@ -0,0 +1,11 @@ +#!/bin/sh +# aerc filter which runs w3m using socksify (from the dante package) to prevent +# any phoning home by rendered emails +export SOCKS_SERVER="127.0.0.1:1" +exec socksify w3m \ + -I UTF-8 \ + -T text/html \ + -cols $(tput cols) \ + -dump \ + -o display_image=false \ + -o display_link_number=true diff --git a/.config/aerc/filters/html-unsafe b/.config/aerc/filters/html-unsafe new file mode 100755 index 0000000..8e0041c --- /dev/null +++ b/.config/aerc/filters/html-unsafe @@ -0,0 +1,17 @@ +#!/bin/sh +# aerc filter which runs w3m using socksify (from the dante package) to prevent +# any phoning home by rendered emails. If socksify is not installed then w3m is +# used without it. +if [ $(command -v socksify) ]; then + export SOCKS_SERVER="127.0.0.1:1" + PRE_CMD=socksify +else + PRE_CMD="" +fi +exec $PRE_CMD w3m \ + -I UTF-8 \ + -T text/html \ + -cols $(tput cols) \ + -dump \ + -o display_image=false \ + -o display_link_number=true diff --git a/.config/aerc/filters/plaintext b/.config/aerc/filters/plaintext new file mode 100755 index 0000000..aa22eb7 --- /dev/null +++ b/.config/aerc/filters/plaintext @@ -0,0 +1,17 @@ +#!/usr/bin/awk -f + +BEGIN { + dim = "\033[2m" + cyan = "\033[36m" + reset = "\033[0m" +} +{ + # Strip carriage returns from line + gsub(/\r/, "", $0) + + if ($0 ~ /^On .*, .* wrote:/ || $0 ~ /^>+/) { + print dim cyan $0 reset + } else { + print $0 + } +} diff --git a/.config/aerc/filters/show-ics-details.py b/.config/aerc/filters/show-ics-details.py new file mode 100755 index 0000000..18b2b76 --- /dev/null +++ b/.config/aerc/filters/show-ics-details.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +"""Parse a vcard file given via stdin and output some details. +Currently the following details are displayed if present: + +- start date and time +- the summary information of the event +- a list of attendees +- the description of the event + +Please note: if multiple events are included in the data then only the +first one will be parsed and displayed! + +REQUIREMENTS: +- Python 3 +- Python 3 - vobject library + +To use as a filter in aerc, add the following line to your aerc.config: +text/calendar=show-ics-details.py +""" + +import re +import sys + +import vobject + + +def remove_mailto(message: str) -> str: + """Remove a possible existing 'mailto:' from the given message. + + Keyword arguments: + message -- A message string. + """ + return re.sub(r'^mailto:', '', message, flags=re.IGNORECASE) + +def extract_field(cal: vobject.icalendar.VCalendar2_0, name: str) -> str: + """Extract the desired field from the given calendar object. + + Keyword arguments: + cal -- A VCalendar 2.0 object. + name -- The field name. + """ + try: + name = name.strip() + if name == 'attendees': + attendees = [] + for attendee in cal.vevent.attendee_list: + attendees.append(remove_mailto(attendee.valueRepr()).strip()) + return ', '.join(attendees) + elif name == 'description': + return cal.vevent.description.valueRepr().strip() + elif name == 'dtstart': + return str(cal.vevent.dtstart.valueRepr()).strip() + elif name == 'organizer': + return remove_mailto(cal.vevent.organizer.valueRepr()).strip() + elif name == 'summary': + return cal.vevent.summary.valueRepr().strip() + else: + return '' + except AttributeError: + return '' + +attendees = '' +description = '' +dtstart = '' +error = '' +organizer = '' +summary = '' + +try: + cal = vobject.readOne(sys.stdin) + attendees = extract_field(cal, 'attendees') + description = extract_field(cal, 'description') + dtstart = extract_field(cal, 'dtstart') + organizer = extract_field(cal, 'organizer') + summary = extract_field(cal, 'summary') +except vobject.base.ParseError: + error = '**Sorry, but we could not parse the calendar!**' + +if error: + print(error) + print("") + +print(f"Date/Time : {dtstart}") +print(f"Summary : {summary}") +print(f"Organizer : {organizer}") +print(f"Attendees : {attendees}") +print("") +print(description) diff --git a/.config/aerc/filters/wrap b/.config/aerc/filters/wrap new file mode 100755 index 0000000..0d29ce2 Binary files /dev/null and b/.config/aerc/filters/wrap differ -- cgit v1.2.3