packages feed

hMollom-0.3.1: src/Network/Mollom/Content.hs

{-# LANGUAGE OverloadedStrings #-}
{-
 - (C) 2012, Andy Georges
 -
 - This modules provides the interface to the Mollom content API.
 -
 -}

module Network.Mollom.Content
  ( Check(..)
  , ContentLanguage(..)
  , ContentResponse(..)
  , SpamClassification(..)
  , Strictness(..)
  , checkContent
  ) where

import Control.Applicative
import Control.Monad.Error
import Control.Monad.Reader
import Control.Monad.State
import qualified Data.Aeson as A
import Data.List (intercalate)
import Network.HTTP.Base (RequestMethod(..))

import Network.Mollom.Helper
import Network.Mollom.MollomMonad
import Network.Mollom.Types

-- | Possible checks Mollom can perform on the provided content.
data Check = Spam | Quality | Profanity | Language | Sentiment | None deriving (Eq)

instance Show Check where
  show Spam = "spam"
  show Quality = "quality"
  show Profanity = "profanity"
  show Language = "language"
  show Sentiment = "sentiment"
  show None = "none"

-- | Strictness of the Mollom service analysis
data Strictness = Strict | Normal | Relaxed deriving (Eq)

instance Show Strictness where
  show Strict = "strict"
  show Normal = "normal"
  show Relaxed = "relaxed"


-- | Data type representing the language that was detected by 
--   the Mollom service.
data ContentLanguage =
     ContentLanguage { languageCode  :: String
                     , languageScore :: Double
                     }

instance A.FromJSON ContentLanguage where
    parseJSON (A.Object o) = ContentLanguage <$>
                              o A..: "languageCode" <*>
                              o A..: "languageScore"

-- | Data type representing the classification of the content
--   by the Mollom service.
data SpamClassification = SpamClass | HamClass | UnsureClass deriving (Eq, Show)

instance A.FromJSON SpamClassification where
    parseJSON (A.String s) = return $ case s of
                              "spam"   -> SpamClass
                              "ham"    -> HamClass
                              "unsure" -> UnsureClass
    parseJSON _ = mzero

-- | Data type representing a response in the content API.
data ContentResponse =
     ContentResponse { contentId                 :: String
                     , contentSpamScore          :: Maybe Double
                     , contentSpamClassification :: Maybe SpamClassification
                     , contentProfanityScore     :: Maybe Double
                     , contentQualityScore       :: Maybe Double
                     , contentSentimentScore     :: Maybe Double
                     , contentReason             :: Maybe String
                     , contentLanguages          :: [ContentLanguage]
                     , contentPostTitle          :: String
                     , contentPostBody           :: String
                     , contentAuthorName         :: String
                     , contentAuthorUrl          :: String
                     , contentAuthorMail         :: String
                     , contentAuthorIP           :: String
                     , contentAuthorId           :: String
                     , contentAuthorOpenId       :: [String]
                     }


instance A.FromJSON ContentResponse where
    parseJSON j = do
        o <- A.parseJSON j
        s <- o A..: "content"
        ContentResponse <$>
          s A..: "contentId" <*>
          s A..:? "spamScore" <*>
          s A..:? "spamClassification" <*>
          s A..:? "profanityScore" <*>
          s A..:? "qualityScore" <*>
          s A..:? "sentimentScore" <*>
          s A..:? "reason" <*>
          s A..: "languages" <*>
          s A..: "postTitle" <*>
          s A..: "postBody" <*>
          s A..: "authorName" <*>
          s A..: "authorUrl" <*>
          s A..: "authorMail" <*>
          s A..: "authorIp" <*>
          s A..: "authorId" <*>
          s A..: "authorOpenId"
    parseJSON _ = mzero

-- | Asks Mollom whether the specified message is legitimate.
--   FIXME: contentID should be taken from the Mollom state
checkContent :: Maybe String     -- ^Title of submitted post.
             -> Maybe String     -- ^Body of submitted post.
             -> Maybe String     -- ^Content author's name.
             -> Maybe String     -- ^Content author's URL or website.
             -> Maybe String     -- ^Content author's email address.
             -> Maybe String     -- ^Content author's openID.
             -> Maybe String     -- ^Content author's current IP.
             -> Maybe String     -- ^Content author's unique local site user ID.
             -> Maybe [Check]    -- ^The check(s) to perform. If Nothing or Just None,
                                 --  this will default to Spam when the existing content ID is Nothing.
             -> Maybe Bool       -- ^Do we want to allow unsure results, leading to a CAPTCHA or not.
             -> Maybe Strictness -- ^How strict should Mollom be when checking this content?
             -> Maybe Int        -- ^Rate limit imposing a bound on the time between submitted posts from the same author.
             -> Maybe String     -- ^Value of the honeypot form-element, if any.
             -> Maybe Bool       -- ^Was the content stored on the client-side? 
                                 --  Should be False during validation of a form, 
                                 --  True after a succesfull submission.
             -> Maybe String     -- ^Absolute URL for the stored content.
             -> Maybe String     -- ^Absolute URL to the content's parent context, e.g., 
                                 --  the article of forum thread a comment is placed on.
             -> Maybe String     -- ^Title of said parental context.
             -> Mollom (MollomResponse ContentResponse) -- ^The monad in which the function is executing.
checkContent title body authorName authorURL authorEmail authorOpenID authorIP authorSiteID checks unsure strictness rateLimit honeypot stored storedURL storedParentURL parentTitle = do
    config <- ask 
    contentID <- get
    let pubKey = mcPublicKey config
        privKey = mcPrivateKey config
        kvs = [ ("postTitle", title)
              , ("postBody", body)
              , ("authorName", authorName)
              , ("authorUrl", authorURL)
              , ("authorMail", authorEmail)
              , ("authorOpenid", authorOpenID)
              , ("authorIp", authorIP)
              , ("authorId", authorSiteID)
              , ("checks", fmap (intercalate "|" . map show) checks)
              , ("unsure", fmap boolToOneZeroString unsure)
              , ("strictness", fmap show strictness)
              , ("rateLimit", fmap show rateLimit)
              , ("honeypot", honeypot)
              , ("stored", fmap boolToOneZeroString stored)
              , ("url", storedURL)
              , ("contextUrl", storedParentURL)
              , ("contextTitle", parentTitle)
              ]
        path = case contentID of
                  Just cid -> "content/" ++ cid
                  Nothing -> "content"
        errors = generalErrors
    ms <- mollomService pubKey privKey POST path kvs [] errors
    let contentID' = contentId . response $ ms
    put $ Just contentID'
    return ms