123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- {-# LANGUAGE FlexibleContexts #-}
-
- module Network.IRC.Handlers.MessageLogger (messageLoggerMsgHandlerMaker) where
-
- import qualified Data.Text.Format as TF
- import qualified Data.Text.Format.Params as TF
-
- import ClassyPrelude hiding ((</>), (<.>), FilePath, log)
- import Data.Time (diffDays)
- import System.Directory (createDirectoryIfMissing, getModificationTime, copyFile, removeFile)
- import System.FilePath (FilePath, (</>), (<.>))
- import System.IO (openFile, IOMode(..), hSetBuffering, BufferMode(..))
-
- import qualified Network.IRC.Configuration as CF
- import Network.IRC
- import Network.IRC.Util
-
- type LoggerState = Maybe (Handle, Day)
-
- messageLoggerMsgHandlerMaker :: MsgHandlerMaker
- messageLoggerMsgHandlerMaker = MsgHandlerMaker "messagelogger" go
- where
- go botConfig _ = do
- state <- io $ newIORef Nothing
- initMessageLogger botConfig state
- return $ newMsgHandler { onMessage = flip messageLogger state
- , onStop = exitMessageLogger state }
-
- getLogFilePath :: BotConfig -> IO FilePath
- getLogFilePath BotConfig { .. } = do
- let logFileDir = CF.require "messagelogger.logdir" config :: Text
- createDirectoryIfMissing True (unpack logFileDir)
- return $ unpack logFileDir </> unpack (botChannel ++ "-" ++ 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
- time <- getModificationTime logFilePath
- atomicWriteIORef state $ Just (logFileHandle, utctDay time)
-
- exitMessageLogger :: MonadMsgHandler m => IORef LoggerState -> m ()
- exitMessageLogger state = io $ readIORef state >>= flip whenJust (hClose . fst)
-
- withLogFile :: MonadMsgHandler m => (Handle -> IO ()) -> IORef LoggerState -> m [Message]
- withLogFile action state = do
- botConfig <- ask
- io $ do
- Just (logFileHandle, prevDay) <- readIORef state
- 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)
-
- return []
-
- messageLogger :: MonadMsgHandler m => Message -> IORef LoggerState -> m [Message]
- messageLogger Message { .. }
- | Just (ChannelMsg user msg) <- fromMessage message =
- log "<{}> {}" [nick user, msg]
- | Just (ActionMsg user msg) <- fromMessage message =
- log "<{}> {} {}" [nick user, nick user, msg]
- | Just (KickMsg user kickedNick msg) <- fromMessage message =
- log "** {} KICKED {} :{}" [nick user, nickToText kickedNick, msg]
- | Just (JoinMsg user) <- fromMessage message =
- log "** {} JOINED" [nick user]
- | Just (PartMsg user msg) <- fromMessage message =
- log "** {} PARTED :{}" [nick user, msg]
- | Just (QuitMsg user msg) <- fromMessage message =
- log "** {} QUIT :{}" [nick user, msg]
- | Just (NickMsg user newNick) <- fromMessage message =
- log "** {} CHANGED NICK TO {}" [nick user, nickToText newNick]
- | Just (NamesMsg nicks) <- fromMessage message =
- log "** USERS {}" [unwords . map nickToText $ nicks]
- | otherwise =
- const $ return []
- where
- nick = nickToText . userNick
-
- log format args = withLogFile $ \logFile ->
- TF.hprint logFile ("[{}] " ++ format ++ "\n") $ TF.buildParams (fmtTime msgTime : args)
-
- fmtTime = pack . formatTime defaultTimeLocale "%F %T"
|