Migrate useful tools from MailWatcherDaemon (old python attempt).

This commit is contained in:
Bill Thiede 2016-12-23 17:46:37 -08:00
parent a9ed300a03
commit 0f066809b2
2 changed files with 202 additions and 0 deletions

12
cmd/abook-lookup/abook-lookup Executable file
View File

@ -0,0 +1,12 @@
#!/usr/bin/env bash
echo "Searching for $1"
sqlite3 -list -separator ' ' ~/.mutt/abook.db << EOSQL
SELECT s.email, s.name, s.frequency
FROM search s
INNER JOIN
(SELECT email, max(frequency) AS maxfrequency FROM search GROUP BY email) m
ON s.email = m.email AND s.frequency = m.maxfrequency
WHERE s.name
LIKE '%$1%' OR s.email LIKE '%$1%'
ORDER BY s.frequency DESC;
EOSQL

View File

@ -0,0 +1,190 @@
package main
import (
"database/sql"
"flag"
"fmt"
"log"
"net/mail"
"os"
"path/filepath"
"strings"
_ "github.com/mattn/go-sqlite3"
)
var (
quiet = flag.Bool("q", false, "don't print status messages to stdout")
debugLogFn = flag.String("debugLogFn", "/dev/null", "file to store debugging logs")
sentDirGlob = flag.String("sentGlob", "${HOME}/Maildir/.Sent/*/*",
"Pattern for email sent files")
addressDbFn = flag.String("addressDbFn", "${HOME}/.mutt/abook.db",
"Location to store sqlite database of contacts")
)
type Contact struct {
mail.Address
fn string
}
func getAddresses(fn string, results chan<- *Contact) {
file, err := os.Open(fn)
if err != nil {
log.Fatalf("Failed to open %q: %s\n", fn, err)
}
msg, err := mail.ReadMessage(file)
if err != nil {
log.Printf("Failed to parse message %q: %s\n", fn, err)
return
}
headers := []string{"to", "cc", "resent-to", "resent-cc", "bcc"}
for _, header := range headers {
addrs, err := msg.Header.AddressList(header)
if err != nil {
//log.Printf("Failed to parse %s: header in %q: %s", header, fn, err)
continue
}
for _, addr := range addrs {
c := &Contact{*addr, fn}
results <- c
}
}
}
func getEmails(results chan<- *Contact) {
files, err := filepath.Glob(os.ExpandEnv(*sentDirGlob))
if !(*quiet) {
fmt.Printf("Scanning %d files\n", len(files))
}
if err != nil {
log.Fatalf("Failed to glob %q: %s\n", *sentDirGlob, err)
}
for _, fn := range files {
getAddresses(fn, results)
}
close(results)
}
func createEmailTable(db *sql.DB) {
sqls := []string{
"DROP TABLE IF EXISTS emails",
"CREATE TABLE emails (email, name, fn)",
}
for _, sql := range sqls {
_, err := db.Exec(sql)
if err != nil {
log.Fatalf("Error initializing DB %q: %s\n", err, sql)
}
}
}
func createSearchTable(db *sql.DB) {
sqls := []string{
"DROP TABLE IF EXISTS search",
`CREATE TABLE search AS
SELECT email, name, count(*) AS frequency
FROM emails group BY name, email
ORDER BY frequency`,
}
for _, sql := range sqls {
_, err := db.Exec(sql)
if err != nil {
log.Fatalf("Error creating search table %q: %s\n", err, sql)
}
}
}
func fillEmailTable(db *sql.DB, results <-chan *Contact) {
tx, err := db.Begin()
if err != nil {
log.Fatalf("Faild to being transaction: %s\n", err)
return
}
defer tx.Commit()
stmt, err := tx.Prepare("INSERT INTO emails VALUES (?, ?, ?)")
defer stmt.Close()
if err != nil {
log.Fatalf("Error preparing statement: %s\n", err)
return
}
count := 1
for c := range results {
addr := c.Address
if !(*quiet) {
if 0 == count%50 {
fmt.Print(count)
}
if 0 == count%10 {
fmt.Print(".")
}
}
count += 1
var name string
if addr.Name != "" {
name = addr.Name
if -1 != strings.Index(name, ",") {
parts := strings.SplitN(name, ",", 2)
first, second := strings.TrimSpace(parts[1]), parts[0]
name = fmt.Sprintf("%s %s", first, second)
}
} else {
name = addr.Address
}
address := strings.ToLower(addr.Address)
_, err = stmt.Exec(address, name, c.fn)
if err != nil {
log.Fatalf("Failed to insert value: %s\n", err)
}
}
}
func buildDb(results <-chan *Contact) {
os.Remove(os.ExpandEnv(*addressDbFn))
db, err := sql.Open("sqlite3", os.ExpandEnv(*addressDbFn))
if err != nil {
fmt.Println(err)
return
}
defer db.Close()
createEmailTable(db)
fillEmailTable(db, results)
createSearchTable(db)
}
func init() {
flag.Parse()
}
func main() {
logFile, err := os.OpenFile(*debugLogFn, os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
log.Fatalf("Failed to open log %q: %s\n", *debugLogFn, err)
}
defer logFile.Close()
log.SetOutput(logFile)
log.Println("Started")
results := make(chan *Contact)
go getEmails(results)
buildDb(results)
log.Println("Finished successfully")
}