Added support for new messages and commands
1. Added support for pong, nick in use and names messages 2. Added support for ping, quit and names commands 3. Refactored message logger 4. Other minor changesmaster
parent
8a83053dee
commit
8659c5f755
|
@ -1,3 +1,4 @@
|
||||||
|
{-# LANGUAGE BangPatterns #-}
|
||||||
{-# LANGUAGE NoImplicitPrelude #-}
|
{-# LANGUAGE NoImplicitPrelude #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# LANGUAGE RecordWildCards #-}
|
{-# LANGUAGE RecordWildCards #-}
|
||||||
|
@ -39,22 +40,20 @@ awaitLatch latch = void $ takeMVar latch
|
||||||
|
|
||||||
type EChannel a = (Chan a, Latch)
|
type EChannel a = (Chan a, Latch)
|
||||||
|
|
||||||
data Cmd = CmdQuit | Cmd !Command deriving (Show, Eq)
|
|
||||||
data Line = Timeout | EOF | Line !Message deriving (Show, Eq)
|
data Line = Timeout | EOF | Line !Message deriving (Show, Eq)
|
||||||
|
|
||||||
sendCommandLoop :: EChannel Cmd -> Bot -> IO ()
|
sendCommandLoop :: EChannel Command -> Bot -> IO ()
|
||||||
sendCommandLoop (commandChan, latch) bot@Bot { .. } = do
|
sendCommandLoop (commandChan, latch) bot@Bot { .. } = do
|
||||||
cmd <- readChan commandChan
|
cmd <- readChan commandChan
|
||||||
|
time <- getCurrentTime
|
||||||
|
let line = lineFromCommand botConfig cmd
|
||||||
|
TF.hprint socket "{}\r\n" $ TF.Only line
|
||||||
|
TF.print "[{}] > {}\n" $ TF.buildParams (formatTime defaultTimeLocale "%F %T" time, line)
|
||||||
case cmd of
|
case cmd of
|
||||||
CmdQuit -> latchIt latch
|
QuitCmd -> latchIt latch
|
||||||
Cmd command -> do
|
_ -> sendCommandLoop (commandChan, latch) bot
|
||||||
time <- getCurrentTime
|
|
||||||
let line = lineFromCommand botConfig command
|
|
||||||
TF.hprint socket "{}\r\n" $ TF.Only line
|
|
||||||
TF.print "[{}] > {}\n" $ TF.buildParams (formatTime defaultTimeLocale "%F %T" time, line)
|
|
||||||
sendCommandLoop (commandChan, latch) bot
|
|
||||||
|
|
||||||
sendCommand :: Chan Cmd -> Cmd -> IO ()
|
sendCommand :: Chan Command -> Command -> IO ()
|
||||||
sendCommand = writeChan
|
sendCommand = writeChan
|
||||||
|
|
||||||
readLineLoop :: MVar BotStatus -> EChannel Line -> Bot -> Int -> IO ()
|
readLineLoop :: MVar BotStatus -> EChannel Line -> Bot -> Int -> IO ()
|
||||||
|
@ -85,29 +84,31 @@ readLine = readChan
|
||||||
sendMessage :: Chan Line -> Message -> IO ()
|
sendMessage :: Chan Line -> Message -> IO ()
|
||||||
sendMessage = (. Line) . writeChan
|
sendMessage = (. Line) . writeChan
|
||||||
|
|
||||||
listenerLoop :: Chan Line -> Chan Cmd -> Int -> IRC ()
|
listenerLoop :: Chan Line -> Chan Command -> Int -> IRC ()
|
||||||
listenerLoop lineChan commandChan idleFor = do
|
listenerLoop lineChan commandChan !idleFor = do
|
||||||
status <- get
|
status <- get
|
||||||
bot@Bot { .. } <- ask
|
bot@Bot { .. } <- ask
|
||||||
let nick = botNick botConfig
|
let nick = botNick botConfig
|
||||||
|
|
||||||
nStatus <- liftIO $
|
nStatus <- liftIO . mask_ $
|
||||||
if idleFor >= (oneSec * botTimeout botConfig)
|
if idleFor >= (oneSec * botTimeout botConfig)
|
||||||
then return Disconnected
|
then debug "Timeout" >> return Disconnected
|
||||||
else do
|
else do
|
||||||
when (status == Kicked) $
|
when (status == Kicked) $
|
||||||
threadDelay (5 * oneSec) >> sendCommand commandChan (Cmd JoinCmd)
|
threadDelay (5 * oneSec) >> sendCommand commandChan JoinCmd
|
||||||
|
|
||||||
mLine <- readLine lineChan
|
mLine <- readLine lineChan
|
||||||
case mLine of
|
case mLine of
|
||||||
Timeout -> dispatchHandlers bot IdleMsg >> return Idle
|
Timeout -> getCurrentTime >>= dispatchHandlers bot . IdleMsg >> return Idle
|
||||||
EOF -> return Disconnected
|
EOF -> debug "Connection closed" >> return Disconnected
|
||||||
Line message -> do
|
Line message -> do
|
||||||
nStatus <- case message of
|
nStatus <- case message of
|
||||||
JoinMsg { .. } | userNick user == nick -> debug "Joined" >> return Joined
|
JoinMsg { .. } | userNick user == nick -> debug "Joined" >> return Joined
|
||||||
KickMsg { .. } | kickedNick == nick -> debug "Kicked" >> return Kicked
|
KickMsg { .. } | kickedNick == nick -> debug "Kicked" >> return Kicked
|
||||||
|
NickInUseMsg { .. } ->
|
||||||
|
debug "Nick already in use" >> return NickNotAvailable
|
||||||
ModeMsg { user = Self, .. } ->
|
ModeMsg { user = Self, .. } ->
|
||||||
sendCommand commandChan (Cmd JoinCmd) >> return Connected
|
sendCommand commandChan JoinCmd >> return Connected
|
||||||
_ -> return Connected
|
_ -> return Connected
|
||||||
|
|
||||||
dispatchHandlers bot message
|
dispatchHandlers bot message
|
||||||
|
@ -115,9 +116,10 @@ listenerLoop lineChan commandChan idleFor = do
|
||||||
|
|
||||||
put nStatus
|
put nStatus
|
||||||
case nStatus of
|
case nStatus of
|
||||||
Idle -> listenerLoop lineChan commandChan (idleFor + oneSec)
|
Idle -> listenerLoop lineChan commandChan (idleFor + oneSec)
|
||||||
Disconnected -> return ()
|
Disconnected -> return ()
|
||||||
_ -> listenerLoop lineChan commandChan 0
|
NickNotAvailable -> return ()
|
||||||
|
_ -> listenerLoop lineChan commandChan 0
|
||||||
|
|
||||||
where
|
where
|
||||||
dispatchHandlers Bot { .. } message =
|
dispatchHandlers Bot { .. } message =
|
||||||
|
@ -128,7 +130,7 @@ listenerLoop lineChan commandChan idleFor = do
|
||||||
Nothing -> return ()
|
Nothing -> return ()
|
||||||
Just cmd -> case cmd of
|
Just cmd -> case cmd of
|
||||||
MessageCmd msg -> sendMessage lineChan msg
|
MessageCmd msg -> sendMessage lineChan msg
|
||||||
_ -> sendCommand commandChan (Cmd cmd)
|
_ -> sendCommand commandChan cmd
|
||||||
|
|
||||||
loadMsgHandlers :: BotConfig -> IO (Map MsgHandlerName MsgHandler)
|
loadMsgHandlers :: BotConfig -> IO (Map MsgHandlerName MsgHandler)
|
||||||
loadMsgHandlers botConfig@BotConfig { .. } =
|
loadMsgHandlers botConfig@BotConfig { .. } =
|
||||||
|
@ -145,7 +147,7 @@ unloadMsgHandlers Bot { .. } =
|
||||||
debug $ "Unloading msg handler: " ++ msgHandlerName
|
debug $ "Unloading msg handler: " ++ msgHandlerName
|
||||||
stopMsgHandler msgHandler botConfig
|
stopMsgHandler msgHandler botConfig
|
||||||
|
|
||||||
connect :: BotConfig -> IO (Bot, MVar BotStatus, EChannel Line, EChannel Cmd)
|
connect :: BotConfig -> IO (Bot, MVar BotStatus, EChannel Line, EChannel Command)
|
||||||
connect botConfig@BotConfig { .. } = do
|
connect botConfig@BotConfig { .. } = do
|
||||||
debug "Connecting ..."
|
debug "Connecting ..."
|
||||||
socket <- connectToWithRetry
|
socket <- connectToWithRetry
|
||||||
|
@ -167,10 +169,10 @@ connect botConfig@BotConfig { .. } = do
|
||||||
threadDelay (5 * oneSec)
|
threadDelay (5 * oneSec)
|
||||||
connectToWithRetry)
|
connectToWithRetry)
|
||||||
|
|
||||||
disconnect :: (Bot, MVar BotStatus, EChannel Line, EChannel Cmd) -> IO ()
|
disconnect :: (Bot, MVar BotStatus, EChannel Line, EChannel Command) -> IO ()
|
||||||
disconnect (bot@Bot { .. }, mvBotStatus, (_, readLatch), (commandChan, sendLatch)) = do
|
disconnect (bot@Bot { .. }, mvBotStatus, (_, readLatch), (commandChan, sendLatch)) = do
|
||||||
debug "Disconnecting ..."
|
debug "Disconnecting ..."
|
||||||
sendCommand commandChan CmdQuit
|
sendCommand commandChan QuitCmd
|
||||||
awaitLatch sendLatch
|
awaitLatch sendLatch
|
||||||
swapMVar mvBotStatus Disconnected
|
swapMVar mvBotStatus Disconnected
|
||||||
awaitLatch readLatch
|
awaitLatch readLatch
|
||||||
|
@ -190,10 +192,11 @@ run botConfig' = withSocketsDo $ do
|
||||||
print botConfig
|
print botConfig
|
||||||
status <- run_
|
status <- run_
|
||||||
case status of
|
case status of
|
||||||
Disconnected -> debug "Connection timed out" >> run botConfig
|
Disconnected -> debug "Restarting .." >> run botConfig
|
||||||
Interrupted -> return ()
|
Interrupted -> return ()
|
||||||
Errored -> debug "Errored, restarting" >> run botConfig
|
NickNotAvailable -> return ()
|
||||||
_ -> error "Unsupported status"
|
Errored -> debug "Restarting .." >> run botConfig
|
||||||
|
_ -> error "Unsupported status"
|
||||||
where
|
where
|
||||||
botConfig = addCoreMsgHandlers botConfig'
|
botConfig = addCoreMsgHandlers botConfig'
|
||||||
|
|
||||||
|
@ -205,8 +208,8 @@ run botConfig' = withSocketsDo $ do
|
||||||
run_ = bracket (connect botConfig) disconnect $
|
run_ = bracket (connect botConfig) disconnect $
|
||||||
\(bot, mvBotStatus, (lineChan, readLatch), (commandChan, sendLatch)) ->
|
\(bot, mvBotStatus, (lineChan, readLatch), (commandChan, sendLatch)) ->
|
||||||
handle handleErrors $ do
|
handle handleErrors $ do
|
||||||
sendCommand commandChan (Cmd NickCmd)
|
sendCommand commandChan NickCmd
|
||||||
sendCommand commandChan (Cmd UserCmd)
|
sendCommand commandChan UserCmd
|
||||||
|
|
||||||
fork $ sendCommandLoop (commandChan, sendLatch) bot
|
fork $ sendCommandLoop (commandChan, sendLatch) bot
|
||||||
fork $ readLineLoop mvBotStatus (lineChan, readLatch) bot oneSec
|
fork $ readLineLoop mvBotStatus (lineChan, readLatch) bot oneSec
|
||||||
|
|
|
@ -10,7 +10,9 @@ import qualified Network.IRC.Handlers.SongSearch as SS
|
||||||
|
|
||||||
import ClassyPrelude
|
import ClassyPrelude
|
||||||
import Control.Monad.Reader.Class
|
import Control.Monad.Reader.Class
|
||||||
|
import Data.Convertible
|
||||||
import Data.Text (strip)
|
import Data.Text (strip)
|
||||||
|
import Data.Time (addUTCTime)
|
||||||
|
|
||||||
import Network.IRC.Types
|
import Network.IRC.Types
|
||||||
|
|
||||||
|
@ -21,18 +23,35 @@ coreMsgHandlerNames :: [Text]
|
||||||
coreMsgHandlerNames = ["pingpong", "messagelogger"]
|
coreMsgHandlerNames = ["pingpong", "messagelogger"]
|
||||||
|
|
||||||
mkMsgHandler :: BotConfig -> MsgHandlerName -> IO (Maybe MsgHandler)
|
mkMsgHandler :: BotConfig -> MsgHandlerName -> IO (Maybe MsgHandler)
|
||||||
mkMsgHandler _ "greeter" = return . Just $ newMsgHandler { msgHandlerRun = greeter }
|
mkMsgHandler _ "greeter" = return . Just $ newMsgHandler { msgHandlerRun = greeter }
|
||||||
mkMsgHandler _ "welcomer" = return . Just $ newMsgHandler { msgHandlerRun = welcomer }
|
mkMsgHandler _ "welcomer" = return . Just $ newMsgHandler { msgHandlerRun = welcomer }
|
||||||
mkMsgHandler _ "pingpong" = return . Just $ newMsgHandler { msgHandlerRun = pingPong }
|
|
||||||
mkMsgHandler botConfig name =
|
mkMsgHandler _ "pingpong" = do
|
||||||
|
state <- getCurrentTime >>= newIORef
|
||||||
|
return . Just $ newMsgHandler { msgHandlerRun = pingPong state }
|
||||||
|
|
||||||
|
mkMsgHandler botConfig name =
|
||||||
flip (`foldM` Nothing) [L.mkMsgHandler, SS.mkMsgHandler] $ \acc h ->
|
flip (`foldM` Nothing) [L.mkMsgHandler, SS.mkMsgHandler] $ \acc h ->
|
||||||
case acc of
|
case acc of
|
||||||
Just _ -> return acc
|
Just _ -> return acc
|
||||||
Nothing -> h botConfig name
|
Nothing -> h botConfig name
|
||||||
|
|
||||||
pingPong :: MonadMsgHandler m => Message -> m (Maybe Command)
|
pingPong :: MonadMsgHandler m => IORef UTCTime -> Message -> m (Maybe Command)
|
||||||
pingPong Ping { .. } = return . Just $ Pong msg
|
pingPong state PingMsg { .. } = do
|
||||||
pingPong _ = return Nothing
|
liftIO $ atomicWriteIORef state msgTime
|
||||||
|
return . Just $ PongCmd msg
|
||||||
|
pingPong state PongMsg { .. } = do
|
||||||
|
liftIO $ atomicWriteIORef state msgTime
|
||||||
|
return Nothing
|
||||||
|
pingPong state IdleMsg { .. } | even (convert msgTime :: Int) = do
|
||||||
|
BotConfig { .. } <- ask
|
||||||
|
let limit = fromIntegral $ botTimeout `div` 2
|
||||||
|
liftIO $ do
|
||||||
|
lastComm <- readIORef state
|
||||||
|
if addUTCTime limit lastComm < msgTime
|
||||||
|
then return . Just . PingCmd . pack . formatTime defaultTimeLocale "%s" $ msgTime
|
||||||
|
else return Nothing
|
||||||
|
pingPong _ _ = return Nothing
|
||||||
|
|
||||||
greeter :: MonadMsgHandler m => Message -> m (Maybe Command)
|
greeter :: MonadMsgHandler m => Message -> m (Maybe Command)
|
||||||
greeter ChannelMsg { .. } = case find (== clean msg) greetings of
|
greeter ChannelMsg { .. } = case find (== clean msg) greetings of
|
||||||
|
|
|
@ -10,7 +10,8 @@ import qualified Data.Configurator as C
|
||||||
import qualified Data.Text.Format as TF
|
import qualified Data.Text.Format as TF
|
||||||
import qualified Data.Text.Format.Params as TF
|
import qualified Data.Text.Format.Params as TF
|
||||||
|
|
||||||
import ClassyPrelude hiding (try, (</>), (<.>), FilePath)
|
import ClassyPrelude hiding (try, (</>), (<.>), FilePath, log)
|
||||||
|
import Control.Exception.Lifted
|
||||||
import Control.Monad.Reader
|
import Control.Monad.Reader
|
||||||
import Data.Time (diffDays)
|
import Data.Time (diffDays)
|
||||||
import System.Directory
|
import System.Directory
|
||||||
|
@ -33,7 +34,7 @@ getLogFilePath :: BotConfig -> IO FilePath
|
||||||
getLogFilePath BotConfig { .. } = do
|
getLogFilePath BotConfig { .. } = do
|
||||||
logFileDir <- C.require config "messagelogger.logdir"
|
logFileDir <- C.require config "messagelogger.logdir"
|
||||||
createDirectoryIfMissing True logFileDir
|
createDirectoryIfMissing True logFileDir
|
||||||
return $ logFileDir </> unpack botNick <.> "log"
|
return $ logFileDir </> unpack (channel ++ "-" ++ botNick) <.> "log"
|
||||||
|
|
||||||
openLogFile :: FilePath -> IO Handle
|
openLogFile :: FilePath -> IO Handle
|
||||||
openLogFile logFilePath = do
|
openLogFile logFilePath = do
|
||||||
|
@ -67,8 +68,9 @@ withLogFile action state = do
|
||||||
then do
|
then do
|
||||||
hClose logFileHandle
|
hClose logFileHandle
|
||||||
logFilePath <- getLogFilePath botConfig
|
logFilePath <- getLogFilePath botConfig
|
||||||
copyFile logFilePath (logFilePath <.> show prevDay)
|
mask_ $ do
|
||||||
removeFile logFilePath
|
copyFile logFilePath (logFilePath <.> show prevDay)
|
||||||
|
removeFile logFilePath
|
||||||
openLogFile logFilePath
|
openLogFile logFilePath
|
||||||
else return logFileHandle
|
else return logFileHandle
|
||||||
|
|
||||||
|
@ -77,39 +79,24 @@ withLogFile action state = do
|
||||||
|
|
||||||
return Nothing
|
return Nothing
|
||||||
|
|
||||||
fmtTime :: UTCTime -> String
|
|
||||||
fmtTime = formatTime defaultTimeLocale "%F %T"
|
|
||||||
|
|
||||||
messageLogger :: MonadMsgHandler m => Message -> IORef LoggerState -> m (Maybe Command)
|
messageLogger :: MonadMsgHandler m => Message -> IORef LoggerState -> m (Maybe Command)
|
||||||
messageLogger ChannelMsg { .. } = withLogFile $ \logFile ->
|
messageLogger message = go message
|
||||||
TF.hprint logFile "[{}] {}: {}\n" $ TF.buildParams (fmtTime msgTime, userNick user, msg)
|
where
|
||||||
|
go ChannelMsg { .. } = log "<{}> {}" [userNick user, msg]
|
||||||
|
go ActionMsg { .. } = log "<{}> {} {}" [userNick user, userNick user, msg]
|
||||||
|
go KickMsg { .. } = log "** {} KICKED {} :{}" [userNick user, kickedNick, msg]
|
||||||
|
go JoinMsg { .. } = log "** {} JOINED" [userNick user]
|
||||||
|
go PartMsg { .. } = log "** {} PARTED :{}" [userNick user, msg]
|
||||||
|
go QuitMsg { .. } = log "** {} QUIT :{}" [userNick user, msg]
|
||||||
|
go NickMsg { .. } = log "** {} CHANGED NICK TO {}" [userNick user, nick]
|
||||||
|
go NamesMsg { .. } = log "** USERS {}" [unwords nicks]
|
||||||
|
go _ = const $ return Nothing
|
||||||
|
|
||||||
messageLogger ActionMsg { .. } = withLogFile $ \logFile ->
|
log format args = withLogFile $ \logFile ->
|
||||||
TF.hprint logFile "[{}] {}: {} {}\n" $
|
TF.hprint logFile ("[{}] " ++ format ++ "\n") $ TF.buildParams (fmtTime (msgTime message) : args)
|
||||||
TF.buildParams (fmtTime msgTime, userNick user, userNick user, msg)
|
|
||||||
|
|
||||||
messageLogger KickMsg { .. } = withLogFile $ \logFile ->
|
fmtTime = pack . formatTime defaultTimeLocale "%F %T"
|
||||||
TF.hprint logFile "[{}] ** {} KICKED {} :{}\n" $
|
|
||||||
TF.buildParams (fmtTime msgTime, userNick user, kickedNick, msg)
|
|
||||||
|
|
||||||
messageLogger JoinMsg { .. } = withLogFile $ \logFile ->
|
|
||||||
TF.hprint logFile "[{}] ** {} JOINED\n" $
|
|
||||||
TF.buildParams (fmtTime msgTime, userNick user)
|
|
||||||
|
|
||||||
messageLogger PartMsg { .. } = withLogFile $ \logFile ->
|
|
||||||
TF.hprint logFile "[{}] ** {} PARTED :{}\n" $
|
|
||||||
TF.buildParams (fmtTime msgTime, userNick user, msg)
|
|
||||||
|
|
||||||
messageLogger QuitMsg { .. } = withLogFile $ \logFile ->
|
|
||||||
TF.hprint logFile "[{}] ** {} QUIT :{}\n" $
|
|
||||||
TF.buildParams (fmtTime msgTime, userNick user, msg)
|
|
||||||
|
|
||||||
messageLogger NickMsg { .. } = withLogFile $ \logFile ->
|
|
||||||
TF.hprint logFile "[{}] ** {} CHANGED NICK TO {}\n" $
|
|
||||||
TF.buildParams (fmtTime msgTime, userNick user, nick)
|
|
||||||
|
|
||||||
--messageLogger IdleMsg = const . liftIO $ do
|
--messageLogger IdleMsg = const . liftIO $ do
|
||||||
-- now <- getCurrentTime
|
-- now <- getCurrentTime
|
||||||
-- return . Just . MessageCmd $
|
-- return . Just . MessageCmd $
|
||||||
|
|
||||||
messageLogger _ = const $ return Nothing
|
|
||||||
|
|
|
@ -12,8 +12,9 @@ import Network.IRC.Types
|
||||||
|
|
||||||
msgFromLine :: BotConfig -> UTCTime -> Text -> Message
|
msgFromLine :: BotConfig -> UTCTime -> Text -> Message
|
||||||
msgFromLine (BotConfig { .. }) time line
|
msgFromLine (BotConfig { .. }) time line
|
||||||
| "PING :" `isPrefixOf` line = Ping time (drop 6 line) line
|
| "PING :" `isPrefixOf` line = PingMsg time (drop 6 line) line
|
||||||
| otherwise = case command of
|
| otherwise = case command of
|
||||||
|
"PONG" -> PongMsg time message line
|
||||||
"JOIN" -> JoinMsg time user line
|
"JOIN" -> JoinMsg time user line
|
||||||
"QUIT" -> QuitMsg time user quitMessage line
|
"QUIT" -> QuitMsg time user quitMessage line
|
||||||
"PART" -> PartMsg time user message line
|
"PART" -> PartMsg time user message line
|
||||||
|
@ -27,27 +28,38 @@ msgFromLine (BotConfig { .. }) time line
|
||||||
then ActionMsg time user (initDef . drop 8 $ message) line
|
then ActionMsg time user (initDef . drop 8 $ message) line
|
||||||
else ChannelMsg time user message line
|
else ChannelMsg time user message line
|
||||||
else PrivMsg time user message line
|
else PrivMsg time user message line
|
||||||
|
"353" -> NamesMsg time namesNicks
|
||||||
|
"433" -> NickInUseMsg time line
|
||||||
_ -> OtherMsg time source command target message line
|
_ -> OtherMsg time source command target message line
|
||||||
where
|
where
|
||||||
isSpc = (== ' ')
|
isSpc = (== ' ')
|
||||||
isNotSpc = not . isSpc
|
isNotSpc = not . isSpc
|
||||||
splits = split isSpc line
|
splits = split isSpc line
|
||||||
source = drop 1 . takeWhile isNotSpc $ line
|
source = drop 1 . takeWhile isNotSpc $ line
|
||||||
target = splits !! 2
|
target = splits !! 2
|
||||||
command = splits !! 1
|
command = splits !! 1
|
||||||
message = drop 1 . unwords . drop 3 $ splits
|
message = drop 1 . unwords . drop 3 $ splits
|
||||||
quitMessage = drop 1 . unwords . drop 2 $ splits
|
quitMessage = drop 1 . unwords . drop 2 $ splits
|
||||||
user = uncurry User . break (== '!') $ source
|
user = uncurry User . break (== '!') $ source
|
||||||
mode = splits !! 3
|
mode = splits !! 3
|
||||||
modeArgs = drop 4 splits
|
modeArgs = drop 4 splits
|
||||||
kicked = splits !! 3
|
kicked = splits !! 3
|
||||||
kickReason = drop 1 . unwords . drop 4 $ splits
|
kickReason = drop 1 . unwords . drop 4 $ splits
|
||||||
|
|
||||||
|
nickPrefixes :: String
|
||||||
|
nickPrefixes = "~&@%+"
|
||||||
|
namesNicks = map stripNickPrefix . words . drop 1 . unwords . drop 5 $ splits
|
||||||
|
stripNickPrefix = pack . dropWhile (`elem` nickPrefixes) . unpack
|
||||||
|
|
||||||
lineFromCommand :: BotConfig -> Command -> Text
|
lineFromCommand :: BotConfig -> Command -> Text
|
||||||
lineFromCommand (BotConfig { .. }) reply = case reply of
|
lineFromCommand (BotConfig { .. }) command = case command of
|
||||||
Pong { .. } -> "PONG :" ++ rmsg
|
PongCmd { .. } -> "PONG :" ++ rmsg
|
||||||
|
PingCmd { .. } -> "PING :" ++ rmsg
|
||||||
NickCmd -> "NICK " ++ botNick
|
NickCmd -> "NICK " ++ botNick
|
||||||
UserCmd -> "USER " ++ botNick ++ " 0 * :" ++ botNick
|
UserCmd -> "USER " ++ botNick ++ " 0 * :" ++ botNick
|
||||||
JoinCmd -> "JOIN " ++ channel
|
JoinCmd -> "JOIN " ++ channel
|
||||||
|
QuitCmd -> "QUIT"
|
||||||
ChannelMsgReply { .. } -> "PRIVMSG " ++ channel ++ " :" ++ rmsg
|
ChannelMsgReply { .. } -> "PRIVMSG " ++ channel ++ " :" ++ rmsg
|
||||||
PrivMsgReply (User { .. }) rmsg -> "PRIVMSG " ++ botNick ++ " :" ++ rmsg
|
PrivMsgReply (User { .. }) rmsg -> "PRIVMSG " ++ botNick ++ " :" ++ rmsg
|
||||||
|
NamesCmd -> "NAMES " ++ channel
|
||||||
|
_ -> error $ "Unsupported command " ++ show command
|
||||||
|
|
|
@ -26,30 +26,36 @@ data User = Self | User { userNick :: !Nick, userServer :: !Text }
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data Message =
|
data Message =
|
||||||
IdleMsg
|
IdleMsg { msgTime :: !UTCTime}
|
||||||
| ChannelMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
| PingMsg { msgTime :: !UTCTime, msg :: !Text, msgLine :: !Text }
|
||||||
| PrivMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
| PongMsg { msgTime :: !UTCTime, msg :: !Text, msgLine :: !Text }
|
||||||
| ActionMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
| ChannelMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
||||||
| Ping { msgTime :: !UTCTime, msg :: !Text, msgLine :: !Text }
|
| PrivMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
||||||
| JoinMsg { msgTime :: !UTCTime, user :: !User, msgLine :: !Text }
|
| ActionMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
||||||
| QuitMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
| JoinMsg { msgTime :: !UTCTime, user :: !User, msgLine :: !Text }
|
||||||
| PartMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
| QuitMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
||||||
| ModeMsg { msgTime :: !UTCTime, user :: !User, target :: !Text, mode :: !Text
|
| PartMsg { msgTime :: !UTCTime, user :: !User, msg :: !Text, msgLine :: !Text }
|
||||||
, modeArgs :: ![Text], msgLine :: !Text }
|
| NickMsg { msgTime :: !UTCTime, user :: !User, nick :: !Nick, msgLine :: !Text }
|
||||||
| NickMsg { msgTime :: !UTCTime, user :: !User, nick :: !Text, msgLine :: !Text }
|
| NickInUseMsg { msgTime :: !UTCTime, msgLine :: !Text }
|
||||||
| KickMsg { msgTime :: !UTCTime, user :: !User, kickedNick :: !Text, msg :: !Text
|
| KickMsg { msgTime :: !UTCTime, user :: !User, kickedNick :: !Nick, msg :: !Text
|
||||||
, msgLine :: !Text }
|
, msgLine :: !Text }
|
||||||
| OtherMsg { msgTime :: !UTCTime, source :: !Text, command :: !Text, target :: !Text
|
| ModeMsg { msgTime :: !UTCTime, user :: !User, target :: !Text, mode :: !Text
|
||||||
, msg :: !Text, msgLine :: !Text }
|
, modeArgs :: ![Text], msgLine :: !Text }
|
||||||
|
| NamesMsg { msgTime :: !UTCTime, nicks :: ![Nick] }
|
||||||
|
| OtherMsg { msgTime :: !UTCTime, source :: !Text, command :: !Text, target :: !Text
|
||||||
|
, msg :: !Text, msgLine :: !Text }
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
data Command =
|
data Command =
|
||||||
Pong { rmsg :: !Text }
|
PingCmd { rmsg :: !Text }
|
||||||
|
| PongCmd { rmsg :: !Text }
|
||||||
| ChannelMsgReply { rmsg :: !Text }
|
| ChannelMsgReply { rmsg :: !Text }
|
||||||
| PrivMsgReply { ruser :: !User, rmsg :: !Text }
|
| PrivMsgReply { ruser :: !User, rmsg :: !Text }
|
||||||
| NickCmd
|
| NickCmd
|
||||||
| UserCmd
|
| UserCmd
|
||||||
| JoinCmd
|
| JoinCmd
|
||||||
|
| QuitCmd
|
||||||
|
| NamesCmd
|
||||||
| MessageCmd Message
|
| MessageCmd Message
|
||||||
deriving (Show, Eq)
|
deriving (Show, Eq)
|
||||||
|
|
||||||
|
@ -73,8 +79,15 @@ data Bot = Bot { botConfig :: !BotConfig
|
||||||
, socket :: !Handle
|
, socket :: !Handle
|
||||||
, msgHandlers :: !(Map MsgHandlerName MsgHandler) }
|
, msgHandlers :: !(Map MsgHandlerName MsgHandler) }
|
||||||
|
|
||||||
data BotStatus = Connected | Disconnected | Joined | Kicked | Errored | Idle | Interrupted
|
data BotStatus = Connected
|
||||||
deriving (Show, Eq)
|
| Disconnected
|
||||||
|
| Joined
|
||||||
|
| Kicked
|
||||||
|
| Errored
|
||||||
|
| Idle
|
||||||
|
| Interrupted
|
||||||
|
| NickNotAvailable
|
||||||
|
deriving (Show, Eq)
|
||||||
|
|
||||||
newtype IRC a = IRC { _runIRC :: StateT BotStatus (ReaderT Bot IO) a }
|
newtype IRC a = IRC { _runIRC :: StateT BotStatus (ReaderT Bot IO) a }
|
||||||
deriving ( Functor
|
deriving ( Functor
|
||||||
|
|
|
@ -49,7 +49,7 @@ build-type: Simple
|
||||||
cabal-version: >=1.10
|
cabal-version: >=1.10
|
||||||
|
|
||||||
library
|
library
|
||||||
build-depends: base >=4.5 && <4.7,
|
build-depends: base >=4.5 && <4.8,
|
||||||
text >=0.11 && <0.12,
|
text >=0.11 && <0.12,
|
||||||
mtl >=2.1 && <2.2,
|
mtl >=2.1 && <2.2,
|
||||||
network >=2.3 && <2.5,
|
network >=2.3 && <2.5,
|
||||||
|
@ -64,7 +64,8 @@ library
|
||||||
filepath >=1.3,
|
filepath >=1.3,
|
||||||
directory >=1.2,
|
directory >=1.2,
|
||||||
lifted-base >=0.2,
|
lifted-base >=0.2,
|
||||||
unix >=2.7
|
unix >=2.7,
|
||||||
|
convertible >=1.1
|
||||||
|
|
||||||
exposed-modules: Network.IRC.Types, Network.IRC.Protocol,
|
exposed-modules: Network.IRC.Types, Network.IRC.Protocol,
|
||||||
Network.IRC.Handlers, Network.IRC.Client
|
Network.IRC.Handlers, Network.IRC.Client
|
||||||
|
@ -85,7 +86,7 @@ executable hask-irc
|
||||||
other-extensions: RecordWildCards, OverloadedStrings, ScopedTypeVariables, OverlappingInstances
|
other-extensions: RecordWildCards, OverloadedStrings, ScopedTypeVariables, OverlappingInstances
|
||||||
|
|
||||||
-- Other library packages from which modules are imported.
|
-- Other library packages from which modules are imported.
|
||||||
build-depends: base >=4.5 && <4.7,
|
build-depends: base >=4.5 && <4.8,
|
||||||
text >=0.11 && <0.12,
|
text >=0.11 && <0.12,
|
||||||
mtl >=2.1 && <2.2,
|
mtl >=2.1 && <2.2,
|
||||||
network >=2.3 && <2.5,
|
network >=2.3 && <2.5,
|
||||||
|
@ -100,7 +101,8 @@ executable hask-irc
|
||||||
filepath >=1.3,
|
filepath >=1.3,
|
||||||
directory >=1.2,
|
directory >=1.2,
|
||||||
lifted-base >=0.2,
|
lifted-base >=0.2,
|
||||||
unix >=2.7
|
unix >=2.7,
|
||||||
|
convertible >=1.1
|
||||||
|
|
||||||
-- Directories containing source files.
|
-- Directories containing source files.
|
||||||
-- hs-source-dirs:
|
-- hs-source-dirs:
|
||||||
|
|
Loading…
Reference in New Issue