code.gitea.io/gitea@v1.21.7/services/uinotification/notify.go (about) 1 // Copyright 2018 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 package uinotification 5 6 import ( 7 "context" 8 9 activities_model "code.gitea.io/gitea/models/activities" 10 "code.gitea.io/gitea/models/db" 11 issues_model "code.gitea.io/gitea/models/issues" 12 repo_model "code.gitea.io/gitea/models/repo" 13 user_model "code.gitea.io/gitea/models/user" 14 "code.gitea.io/gitea/modules/container" 15 "code.gitea.io/gitea/modules/graceful" 16 "code.gitea.io/gitea/modules/log" 17 "code.gitea.io/gitea/modules/queue" 18 notify_service "code.gitea.io/gitea/services/notify" 19 ) 20 21 type ( 22 notificationService struct { 23 notify_service.NullNotifier 24 issueQueue *queue.WorkerPoolQueue[issueNotificationOpts] 25 } 26 27 issueNotificationOpts struct { 28 IssueID int64 29 CommentID int64 30 NotificationAuthorID int64 31 ReceiverID int64 // 0 -- ALL Watcher 32 } 33 ) 34 35 func Init() error { 36 notify_service.RegisterNotifier(NewNotifier()) 37 38 return nil 39 } 40 41 var _ notify_service.Notifier = ¬ificationService{} 42 43 // NewNotifier create a new notificationService notifier 44 func NewNotifier() notify_service.Notifier { 45 ns := ¬ificationService{} 46 ns.issueQueue = queue.CreateSimpleQueue(graceful.GetManager().ShutdownContext(), "notification-service", handler) 47 if ns.issueQueue == nil { 48 log.Fatal("Unable to create notification-service queue") 49 } 50 return ns 51 } 52 53 func handler(items ...issueNotificationOpts) []issueNotificationOpts { 54 for _, opts := range items { 55 if err := activities_model.CreateOrUpdateIssueNotifications(db.DefaultContext, opts.IssueID, opts.CommentID, opts.NotificationAuthorID, opts.ReceiverID); err != nil { 56 log.Error("Was unable to create issue notification: %v", err) 57 } 58 } 59 return nil 60 } 61 62 func (ns *notificationService) Run() { 63 go graceful.GetManager().RunWithCancel(ns.issueQueue) // TODO: using "go" here doesn't seem right, just leave it as old code 64 } 65 66 func (ns *notificationService) CreateIssueComment(ctx context.Context, doer *user_model.User, repo *repo_model.Repository, 67 issue *issues_model.Issue, comment *issues_model.Comment, mentions []*user_model.User, 68 ) { 69 opts := issueNotificationOpts{ 70 IssueID: issue.ID, 71 NotificationAuthorID: doer.ID, 72 } 73 if comment != nil { 74 opts.CommentID = comment.ID 75 } 76 _ = ns.issueQueue.Push(opts) 77 for _, mention := range mentions { 78 opts := issueNotificationOpts{ 79 IssueID: issue.ID, 80 NotificationAuthorID: doer.ID, 81 ReceiverID: mention.ID, 82 } 83 if comment != nil { 84 opts.CommentID = comment.ID 85 } 86 _ = ns.issueQueue.Push(opts) 87 } 88 } 89 90 func (ns *notificationService) NewIssue(ctx context.Context, issue *issues_model.Issue, mentions []*user_model.User) { 91 _ = ns.issueQueue.Push(issueNotificationOpts{ 92 IssueID: issue.ID, 93 NotificationAuthorID: issue.Poster.ID, 94 }) 95 for _, mention := range mentions { 96 _ = ns.issueQueue.Push(issueNotificationOpts{ 97 IssueID: issue.ID, 98 NotificationAuthorID: issue.Poster.ID, 99 ReceiverID: mention.ID, 100 }) 101 } 102 } 103 104 func (ns *notificationService) IssueChangeStatus(ctx context.Context, doer *user_model.User, commitID string, issue *issues_model.Issue, actionComment *issues_model.Comment, isClosed bool) { 105 _ = ns.issueQueue.Push(issueNotificationOpts{ 106 IssueID: issue.ID, 107 NotificationAuthorID: doer.ID, 108 CommentID: actionComment.ID, 109 }) 110 } 111 112 func (ns *notificationService) IssueChangeTitle(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, oldTitle string) { 113 if err := issue.LoadPullRequest(ctx); err != nil { 114 log.Error("issue.LoadPullRequest: %v", err) 115 return 116 } 117 if issue.IsPull && issues_model.HasWorkInProgressPrefix(oldTitle) && !issue.PullRequest.IsWorkInProgress() { 118 _ = ns.issueQueue.Push(issueNotificationOpts{ 119 IssueID: issue.ID, 120 NotificationAuthorID: doer.ID, 121 }) 122 } 123 } 124 125 func (ns *notificationService) MergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { 126 _ = ns.issueQueue.Push(issueNotificationOpts{ 127 IssueID: pr.Issue.ID, 128 NotificationAuthorID: doer.ID, 129 }) 130 } 131 132 func (ns *notificationService) AutoMergePullRequest(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest) { 133 ns.MergePullRequest(ctx, doer, pr) 134 } 135 136 func (ns *notificationService) NewPullRequest(ctx context.Context, pr *issues_model.PullRequest, mentions []*user_model.User) { 137 if err := pr.LoadIssue(ctx); err != nil { 138 log.Error("Unable to load issue: %d for pr: %d: Error: %v", pr.IssueID, pr.ID, err) 139 return 140 } 141 toNotify := make(container.Set[int64], 32) 142 repoWatchers, err := repo_model.GetRepoWatchersIDs(ctx, pr.Issue.RepoID) 143 if err != nil { 144 log.Error("GetRepoWatchersIDs: %v", err) 145 return 146 } 147 for _, id := range repoWatchers { 148 toNotify.Add(id) 149 } 150 issueParticipants, err := issues_model.GetParticipantsIDsByIssueID(ctx, pr.IssueID) 151 if err != nil { 152 log.Error("GetParticipantsIDsByIssueID: %v", err) 153 return 154 } 155 for _, id := range issueParticipants { 156 toNotify.Add(id) 157 } 158 delete(toNotify, pr.Issue.PosterID) 159 for _, mention := range mentions { 160 toNotify.Add(mention.ID) 161 } 162 for receiverID := range toNotify { 163 _ = ns.issueQueue.Push(issueNotificationOpts{ 164 IssueID: pr.Issue.ID, 165 NotificationAuthorID: pr.Issue.PosterID, 166 ReceiverID: receiverID, 167 }) 168 } 169 } 170 171 func (ns *notificationService) PullRequestReview(ctx context.Context, pr *issues_model.PullRequest, r *issues_model.Review, c *issues_model.Comment, mentions []*user_model.User) { 172 opts := issueNotificationOpts{ 173 IssueID: pr.Issue.ID, 174 NotificationAuthorID: r.Reviewer.ID, 175 } 176 if c != nil { 177 opts.CommentID = c.ID 178 } 179 _ = ns.issueQueue.Push(opts) 180 for _, mention := range mentions { 181 opts := issueNotificationOpts{ 182 IssueID: pr.Issue.ID, 183 NotificationAuthorID: r.Reviewer.ID, 184 ReceiverID: mention.ID, 185 } 186 if c != nil { 187 opts.CommentID = c.ID 188 } 189 _ = ns.issueQueue.Push(opts) 190 } 191 } 192 193 func (ns *notificationService) PullRequestCodeComment(ctx context.Context, pr *issues_model.PullRequest, c *issues_model.Comment, mentions []*user_model.User) { 194 for _, mention := range mentions { 195 _ = ns.issueQueue.Push(issueNotificationOpts{ 196 IssueID: pr.Issue.ID, 197 NotificationAuthorID: c.Poster.ID, 198 CommentID: c.ID, 199 ReceiverID: mention.ID, 200 }) 201 } 202 } 203 204 func (ns *notificationService) PullRequestPushCommits(ctx context.Context, doer *user_model.User, pr *issues_model.PullRequest, comment *issues_model.Comment) { 205 opts := issueNotificationOpts{ 206 IssueID: pr.IssueID, 207 NotificationAuthorID: doer.ID, 208 CommentID: comment.ID, 209 } 210 _ = ns.issueQueue.Push(opts) 211 } 212 213 func (ns *notificationService) PullReviewDismiss(ctx context.Context, doer *user_model.User, review *issues_model.Review, comment *issues_model.Comment) { 214 opts := issueNotificationOpts{ 215 IssueID: review.IssueID, 216 NotificationAuthorID: doer.ID, 217 CommentID: comment.ID, 218 } 219 _ = ns.issueQueue.Push(opts) 220 } 221 222 func (ns *notificationService) IssueChangeAssignee(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, assignee *user_model.User, removed bool, comment *issues_model.Comment) { 223 if !removed && doer.ID != assignee.ID { 224 opts := issueNotificationOpts{ 225 IssueID: issue.ID, 226 NotificationAuthorID: doer.ID, 227 ReceiverID: assignee.ID, 228 } 229 230 if comment != nil { 231 opts.CommentID = comment.ID 232 } 233 234 _ = ns.issueQueue.Push(opts) 235 } 236 } 237 238 func (ns *notificationService) PullRequestReviewRequest(ctx context.Context, doer *user_model.User, issue *issues_model.Issue, reviewer *user_model.User, isRequest bool, comment *issues_model.Comment) { 239 if isRequest { 240 opts := issueNotificationOpts{ 241 IssueID: issue.ID, 242 NotificationAuthorID: doer.ID, 243 ReceiverID: reviewer.ID, 244 } 245 246 if comment != nil { 247 opts.CommentID = comment.ID 248 } 249 250 _ = ns.issueQueue.Push(opts) 251 } 252 } 253 254 func (ns *notificationService) RepoPendingTransfer(ctx context.Context, doer, newOwner *user_model.User, repo *repo_model.Repository) { 255 err := db.WithTx(ctx, func(ctx context.Context) error { 256 return activities_model.CreateRepoTransferNotification(ctx, doer, newOwner, repo) 257 }) 258 if err != nil { 259 log.Error("CreateRepoTransferNotification: %v", err) 260 } 261 }