module Network.IRC.Conduit
(
ChannelName
, NickName
, ServerName
, Reason
, IsModeSet
, ModeFlag
, ModeArg
, NumericArg
, Target
, IrcEvent
, IrcSource
, IrcMessage
, Event(..)
, Source(..)
, Message(..)
, ircDecoder
, ircLossyDecoder
, ircEncoder
, floodProtector
, ircClient
, ircTLSClient
, ircWithConn
, rawMessage
, toByteString
) where
import Control.Applicative ((*>))
import Control.Concurrent (newMVar, takeMVar, putMVar, threadDelay)
import Control.Concurrent.Async (Concurrently(..))
import Control.Monad (when)
import Control.Monad.IO.Class (MonadIO, liftIO)
import Data.ByteString (ByteString)
import Data.Conduit (Conduit, Consumer, Producer, (=$), ($$), (=$=), awaitForever, yield)
import Data.Conduit.Network (AppData, clientSettings, runTCPClient, appSource, appSink)
import Data.Conduit.Network.TLS (tlsClientConfig, runTLSClient)
import Data.Monoid ((<>))
import Data.Time.Clock (NominalDiffTime, getCurrentTime, addUTCTime, diffUTCTime)
import Network.IRC.Conduit.Internal
import System.IO.Error (catchIOError)
ircDecoder :: Monad m => Conduit ByteString m (Either ByteString IrcEvent)
ircDecoder = chunked =$= awaitForever (yield . fromByteString)
ircLossyDecoder :: Monad m => Conduit ByteString m IrcEvent
ircLossyDecoder = chunked =$= awaitForever lossy
where
lossy bs = either (\_ -> return ()) yield $ fromByteString bs
ircEncoder :: Monad m => Conduit IrcMessage m ByteString
ircEncoder = awaitForever (yield . (<>"\r\n") . toByteString)
floodProtector :: MonadIO m
=> NominalDiffTime
-> IO (Conduit a m a)
floodProtector delay = do
now <- getCurrentTime
mvar <- newMVar now
return $ conduit mvar
where
conduit mvar = awaitForever $ \val -> do
liftIO $ do
lastT <- takeMVar mvar
now <- getCurrentTime
let next = addUTCTime delay lastT
when (next < now) $
threadDelay . ceiling $ 1000000 * diffUTCTime next now
now' <- getCurrentTime
putMVar mvar now'
yield val
ircClient :: Int
-> ByteString
-> IO ()
-> Consumer (Either ByteString IrcEvent) IO ()
-> Producer IO IrcMessage
-> IO ()
ircClient port host = ircWithConn . runTCPClient $ clientSettings port host
ircTLSClient :: Int -> ByteString -> IO () -> Consumer (Either ByteString IrcEvent) IO () -> Producer IO IrcMessage -> IO ()
ircTLSClient port host = ircWithConn . runTLSClient $ tlsClientConfig port host
ircWithConn :: ((AppData -> IO ()) -> IO ())
-> IO ()
-> Consumer (Either ByteString IrcEvent) IO ()
-> Producer IO IrcMessage
-> IO ()
ircWithConn runner start cons prod = go `catchIOError` ignore
where
go = runner $ \appdata ->
runConcurrently $
Concurrently start *>
Concurrently (appSource appdata =$= exceptionalConduit $$ ircDecoder =$ cons) *>
Concurrently (prod $$ ircEncoder =$ appSink appdata)
ignore _ = return ()