- commit
- 0a997c5
- parent
- d80b355
- author
- Eric Bower
- date
- 2025-08-30 08:22:02 -0400 EDT
chore(feeds): better formatting for smtp emails
1 files changed,
+23,
-11
+23,
-11
1@@ -132,11 +132,14 @@ type Fetcher struct {
2 db db.DB
3 auth sasl.Client
4 gron *gronx.Gronx
5+ host string
6 }
7
8 func NewFetcher(dbpool db.DB, cfg *shared.ConfigSite) *Fetcher {
9+ host := os.Getenv("PICO_SMTP_HOST")
10 smtPass := os.Getenv("PICO_SMTP_PASS")
11 emailLogin := os.Getenv("PICO_SMTP_USER")
12+
13 auth := sasl.NewPlainClient("", emailLogin, smtPass)
14 gron := gronx.New()
15 return &Fetcher{
16@@ -144,6 +147,7 @@ func NewFetcher(dbpool db.DB, cfg *shared.ConfigSite) *Fetcher {
17 cfg: cfg,
18 auth: auth,
19 gron: gron,
20+ host: host,
21 }
22 }
23
24@@ -253,7 +257,8 @@ func (f *Fetcher) RunPost(logger *slog.Logger, user *db.User, post *db.Post, ski
25 return err
26 }
27
28- subject := fmt.Sprintf("%s feed digest", post.Title)
29+ subject := fmt.Sprintf("%s feed digest", post.Filename)
30+ unsubURL := getUnsubURL(post)
31
32 msgBody, err := f.FetchAll(logger, urls, parsed.InlineContent, user.Name, post)
33 if err != nil {
34@@ -277,9 +282,11 @@ Also, we have centralized logs in our pico.sh TUI that will display realtime fee
35
36 %s`, post.Data.Attempts, maxAttempts, errForUser.Error(), post.Text)
37 err = f.SendEmail(
38- logger, user.Name,
39+ logger,
40+ user.Name,
41 parsed.Email,
42 subject,
43+ unsubURL,
44 &MsgBody{Html: strings.ReplaceAll(errBody, "\n", "<br />"), Text: errBody},
45 )
46 if err != nil {
47@@ -307,7 +314,7 @@ Also, we have centralized logs in our pico.sh TUI that will display realtime fee
48 }
49
50 if msgBody != nil {
51- err = f.SendEmail(logger, user.Name, parsed.Email, subject, msgBody)
52+ err = f.SendEmail(logger, user.Name, parsed.Email, subject, unsubURL, msgBody)
53 if err != nil {
54 return err
55 }
56@@ -460,6 +467,10 @@ type MsgBody struct {
57 Text string
58 }
59
60+func getUnsubURL(post *db.Post) string {
61+ return fmt.Sprintf("https://feeds.pico.sh/unsub/%s", post.ID)
62+}
63+
64 func (f *Fetcher) FetchAll(logger *slog.Logger, urls []string, inlineContent bool, username string, post *db.Post) (*MsgBody, error) {
65 logger.Info("fetching feeds", "inlineContent", inlineContent)
66 fp := gofeed.NewParser()
67@@ -475,7 +486,7 @@ func (f *Fetcher) FetchAll(logger *slog.Logger, urls []string, inlineContent boo
68 }
69 feeds := &DigestFeed{
70 KeepAliveURL: fmt.Sprintf("https://feeds.pico.sh/keep-alive/%s", post.ID),
71- UnsubURL: fmt.Sprintf("https://feeds.pico.sh/unsub/%s", post.ID),
72+ UnsubURL: getUnsubURL(post),
73 DaysLeft: daysLeft,
74 ShowBanner: showBanner,
75 Options: DigestOptions{InlineContent: inlineContent},
76@@ -564,19 +575,20 @@ func (f *Fetcher) FetchAll(logger *slog.Logger, urls []string, inlineContent boo
77 }, nil
78 }
79
80-func (f *Fetcher) SendEmail(logger *slog.Logger, username, email, subject string, msg *MsgBody) error {
81+func (f *Fetcher) SendEmail(logger *slog.Logger, username, email, subject, unsubURL string, msg *MsgBody) error {
82 if email == "" {
83 return fmt.Errorf("(%s) does not have an email associated with their feed post", username)
84 }
85- smtpAddr := "smtp.fastmail.com:587"
86+ smtpAddr := f.host
87 fromEmail := "hello@pico.sh"
88 to := []string{email}
89 headers := map[string]string{
90- "From": fromEmail,
91- "To": email,
92- "Subject": subject,
93- "MIME-Version": "1.0",
94- "Content-Type": `multipart/alternative; boundary="boundary123"`,
95+ "From": fromEmail,
96+ "Subject": subject,
97+ "To": email,
98+ "MIME-Version": "1.0",
99+ "Content-Type": `multipart/alternative; boundary="boundary123"`,
100+ "List-Unsubscribe": "<" + unsubURL + ">",
101 }
102 var content strings.Builder
103 for k, v := range headers {