diff --git a/pkg/models/listeners.go b/pkg/models/listeners.go index 1751e4c51..390d2724d 100644 --- a/pkg/models/listeners.go +++ b/pkg/models/listeners.go @@ -97,6 +97,33 @@ func (s *SendTaskCommentNotification) Handle(msg *message.Message) (err error) { sess := db.NewSession() defer sess.Close() + mentionedUsers, err := FindMentionedUsersInText(sess, event.Comment.Comment) + if err != nil { + return err + } + + for _, user := range mentionedUsers { + can, _, err := event.Task.CanRead(sess, user) + if err != nil { + return err + } + + if !can { + continue + } + + n := &TaskCommentNotification{ + Doer: event.Doer, + Task: event.Task, + Comment: event.Comment, + Mentioned: true, + } + err = notifications.Notify(user, n) + if err != nil { + return err + } + } + subscribers, err := getSubscribersForEntity(sess, SubscriptionEntityTask, event.Task.ID) if err != nil { return err @@ -109,6 +136,10 @@ func (s *SendTaskCommentNotification) Handle(msg *message.Message) (err error) { continue } + if _, has := mentionedUsers[subscriber.UserID]; has { + continue + } + n := &TaskCommentNotification{ Doer: event.Doer, Task: event.Task, diff --git a/pkg/models/mentions.go b/pkg/models/mentions.go index f91c468c1..0aecb7beb 100644 --- a/pkg/models/mentions.go +++ b/pkg/models/mentions.go @@ -23,7 +23,7 @@ import ( "xorm.io/xorm" ) -func FindMentionedUsersInText(s *xorm.Session, text string) (users []*user.User, err error) { +func FindMentionedUsersInText(s *xorm.Session, text string) (users map[int64]*user.User, err error) { reg := regexp.MustCompile(`@\w+`) matches := reg.FindAllString(text, -1) if matches == nil { diff --git a/pkg/models/mentions_test.go b/pkg/models/mentions_test.go index 508ec6359..2f6796b87 100644 --- a/pkg/models/mentions_test.go +++ b/pkg/models/mentions_test.go @@ -77,9 +77,10 @@ func TestFindMentionedUsersInText(t *testing.T) { t.Errorf("FindMentionedUsersInText() error = %v, wantErr %v", err, tt.wantErr) return } - for i, u := range gotUsers { - if u.ID != tt.wantUsers[i].ID { - t.Errorf("wanted user %d, got %d at position %d", tt.wantUsers[i].ID, u.ID, i) + for _, u := range tt.wantUsers { + _, has := gotUsers[u.ID] + if !has { + t.Errorf("wanted user %d but did not get it", u.ID) } } }) diff --git a/pkg/models/notifications.go b/pkg/models/notifications.go index 77cb8fb61..a1f910e1e 100644 --- a/pkg/models/notifications.go +++ b/pkg/models/notifications.go @@ -58,17 +58,25 @@ func (n *ReminderDueNotification) Name() string { // TaskCommentNotification represents a TaskCommentNotification notification type TaskCommentNotification struct { - Doer *user.User `json:"doer"` - Task *Task `json:"task"` - Comment *TaskComment `json:"comment"` + Doer *user.User `json:"doer"` + Task *Task `json:"task"` + Comment *TaskComment `json:"comment"` + Mentioned bool `json:"mentioned"` } // ToMail returns the mail notification for TaskCommentNotification func (n *TaskCommentNotification) ToMail() *notifications.Mail { mail := notifications.NewMail(). - From(n.Doer.GetNameAndFromEmail()). - Subject("Re: " + n.Task.Title) + From(n.Doer.GetNameAndFromEmail()) + + subject := "Re: " + n.Task.Title + if n.Mentioned { + subject = n.Doer.GetName() + " mentioned you in a comment at " + n.Task.Title + mail.Line(n.Doer.GetName() + " mentioned you in a comment:") + } + + mail.Subject(subject) lines := bufio.NewScanner(strings.NewReader(n.Comment.Comment)) for lines.Scan() { diff --git a/pkg/user/user.go b/pkg/user/user.go index b0f612c58..5ade58ba0 100644 --- a/pkg/user/user.go +++ b/pkg/user/user.go @@ -198,12 +198,12 @@ func GetUserByUsername(s *xorm.Session, username string) (user *User, err error) } // GetUsersByUsername returns a slice of users with the provided usernames -func GetUsersByUsername(s *xorm.Session, usernames []string, withEmails bool) (users []*User, err error) { +func GetUsersByUsername(s *xorm.Session, usernames []string, withEmails bool) (users map[int64]*User, err error) { if len(usernames) == 0 { return } - users = []*User{} + users = make(map[int64]*User) err = s.In("username", usernames).Find(&users) if err != nil { return