forked from tangled.org/core
this repo has no description

appview: email: send email notification on mention

Signed-off-by: Seongmin Lee <boltlessengineer@proton.me>

boltless.me cad6e912 1b5db515

verified
Changed files
+104
appview
email
state
+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
··· 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 }