kevin 0.1.3 → 0.1.3.1
raw patch · 9 files changed
+110/−137 lines, 9 filesdep +data-lens-template
Dependencies added: data-lens-template
Files
- Kevin/Base.hs +6/−43
- Kevin/Damn/Protocol.hs +25/−25
- Kevin/Damn/Protocol/Send.hs +3/−3
- Kevin/IRC/Protocol.hs +7/−6
- Kevin/IRC/Protocol/Send.hs +1/−1
- Kevin/Protocol.hs +15/−15
- Kevin/Types.hs +48/−39
- Kevin/Util/Logger.hs +1/−1
- kevin.cabal +4/−4
Kevin/Base.hs view
@@ -1,40 +1,26 @@ module Kevin.Base (- Kevin(..),- KevinIO,+ module Kevin.Types, KevinException(..), KevinServer(..), User(..),- Privclass,- Chatroom,- Title,- UserStore,- PrivclassStore,- TitleStore, -- * Modifiers addUser, removeUser, removeUserAll, setUsers,- usersL, numUsers, addPrivclass, setPrivclasses,- privclassL, getPcLevel, getPc, setUserPrivclass, changePrivclassName, - logIn,- - addToJoin,- joiningL, removeRoom, setTitle,- titlesL, -- * Exports module K,@@ -42,10 +28,6 @@ -- * Working with KevinState io, runPrinter,- getK,- putK,- getsK,- modifyK, if', @@ -56,14 +38,13 @@ import qualified Data.Text as T import qualified Data.ByteString.Char8 as T (hGetLine, hPutStr) import qualified Data.Text.Encoding as T-import Data.List (intercalate, nub, findIndices)+import Data.List (intercalate, findIndices) import Data.Maybe import System.IO as K (Handle, hClose, hIsClosed, hGetChar) import Control.Exception as K (IOException) import Network as K import Control.Applicative ((<$>))-import Control.Monad.Reader-import Control.Monad.State as K+import Control.Monad.Reader as K import Control.Concurrent as K (forkIO) import Control.Concurrent.Chan as K import Control.Concurrent.STM.TVar as K@@ -126,14 +107,8 @@ closeServer = hClose . damn -- Kevin modifiers-logIn :: Kevin -> Kevin-logIn k = k { loggedIn = True }--addToJoin :: [T.Text] -> Kevin -> Kevin-addToJoin rooms k = k { toJoin = nub $ rooms ++ toJoin k }- removeRoom :: Chatroom -> Kevin -> Kevin-removeRoom c = (privclassL ^%= M.delete c) . (usersL ^%= M.delete c)+removeRoom c = (privclasses ^%= M.delete c) . (users ^%= M.delete c) addUser :: Chatroom -> User -> UserStore -> UserStore addUser = (. return) . M.insertWith (++)@@ -156,18 +131,12 @@ setUsers :: Chatroom -> [User] -> UserStore -> UserStore setUsers = M.insert -usersL :: Lens Kevin UserStore-usersL = lens users (\t k -> k { users = t })- addPrivclass :: Chatroom -> Privclass -> PrivclassStore -> PrivclassStore addPrivclass room (p,i) = M.insertWith M.union room (M.singleton p i) setPrivclasses :: Chatroom -> [Privclass] -> PrivclassStore -> PrivclassStore setPrivclasses room ps = M.insert room (M.fromList ps) -privclassL :: Lens Kevin PrivclassStore-privclassL = lens privclasses (\t k -> k { privclasses = t })- getPc :: Chatroom -> T.Text -> UserStore -> Maybe T.Text getPc room user st = case M.lookup room st of Just qs -> privclass <$> listToMaybe (filter (\u -> username u == user) qs)@@ -177,18 +146,12 @@ getPcLevel room pcname store = fromMaybe 0 $ M.lookup room store >>= M.lookup pcname setUserPrivclass :: Chatroom -> T.Text -> T.Text -> Kevin -> Kevin-setUserPrivclass room user pc k = (usersL ^%= M.adjust (mapWhen ((user ==) . username) (\u -> u {privclass = pc, privclassLevel = pclevel})) room) k+setUserPrivclass room user pc k = (users ^%= M.adjust (mapWhen ((user ==) . username) (\u -> u {privclass = pc, privclassLevel = pclevel})) room) k where- pclevel = getPcLevel room pc $ privclasses k+ pclevel = getPcLevel room pc $ k ^. privclasses changePrivclassName :: Chatroom -> T.Text -> T.Text -> UserStore -> UserStore changePrivclassName room old new = M.adjust (mapWhen ((old ==) . privclass) (\u -> u {privclass = new})) room setTitle :: Chatroom -> Title -> TitleStore -> TitleStore setTitle = M.insert--titlesL :: Lens Kevin TitleStore-titlesL = lens titles (\t k -> k { titles = t })--joiningL :: Lens Kevin [T.Text]-joiningL = lens joining (\t k -> k { joining = t })
Kevin/Damn/Protocol.hs view
@@ -28,7 +28,7 @@ listen :: KevinIO () listen = fix (\f -> flip catches errHandlers $ do- k <- getK+ k <- get_ pkt <- io $ parsePacket <$> readServer k respond pkt (command pkt) f)@@ -36,23 +36,23 @@ -- main responder respond :: Packet -> T.Text -> KevinIO () respond _ "dAmnServer" = do- set <- getsK settings+ set <- gets_ settings let uname = getUsername set token = getAuthtoken set sendLogin uname token respond pkt "login" = if okay pkt then do- modifyK logIn- getsK toJoin >>= mapM_ sendJoin+ modify_ $ loggedIn ^= True+ gets_ (^. toJoin) >>= mapM_ sendJoin else I.sendNotice $ "Login failed: " `T.append` getArg "e" pkt respond pkt "join" = do roomname <- deformatRoom . fromJust . parameter $ pkt if okay pkt then do- modifyK $ joiningL ^%= (roomname:)- uname <- getsK $ getUsername . settings+ modify_ $ joining ^%= (roomname:)+ uname <- gets_ $ getUsername . settings I.sendJoin uname roomname else I.sendNotice $ T.concat ["Couldn't join ", roomname, ": ", getArg "e" pkt] @@ -60,8 +60,8 @@ roomname <- deformatRoom . fromJust . parameter $ pkt if okay pkt then do- uname <- getsK $ getUsername . settings- modifyK $ removeRoom roomname+ uname <- gets_ $ getUsername . settings+ modify_ $ removeRoom roomname I.sendPart uname roomname Nothing else I.sendNotice $ T.concat ["Couldn't part ", roomname, ": ", getArg "e" pkt] @@ -69,28 +69,28 @@ case getArg "p" pkt of "privclasses" -> do let pcs = parsePrivclasses . fromJust . body $ pkt- modifyK $ privclassL ^%= setPrivclasses roomname pcs+ modify_ $ privclasses ^%= setPrivclasses roomname pcs "topic" -> do- uname <- getsK $ getUsername . settings+ uname <- gets_ $ getUsername . settings I.sendTopic uname roomname (getArg "by" pkt) (T.replace "\n" " - " . entityDecode . tablumpDecode . fromJust . body $ pkt) (getArg "ts" pkt) - "title" -> modifyK $ titlesL ^%= setTitle roomname (T.replace "\n" " - " . entityDecode . tablumpDecode . fromJust . body $ pkt)+ "title" -> modify_ $ titles ^%= setTitle roomname (T.replace "\n" " - " . entityDecode . tablumpDecode . fromJust . body $ pkt) "members" -> do- (pcs,(uname,j)) <- getsK $ privclasses &&& getUsername . settings &&& joining+ (pcs,(uname,j)) <- gets_ $ getL privclasses &&& getUsername . settings &&& getL joining let members = map (mkUser roomname pcs . parsePacket) . init . splitOn "\n\n" . fromJust $ body pkt pc = privclass . head . filter (\x -> username x == uname) $ members n = nub members- modifyK $ usersL ^%= setUsers roomname members+ modify_ $ users ^%= setUsers roomname members when (roomname `elem` j) $ do I.sendUserList uname n roomname I.sendWhoList uname n roomname I.sendSetUserMode uname roomname $ getPcLevel roomname pc pcs- modifyK $ joiningL ^%= delete roomname+ modify_ $ joining ^%= delete roomname "info" -> do- us <- getsK $ getUsername . settings+ us <- gets_ $ getUsername . settings curtime <- io $ floor <$> getPOSIXTime let fixedPacket = parsePacket . T.init . T.replace "\n\nusericon" "\nusericon" . readable $ pkt uname = T.drop 6 . fromJust . parameter $ pkt@@ -107,9 +107,9 @@ case command pkt of "join" -> do let usname = fromJust $ parameter pkt- (pcs,countUser) <- getsK $ privclasses &&& numUsers roomname usname . users+ (pcs,countUser) <- gets_ $ getL privclasses &&& numUsers roomname usname . getL users let us = mkUser roomname pcs modifiedPkt- modifyK $ usersL ^%= addUser roomname us+ modify_ $ users ^%= addUser roomname us if countUser == 0 then do I.sendJoin usname roomname@@ -118,8 +118,8 @@ "part" -> do let uname = fromJust $ parameter pkt- modifyK $ usersL ^%= removeUser roomname uname- countUser <- getsK $ numUsers roomname uname . users+ modify_ $ users ^%= removeUser roomname uname+ countUser <- gets_ $ numUsers roomname uname . getL users if countUser < 1 then I.sendPart uname roomname $ case getArg "r" pkt of { "" -> Nothing; x -> Just x } else I.sendNoticeUnclone uname countUser roomname@@ -127,30 +127,30 @@ "msg" -> do let uname = arg "from" msg = fromJust (body pkt)- un <- getsK $ getUsername . settings+ un <- gets_ $ getUsername . settings unless (un == uname) $ I.sendChanMsg uname roomname (entityDecode $ tablumpDecode msg) "action" -> do let uname = arg "from" msg = fromJust (body pkt)- un <- getsK $ getUsername . settings+ un <- gets_ $ getUsername . settings unless (un == uname) $ I.sendChanAction uname roomname (entityDecode $ tablumpDecode msg) "privchg" -> do- (pcs,us) <- getsK $ privclasses &&& users+ (pcs,us) <- gets_ $ getL privclasses &&& getL users let user = fromJust $ parameter pkt by = arg "by" oldPc = getPc roomname user us newPc = arg "pc" oldPcLevel = fmap (\p -> getPcLevel roomname p pcs) oldPc newPcLevel = getPcLevel roomname newPc pcs- modifyK $ setUserPrivclass roomname user newPc+ modify_ $ setUserPrivclass roomname user newPc I.sendRoomNotice roomname $ T.concat [user, " has been moved", maybe "" (T.append " from ") oldPc, " to ", newPc, " by ", by] I.sendChangeUserMode user roomname (fromMaybe 0 oldPcLevel) newPcLevel "kicked" -> do let uname = fromJust $ parameter pkt- modifyK $ usersL ^%= removeUserAll roomname uname+ modify_ $ users ^%= removeUserAll roomname uname I.sendKick uname (arg "by") roomname $ case body pkt of {Just "" -> Nothing; x -> x} "admin" -> case fromJust $ parameter pkt of@@ -172,7 +172,7 @@ respond pkt "send" = I.sendNotice $ T.concat ["Send error: ", getArg "e" pkt] -respond _ "ping" = getK >>= \k -> io . writeServer k $ ("pong\n\0" :: T.Text)+respond _ "ping" = get_ >>= \k -> io . writeServer k $ ("pong\n\0" :: T.Text) respond _ str = klog Yellow $ "Got the packet called " ++ T.unpack str
Kevin/Damn/Protocol/Send.hs view
@@ -31,14 +31,14 @@ maybeBody = maybe "" (T.append "\n\n") sendPacket :: T.Text -> KevinIO ()-sendPacket p = getK >>= \k -> io . writeServer k . T.snoc p $ '\0'+sendPacket p = get_ >>= \k -> io . writeServer k . T.snoc p $ '\0' formatRoom :: T.Text -> KevinIO T.Text formatRoom b = case T.splitAt 1 b of ("#",s) -> return $ "chat:" `T.append` s ("&",s) -> do- uname <- getsK (getUsername . settings)+ uname <- gets_ (getUsername . settings) return . T.append "pchat:" . T.intercalate ":" . sort . map (T.map toLower) $ [uname, s] r -> return $ "chat" `T.append` uncurry T.append r @@ -46,7 +46,7 @@ deformatRoom room = if "chat:" `T.isPrefixOf` room then return $ '#' `T.cons` T.drop 5 room else do- uname <- getsK (getUsername . settings)+ uname <- gets_ (getUsername . settings) return $ '&' `T.cons` head (filter (/= uname) . T.splitOn ":" . T.drop 6 $ room) type Str = T.Text -- just make it shorter
Kevin/IRC/Protocol.hs view
@@ -16,6 +16,7 @@ import Kevin.IRC.Protocol.Send import Control.Applicative ((<$>)) import Control.Arrow+import Control.Monad.State import Data.Maybe import Data.List (nubBy) import Data.Function (on)@@ -28,7 +29,7 @@ listen :: KevinIO () listen = fix (\f -> flip catches errHandlers $ do- k <- getK+ k <- get_ pkt <- io $ parsePacket <$> readClient k respond pkt (command pkt) f)@@ -36,10 +37,10 @@ respond :: Packet -> T.Text -> KevinIO () respond BadPacket _ = sendNotice "Bad packet, try again." respond pkt "JOIN" = do- l <- getsK loggedIn+ l <- gets_ (^. loggedIn) if l then mapM_ D.sendJoin rooms- else modifyK (addToJoin rooms)+ else modify_ $ joining ^%= (rooms ++) where rooms = T.splitOn "," . head . params $ pkt @@ -61,7 +62,7 @@ "o" -> if' toggle D.sendPromote D.sendDemote (head $ params pkt) (last $ params pkt) Nothing _ -> sendRoomNotice (head $ params pkt) $ "Unsupported mode " `T.append` mode else do- uname <- getsK (getUsername . settings)+ uname <- gets_ (getUsername . settings) sendChanMode uname (head $ params pkt) respond pkt "TOPIC" = case params pkt of@@ -72,7 +73,7 @@ respond pkt "TITLE" = case params pkt of [] -> sendNotice "Malformed packet" [room] -> do- title <- getsK (M.lookup room . titles)+ title <- gets_ $ M.lookup room . (^. titles) let pre = T.concat ["Title for ", room, ": "] in mapM_ (sendRoomNotice room . T.append pre) (T.splitOn "\n" $ fromMaybe "" title) (room:title) -> D.sendSet room "title" $ T.unwords title @@ -82,7 +83,7 @@ respond pkt "NAMES" = do let (room:_) = params pkt- (me,uss) <- getsK (getUsername . settings &&& M.lookup room . users)+ (me,uss) <- gets_ $ getUsername . settings &&& M.lookup room . getL users sendUserList me (nubBy ((==) `on` username) $ fromMaybe [] uss) room respond pkt "KICK" = let p = params pkt in D.sendKick (head p) (p !! 1) (if length p > 2 then Just $ last p else Nothing)
Kevin/IRC/Protocol/Send.hs view
@@ -28,7 +28,7 @@ getHost u = T.concat [":", u, "!", u, "@chat.deviantart.com"] sendPacket :: T.Text -> KevinIO ()-sendPacket p = getK >>= \k -> io . writeClient k $ T.append p "\r\n"+sendPacket p = get_ >>= \k -> io . writeClient k $ T.append p "\r\n" maybeBody :: Maybe T.Text -> T.Text maybeBody = maybe "" (T.append " :")
Kevin/Protocol.hs view
@@ -7,6 +7,7 @@ import qualified Kevin.IRC.Protocol as C import qualified Kevin.Damn.Protocol as S import System.IO (hSetBuffering, BufferMode(..))+import Control.Monad.State import Data.Monoid (mempty) watchInterrupt :: [E.Handler (Maybe Kevin)]@@ -24,19 +25,18 @@ logChan <- newChan damnChan <- newChan ircChan <- newChan- return $ Just Kevin { damn = damnSock- , irc = client- , dChan = damnChan- , iChan = ircChan- , settings = set- , users = mempty- , privclasses = mempty- , titles = mempty- , toJoin = mempty- , joining = mempty- , loggedIn = False- , logger = logChan- }+ return . Just $ Kevin damnSock+ client+ damnChan+ ircChan+ set+ mempty+ mempty+ mempty+ mempty+ mempty+ False+ logChan mkListener :: Int -> IO Socket mkListener = listenOn . PortNumber . fromIntegral@@ -57,6 +57,6 @@ runLogger (logger kevin) runPrinter (dChan kevin) (damn kevin) runPrinter (iChan kevin) (irc kevin)- forkIO $ evalStateT (bracket_ S.initialize (S.cleanup >> io (closeClient kevin)) S.listen) mvar- forkIO $ evalStateT (bracket_ (return ()) (C.cleanup >> io (closeServer kevin)) C.listen) mvar+ forkIO . void $ runReaderT (bracket_ S.initialize (S.cleanup >> io (closeClient kevin)) S.listen) mvar+ forkIO . void $ runReaderT (bracket_ (return ()) (C.cleanup >> io (closeServer kevin)) C.listen) mvar return ()
Kevin/Types.hs view
@@ -1,5 +1,5 @@ module Kevin.Types (- Kevin(..),+ Kevin(Kevin), KevinIO, Privclass, Chatroom,@@ -8,10 +8,16 @@ PrivclassStore, UserStore, TitleStore,- getK,- getsK,- putK,- modifyK+ get_,+ gets_,+ put_,+ modify_,+ + -- lenses+ users, privclasses, titles, toJoin, joining, loggedIn,+ + -- other accessors+ damn, irc, dChan, iChan, settings, logger ) where import qualified Data.Text as T@@ -19,42 +25,10 @@ import System.IO import Control.Concurrent import Control.Concurrent.STM.TVar-import Control.Monad.State+import Control.Monad.Reader import Control.Monad.STM (atomically) import Kevin.Settings--data Kevin = Kevin { damn :: Handle- , irc :: Handle- , dChan :: Chan T.Text- , iChan :: Chan T.Text- , settings :: Settings- , users :: UserStore- , privclasses :: PrivclassStore- , titles :: TitleStore- , toJoin :: [T.Text]- , joining :: [T.Text]- , loggedIn :: Bool- , logger :: Chan String- }--type KevinIO = StateT (TVar Kevin) IO--getK :: KevinIO Kevin-getK = get >>= liftIO . readTVarIO--putK :: Kevin -> KevinIO ()-putK k = get >>= io . atomically . flip writeTVar k--getsK :: (Kevin -> a) -> KevinIO a-getsK = flip liftM getK--modifyK :: (Kevin -> Kevin) -> KevinIO ()-modifyK f = do- var <- get- io . atomically $ modifyTVar var f--io :: MonadIO m => IO a -> m a-io = liftIO+import Data.Lens.Template type Chatroom = T.Text @@ -75,3 +49,38 @@ type Title = T.Text type TitleStore = M.Map Chatroom Title++data Kevin = Kevin { damn :: Handle+ , irc :: Handle+ , dChan :: Chan T.Text+ , iChan :: Chan T.Text+ , settings :: Settings+ , _users :: UserStore+ , _privclasses :: PrivclassStore+ , _titles :: TitleStore+ , _toJoin :: [T.Text]+ , _joining :: [T.Text]+ , _loggedIn :: Bool+ , logger :: Chan String+ }++$( makeLens ''Kevin )++type KevinIO = ReaderT (TVar Kevin) IO++get_ :: KevinIO Kevin+get_ = ask >>= liftIO . readTVarIO++put_ :: Kevin -> KevinIO ()+put_ k = ask >>= io . atomically . flip writeTVar k++gets_ :: (Kevin -> a) -> KevinIO a+gets_ = flip liftM get_++modify_ :: (Kevin -> Kevin) -> KevinIO ()+modify_ f = do+ var <- ask+ io . atomically $ modifyTVar var f++io :: MonadIO m => IO a -> m a+io = liftIO
Kevin/Util/Logger.hs view
@@ -49,7 +49,7 @@ klogNow c s = putStrLn $ render c s klog :: Color -> String -> KevinIO ()-klog c str = getsK logger >>= \ch -> liftIO $ klog_ ch c str+klog c str = gets_ logger >>= \ch -> liftIO $ klog_ ch c str klogError, klogWarn :: String -> KevinIO ()
kevin.cabal view
@@ -1,5 +1,5 @@ Name: kevin-Version: 0.1.3+Version: 0.1.3.1 Synopsis: a dAmn ↔ IRC proxy Description: a dAmn ↔ IRC proxy License: GPL@@ -16,8 +16,8 @@ Executable kevin Main-is: Main.hs- Build-Depends: attoparsec == 0.10.*, base == 4.*, bytestring == 0.9.*, containers == 0.4.*, cprng-aes == 0.2.*, data-lens == 2.10.*, HTTP == 4000.2.*, MonadCatchIO-mtl == 0.3.*, mtl == 2.1.*, network == 2.3.*, regex-pcre-builtin == 0.94.*, stm == 2.3.*, text == 0.11.*, time == 1.4.*, tls == 0.9.*, tls-extra == 0.4.*+ Build-Depends: attoparsec == 0.10.*, base == 4.*, bytestring == 0.9.*, containers == 0.4.*, cprng-aes == 0.2.*, data-lens == 2.10.*, data-lens-template == 2.1.*, HTTP == 4000.2.*, MonadCatchIO-mtl == 0.3.*, mtl == 2.1.*, network == 2.3.*, regex-pcre-builtin == 0.94.*, stm == 2.3.*, text == 0.11.*, time == 1.4.*, tls == 0.9.*, tls-extra == 0.4.* Other-Modules: Kevin, Kevin.Protocol, Kevin.Base, Kevin.Util.Logger, Kevin.IRC.Protocol, Kevin.Damn.Protocol, Kevin.Util.Entity, Kevin.Util.Tablump, Kevin.Damn.Packet, Kevin.Damn.Protocol.Send, Kevin.IRC.Protocol.Send, Kevin.IRC.Packet, Kevin.Settings, Kevin.Types, Kevin.Util.Token- extensions: CPP, DeriveDataTypeable, ExistentialQuantification, OverloadedStrings, ScopedTypeVariables+ extensions: CPP, DeriveDataTypeable, ExistentialQuantification, OverloadedStrings, ScopedTypeVariables, TemplateHaskell ghc-options: -Wall -fno-warn-unused-do-bind -threaded- cpp-options: -DVERSION="0.1.3"+ cpp-options: -DVERSION="0.1.3.1"