Monorepo for Tangled tangled.org
6

Configure Feed

Select the types of activity you want to include in your feed.

1package email 2 3import ( 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 15type EmailNotifier struct { 16 db *db.DB 17 idResolver *idresolver.Resolver 18 Config *config.Config 19 notify.BaseNotifier 20} 21 22func 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 35var _ notify.Notifier = &EmailNotifier{} 36 37// TODO: yeah this is just bad design. should be moved under idResolver ore include repoResolver at first place 38func (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 53func (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 72func (n *EmailNotifier) buildPullEmail(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment) (Email, error) { 73 commentOwner, err := n.idResolver.ResolveIdent(ctx, comment.OwnerDid) 74 if err != nil || commentOwner.Handle.IsInvalidHandle() { 75 return Email{}, fmt.Errorf("resolve comment owner did: %w", err) 76 } 77 repoOwnerSlashName, err := n.repoOwnerSlashName(ctx, repo) 78 if err != nil { 79 return Email{}, nil 80 } 81 baseUrl := n.Config.Core.AppviewHost 82 url := fmt.Sprintf("%s/%s/pulls/%d#comment-%d", baseUrl, repoOwnerSlashName, comment.PullId, comment.ID) 83 return Email{ 84 APIKey: n.Config.Resend.ApiKey, 85 From: n.Config.Resend.SentFrom, 86 Subject: fmt.Sprintf("[%s] %s (pr#%d)", repoOwnerSlashName, pull.Title, pull.PullId), 87 Html: fmt.Sprintf(`<p><b>@%s</b> mentioned you</p><a href="%s">View it on tangled.sh</a>.`, commentOwner.Handle.String(), url), 88 }, nil 89} 90 91func (n *EmailNotifier) gatherRecipientEmails(ctx context.Context, handles []string) []string { 92 recipients := []string{} 93 resolvedIdents := n.idResolver.ResolveIdents(ctx, handles) 94 for _, id := range resolvedIdents { 95 email, err := db.GetPrimaryEmail(n.db, id.DID.String()) 96 if err != nil { 97 log.Println("failed to get primary email:", err) 98 continue 99 } 100 recipients = append(recipients, email.Address) 101 } 102 return recipients 103} 104 105func (n *EmailNotifier) NewIssueComment(ctx context.Context, repo *db.Repo, issue *db.Issue, comment *db.Comment, mentions []string) { 106 email, err := n.buildIssueEmail(ctx, repo, issue, comment) 107 if err != nil { 108 log.Println("failed to create issue-email:", err) 109 return 110 } 111 // TODO: get issue-subscribed user DIDs and merge with mentioned users 112 recipients := n.gatherRecipientEmails(ctx, mentions) 113 log.Println("sending email to:", recipients) 114 if err = SendEmail(email, recipients...); err != nil { 115 log.Println("error sending email:", err) 116 } 117} 118 119func (n *EmailNotifier) NewPullComment(ctx context.Context, repo *db.Repo, pull *db.Pull, comment *db.PullComment, mentions []string) { 120 email, err := n.buildPullEmail(ctx, repo, pull, comment) 121 if err != nil { 122 log.Println("failed to create pull-email:", err) 123 } 124 recipients := n.gatherRecipientEmails(ctx, mentions) 125 log.Println("sending email to:", recipients) 126 if err = SendEmail(email); err != nil { 127 log.Println("error sending email:", err) 128 } 129}