Keith Vetter 2014-04-08 : I love Gmail, but my biggest complaint is in managing filters. After many years of usage, I've accumulated dozens of filter rules which are a big jumble. I get confused why some messages show up with an unexpected label while others don't go where I expected them too.
Gmail has very limited support in untangling this mess--even just sorting the list of filters by which label gets applied would be a great help.
Thus, I wrote this tool which converts Gmail's exported xml of the filters into a csv file which you can manipulate in a spreadsheet. You can export the filters from the Gmail->Settings->Filters page.
Usage:
- In Gmail: Settings->Filter->export and save to your_exported_filters
- tclsh gmailFilters.tsh your_exported_filters > your_filters.csv
- open your_filters.csv in Excel
[liyang
] 2015-05-06: Alternatively, you might want to write your filters as an EDSL in another language, and delete/re-import them wholesale. Available options include:
Haskell,
Ruby, or
PHP…
# gmailFilters.tsh -- parses Gmail filters xml and creates a CSV file
# you can create xml of gmail filters via Gmail->Setting->Filters->Export
# by Keith Vetter 2014-04-08
#
package require tdom
package require csv
set gmail_namespaces {
atom "http://www.w3.org/2005/Atom"
apps "http://schemas.google.com/apps/2006"
}
# These fields describe who to filter and what to do on a match
# probably missing some
set filterFields {
label
shouldArchive
shouldMarkAsRead
shouldTrash
shouldNeverSpam
shouldAlwaysMarkAsImportant
shouldNeverMarkAsImportant
from
to
subject
doesNotHaveTheWord
hasTheWord
}
proc ParseFilterFile {gmailFilter} {
set fin [open $gmailFilter]
set xml [string trim [read $fin]] ; list
close $fin
dom parse $xml doc
$doc selectNodesNamespaces $::gmail_namespaces
set root [$doc documentElement]
set data {}
for {set idx 1} {1} {incr idx} {
set csvItems [ParseOneEntry $root $idx]
if {$csvItems eq ""} break
lappend data $csvItems
}
unset doc
return [CreateCsv $data]
}
proc ParseOneEntry {root idx} {
set idNode [$root selectNodes /*/atom:entry\[$idx\]/atom:id]
if {$idNode eq ""} { return {} }
set id [[$idNode firstChild] nodeValue]
set csvItems [list $id]
foreach name $::filterFields {
set value [ExtractProperty $root $idx $name]
lappend csvItems $value
}
return $csvItems
}
proc ExtractProperty {root idx name} {
set node [$root selectNodes /*/atom:entry\[$idx\]/apps:property\[@name='$name'\]]
set value ""
if {[llength $node] == 1} {
set value [$node getAttribute value]
}
return $value
}
proc CreateCsv {data} {
# Sort by label
set data [lsort -index 1 -command lsortCommand $data]
set csv "[::csv::join [concat id $::filterFields]]\n"
foreach datum $data {
append csv [::csv::join $datum] "\n"
}
return $csv
}
proc lsortCommand {a b} {
# Sort empty strings last
if {$a eq ""} { return 1}
if {$b eq ""} { return -1}
return [string compare $a $b]
}
if {$tcl_interactive && $argc == 0} {
set gmailFilter ~/Downloads/mailFilters.xml
set argv $gmailFilter
return
}
if {[llength $argv] != 1} {
puts stderr "usage: gmailFilter gmail_exported_filter_file"
exit 1
}
set csv [ParseFilterFile [lindex $argv 0]]
puts $csv
return