packages feed

vigilance-0.1.0.0: src/Utils/Vigilance/Notifiers/Email.hs

{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes       #-}
{-# LANGUAGE TemplateHaskell   #-}
module Utils.Vigilance.Notifiers.Email ( notify
                                       , generateEmails
                                       , EmailContext(..)
                                       , HasEmailContext(..)
                                       , NotificationMail(..)
                                       , HasNotificationMail(..)
                                       , Address(..)) where

import ClassyPrelude
import Control.Lens hiding (from)
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Map.Lazy as M
import Text.InterpolatedString.Perl6 (qc)
import Network.Mail.Mime ( Address(..)
                         , emptyMail
                         , Part(..)
                         , renderSendMail
                         , Encoding( QuotedPrintableText )
                         , Mail(..))

import Utils.Vigilance.Logger ( pushLog
                              , renameLogCtx )
import Utils.Vigilance.Types
import Utils.Vigilance.Utils (concatMapM)

data EmailContext = EmailContext { _fromEmail :: EmailAddress } deriving (Show, Eq)

makeClassy ''EmailContext

data NotificationMail = NotificationMail { _nmWatches :: [EWatch]
                                         , _nmMail    :: Mail }

makeClassy ''NotificationMail

notify :: EmailContext -> EmailNotifier
notify ctx = EmailNotifier notifierBody
  where notifierBody watches = renameLogCtx "Email Notifier" $ concatMapM renderSendMail' mails
          where mails = generateEmails watches ctx

renderSendMail' :: NotificationMail -> LogCtxT IO [FailedNotification]
renderSendMail' (NotificationMail ws mail) = do
  pushLog [qc|Sending email notification to {emailList}|]
  lift $ handleAny buildFailures $ renderSendMail mail >> return []
  where emailList = intercalate ", " emails
        emails    = map addressEmail . mailTo $ mail
        addrs     = map EmailAddress emails
        buildFailures e = return [ FailedNotification w n (FailedByException $ show e) 0
                                 | w <- ws
                                 , n@(EmailNotification addr) <- w ^. watchNotifications
                                 , addr `elem` addrs ]

generateEmails :: [EWatch] -> EmailContext -> [NotificationMail]
generateEmails watches ctx = M.elems $ M.mapWithKey createMail groupedByEmail -- ehhhhh
  where groupedByEmail = M.fromListWith mappend $ concatMap watchesWithEmails watches
        createMail toAddr ws = NotificationMail ws mail
          where mail = (emptyMail from) { mailTo      = [e2a toAddr]
                                        , mailHeaders = [("Subject", subject)]
                                        , mailParts   = [[mailPart ws]] }
                subject = [qc|Vigilence notification {watchCount} activated|] :: Text
                watchCount = length ws
        from :: Address
        from = ctx ^. fromEmail . to e2a

mailPart :: [EWatch] -> Part
mailPart ws =  Part "text/plain; charset=utf-8" QuotedPrintableText Nothing [] (bodyLBS ws)

bodyLBS :: [EWatch] -> LBS.ByteString
bodyLBS ws = [qc|
The following watches were triggered:

{watchSummary}

Sincerely,
Vigilence
  |]
  where watchSummary = mconcat $ map summarize ws
        summarize w  = [qc|- {name} ({interval})|] :: LBS.ByteString
          where interval = w ^. watchInterval
                name     = w ^. watchName . unWatchName . to unpack

watchesWithEmails :: EWatch -> [(EmailAddress, [EWatch])]
watchesWithEmails w = zip emails (repeat [w] :: [[EWatch]])
  where emails = mapMaybe extractEmail $ w ^. watchNotifications
        extractEmail (EmailNotification e) = Just e
        extractEmail _                     = Nothing

e2a :: EmailAddress -> Address
e2a = Address Nothing . view unEmailAddress