{-# OPTIONS -fglasgow-exts #-} module Net ( IState(IState), -- types IrcLine(..), ReportChan(..), Net, runNet, -- functions netlog, runPlugin, setupNick, identify, joinC, send, recv, privmsg, ping, pong, parseIRCmsg, quit, ifadmin, logmsg, waitForInput, getplugins, die, rebootmsg, chkreboot, getnick ) where import Text.ParserCombinators.Parsec import Control.Concurrent.STM import Control.Monad.Reader import Control.Concurrent import System.Plugins import Text.Printf import qualified Network.IRC as IRC import System.IO import qualified Data.Map as Map import Network import PLoader import Config as C import Logger import List import API hiding (send) data IState = IState { rebootvar :: TMVar Int, rchan :: Chan ReportChan, plugins :: [InfinityPlugin], server :: (Handle,Server), conf :: Config, lFile :: FilePath } type Net a = ReaderT IState IO a data IrcLine = Cmd String String (String,String) | Line String String String | Err String deriving (Eq,Show) data ReportChan = Reboot -- sent when reboot is sent | Msg String -- a regular message | Quit String -- signifies that a thread quit from a @quit command | Nil -- signifies the death of a thread; only happens after a Reboot msg deriving (Eq,Show) -- net API runNet = runReaderT parseIRCmsg :: String -> Net IrcLine parseIRCmsg s = do let s' = if (last s) == '\n' then s else (reverse . (:) '\n' . reverse) s case (parse IRC.message "message" s') of Right x -> do let (IRC.Message ircprefix irccmd (towho:params)) = x nick = getnick ircprefix prefix = ifnil params ' ' $ (head . head . words . head) params cmdprefixes <- asks conf >>= return . commandPrefixes if prefix `elem` cmdprefixes then do let command = (tail . head . words . head) params args = (unwords . tail . words . head) params return $ Cmd nick towho (command,args) else return $ Line nick towho (ifnil params "" (head params)) Left e -> return (Err $ "irc parser err") where ifnil [] x _ = x ifnil _ _ y = y getnick Nothing = "nobody" getnick (Just x) | (IRC.Server s) <- x = s | (IRC.NickName nick user vhost) <- x = nick netlog :: PrintStatus -> String -> Net () netlog p s = do tid <- io $ myThreadId f <- asks lFile io $ logger f p (concat ["Net: ","[",show tid,"]: ",s]) logmsg :: (String,String,String) -> Net () logmsg x = do (h,s) <- asks server io $ logstr (address s) x ifadmin :: String -> Net a -> Net a -> Net a ifadmin u y n = do (h,s) <- asks server if (u `elem` (administrators s)) then y else n rebootmsg :: Net () rebootmsg = do r <- asks rchan io (writeChan r Reboot) netlog Normal "Sent Reboot msg..." chkreboot :: Net Bool chkreboot = do s <- asks rebootvar b <- atom $ (readTMVar s >> return True) `orElse` return False return b where atom = (io . atomically) die :: Net () die = do r <- asks rchan io $ writeChan r Nil netlog Normal "Transactional Variable full, sent Nil msg, exiting..." getplugins :: Net [InfinityPlugin] getplugins = io $ getPlugins >>= return . fst . unzip runPlugin :: String -> String -> String -> (Maybe String) -> Net (Maybe String) runPlugin s user chan str = do st <- ask pls <- asks plugins let x = lookupPlugin s pls case x of Left err -> return $ Just err Right y -> do let z = action y let ps = PluginState { handle = (fst . server $ st), addr = (address . snd . server $ st), admins = (administrators . snd . server $ st), chans = (channels . snd . server $ st), pluglist = (plugins st), self = y } (r,_) <- liftIO $ runPlug ps [] (z user chan s str) return r getnick :: Net String getnick = do (_,s) <- asks server return $ nickname s setupNick :: Net () setupNick = do (h,s) <- asks server let n = nickname s p = password s r = realname s c = channels s -- set nick, identify and join send "NICK" n send "USER" (n ++ " 0 * :" ++ r) identify = do (_,s) <- asks server let p = password s unless (null p) $ do privmsg "nickserv" $ "identify "++p joinC = do (_,s) <- asks server let c = channels s mapM_ (send "JOIN") c quit = do (h,s) <- asks server r <- asks rchan let sname = C.address s send "QUIT" ":Exiting..." io $ writeChan r (Quit sname) io $ hClose h io $ (myThreadId >>= killThread) report s = do c <- asks rchan (_,se) <- asks server let a = address se io $ writeChan c $ Msg (a ++ " *** " ++ s) waitForInput n = do h <- asks server >>= return . fst b <- io (hWaitForInput h (n*1000)) return b -- lower level primitives send msg str = do (h,s) <- asks server io $ hPrintf h "%s %s\r\n" msg str netlog Normal $ printf "%s %s" msg str recv = do (h,_) <- asks server s <- io $ hGetLine h report s return s privmsg n s = do send "PRIVMSG" (n++" :"++s) ping = isPrefixOf "PING :" pong = send "PONG" . (:) ':' . drop 6 -- convenience function io :: IO a -> Net a io = liftIO