History

first revision

Sigrid Haflínudóttir committed 6 years ago

Commit b3df5ea1 parent 56950592 files · +239 −0Browse files

Unified · Split

2 files
  1. README.md +24 −0
  2. main.go +215 −0
README.md+24−0
@@ -0,0 +1,24 @@
1# mkfaces
2
3Some kind of Gravatar `face(6)` fetcher for Plan 9?
4
5Goes through `/mail/fs/*` and tries to create a face based on "from" of
6each email. Stores information according to `face(6)`. It keeps the old
7information intact so it should be considered safe to use on top of
8whatever faces one already had in `/usr/$user/lib/face`.
9
10In addition it ignores "machine/user" if it matches any of regexps
11stored in `/usr/$user/lib/face/.ignorelist`.
12
13Example of `.ignorelist`:
14
15```
16meetup.com
17slack.com
18amazon.com
19```
20
21Maybe I will have time to rewrite it in `rc` later, for now one can
22build it this way: `GOOS=plan9 GOARCH=amd64 go build`.
23
24This sucks.
main.go+215−0
@@ -0,0 +1,215 @@
1package main
2
3import (
4 "bytes"
5 "crypto/md5"
6 "fmt"
7 "io/ioutil"
8 "log"
9 "net/http"
10 "os"
11 "os/exec"
12 "regexp"
13 "sort"
14 "strings"
15)
16
17var (
18 httpClient = &http.Client{}
19 base = "/mail/fs"
20)
21
22func main() {
23 home, err := os.UserHomeDir()
24 if err != nil {
25 log.Fatal(err)
26 }
27 faceBase := home + "/lib/face"
28 outBase := faceBase + "/48x48x8"
29 notFoundPath := faceBase + "/.notfound"
30 ignorePath := faceBase + "/.ignorelist"
31 dictPath := outBase + "/.dict"
32
33 froms := map[string][md5.Size]byte{}
34
35 fs, err := ioutil.ReadDir(base)
36 if err != nil {
37 log.Fatal(err)
38 }
39 for _, fsi := range fs {
40 if !fsi.IsDir() {
41 continue
42 }
43
44 msgsBase := base + "/" + fsi.Name()
45 msgs, err := ioutil.ReadDir(msgsBase)
46 if err != nil {
47 log.Fatal(err)
48 }
49
50 for _, mi := range msgs {
51 if !mi.IsDir() {
52 continue
53 }
54 if from, err := ioutil.ReadFile(msgsBase + "/" + mi.Name() + "/from"); err != nil {
55 log.Fatal(err)
56 } else {
57 f := strings.ToLower(string(from))
58 froms[f] = md5.Sum([]byte(f))
59 }
60 }
61 }
62
63 if err = os.MkdirAll(outBase, 0700); err != nil {
64 log.Fatal(err)
65 }
66
67 var ignoreList []*regexp.Regexp
68 if s, err := ioutil.ReadFile(ignorePath); err == nil {
69 for _, v := range strings.Split(string(s), "\n") {
70 if v != "" {
71 if r, err := regexp.Compile(v); err != nil {
72 log.Fatalf("%s: %s", ignorePath, err)
73 } else {
74 ignoreList = append(ignoreList, r)
75 }
76 }
77 }
78 }
79
80 notFound := make(map[string]struct{})
81 if s, err := ioutil.ReadFile(notFoundPath); err == nil {
82 for _, v := range strings.Split(string(s), "\n") {
83 if v != "" {
84 notFound[v] = struct{}{}
85 }
86 }
87 }
88
89 dict := make(map[string]string)
90 if s, err := ioutil.ReadFile(dictPath); err == nil {
91 for _, v := range strings.Split(string(s), "\n") {
92 if v != "" {
93 parts := strings.Split(v, " ")
94 dict[parts[0]] = parts[1]
95 }
96 }
97 }
98
99 numTotal := len(froms)
100 i := 0
101 failed := 0
102 saved := 0
103 ignored := 0
104 progress := ""
105
106 for f, h := range froms {
107 hash := fmt.Sprintf("%x", h)
108 imagePath := outBase + "/" + hash
109
110 i++
111 clear := strings.Repeat("\x08", len(progress))
112 progress = fmt.Sprintf("%d/%d", i, numTotal)
113 fmt.Printf("%s%s", clear, progress)
114
115 var machineUser string
116 if parts := strings.Split(f, "@"); len(parts) != 2 {
117 failed++
118 continue
119 } else {
120 userParts := strings.Split(parts[0], "+")
121 machineUser = fmt.Sprintf("%s/%s", parts[1], userParts[0])
122
123 skip := false
124 for _, ignore := range ignoreList {
125 if ignore.MatchString(machineUser) {
126 ignored++
127 os.Remove(imagePath)
128 delete(dict, machineUser)
129 delete(notFound, machineUser)
130 skip = true
131 }
132 }
133 if skip {
134 continue
135 }
136 if _, ok := dict[machineUser]; ok {
137 continue
138 }
139 if _, ok := notFound[machineUser]; ok {
140 continue
141 }
142 }
143
144 url := fmt.Sprintf("http://gravatar.com/avatar/%s.jpg?s=48&d=404", hash)
145 if res, err := httpClient.Get(url); err != nil {
146 log.Fatal(err)
147 } else if res.StatusCode != http.StatusOK {
148 if res.StatusCode == http.StatusNotFound {
149 notFound[machineUser] = struct{}{}
150 }
151 res.Body.Close()
152 continue
153 } else {
154 b := new(bytes.Buffer)
155 b.ReadFrom(res.Body)
156 res.Body.Close()
157
158 data := new(bytes.Buffer)
159
160 cmd := exec.Command("/bin/jpg", "-c")
161 cmd.Stdin = bytes.NewReader(b.Bytes())
162 cmd.Stdout = data
163 if err = cmd.Run(); err != nil {
164 data.Reset()
165 cmd = exec.Command("/bin/png", "-c")
166 cmd.Stdin = bytes.NewReader(b.Bytes())
167 cmd.Stdout = data
168 err = cmd.Run()
169 }
170
171 if err != nil {
172 failed++
173 } else if err = ioutil.WriteFile(imagePath, data.Bytes(), 0644); err != nil {
174 log.Fatal(err)
175 } else {
176 dict[machineUser] = hash
177 saved++
178 }
179 }
180 }
181
182 if f, err := os.Create(notFoundPath); err != nil {
183 log.Fatal(err)
184 } else {
185 var sorted []string
186 for machineUser := range notFound {
187 sorted = append(sorted, machineUser)
188 }
189 sort.Strings(sorted)
190 for _, s := range sorted {
191 fmt.Fprintf(f, "%s\n", s)
192 }
193 f.Close()
194 }
195
196 if f, err := os.Create(dictPath); err != nil {
197 log.Fatal(err)
198 } else {
199 var sorted []string
200 for machineUser := range dict {
201 sorted = append(sorted, machineUser)
202 }
203 sort.Strings(sorted)
204 for _, s := range sorted {
205 fmt.Fprintf(f, "%s %s\n", s, dict[s])
206 }
207 f.Close()
208 }
209
210 fmt.Printf("%s", strings.Repeat("\x08", len(progress)))
211 fmt.Printf("%d addresses\n", numTotal)
212 fmt.Printf("%d faces added\n", saved)
213 fmt.Printf("%d failed to decode\n", failed)
214 fmt.Printf("%d ignored\n", ignored)
215}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t