499a689702ef2e502d8c897a2998f07763d1f0f1
[cacert-boardvoting.git] / notifications.go
1 package main
2
3 import (
4 "bytes"
5 "fmt"
6 "github.com/Masterminds/sprig"
7 "gopkg.in/gomail.v2"
8 "text/template"
9 )
10
11 type NotificationMail interface {
12 GetData() interface{}
13 GetTemplate() string
14 GetSubject() string
15 GetHeaders() map[string]string
16 GetRecipient() (string, string)
17 }
18
19 var NotifyMailChannel = make(chan NotificationMail, 1)
20
21 func MailNotifier(quitMailNotifier chan int) {
22 logger.Println("Launched mail notifier")
23 for {
24 select {
25 case notification := <-NotifyMailChannel:
26 mailText, err := buildMail(notification.GetTemplate(), notification.GetData())
27 if err != nil {
28 logger.Println("ERROR building mail:", err)
29 continue
30 }
31
32 m := gomail.NewMessage()
33 m.SetHeader("From", config.NotificationSenderAddress)
34 address, name := notification.GetRecipient()
35 m.SetAddressHeader("To", address, name)
36 m.SetHeader("Subject", notification.GetSubject())
37 for header, value := range notification.GetHeaders() {
38 m.SetHeader(header, value)
39 }
40 m.SetBody("text/plain", mailText.String())
41
42 d := gomail.NewDialer(config.MailServer.Host, config.MailServer.Port, "", "")
43 if err := d.DialAndSend(m); err != nil {
44 logger.Println("ERROR sending mail:", err)
45 }
46 case <-quitMailNotifier:
47 fmt.Println("Ending mail notifier")
48 return
49 }
50 }
51 }
52
53 func buildMail(templateName string, context interface{}) (mailText *bytes.Buffer, err error) {
54 t, err := template.New(templateName).Funcs(
55 sprig.GenericFuncMap()).ParseFiles(fmt.Sprintf("templates/%s", templateName))
56 if err != nil {
57 return
58 }
59
60 mailText = bytes.NewBufferString("")
61 t.Execute(mailText, context)
62
63 return
64 }
65
66 type notificationBase struct{}
67
68 func (n *notificationBase) GetRecipient() (address string, name string) {
69 address, name = config.BoardMailAddress, "CAcert board mailing list"
70 return
71 }
72
73 type decisionReplyBase struct {
74 decision Decision
75 }
76
77 func (n *decisionReplyBase) GetHeaders() map[string]string {
78 return map[string]string{
79 "References": fmt.Sprintf("<%s>", n.decision.Tag),
80 "In-Reply-To": fmt.Sprintf("<%s>", n.decision.Tag),
81 }
82 }
83
84 type NotificationClosedDecision struct {
85 notificationBase
86 decisionReplyBase
87 voteSums VoteSums
88 }
89
90 func NewNotificationClosedDecision(decision *Decision, voteSums *VoteSums) *NotificationClosedDecision {
91 notification := &NotificationClosedDecision{voteSums: *voteSums}
92 notification.decision = *decision
93 return notification
94 }
95
96 func (n *NotificationClosedDecision) GetData() interface{} {
97 return struct {
98 *Decision
99 *VoteSums
100 }{&n.decision, &n.voteSums}
101 }
102
103 func (n *NotificationClosedDecision) GetTemplate() string { return "closed_motion_mail.txt" }
104
105 func (n *NotificationClosedDecision) GetSubject() string {
106 return fmt.Sprintf("Re: %s - %s - finalised", n.decision.Tag, n.decision.Title)
107 }
108
109 type NotificationCreateMotion struct {
110 notificationBase
111 decision Decision
112 voter Voter
113 }
114
115 func (n *NotificationCreateMotion) GetData() interface{} {
116 voteURL := fmt.Sprintf("%s/vote", config.BaseURL)
117 unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL)
118 return struct {
119 *Decision
120 Name string
121 VoteURL string
122 UnvotedURL string
123 }{&n.decision, n.voter.Name, voteURL, unvotedURL}
124 }
125
126 func (n *NotificationCreateMotion) GetTemplate() string { return "create_motion_mail.txt" }
127
128 func (n *NotificationCreateMotion) GetSubject() string {
129 return fmt.Sprintf("%s - %s", n.decision.Tag, n.decision.Title)
130 }
131
132 func (n *NotificationCreateMotion) GetHeaders() map[string]string {
133 return map[string]string{"Message-ID": fmt.Sprintf("<%s>", n.decision.Tag)}
134 }
135
136 type NotificationUpdateMotion struct {
137 notificationBase
138 decisionReplyBase
139 voter Voter
140 }
141
142 func NewNotificationUpdateMotion(decision Decision, voter Voter) *NotificationUpdateMotion {
143 notification := NotificationUpdateMotion{voter: voter}
144 notification.decision = decision
145 return &notification
146 }
147
148 func (n *NotificationUpdateMotion) GetData() interface{} {
149 voteURL := fmt.Sprintf("%s/vote", config.BaseURL)
150 unvotedURL := fmt.Sprintf("%s/motions/?unvoted=1", config.BaseURL)
151 return struct {
152 *Decision
153 Name string
154 VoteURL string
155 UnvotedURL string
156 }{&n.decision, n.voter.Name, voteURL, unvotedURL}
157 }
158
159 func (n *NotificationUpdateMotion) GetTemplate() string { return "update_motion_mail.txt" }
160
161 func (n *NotificationUpdateMotion) GetSubject() string {
162 return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title)
163 }
164
165 type NotificationWithDrawMotion struct {
166 notificationBase
167 decisionReplyBase
168 voter Voter
169 }
170
171 func NewNotificationWithDrawMotion(decision *Decision, voter *Voter) *NotificationWithDrawMotion {
172 notification := &NotificationWithDrawMotion{voter: *voter}
173 notification.decision = *decision
174 return notification
175 }
176
177 func (n *NotificationWithDrawMotion) GetData() interface{} {
178 return struct {
179 *Decision
180 Name string
181 }{&n.decision, n.voter.Name}
182 }
183
184 func (n *NotificationWithDrawMotion) GetTemplate() string { return "withdraw_motion_mail.txt" }
185
186 func (n *NotificationWithDrawMotion) GetSubject() string {
187 return fmt.Sprintf("Re: %s - %s - withdrawn", n.decision.Tag, n.decision.Title)
188 }
189
190 type RemindVoterNotification struct {
191 voter Voter
192 decisions []Decision
193 }
194
195 func (n *RemindVoterNotification) GetData() interface{} {
196 return struct {
197 Decisions []Decision
198 Name string
199 BaseURL string
200 }{n.decisions, n.voter.Name, config.BaseURL}
201 }
202
203 func (n *RemindVoterNotification) GetTemplate() string { return "remind_voter_mail.txt" }
204
205 func (n *RemindVoterNotification) GetSubject() string { return "Outstanding CAcert board votes" }
206
207 func (n *RemindVoterNotification) GetHeaders() map[string]string {
208 return map[string]string{}
209 }
210
211 func (n *RemindVoterNotification) GetRecipient() (address string, name string) {
212 address, name = n.voter.Reminder, n.voter.Name
213 return
214 }
215
216 type voteNotificationBase struct{}
217
218 func (n *voteNotificationBase) GetRecipient() (address string, name string) {
219 address, name = config.VoteNoticeAddress, "CAcert board votes mailing list"
220 return
221 }
222
223 type NotificationProxyVote struct {
224 voteNotificationBase
225 decisionReplyBase
226 proxy Voter
227 voter Voter
228 vote Vote
229 justification string
230 }
231
232 func NewNotificationProxyVote(decision *Decision, proxy *Voter, voter *Voter, vote *Vote, justification string) *NotificationProxyVote {
233 notification := &NotificationProxyVote{proxy: *proxy, voter: *voter, vote: *vote, justification: justification}
234 notification.decision = *decision
235 return notification
236 }
237
238 func (n *NotificationProxyVote) GetData() interface{} {
239 return struct {
240 Proxy string
241 Vote VoteChoice
242 Voter string
243 Decision *Decision
244 Justification string
245 }{n.proxy.Name, n.vote.Vote, n.voter.Name, &n.decision, n.justification}
246 }
247
248 func (n *NotificationProxyVote) GetTemplate() string { return "proxy_vote_mail.txt" }
249
250 func (n *NotificationProxyVote) GetSubject() string {
251 return fmt.Sprintf("Re: %s - %s", n.decision.Tag, n.decision.Title)
252 }