+102
appview/email/notifier.go
+102
appview/email/notifier.go
···
1
+
package email
2
+
3
+
import (
4
+
"context"
5
+
"fmt"
6
+
"log"
7
+
8
+
securejoin "github.com/cyphar/filepath-securejoin"
9
+
"tangled.sh/tangled.sh/core/appview/config"
10
+
"tangled.sh/tangled.sh/core/appview/db"
11
+
"tangled.sh/tangled.sh/core/appview/notify"
12
+
"tangled.sh/tangled.sh/core/idresolver"
13
+
)
14
+
15
+
type EmailNotifier struct {
16
+
db *db.DB
17
+
idResolver *idresolver.Resolver
18
+
Config *config.Config
19
+
notify.BaseNotifier
20
+
}
21
+
22
+
func NewEmailNotifier(
23
+
db *db.DB,
24
+
idResolver *idresolver.Resolver,
25
+
config *config.Config,
26
+
) notify.Notifier {
27
+
return &EmailNotifier{
28
+
db,
29
+
idResolver,
30
+
config,
31
+
notify.BaseNotifier{},
32
+
}
33
+
}
34
+
35
+
var _ notify.Notifier = &EmailNotifier{}
36
+
37
+
// TODO: yeah this is just bad design. should be moved under idResolver ore include repoResolver at first place
38
+
func (n *EmailNotifier) repoOwnerSlashName(ctx context.Context, repo *db.Repo) (string, error) {
39
+
repoOwnerID, err := n.idResolver.ResolveIdent(ctx, repo.Did)
40
+
if err != nil || repoOwnerID.Handle.IsInvalidHandle() {
41
+
return "", fmt.Errorf("resolve comment owner did: %w", err)
42
+
}
43
+
repoOwnerHandle := repoOwnerID.Handle
44
+
var repoOwnerSlashName string
45
+
if repoOwnerHandle != "" && !repoOwnerHandle.IsInvalidHandle() {
46
+
repoOwnerSlashName, _ = securejoin.SecureJoin(fmt.Sprintf("@%s", repoOwnerHandle), repo.Name)
47
+
} else {
48
+
repoOwnerSlashName = repo.DidSlashRepo()
49
+
}
50
+
return repoOwnerSlashName, nil
51
+
}
52
+
53
+
func (n *EmailNotifier) buildIssueEmail(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment) (Email, error) {
54
+
commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid)
55
+
if err != nil || commentOwner.Handle.IsInvalidHandle() {
56
+
return Email{}, fmt.Errorf("resolve comment owner did: %w", err)
57
+
}
58
+
baseUrl := n.Config.Core.AppviewHost
59
+
repoOwnerSlashName, err := n.repoOwnerSlashName(ctx, repo)
60
+
if err != nil {
61
+
return Email{}, nil
62
+
}
63
+
url := fmt.Sprintf("%s/%s/issues/%d#comment-%d", baseUrl, repoOwnerSlashName, comment.Issue, comment.CommentId)
64
+
return Email{
65
+
APIKey: n.Config.Resend.ApiKey,
66
+
From: n.Config.Resend.SentFrom,
67
+
Subject: fmt.Sprintf("[%s] %s (issue#%d)", repoOwnerSlashName, issue.Title, issue.IssueId),
68
+
Html: fmt.Sprintf(`<p><b>@%s</b> mentioned you</p><a href="%s">View it on tangled.sh</a>.`, commentOwner.Handle.String(), url),
69
+
}, nil
70
+
}
71
+
72
+
func (n *EmailNotifier) gatherRecipientEmails(ctx context.Context, handles []string) []string {
73
+
recipients := []string{}
74
+
resolvedIdents := n.idResolver.ResolveIdents(ctx, handles)
75
+
for _, id := range resolvedIdents {
76
+
email, err := db.GetPrimaryEmail(n.db, id.DID.String())
77
+
if err != nil {
78
+
log.Println("failed to get primary email:", err)
79
+
continue
80
+
}
81
+
recipients = append(recipients, email.Address)
82
+
}
83
+
return recipients
84
+
}
85
+
86
+
func (n *EmailNotifier) NewIssueComment(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment, mentions []string) {
87
+
email, err := n.buildIssueEmail(ctx, repo, issue, comment)
88
+
if err != nil {
89
+
log.Println("failed to create issue-email:", err)
90
+
return
91
+
}
92
+
// TODO: get issue-subscribed user DIDs and merge with mentioned users
93
+
recipients := n.gatherRecipientEmails(ctx, mentions)
94
+
log.Println("sending email to:", recipients)
95
+
if err = SendEmail(email, recipients...); err != nil {
96
+
log.Println("error sending email:", err)
97
+
}
98
+
}
99
+
100
+
// func (n *EmailNotifier) NewPullComment(ctx context.Context, comment *db.PullComment, []string) {
101
+
// n.usersMentioned(ctx, mentions)
102
+
// }
+2
appview/state/state.go
+2
appview/state/state.go
···
23
23
"tangled.sh/tangled.sh/core/appview/cache/session"
24
24
"tangled.sh/tangled.sh/core/appview/config"
25
25
"tangled.sh/tangled.sh/core/appview/db"
26
+
"tangled.sh/tangled.sh/core/appview/email"
26
27
"tangled.sh/tangled.sh/core/appview/notify"
27
28
"tangled.sh/tangled.sh/core/appview/oauth"
28
29
"tangled.sh/tangled.sh/core/appview/pages"
···
138
139
spindlestream.Start(ctx)
139
140
140
141
var notifiers []notify.Notifier
142
+
notifiers = append(notifiers, email.NewEmailNotifier(d, res, config))
141
143
if !config.Core.Dev {
142
144
notifiers = append(notifiers, posthogService.NewPosthogNotifier(posthog))
143
145
}