emailer.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. package messenger
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "net/smtp"
  6. "github.com/jaytaylor/html2text"
  7. "github.com/knadh/smtppool"
  8. )
  9. const emName = "email"
  10. // Server represents an SMTP server's credentials.
  11. type Server struct {
  12. Name string
  13. Username string `json:"username"`
  14. Password string `json:"password"`
  15. AuthProtocol string `json:"auth_protocol"`
  16. EmailFormat string `json:"email_format"`
  17. // Rest of the options are embedded directly from the smtppool lib.
  18. // The JSON tag is for config unmarshal to work.
  19. smtppool.Opt `json:",squash"`
  20. pool *smtppool.Pool
  21. }
  22. // Emailer is the SMTP e-mail messenger.
  23. type Emailer struct {
  24. servers map[string]*Server
  25. serverNames []string
  26. numServers int
  27. }
  28. // NewEmailer creates and returns an e-mail Messenger backend.
  29. // It takes multiple SMTP configurations.
  30. func NewEmailer(srv ...Server) (*Emailer, error) {
  31. e := &Emailer{
  32. servers: make(map[string]*Server),
  33. }
  34. for _, server := range srv {
  35. s := server
  36. var auth smtp.Auth
  37. switch s.AuthProtocol {
  38. case "cram":
  39. auth = smtp.CRAMMD5Auth(s.Username, s.Password)
  40. case "plain":
  41. auth = smtp.PlainAuth("", s.Username, s.Password, s.Host)
  42. case "login":
  43. auth = &smtppool.LoginAuth{Username: s.Username, Password: s.Password}
  44. case "":
  45. default:
  46. return nil, fmt.Errorf("unknown SMTP auth type '%s'", s.AuthProtocol)
  47. }
  48. s.Opt.Auth = auth
  49. pool, err := smtppool.New(s.Opt)
  50. if err != nil {
  51. return nil, err
  52. }
  53. s.pool = pool
  54. e.servers[s.Name] = &s
  55. e.serverNames = append(e.serverNames, s.Name)
  56. }
  57. e.numServers = len(e.serverNames)
  58. return e, nil
  59. }
  60. // Name returns the Server's name.
  61. func (e *Emailer) Name() string {
  62. return emName
  63. }
  64. // Push pushes a message to the server.
  65. func (e *Emailer) Push(fromAddr string, toAddr []string, subject string, m []byte, atts []Attachment) error {
  66. var key string
  67. // If there are more than one SMTP servers, send to a random
  68. // one from the list.
  69. if e.numServers > 1 {
  70. key = e.serverNames[rand.Intn(e.numServers)]
  71. } else {
  72. key = e.serverNames[0]
  73. }
  74. // Are there attachments?
  75. var files []smtppool.Attachment
  76. if atts != nil {
  77. files = make([]smtppool.Attachment, 0, len(atts))
  78. for _, f := range atts {
  79. a := smtppool.Attachment{
  80. Filename: f.Name,
  81. Header: f.Header,
  82. Content: make([]byte, len(f.Content)),
  83. }
  84. copy(a.Content, f.Content)
  85. files = append(files, a)
  86. }
  87. }
  88. mtext, err := html2text.FromString(string(m), html2text.Options{PrettyTables: true})
  89. if err != nil {
  90. return err
  91. }
  92. srv := e.servers[key]
  93. em := smtppool.Email{
  94. From: fromAddr,
  95. To: toAddr,
  96. Subject: subject,
  97. Attachments: files,
  98. }
  99. switch srv.EmailFormat {
  100. case "html":
  101. em.HTML = m
  102. case "plain":
  103. em.Text = []byte(mtext)
  104. default:
  105. em.HTML = m
  106. em.Text = []byte(mtext)
  107. }
  108. return srv.pool.Send(em)
  109. }
  110. // Flush flushes the message queue to the server.
  111. func (e *Emailer) Flush() error {
  112. return nil
  113. }