Mirror of @tangled.org/core. Running on a Raspberry Pi Zero 2 (Please be gentle).
0
fork

Configure Feed

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

appview/notify: notify users mentioned in issues

pass mentioned DIDs on `NewIssue*` events

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

authored by

Seongmin Lee and committed by
Tangled
b02199b3 47c8c79d

+85 -19
+1 -1
appview/indexer/notifier.go
··· 11 11 12 12 var _ notify.Notifier = &Indexer{} 13 13 14 - func (ix *Indexer) NewIssue(ctx context.Context, issue *models.Issue) { 14 + func (ix *Indexer) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) { 15 15 l := log.FromContext(ctx).With("notifier", "indexer", "issue", issue) 16 16 l.Debug("indexing new issue") 17 17 err := ix.Issues.Index(ctx, *issue)
+23 -2
appview/issues/issues.go
··· 24 24 "tangled.org/core/appview/notify" 25 25 "tangled.org/core/appview/oauth" 26 26 "tangled.org/core/appview/pages" 27 + "tangled.org/core/appview/pages/markup" 27 28 "tangled.org/core/appview/pagination" 28 29 "tangled.org/core/appview/reporesolver" 29 30 "tangled.org/core/appview/validator" ··· 454 453 455 454 // notify about the new comment 456 455 comment.Id = commentId 457 - rp.notifier.NewIssueComment(r.Context(), &comment) 456 + 457 + rawMentions := markup.FindUserMentions(comment.Body) 458 + idents := rp.idResolver.ResolveIdents(r.Context(), rawMentions) 459 + l.Debug("parsed mentions", "raw", rawMentions, "idents", idents) 460 + var mentions []syntax.DID 461 + for _, ident := range idents { 462 + if ident != nil && !ident.Handle.IsInvalidHandle() { 463 + mentions = append(mentions, ident.DID) 464 + } 465 + } 466 + rp.notifier.NewIssueComment(r.Context(), &comment, mentions) 458 467 459 468 rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d#comment-%d", f.OwnerSlashRepo(), issue.IssueId, commentId)) 460 469 } ··· 959 948 960 949 // everything is successful, do not rollback the atproto record 961 950 atUri = "" 962 - rp.notifier.NewIssue(r.Context(), issue) 951 + 952 + rawMentions := markup.FindUserMentions(issue.Body) 953 + idents := rp.idResolver.ResolveIdents(r.Context(), rawMentions) 954 + l.Debug("parsed mentions", "raw", rawMentions, "idents", idents) 955 + var mentions []syntax.DID 956 + for _, ident := range idents { 957 + if ident != nil && !ident.Handle.IsInvalidHandle() { 958 + mentions = append(mentions, ident.DID) 959 + } 960 + } 961 + rp.notifier.NewIssue(r.Context(), issue, mentions) 963 962 rp.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issue.IssueId)) 964 963 return 965 964 }
+24 -6
appview/notify/db/db.go
··· 64 64 // no-op 65 65 } 66 66 67 - func (n *databaseNotifier) NewIssue(ctx context.Context, issue *models.Issue) { 67 + func (n *databaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) { 68 68 69 69 // build the recipients list 70 70 // - owner of the repo ··· 81 81 } 82 82 83 83 actorDid := syntax.DID(issue.Did) 84 - eventType := models.NotificationTypeIssueCreated 85 84 entityType := "issue" 86 85 entityId := issue.AtUri().String() 87 86 repoId := &issue.Repo.Id ··· 90 91 n.notifyEvent( 91 92 actorDid, 92 93 recipients, 93 - eventType, 94 + models.NotificationTypeIssueCreated, 95 + entityType, 96 + entityId, 97 + repoId, 98 + issueId, 99 + pullId, 100 + ) 101 + n.notifyEvent( 102 + actorDid, 103 + mentions, 104 + models.NotificationTypeUserMentioned, 94 105 entityType, 95 106 entityId, 96 107 repoId, ··· 109 100 ) 110 101 } 111 102 112 - func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment) { 103 + func (n *databaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 113 104 issues, err := db.GetIssues(n.db, db.FilterEq("at_uri", comment.IssueAt)) 114 105 if err != nil { 115 106 log.Printf("NewIssueComment: failed to get issues: %v", err) ··· 141 132 } 142 133 143 134 actorDid := syntax.DID(comment.Did) 144 - eventType := models.NotificationTypeIssueCommented 145 135 entityType := "issue" 146 136 entityId := issue.AtUri().String() 147 137 repoId := &issue.Repo.Id ··· 150 142 n.notifyEvent( 151 143 actorDid, 152 144 recipients, 153 - eventType, 145 + models.NotificationTypeIssueCommented, 146 + entityType, 147 + entityId, 148 + repoId, 149 + issueId, 150 + pullId, 151 + ) 152 + n.notifyEvent( 153 + actorDid, 154 + mentions, 155 + models.NotificationTypeUserMentioned, 154 156 entityType, 155 157 entityId, 156 158 repoId,
+4 -4
appview/notify/merged_notifier.go
··· 54 54 m.fanout("DeleteStar", ctx, star) 55 55 } 56 56 57 - func (m *mergedNotifier) NewIssue(ctx context.Context, issue *models.Issue) { 58 - m.fanout("NewIssue", ctx, issue) 57 + func (m *mergedNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) { 58 + m.fanout("NewIssue", ctx, issue, mentions) 59 59 } 60 60 61 - func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment) { 62 - m.fanout("NewIssueComment", ctx, comment) 61 + func (m *mergedNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 62 + m.fanout("NewIssueComment", ctx, comment, mentions) 63 63 } 64 64 65 65 func (m *mergedNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {
+5 -4
appview/notify/notifier.go
··· 13 13 NewStar(ctx context.Context, star *models.Star) 14 14 DeleteStar(ctx context.Context, star *models.Star) 15 15 16 - NewIssue(ctx context.Context, issue *models.Issue) 17 - NewIssueComment(ctx context.Context, comment *models.IssueComment) 16 + NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) 17 + NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) 18 18 NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) 19 19 DeleteIssue(ctx context.Context, issue *models.Issue) 20 20 ··· 42 42 func (m *BaseNotifier) NewStar(ctx context.Context, star *models.Star) {} 43 43 func (m *BaseNotifier) DeleteStar(ctx context.Context, star *models.Star) {} 44 44 45 - func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue) {} 46 - func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment) {} 45 + func (m *BaseNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) {} 46 + func (m *BaseNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 47 + } 47 48 func (m *BaseNotifier) NewIssueState(ctx context.Context, actor syntax.DID, issue *models.Issue) {} 48 49 func (m *BaseNotifier) DeleteIssue(ctx context.Context, issue *models.Issue) {} 49 50
+4 -2
appview/notify/posthog/notifier.go
··· 57 57 } 58 58 } 59 59 60 - func (n *posthogNotifier) NewIssue(ctx context.Context, issue *models.Issue) { 60 + func (n *posthogNotifier) NewIssue(ctx context.Context, issue *models.Issue, mentions []syntax.DID) { 61 61 err := n.client.Enqueue(posthog.Capture{ 62 62 DistinctId: issue.Did, 63 63 Event: "new_issue", 64 64 Properties: posthog.Properties{ 65 65 "repo_at": issue.RepoAt.String(), 66 66 "issue_id": issue.IssueId, 67 + "mentions": mentions, 67 68 }, 68 69 }) 69 70 if err != nil { ··· 179 178 } 180 179 } 181 180 182 - func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment) { 181 + func (n *posthogNotifier) NewIssueComment(ctx context.Context, comment *models.IssueComment, mentions []syntax.DID) { 183 182 err := n.client.Enqueue(posthog.Capture{ 184 183 DistinctId: comment.Did, 185 184 Event: "new_issue_comment", 186 185 Properties: posthog.Properties{ 187 186 "issue_at": comment.IssueAt, 187 + "mentions": mentions, 188 188 }, 189 189 }) 190 190 if err != nil {
+24
appview/pages/markup/markdown.go
··· 302 302 return path.Join(rctx.CurrentDir, dst) 303 303 } 304 304 305 + // FindUserMentions returns Set of user handles from given markup soruce. 306 + // It doesn't guarntee unique DIDs 307 + func FindUserMentions(source string) []string { 308 + var ( 309 + mentions []string 310 + mentionsSet = make(map[string]struct{}) 311 + md = NewMarkdown() 312 + sourceBytes = []byte(source) 313 + root = md.Parser().Parse(text.NewReader(sourceBytes)) 314 + ) 315 + ast.Walk(root, func(n ast.Node, entering bool) (ast.WalkStatus, error) { 316 + if entering && n.Kind() == textension.KindAt { 317 + handle := n.(*textension.AtNode).Handle 318 + mentionsSet[handle] = struct{}{} 319 + return ast.WalkSkipChildren, nil 320 + } 321 + return ast.WalkContinue, nil 322 + }) 323 + for handle := range mentionsSet { 324 + mentions = append(mentions, handle) 325 + } 326 + return mentions 327 + } 328 + 305 329 func isAbsoluteUrl(link string) bool { 306 330 parsed, err := url.Parse(link) 307 331 if err != nil {