summaryrefslogtreecommitdiffstats
path: root/api/email_batching.go
diff options
context:
space:
mode:
Diffstat (limited to 'api/email_batching.go')
-rw-r--r--api/email_batching.go252
1 files changed, 0 insertions, 252 deletions
diff --git a/api/email_batching.go b/api/email_batching.go
deleted file mode 100644
index 608d839da..000000000
--- a/api/email_batching.go
+++ /dev/null
@@ -1,252 +0,0 @@
-// Copyright (c) 2016 Mattermost, Inc. All Rights Reserved.
-// See License.txt for license information.
-
-package api
-
-import (
- "database/sql"
- "fmt"
- "html/template"
- "strconv"
- "time"
-
- "github.com/mattermost/platform/model"
- "github.com/mattermost/platform/utils"
-
- l4g "github.com/alecthomas/log4go"
- "github.com/nicksnyder/go-i18n/i18n"
-)
-
-const (
- EMAIL_BATCHING_TASK_NAME = "Email Batching"
-)
-
-var emailBatchingJob *EmailBatchingJob
-
-func InitEmailBatching() {
- if *utils.Cfg.EmailSettings.EnableEmailBatching {
- if emailBatchingJob == nil {
- emailBatchingJob = MakeEmailBatchingJob(*utils.Cfg.EmailSettings.EmailBatchingBufferSize)
- }
-
- // note that we don't support changing EmailBatchingBufferSize without restarting the server
-
- emailBatchingJob.Start()
- }
-}
-
-func AddNotificationEmailToBatch(user *model.User, post *model.Post, team *model.Team) *model.AppError {
- if !*utils.Cfg.EmailSettings.EnableEmailBatching {
- return model.NewLocAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.disabled.app_error", nil, "")
- }
-
- if !emailBatchingJob.Add(user, post, team) {
- l4g.Error(utils.T("api.email_batching.add_notification_email_to_batch.channel_full.app_error"))
- return model.NewLocAppError("AddNotificationEmailToBatch", "api.email_batching.add_notification_email_to_batch.channel_full.app_error", nil, "")
- }
-
- return nil
-}
-
-type batchedNotification struct {
- userId string
- post *model.Post
- teamName string
-}
-
-type EmailBatchingJob struct {
- newNotifications chan *batchedNotification
- pendingNotifications map[string][]*batchedNotification
-}
-
-func MakeEmailBatchingJob(bufferSize int) *EmailBatchingJob {
- return &EmailBatchingJob{
- newNotifications: make(chan *batchedNotification, bufferSize),
- pendingNotifications: make(map[string][]*batchedNotification),
- }
-}
-
-func (job *EmailBatchingJob) Start() {
- if task := model.GetTaskByName(EMAIL_BATCHING_TASK_NAME); task != nil {
- task.Cancel()
- }
-
- l4g.Debug(utils.T("api.email_batching.start.starting"), *utils.Cfg.EmailSettings.EmailBatchingInterval)
- model.CreateRecurringTask(EMAIL_BATCHING_TASK_NAME, job.CheckPendingEmails, time.Duration(*utils.Cfg.EmailSettings.EmailBatchingInterval)*time.Second)
-}
-
-func (job *EmailBatchingJob) Add(user *model.User, post *model.Post, team *model.Team) bool {
- notification := &batchedNotification{
- userId: user.Id,
- post: post,
- teamName: team.Name,
- }
-
- select {
- case job.newNotifications <- notification:
- return true
- default:
- // return false if we couldn't queue the email notification so that we can send an immediate email
- return false
- }
-}
-
-func (job *EmailBatchingJob) CheckPendingEmails() {
- job.handleNewNotifications()
-
- // it's a bit weird to pass the send email function through here, but it makes it so that we can test
- // without actually sending emails
- job.checkPendingNotifications(time.Now(), sendBatchedEmailNotification)
-
- l4g.Debug(utils.T("api.email_batching.check_pending_emails.finished_running"), len(job.pendingNotifications))
-}
-
-func (job *EmailBatchingJob) handleNewNotifications() {
- receiving := true
-
- // read in new notifications to send
- for receiving {
- select {
- case notification := <-job.newNotifications:
- userId := notification.userId
-
- if _, ok := job.pendingNotifications[userId]; !ok {
- job.pendingNotifications[userId] = []*batchedNotification{notification}
- } else {
- job.pendingNotifications[userId] = append(job.pendingNotifications[userId], notification)
- }
- default:
- receiving = false
- }
- }
-}
-
-func (job *EmailBatchingJob) checkPendingNotifications(now time.Time, handler func(string, []*batchedNotification)) {
- // look for users who've acted since pending posts were received
- for userId, notifications := range job.pendingNotifications {
- schan := Srv.Store.Status().Get(userId)
- pchan := Srv.Store.Preference().Get(userId, model.PREFERENCE_CATEGORY_NOTIFICATIONS, model.PREFERENCE_NAME_EMAIL_INTERVAL)
- batchStartTime := notifications[0].post.CreateAt
-
- // check if the user has been active and would've seen any new posts
- if result := <-schan; result.Err != nil {
- l4g.Error(utils.T("api.email_batching.check_pending_emails.status.app_error"), result.Err)
- delete(job.pendingNotifications, userId)
- continue
- } else if status := result.Data.(*model.Status); status.LastActivityAt >= batchStartTime {
- delete(job.pendingNotifications, userId)
- continue
- }
-
- // get how long we need to wait to send notifications to the user
- var interval int64
- if result := <-pchan; result.Err != nil {
- // default to 30 seconds to match the send "immediate" setting
- interval, _ = strconv.ParseInt(model.PREFERENCE_DEFAULT_EMAIL_INTERVAL, 10, 64)
- } else {
- preference := result.Data.(model.Preference)
-
- if value, err := strconv.ParseInt(preference.Value, 10, 64); err != nil {
- interval, _ = strconv.ParseInt(model.PREFERENCE_DEFAULT_EMAIL_INTERVAL, 10, 64)
- } else {
- interval = value
- }
- }
-
- // send the email notification if it's been long enough
- if now.Sub(time.Unix(batchStartTime/1000, 0)) > time.Duration(interval)*time.Second {
- go handler(userId, notifications)
- delete(job.pendingNotifications, userId)
- }
- }
-}
-
-func sendBatchedEmailNotification(userId string, notifications []*batchedNotification) {
- uchan := Srv.Store.User().Get(userId)
- pchan := Srv.Store.Preference().Get(userId, model.PREFERENCE_CATEGORY_DISPLAY_SETTINGS, model.PREFERENCE_NAME_DISPLAY_NAME_FORMAT)
-
- var user *model.User
- if result := <-uchan; result.Err != nil {
- l4g.Warn("api.email_batching.send_batched_email_notification.user.app_error")
- return
- } else {
- user = result.Data.(*model.User)
- }
-
- translateFunc := utils.GetUserTranslations(user.Locale)
-
- var displayNameFormat string
- if result := <-pchan; result.Err != nil && result.Err.DetailedError != sql.ErrNoRows.Error() {
- l4g.Warn("api.email_batching.send_batched_email_notification.preferences.app_error")
- return
- } else if result.Err != nil {
- // no display name format saved, so fall back to default
- displayNameFormat = model.PREFERENCE_DEFAULT_DISPLAY_NAME_FORMAT
- } else {
- displayNameFormat = result.Data.(model.Preference).Value
- }
-
- var contents string
- for _, notification := range notifications {
- template := utils.NewHTMLTemplate("post_batched_post", user.Locale)
-
- contents += renderBatchedPost(template, notification.post, notification.teamName, displayNameFormat, translateFunc)
- }
-
- tm := time.Unix(notifications[0].post.CreateAt/1000, 0)
-
- subject := translateFunc("api.email_batching.send_batched_email_notification.subject", len(notifications), map[string]interface{}{
- "SiteName": utils.Cfg.TeamSettings.SiteName,
- "Year": tm.Year(),
- "Month": translateFunc(tm.Month().String()),
- "Day": tm.Day(),
- })
-
- body := utils.NewHTMLTemplate("post_batched_body", user.Locale)
- body.Props["SiteURL"] = *utils.Cfg.ServiceSettings.SiteURL
- body.Props["Posts"] = template.HTML(contents)
- body.Props["BodyText"] = translateFunc("api.email_batching.send_batched_email_notification.body_text", len(notifications))
-
- if err := utils.SendMail(user.Email, subject, body.Render()); err != nil {
- l4g.Warn(utils.T("api.email_batchings.send_batched_email_notification.send.app_error"), user.Email, err)
- }
-}
-
-func renderBatchedPost(template *utils.HTMLTemplate, post *model.Post, teamName string, displayNameFormat string, translateFunc i18n.TranslateFunc) string {
- schan := Srv.Store.User().Get(post.UserId)
- cchan := Srv.Store.Channel().Get(post.ChannelId, true)
-
- template.Props["Button"] = translateFunc("api.email_batching.render_batched_post.go_to_post")
- template.Props["PostMessage"] = getMessageForNotification(post, translateFunc)
- template.Props["PostLink"] = *utils.Cfg.ServiceSettings.SiteURL + "/" + teamName + "/pl/" + post.Id
-
- tm := time.Unix(post.CreateAt/1000, 0)
- timezone, _ := tm.Zone()
-
- template.Props["Date"] = translateFunc("api.email_batching.render_batched_post.date", map[string]interface{}{
- "Year": tm.Year(),
- "Month": translateFunc(tm.Month().String()),
- "Day": tm.Day(),
- "Hour": tm.Hour(),
- "Minute": fmt.Sprintf("%02d", tm.Minute()),
- "Timezone": timezone,
- })
-
- if result := <-schan; result.Err != nil {
- l4g.Warn(utils.T("api.email_batching.render_batched_post.sender.app_error"))
- return ""
- } else {
- template.Props["SenderName"] = result.Data.(*model.User).GetDisplayNameForPreference(displayNameFormat)
- }
-
- if result := <-cchan; result.Err != nil {
- l4g.Warn(utils.T("api.email_batching.render_batched_post.channel.app_error"))
- return ""
- } else if channel := result.Data.(*model.Channel); channel.Type == model.CHANNEL_DIRECT {
- template.Props["ChannelName"] = translateFunc("api.email_batching.render_batched_post.direct_message")
- } else {
- template.Props["ChannelName"] = channel.DisplayName
- }
-
- return template.Render()
-}