xdcc-1.1.0: src/XDCC.hs
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module XDCC
( Env(..)
, Pack(..)
, initialState
, putDccState
, dispatcher
) where
import qualified DCC
import IRC.Types
import Control.Concurrent.STM
import Control.Monad.IO.Class (MonadIO, liftIO)
import qualified Data.CaseInsensitive as CI (mk)
import Data.Monoid ((<>))
import qualified Data.Text as T (Text, pack)
import qualified Network.IRC.Client as IRC
import System.Console.Concurrent (outputConcurrent)
class XdccCommand a where
toText :: a -> T.Text
data XdccSend
= Send !Pack
instance XdccCommand XdccSend where
toText (Send p) = "XDCC SEND #" <> packToText p
newtype Pack = Pack { unpack :: Int }
deriving (Eq, Show)
packToText :: Pack -> T.Text
packToText = T.pack . show . unpack
newtype XdccIO a = XdccIO { runXdccIO :: IRC.StatefulIRC Stati a }
deriving (Functor, Applicative, Monad, MonadIO)
putState :: Status -> XdccIO ()
putState newS = XdccIO $ do
state <- IRC.stateTVar
liftIO . atomically . modifyTVar state $ \s -> s { xdccStatus = newS }
addDccHandler :: IRC.EventHandler Stati -> XdccIO ()
addDccHandler = XdccIO . IRC.addHandler
sendXdcc :: XdccCommand a => Nickname -> a -> XdccIO ()
sendXdcc nick = XdccIO . IRC.send . IRC.Privmsg nick . Right . toText
data Stati = Stati { xdccStatus :: Status
, dccStatus :: DCC.Status
}
initialState :: Channel -> Stati
initialState chan = Stati { xdccStatus = WaitingForJoin chan
, dccStatus = DCC.Requesting
}
data Env = Env { packNumber :: !Pack
, dccEnv :: !(DCC.Env Stati) }
data Status
= WaitingForJoin !Channel
| Joined
deriving (Eq, Show)
dispatcher :: Env -> IRC.EventHandler Stati
dispatcher env = IRC.EventHandler
{ _description = "XDCC SEND workflow handling"
, _matchType = IRC.EEverything
, _eventFunc = \ev -> do
status <- xdccStatus <$> IRC.state
case status of
WaitingForJoin chan -> runXdccIO $ joinedHandler env chan ev
_ -> return ()
}
joinedHandler :: Env -> Channel -> IRC.UnicodeEvent -> XdccIO ()
joinedHandler Env {..} channel IRC.Event { _message = IRC.Join joined }
| CI.mk joined == channel = do
putState Joined
liftIO $ outputConcurrent ( "Joined " <> joined <> ".\n")
liftIO $ outputConcurrent
( "Requesting pack #" <> packToText packNumber <> " from " <> rNick
<> ", awaiting file offer…\n" )
addDccHandler (dispatcherDcc dccEnv)
sendXdcc rNick (Send packNumber)
where
rNick = DCC.remoteNick dccEnv
joinedHandler _ _ _ = return ()
putDccState :: DCC.Status -> IRC.StatefulIRC Stati ()
putDccState newS = do
state <- IRC.stateTVar
liftIO . atomically . modifyTVar state $ \s -> s { dccStatus = newS }
dispatcherDcc :: DCC.Env Stati -> IRC.EventHandler Stati
dispatcherDcc env = IRC.EventHandler
{ _description = "DCC SEND workflow handling"
, _matchType = IRC.EEverything
, _eventFunc = \ev -> do
status <- dccStatus <$> IRC.state
case status of
DCC.Requesting -> DCC.runDccIO env $ DCC.offerReceivedHandler ev
DCC.TryResuming offer -> DCC.runDccIO env $ DCC.acceptResumeHandler offer ev
_ -> return ()
}