forked from chappjc/dcrspy
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathemail.go
132 lines (113 loc) · 3.18 KB
/
email.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
package main
import (
"fmt"
"math"
"net/smtp"
"strconv"
"strings"
"sync"
"time"
)
// EmailConfig contains the email server address and credentials
type EmailConfig struct {
emailAddr string
smtpUser, smtpPass, smtpServer string
smtpPort int
}
// EmailMsgChan is used with EmailQueue to automatically batch messages in to
// single emails.
var EmailMsgChan chan string
func init() {
EmailMsgChan = make(chan string, 200)
}
// SendEmailWatchRecv Sends an email using the input emailConfig and message
// string.
func SendEmailWatchRecv(message, subject string, ecfg *EmailConfig) error {
// Check for nil pointer emailConfig
if ecfg == nil {
return fmt.Errorf("emailConfig must not be a nil pointer")
}
auth := smtp.PlainAuth(
"",
ecfg.smtpUser,
ecfg.smtpPass,
ecfg.smtpServer,
)
// The SMTP server address includes the port
addr := ecfg.smtpServer + ":" + strconv.Itoa(ecfg.smtpPort)
// Make a header using a map for clarity
header := make(map[string]string)
header["From"] = ecfg.smtpUser
header["To"] = ecfg.emailAddr
header["Subject"] = subject
//header["MIME-Version"] = "1.0"
header["Content-Type"] = `text/plain; charset="utf-8"`
//header["Content-Transfer-Encoding"] = "base64"
// Build the full message with the header + input message string
messageFull := ""
for k, v := range header {
messageFull += fmt.Sprintf("%s: %s\r\n", k, v)
}
messageFull += "\r\n" + message
// Send email
err := smtp.SendMail(
addr,
auth,
ecfg.smtpUser, // sender is receiver
[]string{ecfg.emailAddr}, // recipients
[]byte(messageFull),
)
if err != nil {
return fmt.Errorf("Failed to send email: %v", err)
}
return nil
}
// sendEmailWatchRecv is launched as a goroutine by EmailQueue
func sendEmailWatchRecv(message, subject string, ecfg *EmailConfig) {
err := SendEmailWatchRecv(message, subject, ecfg)
if err != nil {
log.Warn(err)
return
}
log.Debugf("Sent email to %v", ecfg.emailAddr)
}
// EmailQueue batches messages into single emails, using a progressively shorter
// delay before sending an email as the number of queued messages increases.
// Messages are received on the package-level channel mpEmailMsgChan. emailQueue
// should be run as a goroutine.
func EmailQueue(emailConf *EmailConfig, subject string,
wg *sync.WaitGroup, quit <-chan struct{}) {
defer wg.Done()
msgIntro := "Watched addresses were observed in the following transactions:\n\n"
var msgStrings []string
lastMsgTime := time.Now()
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
timeToWait := func(numMessages int) time.Duration {
if numMessages == 0 {
return math.MaxInt64
}
return 10 * time.Second / time.Duration(numMessages)
}
for {
//watchquit:
select {
case <-quit:
log.Debugf("Quitting emailQueue.")
return
case msg, ok := <-EmailMsgChan:
if !ok {
log.Info("emailQueue channel closed")
return
}
msgStrings = append(msgStrings, msg)
lastMsgTime = time.Now()
case <-ticker.C:
if time.Since(lastMsgTime) > timeToWait(len(msgStrings)) {
go sendEmailWatchRecv(msgIntro+strings.Join(msgStrings, "\n\n"),
subject, emailConf)
msgStrings = nil
}
}
}
}