hask-irc/hask-irc-handlers/Network/IRC/Handlers/MessageLogger.hs

94 lines
3.8 KiB
Haskell
Raw Normal View History

2014-05-13 00:00:33 +05:30
{-# LANGUAGE FlexibleContexts #-}
2014-06-07 00:50:27 +05:30
module Network.IRC.Handlers.MessageLogger (messageLoggerMsgHandlerMaker) where
2014-06-01 23:14:19 +05:30
import qualified Data.Configurator as CF
2014-05-25 01:09:31 +05:30
import qualified Data.Text.Format as TF
import qualified Data.Text.Format.Params as TF
2014-06-01 23:14:19 +05:30
import ClassyPrelude hiding ((</>), (<.>), FilePath, log)
import Control.Exception.Lifted (mask_)
import Control.Monad.Reader (ask)
import Data.Time (diffDays)
import System.Directory (createDirectoryIfMissing, getModificationTime, copyFile, removeFile)
import System.FilePath (FilePath, (</>), (<.>))
import System.IO (openFile, IOMode(..), hSetBuffering, BufferMode(..))
import Network.IRC.Types
2014-05-23 02:45:45 +05:30
import Network.IRC.Util
type LoggerState = Maybe (Handle, Day)
2014-06-07 00:50:27 +05:30
messageLoggerMsgHandlerMaker :: MsgHandlerMaker
messageLoggerMsgHandlerMaker = MsgHandlerMaker "messagelogger" go
where
go botConfig _ "messagelogger" = do
state <- io $ newIORef Nothing
initMessageLogger botConfig state
return . Just $ newMsgHandler { onMessage = flip messageLogger state
, onStop = exitMessageLogger state }
go _ _ _ = return Nothing
getLogFilePath :: BotConfig -> IO FilePath
getLogFilePath BotConfig { .. } = do
2014-06-01 23:14:19 +05:30
logFileDir <- CF.require config "messagelogger.logdir"
createDirectoryIfMissing True logFileDir
2014-06-01 02:11:20 +05:30
return $ logFileDir </> unpack (channel ++ "-" ++ nickToText botNick) <.> "log"
openLogFile :: FilePath -> IO Handle
openLogFile logFilePath = do
logFileHandle <- openFile logFilePath AppendMode
hSetBuffering logFileHandle LineBuffering
return logFileHandle
initMessageLogger :: BotConfig -> IORef LoggerState -> IO ()
initMessageLogger botConfig state = do
logFilePath <- getLogFilePath botConfig
logFileHandle <- openLogFile logFilePath
2014-05-20 02:40:08 +05:30
time <- getModificationTime logFilePath
atomicWriteIORef state $ Just (logFileHandle, utctDay time)
exitMessageLogger :: MonadMsgHandler m => IORef LoggerState -> m ()
2014-05-23 12:21:38 +05:30
exitMessageLogger state = io $ readIORef state >>= flip whenJust (hClose . fst)
2014-06-01 06:48:24 +05:30
withLogFile :: MonadMsgHandler m => (Handle -> IO ()) -> IORef LoggerState -> m [Command]
withLogFile action state = do
botConfig <- ask
2014-05-23 12:21:38 +05:30
io $ do
Just (logFileHandle, prevDay) <- readIORef state
2014-05-20 02:40:08 +05:30
curDay <- map utctDay getCurrentTime
let diff = diffDays curDay prevDay
logFileHandle' <- if diff >= 1
then do
hClose logFileHandle
logFilePath <- getLogFilePath botConfig
mask_ $ do
copyFile logFilePath (logFilePath <.> show prevDay)
removeFile logFilePath
openLogFile logFilePath
else return logFileHandle
action logFileHandle'
atomicWriteIORef state $ Just (logFileHandle', curDay)
2014-06-01 06:48:24 +05:30
return []
2014-05-11 14:34:05 +05:30
2014-06-01 06:48:24 +05:30
messageLogger :: MonadMsgHandler m => Message -> IORef LoggerState -> m [Command]
2014-05-25 01:09:31 +05:30
messageLogger Message { .. } = case msgDetails of
2014-06-01 02:11:20 +05:30
ChannelMsg { .. } -> log "<{}> {}" [nick user, msg]
ActionMsg { .. } -> log "<{}> {} {}" [nick user, nick user, msg]
KickMsg { .. } -> log "** {} KICKED {} :{}" [nick user, nickToText kickedNick, msg]
JoinMsg { .. } -> log "** {} JOINED" [nick user]
PartMsg { .. } -> log "** {} PARTED :{}" [nick user, msg]
QuitMsg { .. } -> log "** {} QUIT :{}" [nick user, msg]
NickMsg { .. } -> log "** {} CHANGED NICK TO {}" [nick user, nickToText newNick]
NamesMsg { .. } -> log "** USERS {}" [unwords . map nickToText $ nicks]
2014-06-01 06:48:24 +05:30
_ -> const $ return []
where
2014-06-01 02:11:20 +05:30
nick = nickToText . userNick
log format args = withLogFile $ \logFile ->
2014-05-25 01:09:31 +05:30
TF.hprint logFile ("[{}] " ++ format ++ "\n") $ TF.buildParams (fmtTime msgTime : args)
fmtTime = pack . formatTime defaultTimeLocale "%F %T"