hailgun-0.1.0.0: src/Send.hs
module Main where
import Mail.Hailgun
import Control.Applicative ((<$>))
import qualified Data.ByteString.Char8 as BC
import Data.Configurator (load, require, Worth(..))
import qualified Data.List as DL
import qualified Data.Text as T
import System.Console.GetOpt (getOpt, OptDescr(..), ArgDescr(..), ArgOrder(..), usageInfo)
import System.Environment (getArgs)
import System.Exit (exitFailure)
-- The purpose of this module is to provide a way to send emails to the Mailgun service using the
-- command line. This is mainly for the purposes of testing initially. Using this executable should
-- be the basis for our integration tests. It should also be the starting point for everybody that
-- wishes to test this library out.
data Flag
= Help
| From { email :: UnverifiedEmailAddress }
| To { email :: UnverifiedEmailAddress }
| Subject { subject :: MessageSubject }
| TextMessage { textFilePath :: FilePath }
| HTMLMessage { htmlFilePath :: FilePath }
deriving (Eq, Show)
options :: [OptDescr Flag]
options =
[ Option "h" ["help"] (NoArg Help) "displays this help message"
, Option "f" ["from"] (ReqArg fromP "me@test.test") "You are required to provide sender of this email."
, Option "t" ["to"] (ReqArg toP "them@test.test") "You will need to provide atleast one person that you wish to send the email to."
-- TODO Confirm that this is required.
, Option "s" ["subject"] (ReqArg Subject "subject") "You need to send an email subject."
, Option "x" ["text"] (ReqArg TextMessage "email.text") "You need to provide a text email file at a minimum."
, Option "m" ["html"] (ReqArg HTMLMessage "email.html") "You can provide a HTML version of the email to send."
]
where
fromP = From . BC.pack
toP = To . BC.pack
hailgunConfFile :: FilePath
hailgunConfFile = "hailgun.send.conf"
mailgunDomainLabel = T.pack "mailgun-domain"
mailgunApiKeyLabel = T.pack "mailgun-api-key"
loadHailgunContext :: FilePath -> IO HailgunContext
loadHailgunContext configFile = do
hailgunConf <- load [Required configFile]
domain <- require hailgunConf mailgunDomainLabel
apiKey <- require hailgunConf mailgunApiKeyLabel
return HailgunContext
{ hailgunDomain = domain
, hailgunApiKey = apiKey
}
handleSend :: [Flag] -> MessageContent -> Either HailgunErrorMessage HailgunMessage
handleSend flags emailBody =
case (unverifiedFrom, subjects) of
([from], [subject]) -> hailgunMessage subject emailBody from simpleRecipients
([], []) -> fail "You need to provide both a from address and a subject to send an email."
(_ , []) -> fail "You have more than one from address and only one is allowed"
([], _ ) -> fail "You have more than one subject and only one is allowed"
_ -> fail "You have too many from adresses and subjects, you should only have one of each."
where
unverifiedTo = fmap email . filter isTo $ flags
unverifiedFrom = fmap email . filter isFrom $ flags
subjects = fmap subject . filter isSubject $ flags
simpleRecipients = emptyMessageRecipients { recipientsTo = unverifiedTo }
isSubject :: Flag -> Bool
isSubject (Subject _) = True
isSubject _ = False
isTo :: Flag -> Bool
isTo (To _) = True
isTo _ = False
isFrom :: Flag -> Bool
isFrom (From _) = True
isFrom _ = False
isTextMessage :: Flag -> Bool
isTextMessage (TextMessage _) = True
isTextMessage _ = False
isHtmlMessage :: Flag -> Bool
isHtmlMessage (HTMLMessage _) = True
isHtmlMessage _ = False
sendMessage :: HailgunMessage -> IO ()
sendMessage message = do
hailgunContext <- loadHailgunContext hailgunConfFile
response <- sendEmail hailgunContext message
case response of
Left error -> putStrLn $ "Failed to send email: " ++ herMessage error
Right result -> do
putStrLn "Sent Email!"
putStrLn $ "Id: " ++ hsrId result
putStrLn $ "Message: " ++ hsrMessage result
usageMessage = "Send emails using the Mailgun api."
main :: IO ()
main = do
arguments <- getArgs
case getOpt Permute options arguments of
(flags, _, []) -> if Help `elem` flags
then printUsage
else do
potentialEmailBody <- prepareEmailBody flags
case potentialEmailBody of
Nothing -> putStrLn "At the very least you must provide a text file to send as the email body."
(Just messageContent) -> case handleSend flags messageContent of
Left error -> putStrLn $ "Error generating mail: " ++ error
Right message -> sendMessage message
(_, _, xs) -> do
putStrLn "Error parsing arguments:"
mapM_ putStrLn xs
printUsage
exitFailure
where
printUsage = putStrLn $ usageInfo usageMessage options
prepareEmailBody :: [Flag] -> IO (Maybe MessageContent)
prepareEmailBody flags = case (DL.find isTextMessage flags, DL.find isHtmlMessage flags) of
(Nothing, _) -> return Nothing
(Just (TextMessage textPath), Nothing) -> Just . TextOnly <$> BC.readFile textPath
(Just (TextMessage textPath), Just (HTMLMessage htmlPath)) -> do
textContents <- BC.readFile textPath
htmlContents <- BC.readFile htmlPath
return . Just $ TextAndHTML
{ textContent = textContents
, htmlContent = htmlContents
}
_ -> return Nothing -- The type safety should prevent this scenario.