205 lines
6.3 KiB
Haskell
205 lines
6.3 KiB
Haskell
{-# LANGUAGE OverloadedStrings #-}
|
|
|
|
module Pipes.IRC.Server.IrcMonad
|
|
where
|
|
|
|
import Control.Lens
|
|
import Control.Monad.RWS
|
|
import Data.ByteString.Char8 (ByteString)
|
|
import qualified Data.ByteString.Char8 as BS
|
|
import qualified Data.Map as M
|
|
import Data.Maybe (catMaybes, fromJust, isJust)
|
|
import qualified Data.Set as S
|
|
|
|
import Pipes.IRC.Message.Types
|
|
import Pipes.IRC.Server.Channel
|
|
import Pipes.IRC.Server.Types
|
|
import Pipes.IRC.Server.User
|
|
|
|
-- * IrcUser management
|
|
|
|
mkUser :: IrcMonad IrcUser
|
|
mkUser = do
|
|
conn <- use clientConn
|
|
srvname <- view ircHostName
|
|
return $ newUser srvname conn
|
|
|
|
-- * Pretty Printing
|
|
|
|
ppServiceName :: ServiceName -> ByteString
|
|
ppServiceName = BS.pack
|
|
|
|
ppHostPreference :: HostPreference -> ByteString
|
|
ppHostPreference hp = case hp of
|
|
HostAny -> "*"
|
|
HostIPv4 -> "*4"
|
|
HostIPv6 -> "*6"
|
|
Host hn -> BS.pack hn
|
|
|
|
-- * Monadic utilities
|
|
|
|
useChan :: ChanKey -> IrcMonad (Maybe IrcChannel)
|
|
useChan cname = fmap (M.lookup cname) $ use (clientServer . ircChannels)
|
|
|
|
useNick :: IrcMonad (Maybe NickKey)
|
|
useNick = do
|
|
regState <- use clientReg
|
|
return $ case regState of
|
|
Unreg _ (Just nn) _ -> Just nn
|
|
RegUser (NickName nn _ _) -> Just nn
|
|
_ -> Nothing
|
|
|
|
validateNick :: NickKey -> IrcMonad Bool
|
|
validateNick nickname = do
|
|
nickSet <- use $ clientServer . ircNicks
|
|
if S.member nickname nickSet
|
|
then do
|
|
tellNumeric err_nicknameinuse [nickname, ":Nickname is already in use."]
|
|
return False
|
|
else return True
|
|
|
|
channelTargets :: ChanKey -> IrcMonad [Int]
|
|
channelTargets chname = do
|
|
srv <- use clientServer
|
|
Just mynick <- useNick
|
|
let cUsers chan = S.elems (S.delete mynick $ chan ^. chanUsers)
|
|
let chmap = srv ^. ircChannels
|
|
case M.lookup chname chmap of
|
|
Just chan -> fmap catMaybes $ forM (cUsers chan) userTarget
|
|
_ -> do tellNumeric err_nosuchnick [chname, ":No such nick/channel"]
|
|
return []
|
|
|
|
userTarget :: NickKey -> IrcMonad (Maybe Int)
|
|
userTarget uname = do
|
|
srv <- use clientServer
|
|
let umap = srv ^. ircUsers
|
|
case M.lookup uname umap of
|
|
Just u -> return . Just $ u ^. userConn
|
|
_ -> do tellNumeric err_nosuchnick [uname, ":No such nick/channel"]
|
|
return Nothing
|
|
|
|
findReceivers :: [ByteString] -> IrcMonad [([Int], ByteString)]
|
|
findReceivers rcvNames =
|
|
fmap catMaybes $ forM rcvNames $ \name ->
|
|
if BS.head name == '#'
|
|
then do
|
|
cids <- channelTargets name
|
|
return $ case cids of
|
|
[] -> Nothing
|
|
cs -> Just (cs, name)
|
|
else do
|
|
cid <- userTarget name
|
|
return $ case cid of
|
|
Just c -> Just ([c], name)
|
|
Nothing -> Nothing
|
|
|
|
fwdMsg :: IrcMessage -> [([Int], ByteString)] -> IrcMonad ()
|
|
fwdMsg msg rcvs = forM_ rcvs $ \(cId, n) -> do
|
|
-- replace multiple targets with the single target we're doing
|
|
let m = msg{ params = n : tail (params msg) }
|
|
tell [Msg m cId]
|
|
|
|
fwdMsgNoReplace :: IrcMessage -> [([Int], ByteString)] -> IrcMonad ()
|
|
fwdMsgNoReplace msg rcvs = do
|
|
m <- addUserPrefix msg
|
|
forM_ rcvs $ \(cId, _) -> tell [Msg m cId]
|
|
|
|
|
|
addUserPrefix :: IrcMessage -> IrcMonad IrcMessage
|
|
addUserPrefix msg = do
|
|
regState <- use clientReg
|
|
return $ case regState ^? regdNick of
|
|
Just nickname -> msg{ prefix = Just . Right $ nickname }
|
|
_ -> msg{ prefix = Nothing }
|
|
|
|
chanEcho :: [ChanKey] -> IrcMessage -> IrcMonad ()
|
|
chanEcho chans iMsg = do
|
|
msg <- addUserPrefix iMsg
|
|
findReceivers chans >>= fwdMsg msg
|
|
|
|
allChanEcho :: IrcMessage -> IrcMonad ()
|
|
allChanEcho iMsg = do
|
|
mNick <- useNick
|
|
when (isJust mNick) $ do
|
|
let nn = fromJust mNick
|
|
mUser <- use $ clientServer . ircUsers . at nn
|
|
when (isJust mUser) $ do
|
|
let usr = fromJust mUser
|
|
let chans = S.elems $ usr ^. userChannels
|
|
chanEcho chans iMsg
|
|
|
|
-- * Adding responses to the Writer portion of the monad
|
|
|
|
tellYOURHOST :: NickKey -> IrcMonad ()
|
|
tellYOURHOST nickname = do
|
|
srvname <- view ircHostName
|
|
srvhost <- view ircHost
|
|
srvport <- view ircPort
|
|
version <- use $ clientServer . ircVersion
|
|
let hostStr = ppHostPreference srvhost
|
|
portStr = ppServiceName srvport
|
|
tellNumeric rpl_yourhost
|
|
[ nickname
|
|
, BS.concat [ ":Your host is ", srvname
|
|
, "[", hostStr, "/", portStr, "], "
|
|
, "running version ", version ]
|
|
]
|
|
|
|
tellMOTD :: NickKey -> IrcMonad ()
|
|
tellMOTD nickname = do
|
|
motd <- view ircMotd
|
|
tellNumeric rpl_motdstart [nickname, ":- Message of the Day -"]
|
|
forM_ motd $ \line ->
|
|
tellNumeric rpl_motd [nickname, ":- " `BS.append` line]
|
|
tellNumeric rpl_endofmotd [nickname, ":End of MOTD"]
|
|
|
|
tellWELCOME :: NickKey -> IrcMonad ()
|
|
tellWELCOME nickname = do
|
|
srvname <- view ircHostName
|
|
tellNumeric rpl_welcome [ nickname
|
|
, BS.append ":Welcome to IRC on " srvname ]
|
|
|
|
tellTOPIC :: ChanKey -> IrcMonad ()
|
|
tellTOPIC cname = do
|
|
chan <- use $ clientServer . ircChannels . at cname
|
|
case chan of
|
|
Just ch -> when (isJust $ ch ^. chanTopic) $
|
|
tellNumeric rpl_topic [cname, fromJust $ ch ^. chanTopic]
|
|
Nothing -> return ()
|
|
|
|
tellNAMES :: [ChanKey] -> IrcMonad ()
|
|
tellNAMES cnames = do
|
|
Just nn <- useNick
|
|
forM_ cnames $ \cname -> do
|
|
mChan <- use $ clientServer . ircChannels . at cname
|
|
case mChan of
|
|
Just chan -> do
|
|
let chanType = chanSigil chan
|
|
nickL <- forM (S.elems $ chan ^. chanUsers) $ \uname ->
|
|
return $ BS.append (chanUserSigil uname chan) uname
|
|
let cUsers = BS.append ":" (BS.intercalate " " nickL)
|
|
tellNumeric rpl_namreply [nn, chanType, cname, cUsers]
|
|
tellNumeric rpl_endofnames [nn, head cnames, ":End of /NAMES list"]
|
|
Nothing -> return ()
|
|
|
|
tellPrefixedCommand :: IrcCommand -> [IrcParam] -> IrcMonad ()
|
|
tellPrefixedCommand reply desc = do
|
|
srvname <- view ircHostName
|
|
connId <- use clientConn
|
|
let msg = IrcMessage (Just $ Left srvname) (Left reply) desc
|
|
tell [Msg msg [connId]]
|
|
|
|
tellCommand :: IrcCommand -> [IrcParam] -> IrcMonad ()
|
|
tellCommand reply desc = do
|
|
connId <- use clientConn
|
|
let msg = IrcMessage Nothing (Left reply) desc
|
|
tell [Msg msg [connId]]
|
|
|
|
tellNumeric :: IrcReply -> [IrcParam] -> IrcMonad ()
|
|
tellNumeric reply desc = do
|
|
srvname <- view ircHostName
|
|
connId <- use clientConn
|
|
let msg = IrcMessage (Just $ Left srvname) (Right reply) desc
|
|
tell [Msg msg [connId]]
|
|
return ()
|