gitea源码

notify.go 8.2KB

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