Commit fffe43e
Eric Bower
·
2025-07-27 09:57:26 -0400 EDT
parent 7792e55
refactor(feeds): replace sendgrid with smtp
3 files changed,
+51,
-43
M
go.mod
+2,
-2
| ... | ... | @@ -30,6 +30,8 @@ require ( | |
| 30 | 30 | github.com/darkweak/souin v1.7.6 | |
| 31 | 31 | github.com/darkweak/souin/plugins/souin/storages v1.7.6 | |
| 32 | 32 | github.com/darkweak/storages/core v0.0.14 | |
| 33 | + | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 | |
| 34 | + | github.com/emersion/go-smtp v0.23.0 | |
| 33 | 35 | github.com/gkampitakis/go-snaps v0.5.7 | |
| 34 | 36 | github.com/google/go-cmp v0.7.0 | |
| 35 | 37 | github.com/google/renameio/v2 v2.0.0 |
| ... | ... | @@ -51,7 +53,6 @@ require ( | |
| 51 | 53 | github.com/pkg/sftp v1.13.9 | |
| 52 | 54 | github.com/prometheus/client_golang v1.21.1 | |
| 53 | 55 | github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 | |
| 54 | - | github.com/sendgrid/sendgrid-go v3.16.0+incompatible | |
| 55 | 56 | github.com/simplesurance/go-ip-anonymizer v0.0.0-20200429124537-35a880f8e87d | |
| 56 | 57 | github.com/x-way/crawlerdetect v0.2.28 | |
| 57 | 58 | github.com/yuin/goldmark v1.7.8 |
| ... | ... | @@ -232,7 +233,6 @@ require ( | |
| 232 | 233 | github.com/safchain/ethtool v0.5.10 // indirect | |
| 233 | 234 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect | |
| 234 | 235 | github.com/secure-io/sio-go v0.3.1 // indirect | |
| 235 | - | github.com/sendgrid/rest v2.6.9+incompatible // indirect | |
| 236 | 236 | github.com/shirou/gopsutil/v3 v3.24.5 // indirect | |
| 237 | 237 | github.com/shoenig/go-m1cpu v0.1.6 // indirect | |
| 238 | 238 | github.com/shopspring/decimal v1.4.0 // indirect |
M
go.sum
+4,
-4
| ... | ... | @@ -262,6 +262,10 @@ github.com/dsoprea/go-utility/v2 v2.0.0-20200717064901-2fccff4aa15e/go.mod h1:uA | |
| 262 | 262 | github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= | |
| 263 | 263 | github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= | |
| 264 | 264 | github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= | |
| 265 | + | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= | |
| 266 | + | github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= | |
| 267 | + | github.com/emersion/go-smtp v0.23.0 h1:ZiriTOTK7sKep7jbWqgB5kPsiBp5wnE5auEMnwRMnGc= | |
| 268 | + | github.com/emersion/go-smtp v0.23.0/go.mod h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ= | |
| 265 | 269 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | |
| 266 | 270 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | |
| 267 | 271 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= |
| ... | ... | @@ -731,10 +735,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt | |
| 731 | 735 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= | |
| 732 | 736 | github.com/secure-io/sio-go v0.3.1 h1:dNvY9awjabXTYGsTF1PiCySl9Ltofk9GA3VdWlo7rRc= | |
| 733 | 737 | github.com/secure-io/sio-go v0.3.1/go.mod h1:+xbkjDzPjwh4Axd07pRKSNriS9SCiYksWnZqdnfpQxs= | |
| 734 | - | github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= | |
| 735 | - | github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= | |
| 736 | - | github.com/sendgrid/sendgrid-go v3.16.0+incompatible h1:i8eE6IMkiCy7vusSdacHHSBUpXyTcTXy/Rl9N9aZ/Qw= | |
| 737 | - | github.com/sendgrid/sendgrid-go v3.16.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= | |
| 738 | 738 | github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= | |
| 739 | 739 | github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= | |
| 740 | 740 | github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= |
+45,
-37
| ... | ... | @@ -10,15 +10,16 @@ import ( | |
| 10 | 10 | "math" | |
| 11 | 11 | "net/http" | |
| 12 | 12 | "net/url" | |
| 13 | + | "os" | |
| 13 | 14 | "strings" | |
| 14 | 15 | "text/template" | |
| 15 | 16 | "time" | |
| 16 | 17 | ||
| 18 | + | "github.com/emersion/go-sasl" | |
| 19 | + | "github.com/emersion/go-smtp" | |
| 17 | 20 | "github.com/mmcdole/gofeed" | |
| 18 | 21 | "github.com/picosh/pico/pkg/db" | |
| 19 | 22 | "github.com/picosh/pico/pkg/shared" | |
| 20 | - | "github.com/sendgrid/sendgrid-go" | |
| 21 | - | "github.com/sendgrid/sendgrid-go/helpers/mail" | |
| 22 | 23 | ) | |
| 23 | 24 | ||
| 24 | 25 | var ErrNoRecentArticles = errors.New("no recent articles") |
| ... | ... | @@ -125,14 +126,19 @@ func isValidItem(logger *slog.Logger, item *gofeed.Item, feedItems []*db.FeedIte | |
| 125 | 126 | } | |
| 126 | 127 | ||
| 127 | 128 | type Fetcher struct { | |
| 128 | - | cfg *shared.ConfigSite | |
| 129 | - | db db.DB | |
| 129 | + | cfg *shared.ConfigSite | |
| 130 | + | db db.DB | |
| 131 | + | auth sasl.Client | |
| 130 | 132 | } | |
| 131 | 133 | ||
| 132 | 134 | func NewFetcher(dbpool db.DB, cfg *shared.ConfigSite) *Fetcher { | |
| 135 | + | smtPass := os.Getenv("PICO_SMTP_PASS") | |
| 136 | + | emailLogin := os.Getenv("PICO_SMTP_USER") | |
| 137 | + | auth := sasl.NewPlainClient("", emailLogin, smtPass) | |
| 133 | 138 | return &Fetcher{ | |
| 134 | - | db: dbpool, | |
| 135 | - | cfg: cfg, | |
| 139 | + | db: dbpool, | |
| 140 | + | cfg: cfg, | |
| 141 | + | auth: auth, | |
| 136 | 142 | } | |
| 137 | 143 | } | |
| 138 | 144 |
| ... | ... | @@ -516,41 +522,43 @@ func (f *Fetcher) FetchAll(logger *slog.Logger, urls []string, inlineContent boo | |
| 516 | 522 | }, nil | |
| 517 | 523 | } | |
| 518 | 524 | ||
| 519 | - | func (f *Fetcher) SendEmail(logger *slog.Logger, username, email string, subject string, msg *MsgBody) error { | |
| 525 | + | func (f *Fetcher) SendEmail(logger *slog.Logger, username, email, subject string, msg *MsgBody) error { | |
| 520 | 526 | if email == "" { | |
| 521 | 527 | return fmt.Errorf("(%s) does not have an email associated with their feed post", username) | |
| 522 | 528 | } | |
| 523 | - | ||
| 524 | - | from := mail.NewEmail("team pico", shared.DefaultEmail) | |
| 525 | - | to := mail.NewEmail(username, email) | |
| 526 | - | ||
| 527 | - | // f.logger.Infof("message body (%s)", plainTextContent) | |
| 528 | - | ||
| 529 | - | message := mail.NewSingleEmail(from, subject, to, msg.Text, msg.Html) | |
| 530 | - | client := sendgrid.NewSendClient(f.cfg.SendgridKey) | |
| 531 | - | ||
| 529 | + | smtpAddr := "smtp.fastmail.com:587" | |
| 530 | + | fromEmail := "hello@pico.sh" | |
| 531 | + | to := []string{email} | |
| 532 | + | headers := map[string]string{ | |
| 533 | + | "From": fromEmail, | |
| 534 | + | "To": email, | |
| 535 | + | "Subject": subject, | |
| 536 | + | "MIME-Version": "1.0", | |
| 537 | + | "Content-Type": `multipart/alternative; boundary="boundary123"`, | |
| 538 | + | } | |
| 539 | + | var content strings.Builder | |
| 540 | + | for k, v := range headers { | |
| 541 | + | content.WriteString(fmt.Sprintf("%s: %s\r\n", k, v)) | |
| 542 | + | } | |
| 543 | + | content.WriteString("\r\n") | |
| 544 | + | content.WriteString("\r\n--boundary123\r\n") | |
| 545 | + | content.WriteString("Content-Type: text/plain; charset=\"utf-8\"\r\n") | |
| 546 | + | content.WriteString("\r\n" + msg.Text + "\r\n") | |
| 547 | + | content.WriteString("--boundary123\r\n") | |
| 548 | + | content.WriteString("Content-Type: text/html; charset=\"utf-8\"\r\n") | |
| 549 | + | content.WriteString("\r\n" + msg.Html + "\r\n") | |
| 550 | + | content.WriteString("--boundary123--") | |
| 551 | + | ||
| 552 | + | reader := strings.NewReader(content.String()) | |
| 532 | 553 | logger.Info("sending email digest") | |
| 533 | - | response, err := client.Send(message) | |
| 534 | - | if err != nil { | |
| 535 | - | return err | |
| 536 | - | } | |
| 537 | - | ||
| 538 | - | // f.logger.Infof("(%s) email digest response: %v", username, response) | |
| 539 | - | ||
| 540 | - | if len(response.Headers["X-Message-Id"]) > 0 { | |
| 541 | - | logger.Info( | |
| 542 | - | "successfully sent email digest", | |
| 543 | - | "email", email, | |
| 544 | - | "x-message-id", response.Headers["X-Message-Id"][0], | |
| 545 | - | ) | |
| 546 | - | } else { | |
| 547 | - | logger.Error( | |
| 548 | - | "could not find x-message-id, which means sending an email failed", | |
| 549 | - | "email", email, | |
| 550 | - | ) | |
| 551 | - | } | |
| 552 | - | ||
| 553 | - | return nil | |
| 554 | + | err := smtp.SendMail( | |
| 555 | + | smtpAddr, | |
| 556 | + | f.auth, | |
| 557 | + | fromEmail, | |
| 558 | + | to, | |
| 559 | + | reader, | |
| 560 | + | ) | |
| 561 | + | return err | |
| 554 | 562 | } | |
| 555 | 563 | ||
| 556 | 564 | func (f *Fetcher) Run(logger *slog.Logger) error { |