func fireAndForgetNotifications(post *model.Post, teamId, siteURL string) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team uchan := Srv.Store.User().GetProfiles(teamId) echan := Srv.Store.Channel().GetMembers(post.ChannelId) cchan := Srv.Store.Channel().Get(post.ChannelId) tchan := Srv.Store.Team().Get(teamId) var channel *model.Channel var channelName string var bodyText string var subjectText string if result := <-cchan; result.Err != nil { l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err) return } else { channel = result.Data.(*model.Channel) if channel.Type == model.CHANNEL_DIRECT { bodyText = "You have one new message." subjectText = "New Direct Message" } else { bodyText = "You have one new mention." subjectText = "New Mention" channelName = channel.DisplayName } } var mentionedUsers []string if result := <-uchan; result.Err != nil { l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err) return } else { profileMap := result.Data.(map[string]*model.User) if _, ok := profileMap[post.UserId]; !ok { l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel, only keep those profiles if eResult := <-echan; eResult.Err != nil { l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message) return } else { tempProfileMap := make(map[string]*model.User) members := eResult.Data.([]model.ChannelMember) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap } // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) } // Add @all to keywords if user has them turned on if profile.NotifyProps["all"] == "true" { keywordMap["@all"] = append(keywordMap["@all"], profile.Id) } // Add @channel to keywords if user has them turned on if profile.NotifyProps["channel"] == "true" { keywordMap["@channel"] = append(keywordMap["@channel"], profile.Id) } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.FieldsFunc(post.Message, splitF) for _, word := range splitMessage { // Non-case-sensitive check for regular keys userIds1, keyMatch := keywordMap[strings.ToLower(word)] // Case-sensitive check for first name userIds2, firstNameMatch := keywordMap[word] userIds := append(userIds1, userIds2...) // If one of the non-case-senstive keys or the first name matches the word // then we add en entry to the sendEmail map if keyMatch || firstNameMatch { for _, userId := range userIds { if post.UserId == userId { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } } for id := range toEmailMap { fireAndForgetMentionUpdate(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } var teamDisplayName string var teamURL string if result := <-tchan; result.Err != nil { l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) return } else { teamDisplayName = result.Data.(*model.Team).DisplayName teamURL = siteURL + "/" + result.Data.(*model.Team).Name } // Build and send the emails location, _ := time.LoadLocation("UTC") tm := time.Unix(post.CreateAt/1000, 0).In(location) subjectPage := NewServerTemplatePage("post_subject") subjectPage.Props["SiteURL"] = siteURL subjectPage.Props["TeamDisplayName"] = teamDisplayName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year()) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } bodyPage := NewServerTemplatePage("post_body") bodyPage.Props["SiteURL"] = siteURL bodyPage.Props["Nickname"] = profileMap[id].FirstName bodyPage.Props["TeamDisplayName"] = teamDisplayName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour()) bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute()) bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name // attempt to fill in a message body if the post doesn't have any text if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 { // extract the filenames from their paths and determine what type of files are attached filenames := make([]string, len(post.Filenames)) onlyImages := true for i, filename := range post.Filenames { var err error if filenames[i], err = url.QueryUnescape(filepath.Base(filename)); err != nil { // this should never error since filepath was escaped using url.QueryEscape filenames[i] = filepath.Base(filename) } ext := filepath.Ext(filename) onlyImages = onlyImages && model.IsFileExtImage(ext) } filenamesString := strings.Join(filenames, ", ") var attachmentPrefix string if onlyImages { attachmentPrefix = "Image" } else { attachmentPrefix = "File" } if len(post.Filenames) > 1 { attachmentPrefix += "s" } bodyPage.Props["PostMessage"] = fmt.Sprintf("%s: %s sent", attachmentPrefix, filenamesString) } if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" { alreadySeen[session.DeviceId] = session.DeviceId utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1) } } } } } } } message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) if len(post.Filenames) != 0 { message.Add("otherFile", "true") for _, filename := range post.Filenames { ext := filepath.Ext(filename) if model.IsFileExtImage(ext) { message.Add("image", "true") break } } } if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } PublishAndForget(message) }() }
func fireAndForgetNotifications(post *model.Post, teamId, teamUrl string) { go func() { // Get a list of user names (to be used as keywords) and ids for the given team uchan := Srv.Store.User().GetProfiles(teamId) echan := Srv.Store.Channel().GetMembers(post.ChannelId) cchan := Srv.Store.Channel().Get(post.ChannelId) tchan := Srv.Store.Team().Get(teamId) var channel *model.Channel var channelName string var bodyText string var subjectText string if result := <-cchan; result.Err != nil { l4g.Error("Failed to retrieve channel channel_id=%v, err=%v", post.ChannelId, result.Err) return } else { channel = result.Data.(*model.Channel) if channel.Type == model.CHANNEL_DIRECT { bodyText = "You have one new message." subjectText = "New Direct Message" } else { bodyText = "You have one new mention." subjectText = "New Mention" channelName = channel.DisplayName } } var mentionedUsers []string if result := <-uchan; result.Err != nil { l4g.Error("Failed to retrieve user profiles team_id=%v, err=%v", teamId, result.Err) return } else { profileMap := result.Data.(map[string]*model.User) if _, ok := profileMap[post.UserId]; !ok { l4g.Error("Post user_id not returned by GetProfiles user_id=%v", post.UserId) return } senderName := profileMap[post.UserId].Username toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT { var otherUserId string if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId { otherUserId = userIds[1] channelName = profileMap[userIds[1]].Username } else { otherUserId = userIds[0] channelName = profileMap[userIds[0]].Username } otherUser := profileMap[otherUserId] sendEmail := true if _, ok := otherUser.NotifyProps["email"]; ok && otherUser.NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (otherUser.IsOffline() || otherUser.IsAway()) { toEmailMap[otherUserId] = true } } else { // Find out who is a member of the channel only keep those profiles if eResult := <-echan; eResult.Err != nil { l4g.Error("Failed to get channel members channel_id=%v err=%v", post.ChannelId, eResult.Err.Message) return } else { tempProfileMap := make(map[string]*model.User) members := eResult.Data.([]model.ChannelMember) for _, member := range members { tempProfileMap[member.UserId] = profileMap[member.UserId] } profileMap = tempProfileMap } // Build map for keywords keywordMap := make(map[string][]string) for _, profile := range profileMap { if len(profile.NotifyProps["mention_keys"]) > 0 { // Add all the user's mention keys splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",") for _, k := range splitKeys { keywordMap[k] = append(keywordMap[strings.ToLower(k)], profile.Id) } // If turned on, add the user's case sensitive first name if profile.NotifyProps["first_name"] == "true" { splitName := strings.Split(profile.FullName, " ") if len(splitName) > 0 && splitName[0] != "" { keywordMap[splitName[0]] = append(keywordMap[splitName[0]], profile.Id) } } } } // Build a map as a list of unique user_ids that are mentioned in this post splitF := func(c rune) bool { return model.SplitRunes[c] } splitMessage := strings.FieldsFunc(strings.Replace(post.Message, "<br>", " ", -1), splitF) for _, word := range splitMessage { // Non-case-sensitive check for regular keys userIds1, keyMatch := keywordMap[strings.ToLower(word)] // Case-sensitive check for first name userIds2, firstNameMatch := keywordMap[word] userIds := append(userIds1, userIds2...) // If one of the non-case-senstive keys or the first name matches the word // then we add en entry to the sendEmail map if keyMatch || firstNameMatch { for _, userId := range userIds { if post.UserId == userId { continue } sendEmail := true if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" { sendEmail = false } if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) { toEmailMap[userId] = true } else { toEmailMap[userId] = false } } } } for id, _ := range toEmailMap { fireAndForgetMentionUpdate(post.ChannelId, id) } } if len(toEmailMap) != 0 { mentionedUsers = make([]string, 0, len(toEmailMap)) for k := range toEmailMap { mentionedUsers = append(mentionedUsers, k) } var teamName string if result := <-tchan; result.Err != nil { l4g.Error("Failed to retrieve team team_id=%v, err=%v", teamId, result.Err) return } else { teamName = result.Data.(*model.Team).Name } // Build and send the emails location, _ := time.LoadLocation("UTC") tm := time.Unix(post.CreateAt/1000, 0).In(location) subjectPage := NewServerTemplatePage("post_subject", teamUrl) subjectPage.Props["TeamName"] = teamName subjectPage.Props["SubjectText"] = subjectText subjectPage.Props["Month"] = tm.Month().String()[:3] subjectPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) subjectPage.Props["Year"] = fmt.Sprintf("%d", tm.Year()) for id, doSend := range toEmailMap { if !doSend { continue } // skip if inactive if profileMap[id].DeleteAt > 0 { continue } firstName := strings.Split(profileMap[id].FullName, " ")[0] bodyPage := NewServerTemplatePage("post_body", teamUrl) bodyPage.Props["FullName"] = firstName bodyPage.Props["TeamName"] = teamName bodyPage.Props["ChannelName"] = channelName bodyPage.Props["BodyText"] = bodyText bodyPage.Props["SenderName"] = senderName bodyPage.Props["Hour"] = fmt.Sprintf("%02d", tm.Hour()) bodyPage.Props["Minute"] = fmt.Sprintf("%02d", tm.Minute()) bodyPage.Props["Month"] = tm.Month().String()[:3] bodyPage.Props["Day"] = fmt.Sprintf("%d", tm.Day()) bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message) bodyPage.Props["TeamLink"] = teamUrl + "/channels/" + channel.Name if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { l4g.Error("Failed to send mention email successfully email=%v err=%v", profileMap[id].Email, err) } if len(utils.Cfg.EmailSettings.ApplePushServer) > 0 { sessionChan := Srv.Store.Session().GetSessions(id) if result := <-sessionChan; result.Err != nil { l4g.Error("Failed to retrieve sessions in notifications id=%v, err=%v", id, result.Err) } else { sessions := result.Data.([]*model.Session) alreadySeen := make(map[string]string) for _, session := range sessions { if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" { alreadySeen[session.DeviceId] = session.DeviceId utils.FireAndForgetSendAppleNotify(session.DeviceId, subjectPage.Render(), 1) } } } } } } } message := model.NewMessage(teamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message.Add("post", post.ToJson()) if len(mentionedUsers) != 0 { message.Add("mentions", model.ArrayToJson(mentionedUsers)) } store.PublishAndForget(message) }() }