Changed sendNotificationsAndForget to be synchronous and called as a go func

Этот коммит содержится в:
hmhealey
2016-02-04 11:45:19 -05:00
родитель f935be121c
Коммит 21318b7505

Просмотреть файл

@@ -267,7 +267,7 @@ func handlePostEventsAndForget(c *Context, post *model.Post, triggerWebhooks boo
members = result.Data.([]model.ChannelMember) members = result.Data.([]model.ChannelMember)
} }
sendNotificationsAndForget(c, post, team, channel, profiles, members) go sendNotifications(c, post, team, channel, profiles, members)
go checkForOutOfChannelMentions(c, post, channel, profiles, members) go checkForOutOfChannelMentions(c, post, channel, profiles, members)
var user *model.User var user *model.User
@@ -433,293 +433,290 @@ func handleWebhookEventsAndForget(c *Context, post *model.Post, team *model.Team
} }
func sendNotificationsAndForget(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) { func sendNotifications(c *Context, post *model.Post, team *model.Team, channel *model.Channel, profileMap map[string]*model.User, members []model.ChannelMember) {
var channelName string
var bodyText string
var subjectText string
go func() { var mentionedUsers []string
var channelName string
var bodyText string
var subjectText string
var mentionedUsers []string if _, ok := profileMap[post.UserId]; !ok {
l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
return
}
senderName := profileMap[post.UserId].Username
if _, ok := profileMap[post.UserId]; !ok { toEmailMap := make(map[string]bool)
l4g.Error(utils.T("api.post.send_notifications_and_forget.user_id.error"), post.UserId)
return
}
senderName := profileMap[post.UserId].Username
toEmailMap := make(map[string]bool) if channel.Type == model.CHANNEL_DIRECT {
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
}
var otherUserId string
if userIds := strings.Split(channel.Name, "__"); userIds[0] == post.UserId {
otherUserId = userIds[1]
channelName = profileMap[userIds[1]].Username
} else { } else {
// Find out who is a member of the channel, only keep those profiles otherUserId = userIds[0]
tempProfileMap := make(map[string]*model.User) channelName = profileMap[userIds[0]].Username
for _, member := range members { }
tempProfileMap[member.UserId] = profileMap[member.UserId]
}
profileMap = tempProfileMap 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
}
// Build map for keywords } else {
keywordMap := make(map[string][]string) // Find out who is a member of the channel, only keep those profiles
for _, profile := range profileMap { tempProfileMap := make(map[string]*model.User)
if len(profile.NotifyProps["mention_keys"]) > 0 { for _, member := range members {
tempProfileMap[member.UserId] = profileMap[member.UserId]
}
// Add all the user's mention keys profileMap = tempProfileMap
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 // Build map for keywords
if profile.NotifyProps["first_name"] == "true" { keywordMap := make(map[string][]string)
keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id) for _, profile := range profileMap {
} if len(profile.NotifyProps["mention_keys"]) > 0 {
// Add @all to keywords if user has them turned on // Add all the user's mention keys
// if profile.NotifyProps["all"] == "true" { splitKeys := strings.Split(profile.NotifyProps["mention_keys"], ",")
// keywordMap["@all"] = append(keywordMap["@all"], profile.Id) for _, k := range splitKeys {
// } keywordMap[k] = append(keywordMap[strings.ToLower(k)], 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 // If turned on, add the user's case sensitive first name
splitF := func(c rune) bool { if profile.NotifyProps["first_name"] == "true" {
return model.SplitRunes[c] keywordMap[profile.FirstName] = append(keywordMap[profile.FirstName], profile.Id)
}
splitMessage := strings.Fields(post.Message)
for _, word := range splitMessage {
var userIds []string
// Non-case-sensitive check for regular keys
if ids, match := keywordMap[strings.ToLower(word)]; match {
userIds = append(userIds, ids...)
}
// Case-sensitive check for first name
if ids, match := keywordMap[word]; match {
userIds = append(userIds, ids...)
}
if len(userIds) == 0 {
// No matches were found with the string split just on whitespace so try further splitting
// the message on punctuation
splitWords := strings.FieldsFunc(word, splitF)
for _, splitWord := range splitWords {
// Non-case-sensitive check for regular keys
if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
userIds = append(userIds, ids...)
}
// Case-sensitive check for first name
if ids, match := keywordMap[splitWord]; match {
userIds = append(userIds, ids...)
}
}
}
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 { // Add @all to keywords if user has them turned on
updateMentionCountAndForget(post.ChannelId, id) // 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)
} }
} }
if len(toEmailMap) != 0 { // Build a map as a list of unique user_ids that are mentioned in this post
mentionedUsers = make([]string, 0, len(toEmailMap)) splitF := func(c rune) bool {
for k := range toEmailMap { return model.SplitRunes[c]
mentionedUsers = append(mentionedUsers, k) }
splitMessage := strings.Fields(post.Message)
for _, word := range splitMessage {
var userIds []string
// Non-case-sensitive check for regular keys
if ids, match := keywordMap[strings.ToLower(word)]; match {
userIds = append(userIds, ids...)
} }
teamURL := c.GetSiteURL() + "/" + team.Name // Case-sensitive check for first name
if ids, match := keywordMap[word]; match {
userIds = append(userIds, ids...)
}
// Build and send the emails if len(userIds) == 0 {
tm := time.Unix(post.CreateAt/1000, 0) // No matches were found with the string split just on whitespace so try further splitting
// the message on punctuation
splitWords := strings.FieldsFunc(word, splitF)
for id, doSend := range toEmailMap { for _, splitWord := range splitWords {
// Non-case-sensitive check for regular keys
if ids, match := keywordMap[strings.ToLower(splitWord)]; match {
userIds = append(userIds, ids...)
}
if !doSend { // Case-sensitive check for first name
if ids, match := keywordMap[splitWord]; match {
userIds = append(userIds, ids...)
}
}
}
for _, userId := range userIds {
if post.UserId == userId {
continue continue
} }
sendEmail := true
// skip if inactive if _, ok := profileMap[userId].NotifyProps["email"]; ok && profileMap[userId].NotifyProps["email"] == "false" {
if profileMap[id].DeleteAt > 0 { sendEmail = false
continue
} }
if sendEmail && (profileMap[userId].IsAway() || profileMap[userId].IsOffline()) {
userLocale := utils.GetUserTranslations(profileMap[id].Locale) toEmailMap[userId] = true
if channel.Type == model.CHANNEL_DIRECT {
bodyText = userLocale("api.post.send_notifications_and_forget.message_body")
subjectText = userLocale("api.post.send_notifications_and_forget.message_subject")
} else { } else {
bodyText = userLocale("api.post.send_notifications_and_forget.mention_body") toEmailMap[userId] = false
subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject")
channelName = channel.DisplayName
} }
}
}
month := userLocale(tm.Month().String()) for id := range toEmailMap {
day := fmt.Sprintf("%d", tm.Day()) updateMentionCountAndForget(post.ChannelId, id)
year := fmt.Sprintf("%d", tm.Year()) }
zone, _ := tm.Zone() }
subjectPage := NewServerTemplatePage("post_subject", c.Locale) if len(toEmailMap) != 0 {
subjectPage.Props["Subject"] = userLocale("api.templates.post_subject", mentionedUsers = make([]string, 0, len(toEmailMap))
map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName, for k := range toEmailMap {
"Month": month[:3], "Day": day, "Year": year}) mentionedUsers = append(mentionedUsers, k)
}
bodyPage := NewServerTemplatePage("post_body", c.Locale) teamURL := c.GetSiteURL() + "/" + team.Name
bodyPage.Props["SiteURL"] = c.GetSiteURL()
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
bodyPage.Props["BodyText"] = bodyText
bodyPage.Props["Button"] = userLocale("api.templates.post_body.button")
bodyPage.Html["Info"] = template.HTML(userLocale("api.templates.post_body.info",
map[string]interface{}{"ChannelName": channelName, "SenderName": senderName,
"Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()),
"TimeZone": zone, "Month": month, "Day": day}))
// attempt to fill in a message body if the post doesn't have any text // Build and send the emails
if len(strings.TrimSpace(bodyPage.Props["PostMessage"])) == 0 && len(post.Filenames) > 0 { tm := time.Unix(post.CreateAt/1000, 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) for id, doSend := range toEmailMap {
onlyImages = onlyImages && model.IsFileExtImage(ext)
}
filenamesString := strings.Join(filenames, ", ")
var attachmentPrefix string if !doSend {
if onlyImages { continue
attachmentPrefix = "Image" }
} else {
attachmentPrefix = "File" // skip if inactive
} if profileMap[id].DeleteAt > 0 {
if len(post.Filenames) > 1 { continue
attachmentPrefix += "s" }
userLocale := utils.GetUserTranslations(profileMap[id].Locale)
if channel.Type == model.CHANNEL_DIRECT {
bodyText = userLocale("api.post.send_notifications_and_forget.message_body")
subjectText = userLocale("api.post.send_notifications_and_forget.message_subject")
} else {
bodyText = userLocale("api.post.send_notifications_and_forget.mention_body")
subjectText = userLocale("api.post.send_notifications_and_forget.mention_subject")
channelName = channel.DisplayName
}
month := userLocale(tm.Month().String())
day := fmt.Sprintf("%d", tm.Day())
year := fmt.Sprintf("%d", tm.Year())
zone, _ := tm.Zone()
subjectPage := NewServerTemplatePage("post_subject", c.Locale)
subjectPage.Props["Subject"] = userLocale("api.templates.post_subject",
map[string]interface{}{"SubjectText": subjectText, "TeamDisplayName": team.DisplayName,
"Month": month[:3], "Day": day, "Year": year})
bodyPage := NewServerTemplatePage("post_body", c.Locale)
bodyPage.Props["SiteURL"] = c.GetSiteURL()
bodyPage.Props["PostMessage"] = model.ClearMentionTags(post.Message)
bodyPage.Props["TeamLink"] = teamURL + "/channels/" + channel.Name
bodyPage.Props["BodyText"] = bodyText
bodyPage.Props["Button"] = userLocale("api.templates.post_body.button")
bodyPage.Html["Info"] = template.HTML(userLocale("api.templates.post_body.info",
map[string]interface{}{"ChannelName": channelName, "SenderName": senderName,
"Hour": fmt.Sprintf("%02d", tm.Hour()), "Minute": fmt.Sprintf("%02d", tm.Minute()),
"TimeZone": zone, "Month": month, "Day": day}))
// 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)
} }
bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent", ext := filepath.Ext(filename)
map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString}) 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"
} }
if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil { bodyPage.Props["PostMessage"] = userLocale("api.post.send_notifications_and_forget.sent",
l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err) map[string]interface{}{"Prefix": attachmentPrefix, "Filenames": filenamesString})
} }
if *utils.Cfg.EmailSettings.SendPushNotifications { if err := utils.SendMail(profileMap[id].Email, subjectPage.Render(), bodyPage.Render()); err != nil {
sessionChan := Srv.Store.Session().GetSessions(id) l4g.Error(utils.T("api.post.send_notifications_and_forget.send.error"), profileMap[id].Email, err)
if result := <-sessionChan; result.Err != nil { }
l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err)
} else {
sessions := result.Data.([]*model.Session)
alreadySeen := make(map[string]string)
for _, session := range sessions { if *utils.Cfg.EmailSettings.SendPushNotifications {
if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" && sessionChan := Srv.Store.Session().GetSessions(id)
(strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) { if result := <-sessionChan; result.Err != nil {
alreadySeen[session.DeviceId] = session.DeviceId l4g.Error(utils.T("api.post.send_notifications_and_forget.sessions.error"), id, result.Err)
} else {
sessions := result.Data.([]*model.Session)
alreadySeen := make(map[string]string)
msg := model.PushNotification{} for _, session := range sessions {
msg.Badge = 1 if len(session.DeviceId) > 0 && alreadySeen[session.DeviceId] == "" &&
msg.ServerId = utils.CfgDiagnosticId (strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") || strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")) {
alreadySeen[session.DeviceId] = session.DeviceId
if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") { msg := model.PushNotification{}
msg.Platform = model.PUSH_NOTIFY_APPLE msg.Badge = 1
msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") msg.ServerId = utils.CfgDiagnosticId
} else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") {
msg.Platform = model.PUSH_NOTIFY_ANDROID
msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
}
if channel.Type == model.CHANNEL_DIRECT { if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":") {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message") msg.Platform = model.PUSH_NOTIFY_APPLE
} else { msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_APPLE+":")
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName } else if strings.HasPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":") {
} msg.Platform = model.PUSH_NOTIFY_ANDROID
msg.DeviceId = strings.TrimPrefix(session.DeviceId, model.PUSH_NOTIFY_ANDROID+":")
}
httpClient := http.Client{} if channel.Type == model.CHANNEL_DIRECT {
request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson())) msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_message")
} else {
msg.Message = senderName + userLocale("api.post.send_notifications_and_forget.push_mention") + channelName
}
l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message) httpClient := http.Client{}
if _, err := httpClient.Do(request); err != nil { request, _ := http.NewRequest("POST", *utils.Cfg.EmailSettings.PushNotificationServer+"/api/v1/send_push", strings.NewReader(msg.ToJson()))
l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err)
} l4g.Debug(utils.T("api.post.send_notifications_and_forget.push_notification.debug"), msg.DeviceId, msg.Message)
if _, err := httpClient.Do(request); err != nil {
l4g.Error(utils.T("api.post.send_notifications_and_forget.push_notification.error"), id, err)
} }
} }
} }
} }
} }
} }
}
message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED) message := model.NewMessage(c.Session.TeamId, post.ChannelId, post.UserId, model.ACTION_POSTED)
message.Add("post", post.ToJson()) message.Add("post", post.ToJson())
message.Add("channel_type", channel.Type) message.Add("channel_type", channel.Type)
if len(post.Filenames) != 0 { if len(post.Filenames) != 0 {
message.Add("otherFile", "true") message.Add("otherFile", "true")
for _, filename := range post.Filenames { for _, filename := range post.Filenames {
ext := filepath.Ext(filename) ext := filepath.Ext(filename)
if model.IsFileExtImage(ext) { if model.IsFileExtImage(ext) {
message.Add("image", "true") message.Add("image", "true")
break break
}
} }
} }
}
if len(mentionedUsers) != 0 { if len(mentionedUsers) != 0 {
message.Add("mentions", model.ArrayToJson(mentionedUsers)) message.Add("mentions", model.ArrayToJson(mentionedUsers))
} }
PublishAndForget(message) PublishAndForget(message)
}()
} }
func updateMentionCountAndForget(channelId, userId string) { func updateMentionCountAndForget(channelId, userId string) {