/
gmail.go
154 lines (140 loc) · 3.97 KB
/
gmail.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Package gmail provides a gmail barista module.
package gmail // import "barista.run/modules/gsuite/gmail"
import (
"net/http"
"time"
"barista.run/bar"
"barista.run/base/value"
"barista.run/oauth"
"barista.run/outputs"
"barista.run/timing"
"golang.org/x/oauth2/google"
gmail "google.golang.org/api/gmail/v1"
)
// Info represents the unread and total thread counts for labels.
// The keys are the names (not IDs) of the labels, and the values are the thread
// counts (Threads is total threads, while Unread is just unread threads).
type Info struct {
Threads map[string]int64
Unread map[string]int64
}
// TotalUnread is the total number of unread threads across all labels. (as set
// during construction).
func (i Info) TotalUnread() int64 {
t := int64(0)
for _, u := range i.Unread {
t += u
}
return t
}
// TotalThreads is the total number of threads across all configured labels.
func (i Info) TotalThreads() int64 {
t := int64(0)
for _, c := range i.Threads {
t += c
}
return t
}
// Module represents a Gmail barista module.
type Module struct {
config *oauth.Config
labels []string
scheduler *timing.Scheduler
outputFunc value.Value // of func(Info) bar.Output
}
// New creates a gmail module from the given oauth config, that fetches unread
// and total thread counts for the given set of labels.
func New(clientConfig []byte, labels ...string) *Module {
config, err := google.ConfigFromJSON(clientConfig, gmail.GmailLabelsScope)
if err != nil {
panic("Bad client config: " + err.Error())
}
if len(labels) == 0 {
labels = []string{"INBOX"}
}
m := &Module{
config: oauth.Register(config),
labels: labels,
scheduler: timing.NewScheduler(),
}
m.RefreshInterval(5 * time.Minute)
m.Output(func(i Info) bar.Output {
if i.TotalUnread() == 0 {
return nil
}
return outputs.Textf("Gmail: %d", i.TotalUnread())
})
return m
}
// for tests, to wrap the client in a transport that redirects requests.
var wrapForTest func(*http.Client)
// Stream starts the module.
func (m *Module) Stream(sink bar.Sink) {
client, _ := m.config.Client()
if wrapForTest != nil {
wrapForTest(client)
}
srv, _ := gmail.New(client)
r, err := srv.Users.Labels.List("me").Do()
if sink.Error(err) {
return
}
labelIDs := map[string]string{}
for _, l := range r.Labels {
labelIDs[l.Name] = l.Id
}
i, err := fetch(srv, m.labels, labelIDs)
outf := m.outputFunc.Get().(func(Info) bar.Output)
nextOutputFunc, done := m.outputFunc.Subscribe()
defer done()
for {
if sink.Error(err) {
return
}
sink.Output(outf(i))
select {
case <-nextOutputFunc:
outf = m.outputFunc.Get().(func(Info) bar.Output)
case <-m.scheduler.C:
i, err = fetch(srv, m.labels, labelIDs)
}
}
}
func fetch(srv *gmail.Service, labels []string, labelIDs map[string]string) (Info, error) {
i := Info{
Threads: map[string]int64{},
Unread: map[string]int64{},
}
for _, l := range labels {
r, err := srv.Users.Labels.Get("me", labelIDs[l]).Do()
if err != nil {
return i, err
}
i.Threads[l] = r.ThreadsTotal
i.Unread[l] = r.ThreadsUnread
}
return i, nil
}
// Output sets the output format for the module.
func (m *Module) Output(outputFunc func(Info) bar.Output) *Module {
m.outputFunc.Set(outputFunc)
return m
}
// RefreshInterval sets the interval between consecutive checks for new mail.
func (m *Module) RefreshInterval(interval time.Duration) *Module {
m.scheduler.Every(interval)
return m
}