Migrate useful tools from MailWatcherDaemon (old python attempt).
This commit is contained in:
parent
a9ed300a03
commit
0f066809b2
12
cmd/abook-lookup/abook-lookup
Executable file
12
cmd/abook-lookup/abook-lookup
Executable 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
|
||||
190
cmd/extract_accounts/extract_accounts.go
Normal file
190
cmd/extract_accounts/extract_accounts.go
Normal 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")
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user