packages feed

kevin 0.8.2 → 0.9.0

raw patch · 17 files changed

+598/−797 lines, 17 files

Files

Kevin/Base.hs view
@@ -36,16 +36,15 @@ import System.IO.Error  runPrinter :: Chan T.Text -> Handle -> IO ()-runPrinter ch h = void . forkIO . forever $ readChan ch >>=-    T.hPutStr h . T.encodeUtf8+runPrinter ch h = void . forkIO . forever $ readChan ch >>= T.hPutStr h . T.encodeUtf8  io :: MonadIO m => IO a -> m a io = liftIO  class KevinServer a where-    readClient, readServer :: a -> IO T.Text-    writeServer :: a -> T.Text -> IO ()-    writeClient :: a -> T.Text -> IO ()+    readClient, readServer   :: a -> IO T.Text+    writeServer              :: a -> T.Text -> IO ()+    writeClient              :: a -> T.Text -> IO ()     closeClient, closeServer :: a -> IO ()  data KevinException = ParseFailure@@ -59,24 +58,21 @@ -- actions  hGetCharTimeout :: Handle -> Int -> IO Char-hGetCharTimeout h t = do-    hSetBuffering h NoBuffering-    ready <- hWaitForInput h t-    if ready-        then hGetChar h-        else throwIO $ mkIOError eofErrorType "read timeout" (Just h) Nothing+hGetCharTimeout h t = do hSetBuffering h NoBuffering+                         ready <- hWaitForInput h t+                         if ready+                            then hGetChar h+                            else throwIO $ mkIOError eofErrorType "read timeout" (Just h) Nothing  hGetSep :: Char -> Handle -> IO String-hGetSep sep h = fix (\f -> do-    ch <- hGetCharTimeout h 180000-    if ch == sep-        then return ""-        else (ch:) <$> f)+hGetSep sep h = fix (\f -> do ch <- hGetCharTimeout h 180000+                              if ch == sep+                                 then return ""+                                 else (ch:) <$> f)  instance KevinServer Kevin where-    readClient k = do-        line <- T.decodeUtf8 <$> T.hGetLine (irc k)-        return $ T.init line+    readClient k = do line <- T.decodeUtf8 <$> T.hGetLine (irc k)+                      return $ T.init line     readServer k = T.pack <$> hGetSep '\NUL' (damn k)      writeClient k = writeChan (iChan k)
Kevin/Chatrooms.hs view
@@ -25,7 +25,7 @@  deleteBy' :: (a -> Bool) -> [a] -> [a] deleteBy' f (x:xs) = if f x then xs else x:deleteBy' f xs-deleteBy' _ [] = []+deleteBy' _ []     = []  removeRoom :: Chatroom -> KevinIO () removeRoom c = kevin $ privclasses.at c .= Nothing >> users.at c .= Nothing@@ -34,12 +34,9 @@ addUser ch us = kevin $ users.ix ch %= (us:)  numUsers :: Chatroom -> T.Text -> KevinIO Int-numUsers ch us = do-    st <- gets_ $ view users-    case st^.at ch of-        Just usrs -> return . length-            $ findIndices (\u -> us == username u) usrs-        Nothing -> return 0+numUsers ch us = do st <- gets_ $ view users+                    case st^.at ch of Just usrs -> return . length $ findIndices (\u -> us == username u) usrs+                                      Nothing   -> return 0  removeUser :: Chatroom -> T.Text -> KevinIO () removeUser ch us = kevin $ users.ix ch %= deleteBy' ((== us) . username)@@ -54,23 +51,17 @@ setPrivclasses room ps = kevin $ privclasses.at room ?= M.fromList ps  getPrivclass :: Chatroom -> T.Text -> KevinIO (Maybe T.Text)-getPrivclass room user = do-    st <- gets_ $ view users-    case st^.at room of-        Just qs -> return $ privclass-            <$> listToMaybe (filter ((== user) . username) qs)-        Nothing -> return Nothing+getPrivclass room user = do st <- gets_ $ view users+                            case st^.at room of Just qs -> return $ privclass <$> listToMaybe (filter ((== user) . username) qs)+                                                Nothing -> return Nothing          getPrivclassLevel :: Chatroom -> T.Text -> KevinIO Int-getPrivclassLevel room pc = do-    st <- gets_ $ view privclasses-    return . fromMaybe 0 $ st^.at room >>= (^.at pc)+getPrivclassLevel room pc = do st <- gets_ $ view privclasses+                               return . fromMaybe 0 $ st^.at room >>= (^.at pc)  setUserPrivclass :: Chatroom -> T.Text -> T.Text -> KevinIO ()-setUserPrivclass room user pc = do-    pclevel <- getPrivclassLevel room pc-    kevin $ users.ix room.traverse.filtered ((user ==) . username)-            %= (\u -> u {privclass = pc, privclassLevel = pclevel})+setUserPrivclass room user pc = do pclevel <- getPrivclassLevel room pc+                                   kevin $ users.ix room.traverse.filtered ((user ==) . username) %= (\u -> u {privclass = pc, privclassLevel = pclevel})  setTitle :: Chatroom -> T.Text -> KevinIO () setTitle ch t = kevin $ titles.at ch ?= t
Kevin/Damn/Packet.hs view
@@ -45,41 +45,35 @@ parseCommand = takeWhile1 (not . isSpace)  parseParam :: Parser (Maybe T.Text)-parseParam = do-    char ' '-    parm <- takeWhile1 (not . isSpace)-    return $ Just parm+parseParam = do char ' '+                parm <- takeWhile1 (not . isSpace)+                return $ Just parm  parseArgs :: Parser (M.Map T.Text T.Text)-parseArgs = (M.fromList <$>) . many $ do-    char '\n'-    c <- takeTill (=='=')-    char '='-    r <- takeTill (=='\n')-    return (c,r)+parseArgs = (M.fromList <$>) . many $ do char '\n'+                                         c <- takeTill (=='=')+                                         char '='+                                         r <- takeTill (=='\n')+                                         return (c,r)  parseHead :: Parser Packet-parseHead = do-    c <- parseCommand-    p <- option Nothing parseParam-    a <- parseArgs-    return $ Packet c p a Nothing+parseHead = do c <- parseCommand+               p <- option Nothing parseParam+               a <- parseArgs+               return $ Packet c p a Nothing  parsePacket :: T.Text -> Packet-parsePacket pack = case parseOnly parseHead top of-    Left _ -> throw ParseFailure-    Right res -> res & body .~ (T.drop 2 <$> toMaybe (not . T.null) b)-    where-        (top, b) = T.breakOn "\n\n" pack+parsePacket pack = case parseOnly parseHead top of Left _    -> throw ParseFailure+                                                   Right res -> res & body .~ (T.drop 2 <$> toMaybe (not . T.null) b)+    where (top, b) = T.breakOn "\n\n" pack  getResult :: Either String a -> a getResult x = let Right e = x in e  fixLoginPacket :: Packet -> Packet fixLoginPacket pkt = if pkt^.command == "login"-    then pkt & args %~ (<> getResult-        (parseOnly parseArgs . T.cons '\n' . fromJust $ pkt^.body))-    else pkt+                        then pkt & args %~ (<> getResult (parseOnly parseArgs . T.cons '\n' . fromJust $ pkt^.body))+                        else pkt  subPacket :: Packet -> Maybe Packet subPacket = (parsePacket <$>) . view body@@ -88,18 +82,15 @@ okay (Packet _ _ a _) = let e = a ^. at "e" in isNothing e || e == Just "ok"  parsePrivclasses :: T.Text -> [Privclass]-parsePrivclasses = map (liftM2 (,) (!! 1) (read . T.unpack . head)-                       . T.splitOn ":") +parsePrivclasses = map (liftM2 (,) (!! 1) (read . T.unpack . head) . T.splitOn ":")                   . filter (not . T.null)                  . T.splitOn "\n"  readable :: Packet -> T.Text-readable (Packet cmd param arg bod) = cmd-                                   <> maybe "" (' ' `T.cons`) param-                                   <> formattedArgs (M.toList arg)-                                   <> maybe "" ("\n\n" <>) bod-                                   <> "\n\0"+readable (Packet cmd param arg bod) = cmd <> maybe "" (' ' `T.cons`) param+                                          <> formattedArgs (M.toList arg)+                                          <> maybe "" ("\n\n" <>) bod+                                          <> "\n\0"     where         formattedArgs [] = ""-        formattedArgs q = ("\n" <>) . T.intercalate "\n"-                        . map (uncurry (\x y -> x <> "=" <> y)) $ q+        formattedArgs q  = ("\n" <>) . T.intercalate "\n" . map (uncurry (\x y -> x <> "=" <> y)) $ q
Kevin/Damn/Protocol.hs view
@@ -28,197 +28,151 @@ cleanup = klog Blue "cleanup server"  listen :: KevinIO ()-listen = fix (\f -> flip catches errHandlers $ do-    k <- get_-    pkt <- io $ parsePacket <$> readServer k-    respond pkt (view command pkt)-    f)+listen = fix (\f -> flip catches errHandlers $ do k   <- get_+                                                  pkt <- io $ parsePacket <$> readServer k+                                                  respond pkt (view command pkt)+                                                  f)  -- main responder respond :: Packet -> T.Text -> KevinIO ()-respond _ "dAmnServer" = do-    s <- use_ settings-    sendLogin (s^.name) (s^.authtoken)+respond _ "dAmnServer" = do s <- use_ settings+                            sendLogin (s^.name) (s^.authtoken)  respond pkt "login" = if okay pkt-    then do-        j <- kevin $ do-           loggedIn .= True-           use joining-        mapM_ sendJoin j-    else I.sendNotice $ "Login failed: " <> pkt ^. args.ix "e"+                         then do j <- kevin $ do loggedIn .= True+                                                 use joining+                                 mapM_ sendJoin j+                         else I.sendNotice $ "Login failed: " <> pkt ^. args.ix "e" -respond pkt "join" = do-    roomname <- deformatRoom $ pkt^.parameter._Just-    if okay pkt-        then do-            kevin $ joining %= (roomname:)-            uname <- use_ name-            I.sendJoin uname roomname-        else I.sendNotice $ T.concat-            ["Couldn't join ", roomname, ": ", pkt^.args.ix "e"]+respond pkt "join" = do roomname <- deformatRoom $ pkt^.parameter._Just+                        if okay pkt+                           then do kevin $ joining %= (roomname:)+                                   uname <- use_ name+                                   I.sendJoin uname roomname+                           else I.sendNotice $ T.concat ["Couldn't join ", roomname, ": ", pkt^.args.ix "e"] -respond pkt "part" = do-    roomname <- deformatRoom $ pkt^.parameter._Just-    if okay pkt-        then do-            uname <- use_ name-            removeRoom roomname-            I.sendPart uname roomname Nothing-        else I.sendNotice $ T.concat-            ["Couldn't part ", roomname, ": ", pkt^.args.ix "e"]+respond pkt "part" = do roomname <- deformatRoom $ pkt^.parameter._Just+                        if okay pkt+                            then do uname <- use_ name+                                    removeRoom roomname+                                    I.sendPart uname roomname Nothing+                            else I.sendNotice $ T.concat ["Couldn't part ", roomname, ": ", pkt^.args.ix "e"] -respond pkt "property" = do-    roomname <- deformatRoom (pkt^.parameter._Just)-    case pkt^.args.ix "p" of-        "privclasses" -> setPrivclasses roomname . parsePrivclasses-            $ pkt^.body._Just+respond pkt "property" = do roomname <- deformatRoom (pkt^.parameter._Just)+                            case pkt^.args.ix "p" of "privclasses" -> setPrivclasses roomname . parsePrivclasses $ pkt^.body._Just -        "topic" -> do-            uname <- use_ name-            I.sendTopic uname roomname-                        (fromMaybe uname $ pkt^.args.at "by")-                        (T.replace "\n" " - " . entityDecode-                            . tablumpDecode $ pkt^.body._Just)-                        (pkt^.args.ix "ts")+                                                     "topic" -> do uname <- use_ name+                                                                   I.sendTopic uname roomname (fromMaybe uname $ pkt^.args.at "by")+                                                                                              (T.replace "\n" " - " . entityDecode . tablumpDecode $ pkt^.body._Just)+                                                                                              (pkt^.args.ix "ts") -        "title" -> setTitle roomname (T.replace "\n" " - " . entityDecode-                                     . tablumpDecode $ pkt^.body._Just)+                                                     "title" -> setTitle roomname (T.replace "\n" " - " . entityDecode . tablumpDecode $ pkt^.body._Just) -        "members" -> do-            k <- get_-            let members = map (mkUser roomname (k^.privclasses) . parsePacket)-                        . init . T.splitOn "\n\n"-                        $ pkt^.body._Just-                pc = privclass . head . filter (\x -> username x == k^.name)-                   $ members-                n = nub members-            setUsers roomname members-            when (roomname `elem` k^.joining) $ do-                I.sendUserList (k^.name) n roomname-                pclevel <- getPrivclassLevel roomname pc-                I.sendSetUserMode (k^.name) roomname pclevel-                kevin $ joining %= delete roomname+                                                     "members" -> do k <- get_+                                                                     let members = map (mkUser roomname (k^.privclasses) . parsePacket)+                                                                                 . init . T.splitOn "\n\n"+                                                                                 $ pkt^.body._Just+                                                                         pc      = privclass . head . filter (\x -> username x == k^.name) $ members+                                                                         n       = nub members+                                                                     setUsers roomname members+                                                                     when (roomname `elem` k^.joining) $ do I.sendUserList (k^.name) n roomname+                                                                                                            pclevel <- getPrivclassLevel roomname pc+                                                                                                            I.sendSetUserMode (k^.name) roomname pclevel+                                                                                                            kevin $ joining %= delete roomname -        "info" -> do-            us <- use_ name-            curtime <- io $ floor <$> getPOSIXTime-            let fixedPacket = parsePacket . T.init-                            . T.replace "\n\nusericon" "\nusericon"-                            . readable $ pkt-                uname = T.drop 6 $ pkt^.parameter._Just-                rn = fixedPacket^.args.ix "realname"-                conns = map (\pk ->-                    let x = parsePacket $ "conn" <> pk-                    in ( read (T.unpack $ x^.args.ix "online") :: Int-                       , read (T.unpack $ x^.args.ix "idle") :: Int-                       , map (T.drop 8) . filter (not . T.null)-                       . T.splitOn "\n\n" $ x^.body._Just-                    )-                  ) . tail . T.splitOn "conn"-                    $ fixedPacket^.body._Just-                allRooms = nub $ conns >>= (\(_,_,c) -> c)-                (onlinespan,idle,_) = minimumBy (comparing (view _1)) conns-                signon = curtime - onlinespan-            I.sendWhoisReply us uname (entityDecode rn) allRooms idle signon+                                                     "info" -> do us <- use_ name+                                                                  curtime <- io $ floor <$> getPOSIXTime+                                                                  let fixedPacket = parsePacket . T.init+                                                                                  . T.replace "\n\nusericon" "\nusericon"+                                                                                  . readable $ pkt+                                                                      uname = T.drop 6 $ pkt^.parameter._Just+                                                                      rn    = fixedPacket^.args.ix "realname"+                                                                      conns = map (\pk -> let x = parsePacket $ "conn" <> pk+                                                                                           in ( read (T.unpack $ x^.args.ix "online") :: Int+                                                                                              , read (T.unpack $ x^.args.ix "idle"  ) :: Int+                                                                                              , map (T.drop 8) . filter (not . T.null) . T.splitOn "\n\n" $ x^.body._Just+                                                                                              )) . tail . T.splitOn "conn" $ fixedPacket^.body._Just+                                                                      allRooms            = nub $ conns >>= (\(_,_,c) -> c)+                                                                      (onlinespan,idle,_) = minimumBy (comparing (view _1)) conns+                                                                      signon              = curtime - onlinespan+                                                                  I.sendWhoisReply us uname (entityDecode rn) allRooms idle signon -        q -> klogError $ "Unrecognized property " ++ T.unpack q+                                                     q -> klogError $ "Unrecognized property " ++ T.unpack q -respond spk "recv" = deformatRoom (spk^.parameter._Just) >>= \roomname ->-    case pkt^.command of-    "join" -> do-        let usname = pkt^.parameter._Just-        pcs <- gets_ $ view privclasses-        countUser <- numUsers roomname usname-        let us = mkUser roomname pcs modifiedPkt-        addUser roomname us-        if countUser == 0-            then do-                I.sendJoin usname roomname-                pclevel <- getPrivclassLevel roomname-                    $ modifiedPkt^.args.ix "pc"-                I.sendSetUserMode usname roomname pclevel-            else I.sendNoticeClone (username us) (succ countUser) roomname+respond spk "recv" = deformatRoom (spk^.parameter._Just) >>=+    \roomname -> case pkt^.command of "join" -> do let usname = pkt^.parameter._Just+                                                   pcs <- gets_ $ view privclasses+                                                   countUser <- numUsers roomname usname+                                                   let us = mkUser roomname pcs modifiedPkt+                                                   addUser roomname us+                                                   if countUser == 0+                                                      then do I.sendJoin usname roomname+                                                              pclevel <- getPrivclassLevel roomname $ modifiedPkt^.args.ix "pc"+                                                              I.sendSetUserMode usname roomname pclevel+                                                      else I.sendNoticeClone (username us) (succ countUser) roomname -    "part" -> do-        let uname = pkt^.parameter._Just-        removeUser roomname uname-        countUser <- numUsers roomname uname-        if countUser < 1-            then I.sendPart uname roomname $ pkt^.args.at "r"-            else I.sendNoticeUnclone uname countUser roomname+                                      "part" -> do let uname = pkt^.parameter._Just+                                                   removeUser roomname uname+                                                   countUser <- numUsers roomname uname+                                                   if countUser < 1+                                                      then I.sendPart uname roomname $ pkt^.args.at "r"+                                                      else I.sendNoticeUnclone uname countUser roomname -    "msg" -> do-        let uname = arg "from"-            msg   = pkt^.body._Just-        un <- use_ name-        unless (un == uname) $ I.sendChanMsg uname roomname-            (entityDecode $ tablumpDecode msg)+                                      "msg" -> do let uname = arg "from"+                                                      msg   = pkt^.body._Just+                                                  un <- use_ name+                                                  unless (un == uname) $ I.sendChanMsg uname roomname (entityDecode $ tablumpDecode msg) -    "action" -> do-        let uname = arg "from"-            msg   = pkt^.body._Just-        un <- use_ name-        unless (un == uname) $ I.sendChanAction uname roomname-            (entityDecode $ tablumpDecode msg)+                                      "action" -> do let uname = arg "from"+                                                         msg   = pkt^.body._Just+                                                     un <- use_ name+                                                     unless (un == uname) $ I.sendChanAction uname roomname (entityDecode $ tablumpDecode msg) -    "privchg" -> do-        let user = pkt^.parameter._Just-            by = arg "by"-            newPc = arg "pc"-        oldPc <- getPrivclass roomname user-        oldPcLevel <- getPrivclassLevel roomname (fromMaybe "" oldPc)-        newPcLevel <- getPrivclassLevel roomname newPc-        setUserPrivclass roomname user newPc-        I.sendRoomNotice roomname $ T.concat-            [user, " has been moved"-            , maybe "" (" from " <>) oldPc-            , " to ", newPc, " by ", by-            ]-        I.sendChangeUserMode user roomname oldPcLevel newPcLevel+                                      "privchg" -> do let user  = pkt^.parameter._Just+                                                          by    = arg "by"+                                                          newPc = arg "pc"+                                                      oldPc      <- getPrivclass roomname user+                                                      oldPcLevel <- getPrivclassLevel roomname (fromMaybe "" oldPc)+                                                      newPcLevel <- getPrivclassLevel roomname newPc+                                                      setUserPrivclass roomname user newPc+                                                      I.sendRoomNotice roomname $ T.concat [ user, " has been moved"+                                                                                           , maybe "" (" from " <>) oldPc+                                                                                           , " to ", newPc, " by ", by+                                                                                           ]+                                                      I.sendChangeUserMode user roomname oldPcLevel newPcLevel -    "kicked" -> do-        let uname = pkt^.parameter._Just-        removeUserAll roomname uname-        I.sendKick uname (arg "by") roomname $ pkt^.body.traverse ^? notNull_+                                      "kicked" -> do let uname = pkt^.parameter._Just+                                                     removeUserAll roomname uname+                                                     I.sendKick uname (arg "by") roomname $ pkt^.body.traverse ^? notNull_ -    "admin" -> case pkt^.parameter._Just of-        "create" -> I.sendRoomNotice roomname $-            T.concat ["Privclass ", arg "name"-                     , " created by ", arg "by"-                     , " with: ", arg "privs"]-        "update" -> I.sendRoomNotice roomname $-            T.concat ["Privclass ", arg "name"-                     , " updated by ", arg "by"-                     , " with: ", arg "privs"]-        "rename" -> I.sendRoomNotice roomname $-            T.concat ["Privclass ", arg "prev"-                     , " renamed to ", arg "name"-                     , " by ", arg "by"]-        "move"   -> I.sendRoomNotice roomname $-            T.concat [ arg "n", " users in privclass "-                     , arg "prev", " moved to "-                     , arg "name", " by ", arg "by"]-        "remove" -> I.sendRoomNotice roomname $-            T.concat ["Privclass", arg "name", " removed by ", arg "by"]-        "show"   -> mapM_ (I.sendRoomNotice roomname) . T.splitOn "\n"-                        $ pkt^.body._Just-        "privclass" -> I.sendRoomNotice roomname $ "Admin error: " <> arg "e"-        q -> klogError $ "Unknown admin packet type " ++ show q+                                      "admin" -> case pkt^.parameter._Just of "create"    -> I.sendRoomNotice roomname $ T.concat [ "Privclass ", arg "name"+                                                                                                                                  , " created by ", arg "by"+                                                                                                                                  , " with: ", arg "privs" ]+                                                                              "update"    -> I.sendRoomNotice roomname $ T.concat [ "Privclass ", arg "name"+                                                                                                                                  , " updated by ", arg "by"+                                                                                                                                  , " with: ", arg "privs" ]+                                                                              "rename"    -> I.sendRoomNotice roomname $ T.concat [ "Privclass ", arg "prev"+                                                                                                                                  , " renamed to ", arg "name"+                                                                                                                                  , " by ", arg "by" ]+                                                                              "move"      -> I.sendRoomNotice roomname $ T.concat [ arg "n", " users in privclass "+                                                                                                                                  , arg "prev", " moved to "+                                                                                                                                  , arg "name", " by ", arg "by" ]+                                                                              "remove"    -> I.sendRoomNotice roomname $ T.concat [ "Privclass", arg "name"+                                                                                                                                  , " removed by ", arg "by" ]+                                                                              "show"      -> mapM_ (I.sendRoomNotice roomname) . T.splitOn "\n" $ pkt^.body._Just+                                                                              "privclass" -> I.sendRoomNotice roomname $ "Admin error: " <> arg "e"+                                                                              q           -> klogError $ "Unknown admin packet type " ++ show q -    x -> klogError $ "Unknown packet type " ++ show x+                                      x -> klogError $ "Unknown packet type " ++ show x -    where-        pkt = fromJust $ subPacket spk-        modifiedPkt = parsePacket (T.replace "\n\npc" "\npc" $ readable pkt)-        arg s = pkt^.args.ix s+    where pkt         = fromJust $ subPacket spk+          modifiedPkt = parsePacket (T.replace "\n\npc" "\npc" $ readable pkt)+          arg s       = pkt^.args.ix s -respond pkt "kicked" = do-    roomname <- deformatRoom $ pkt^.parameter._Just-    uname <- use_ name-    removeRoom roomname-    I.sendKick uname (pkt^.args.ix "by") roomname-        $ pkt^.body.traverse ^? notNull_+respond pkt "kicked" = do roomname <- deformatRoom $ pkt^.parameter._Just+                          uname <- use_ name+                          removeRoom roomname+                          I.sendKick uname (pkt^.args.ix "by") roomname $ pkt^.body.traverse ^? notNull_  respond pkt "send" = I.sendNotice $ "Send error: " <> pkt^.args.ix "e" @@ -229,16 +183,14 @@  mkUser :: Chatroom -> PrivclassStore -> Packet -> User mkUser room st p = User (p^.parameter._Just)-                       (g "pc")-                       (fromMaybe 0 $ st^.at room >>= (^.at (g "pc")))-                       (g "symbol")-                       (entityDecode $ g "realname")-                       (g "typename")-                       (g "gpc")-    where-        g s = p^.args.ix s+                        (g "pc")+                        (fromMaybe 0 $ st^.at room >>= (^.at (g "pc")))+                        (g "symbol")+                        (entityDecode $ g "realname")+                        (g "typename")+                        (g "gpc")+    where g s = p^.args.ix s  errHandlers :: [Handler KevinIO ()]-errHandlers = [ handler_ _KevinException-                    $ klogError "Malformed communication from server"+errHandlers = [ handler_ _KevinException $ klogError "Malformed communication from server"               , handler _IOException (\e -> klogError $ "server: " ++ show e) ]
Kevin/Damn/Protocol/Send.hs view
@@ -36,99 +36,80 @@ 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:" <> s-        ("&",s) -> do-            uname <- use_ name-            return . ("pchat:" <>) . T.intercalate ":"-                   . sort . map (T.map toLower) $ [uname, s]-        r -> return $ "chat" <> uncurry (<>) r+formatRoom b = case T.splitAt 1 b of ("#",s) -> return $ "chat:" <> s+                                     ("&",s) -> do uname <- use_ name+                                                   return . ("pchat:" <>) . T.intercalate ":" . sort . map (T.map toLower) $ [uname, s]+                                     r -> return $ "chat" <> uncurry (<>) r  deformatRoom :: T.Text -> KevinIO T.Text deformatRoom room = if "chat:" `T.isPrefixOf` room-    then return $ '#' `T.cons` T.drop 5 room-    else do-        uname <- use_ name-        return $ T.cons '&' (head (filter (/= uname) . T.splitOn ":"-                                  . T.drop 6 $ room))+                       then return $ '#' `T.cons` T.drop 5 room+                       else do uname <- use_ name+                               return $ T.cons '&' (head (filter (/= uname) . T.splitOn ":" . T.drop 6 $ room)) -type Str = T.Text -- just make it shorter-type Room = Str+type Str      = T.Text -- just make it shorter+type Room     = Str type Username = Str-type Pc = Str+type Pc       = Str  -- * Communication to the server-sendHandshake :: KevinIO ()-sendLogin :: Username -> Str -> KevinIO ()-sendJoin, sendPart :: Room -> KevinIO ()-sendMsg, sendAction, sendNpMsg :: Room -> Str -> KevinIO ()-sendPromote, sendDemote :: Room -> Username -> Maybe Pc -> KevinIO ()-sendBan, sendUnban :: Room -> Username -> KevinIO ()-sendKick :: Room -> Username -> Maybe Str -> KevinIO ()-sendGet :: Room -> Str -> KevinIO ()-sendWhois :: Username -> KevinIO ()-sendSet :: Room -> Str -> Str -> KevinIO ()-sendAdmin :: Room -> Str -> KevinIO ()-sendKill :: Username -> Str -> KevinIO ()+sendHandshake                  ::                                  KevinIO ()+sendLogin                      :: Username -> Str               -> KevinIO ()+sendJoin, sendPart             :: Room                          -> KevinIO ()+sendMsg, sendAction, sendNpMsg :: Room -> Str                   -> KevinIO ()+sendPromote, sendDemote        :: Room -> Username -> Maybe Pc  -> KevinIO ()+sendBan, sendUnban             :: Room -> Username              -> KevinIO ()+sendKick                       :: Room -> Username -> Maybe Str -> KevinIO ()+sendGet                        :: Room -> Str                   -> KevinIO ()+sendWhois                      :: Username                      -> KevinIO ()+sendSet                        :: Room -> Str -> Str            -> KevinIO ()+sendAdmin                      :: Room -> Str                   -> KevinIO ()+sendKill                       :: Username -> Str               -> KevinIO () -sendHandshake = sendPacket $ printf "dAmnClient 0.3\nagent=kevin%s\n"-                    [versionStr]+sendHandshake = sendPacket $ printf "dAmnClient 0.3\nagent=kevin%s\n" [versionStr]  sendLogin u token = sendPacket $ printf "login %s\npk=%s\n" [u, token] -sendJoin room = do-    roomname <- formatRoom room-    sendPacket $ printf "join %s\n" [roomname]+sendJoin room = do roomname <- formatRoom room+                   sendPacket $ printf "join %s\n" [roomname] -sendPart room = do-    roomname <- formatRoom room-    sendPacket $ printf "part %s\n" [roomname]+sendPart room = do roomname <- formatRoom room+                   sendPacket $ printf "part %s\n" [roomname] -sendMsg room msg = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\nmsg main\n\n%s" [roomname, msg]+sendMsg = sendNpMsg -sendAction room msg = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\naction main\n\n%s" [roomname, msg]+sendAction room msg = do roomname <- formatRoom room+                         sendPacket $ printf "send %s\n\naction main\n\n%s" [roomname, msg] -sendNpMsg = undefined+sendNpMsg room msg = do roomname <- formatRoom room+                        sendPacket $ printf "send %s\n\nnpmsg main\n\n%s" [roomname, msg] -sendPromote room us pc = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\npromote %s%s" [roomname, us, maybeBody pc]+sendPromote room us pc = do roomname <- formatRoom room+                            sendPacket $ printf "send %s\n\npromote %s%s" [roomname, us, maybeBody pc] -sendDemote room us pc = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\ndemote %s%s" [roomname, us, maybeBody pc]+sendDemote room us pc = do roomname <- formatRoom room+                           sendPacket $ printf "send %s\n\ndemote %s%s" [roomname, us, maybeBody pc] -sendBan room us = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\nban %s\n\n" [roomname, us]+sendBan room us = do roomname <- formatRoom room+                     sendPacket $ printf "send %s\n\nban %s\n\n" [roomname, us] -sendUnban room us = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\nunban %s\n\n" [roomname, us]+sendUnban room us = do roomname <- formatRoom room+                       sendPacket $ printf "send %s\n\nunban %s\n\n" [roomname, us] -sendKick room us reason = do-    roomname <- formatRoom room-    sendPacket $ printf "kick %s\nu=%s%s\n" [roomname, us, maybeBody reason]+sendKick room us reason = do roomname <- formatRoom room+                             sendPacket $ printf "kick %s\nu=%s%s\n" [roomname, us, maybeBody reason] -sendGet room prop = do-    guard $ prop `elem` ["title", "topic", "privclasses", "members"]-    roomname <- formatRoom room-    sendPacket $ printf "get %s\np=%s\n" [roomname, prop]+sendGet room prop = do guard $ prop `elem` ["title", "topic", "privclasses", "members"]+                       roomname <- formatRoom room+                       sendPacket $ printf "get %s\np=%s\n" [roomname, prop]  sendWhois us = sendPacket $ printf "get login:%s\np=info\n" [us] -sendSet room prop val = do-    guard (prop == "topic" || prop == "title")-    roomname <- formatRoom room-    sendPacket $ printf "set %s\np=%s\n\n%s\n" [roomname, prop, val]+sendSet room prop val = do guard (prop == "topic" || prop == "title")+                           roomname <- formatRoom room+                           sendPacket $ printf "set %s\np=%s\n\n%s\n" [roomname, prop, val] -sendAdmin room cmd = do-    roomname <- formatRoom room-    sendPacket $ printf "send %s\n\nadmin\n\n%s" [roomname, cmd]+sendAdmin room cmd = do roomname <- formatRoom room+                        sendPacket $ printf "send %s\n\nadmin\n\n%s" [roomname, cmd]  sendKill = undefined
Kevin/IRC/Packet.hs view
@@ -15,9 +15,9 @@ import qualified Data.Text as T import Prelude hiding (takeWhile) -data Packet = Packet { _prefix :: Maybe T.Text+data Packet = Packet { _prefix  :: Maybe T.Text                      , _command :: T.Text-                     , _params :: [T.Text]+                     , _params  :: [T.Text]                      }             | BadPacket deriving (Show) @@ -33,11 +33,10 @@ servername = takeWhile1 (inClass "a-z0-9.-")  username :: Parser T.Text-username = do-    n <- nick-    u <- option "" (T.cons <$> char '!' <*> user)-    h <- option "" (T.cons <$> char '@' <*> servername)-    return $ T.concat [n, u, h]+username = do n <- nick+              u <- option "" (T.cons <$> char '!' <*> user)+              h <- option "" (T.cons <$> char '@' <*> servername)+              return $ T.concat [n, u, h]  nick :: Parser T.Text nick = T.cons <$> letter <*> takeWhile (inClass "a-zA-Z0-9[]\\`^{}-")@@ -49,9 +48,7 @@ parsePrefix = username <|> servername  parseCommand :: Parser T.Text-parseCommand = takeWhile1 isAlpha-           <|> liftM3 (\a b c -> T.pack [a,b,c])-                      digit digit digit+parseCommand = takeWhile1 isAlpha <|> liftM3 (\a b c -> T.pack [a,b,c]) digit digit digit  parseParams :: Parser [T.Text] parseParams = (colonParam <|> nonColonParam) `sepBy` spaces@@ -69,26 +66,23 @@ messageBegin = Just <$> (char ':' *> parsePrefix <* spaces)  packetParser :: Parser Packet-packetParser = do-    pr <- option Nothing messageBegin-    cmd <- T.map toUpper <$> parseCommand-    spaces-    par <- filter (not . T.null) <$> parseParams-    option "" crlf-    return $ Packet pr cmd par+packetParser = do pr <- option Nothing messageBegin+                  cmd <- T.map toUpper <$> parseCommand+                  spaces+                  par <- filter (not . T.null) <$> parseParams+                  option "" crlf+                  return $ Packet pr cmd par  parsePacket :: T.Text -> Packet-parsePacket str = case parseOnly packetParser str of-    Left _ -> BadPacket-    Right p -> p+parsePacket str = case parseOnly packetParser str of Left _  -> BadPacket+                                                     Right p -> p  showParams :: [T.Text] -> T.Text showParams = T.unwords . map (\str -> if " " `T.isInfixOf` str-    then T.cons ':' str-    else str)+                                         then T.cons ':' str+                                         else str)  readable :: Packet -> T.Text-readable (Packet (Just str) cmd pms) = (<> "\r\n")-    $ T.unwords [T.cons ':' str, cmd, showParams pms]-readable (Packet Nothing c p) = (<> "\r\n") $ T.unwords [c, showParams p]-readable _ = ""+readable (Packet (Just str) cmd pms) = (<> "\r\n") $ T.unwords [T.cons ':' str, cmd, showParams pms]+readable (Packet Nothing c p)        = (<> "\r\n") $ T.unwords [c, showParams p]+readable _                           = ""
Kevin/IRC/Protocol.hs view
@@ -30,71 +30,61 @@ cleanup = klog Green "cleanup client"  listen :: KevinIO ()-listen = fix (\f -> flip catches errHandlers $ do-    k <- get_-    pkt <- io $ parsePacket <$> readClient k-    respond pkt (view command pkt)-    f)+listen = fix (\f -> flip catches errHandlers $ do k <- get_+                                                  pkt <- io $ parsePacket <$> readClient k+                                                  respond pkt (view command pkt)+                                                  f)  respond :: Packet -> T.Text -> KevinIO () respond BadPacket _ = sendNotice "Bad packet, try again."-respond pkt "JOIN" = do-    l <- gets_ (view loggedIn)-    if l-        then mapM_ D.sendJoin rooms-        else kevin $ joining %= (rooms ++)-    where-        rooms = T.splitOn "," $ pkt^.params._head+respond pkt "JOIN" = do l <- gets_ (view loggedIn)+                        if l+                           then mapM_ D.sendJoin rooms+                           else kevin $ joining %= (rooms ++)+    where rooms = T.splitOn "," $ pkt^.params._head  respond pkt "PART" = mapM_ D.sendPart . T.splitOn "," $ pkt^.params._head -respond pkt "PRIVMSG" = do-    let (room:msg:_) = pkt^.params-    if "\1ACTION" `T.isPrefixOf` msg-        then do-            let newMsg = T.drop 8 $ T.init msg-            D.sendAction room $ entityEncode newMsg-        else D.sendMsg room $ entityEncode msg+respond pkt "PRIVMSG" = do let (room:msg:_) = pkt^.params+                           if "\1ACTION" `T.isPrefixOf` msg+                              then do let newMsg = T.drop 8 $ T.init msg+                                      D.sendAction room $ entityEncode newMsg+                              else D.sendMsg room $ entityEncode msg  respond pkt "MODE" = if length (pkt^.params) > 1-    then do-        let (toggle,mode) = first (=="+") . T.splitAt 1 $ pkt^.params.ix 1-        case mode of-            "b" -> if' toggle D.sendBan D.sendUnban-                       (pkt^.params._head)-                       (fromMaybe "random unparseable garbage" . unmask-                       $ pkt^.params._last)-            "o" -> if' toggle D.sendPromote D.sendDemote-                       (pkt^.params._head)-                       (pkt^.params._last)-                       Nothing-            _ -> sendRoomNotice (pkt^.params._head)-                    $ "Unsupported mode " <> mode-    else do-        uname <- use_ name-        sendChanMode uname (pkt^.params._head)+                        then do let (toggle,mode) = first (=="+") . T.splitAt 1 $ pkt^.params.ix 1+                                case mode of "b" -> if' toggle+                                                        D.sendBan+                                                        D.sendUnban+                                                        (pkt^.params._head)+                                                        (fromMaybe "random unparseable garbage" . unmask $ pkt^.params._last)+                                             "o" -> if' toggle+                                                        D.sendPromote+                                                        D.sendDemote+                                                        (pkt^.params._head)+                                                        (pkt^.params._last)+                                                        Nothing+                                             _ -> sendRoomNotice (pkt^.params._head) $ "Unsupported mode " <> mode+                        else do uname <- use_ name+                                sendChanMode uname (pkt^.params._head) -respond pkt "TOPIC" = case pkt^.params of-    [] -> sendNotice "Malformed packet"-    [room] -> D.sendGet room "topic"-    (room:topic:_) -> D.sendSet room "topic" topic+respond pkt "TOPIC" = case pkt^.params of []             -> sendNotice "Malformed packet"+                                          [room]         -> D.sendGet room "topic"+                                          (room:topic:_) -> D.sendSet room "topic" topic -respond pkt "TITLE" = case pkt^.params of-    [] -> sendNotice "Malformed packet"-    [room] -> do-        title <- gets_ . view $ titles.ix room-        let p = T.concat ["Title for ", room, ": "]-        mapM_ (sendRoomNotice room . (p <>)) (T.splitOn "\n" title)-    (room:title) -> D.sendSet room "title" $ T.unwords title+respond pkt "TITLE" = case pkt^.params of []           -> sendNotice "Malformed packet"+                                          [room]       -> do title <- gets_ . view $ titles.ix room+                                                             let p = T.concat ["Title for ", room, ": "]+                                                             mapM_ (sendRoomNotice room . (p <>)) (T.splitOn "\n" title)+                                          (room:title) -> D.sendSet room "title" $ T.unwords title  respond pkt "PING" = sendPong $ pkt^.params._head  respond pkt "WHOIS" = D.sendWhois $ pkt^.params._head -respond pkt "NAMES" = do-    let room = pkt^.params._head-    k <- get_-    sendUserList (k^.name) (nubBy ((==) `on` username) (k^.users.ix room)) room+respond pkt "NAMES" = do let room = pkt^.params._head+                         k <- get_+                         sendUserList (k^.name) (nubBy ((==) `on` username) (k^.users.ix room)) room  respond pkt "KICK" = let p = pkt^.params                       in D.sendKick (head p)@@ -108,92 +98,67 @@ respond pkt "ADMIN" = D.sendAdmin p $ T.intercalate " " ps     where (p:ps) = pkt^.params -respond pkt "PROMOTE" = case pkt^.params of-    (room:user:group:_) -> D.sendPromote room user $ Just group-    (room:_) -> sendRoomNotice room "Usage: /promote #room username group"-    _ -> sendNotice "Usage: /promote #room username group"+respond pkt "PROMOTE" = case pkt^.params of (room:user:group:_) -> D.sendPromote room user $ Just group+                                            (room:_)            -> sendRoomNotice room "Usage: /promote #room username group"+                                            _                   -> sendNotice "Usage: /promote #room username group"  respond _ str = klogError $ T.unpack str   unmask :: T.Text -> Maybe T.Text-unmask y = case T.split (`elem` "@!") y of-    [s] -> Just s-    xs -> listToMaybe $ filter (not . T.isInfixOf "*") xs+unmask y = case T.split (`elem` "@!") y of [s] -> Just s+                                           xs  -> listToMaybe $ filter (not . T.isInfixOf "*") xs  errHandlers :: [Handler KevinIO ()]-errHandlers = [ handler_ _KevinException-                    $ klogError "Bad communication from client"-              , handler _IOException (\e -> klogError $ "client: " ++ show e)]+errHandlers = [ handler_ _KevinException $ klogError "Bad communication from client"+              , handler _IOException (\e -> klogError $ "client: " ++ show e) ]  -- * Authentication-getting function notice :: Handle -> T.Text -> IO ()-notice h str = do-    klogNow Blue ("client -> " ++ T.unpack asStr)-    T.hPutStr h (asStr <> "\r\n")-    where-        asStr = printf "NOTICE AUTH :%s" [str]+notice h str = do klogNow Blue ("client -> " ++ T.unpack asStr)+                  T.hPutStr h (asStr <> "\r\n")+    where asStr = printf "NOTICE AUTH :%s" [str]  getAuthInfo :: Handle -> Bool -> KevinState ()-getAuthInfo handle = fix (\f authRetry -> do-    pkt <- io $ parsePacket <$> T.hGetLine handle-    io $ klogNow Yellow $ "client <- " ++ T.unpack (readable pkt)-    case pkt^.command of-        "PASS" -> do-           password .= pkt^.params._head-           passed .= True-        "NICK" -> do-           name .= pkt^.params._head-           nicked .= True-        "USER" -> usered .= True-        _ -> io $ klogNow Red $ "invalid packet: " ++ show pkt-    if authRetry-        then checkToken handle-        else do-            p <- use passed-            n <- use nicked-            u <- use usered-            if p && n && u-                then welcome handle-                else f False-    )+getAuthInfo handle = fix (\f authRetry -> do pkt <- io $ parsePacket <$> T.hGetLine handle+                                             io $ klogNow Yellow $ "client <- " ++ T.unpack (readable pkt)+                                             case pkt^.command of "PASS" -> do password .= pkt^.params._head+                                                                               passed .= True+                                                                  "NICK" -> do name .= pkt^.params._head+                                                                               nicked .= True+                                                                  "USER" -> usered .= True+                                                                  _      -> io $ klogNow Red $ "invalid packet: " ++ show pkt+                                             if authRetry+                                                then checkToken handle+                                                else do p <- use passed+                                                        n <- use nicked+                                                        u <- use usered+                                                        if p && n && u+                                                           then welcome handle+                                                           else f False)  welcome :: Handle -> KevinState ()-welcome handle = do-    nick <- use name-    mapM_ (\x -> io $ do-        klogNow Blue ("client -> " ++ T.unpack x)-        T.hPutStr handle (x <> "\r\n")) [-        printf ":%s 001 %s :Welcome to dAmnServer %s!%s@chat.deviantart.com"-            [hostname, nick, nick, nick],-        printf ":%s 002 %s :Your host is chat.deviantart.com\-            \, running dAmnServer 0.3" [hostname, nick],-        printf ":%s 003 %s :This server was created Thu Apr 28 1994\-            \ at 05:30:00 EDT" [hostname, nick],-        printf ":%s 004 %s chat.deviantart.com dAmnServer0.3 qov i"-            [hostname, nick],-        printf ":%s 005 %s PREFIX=(qov)~@+" [hostname, nick],-        printf ":%s 375 %s :- chat.deviantart.com Message of the day -" -            [hostname, nick],-        printf ":%s 372 %s :- deviantART chat on IRC brought to you\-            \ by kevin %s, created" [hostname, nick, versionStr],-        printf ":%s 372 %s :- and maintained by Joel Taylor <http://otte.rs>"-            [hostname, nick],-        printf ":%s 376 %s :End of MOTD command" [hostname, nick]]-    checkToken handle-    where-        hostname = "chat.deviantart.com"+welcome handle = do nick <- use name+                    mapM_ (\x -> io $ do+                        klogNow Blue ("client -> " ++ T.unpack x)+                        T.hPutStr handle (x <> "\r\n")) [+                            printf ":%s 001 %s :Welcome to dAmnServer %s!%s@chat.deviantart.com" [hostname, nick, nick, nick],+                            printf ":%s 002 %s :Your host is chat.deviantart.com, running dAmnServer 0.3" [hostname, nick],+                            printf ":%s 003 %s :This server was created Thu Apr 28 1994 at 05:30:00 EDT" [hostname, nick],+                            printf ":%s 004 %s chat.deviantart.com dAmnServer0.3 qov i" [hostname, nick],+                            printf ":%s 005 %s PREFIX=(qov)~@+" [hostname, nick],+                            printf ":%s 375 %s :- chat.deviantart.com Message of the day -" [hostname, nick],+                            printf ":%s 372 %s :- deviantART chat on IRC brought to you by kevin %s, created" [hostname, nick, versionStr],+                            printf ":%s 372 %s :- and maintained by Joel Taylor <http://otte.rs>" [hostname, nick],+                            printf ":%s 376 %s :End of MOTD command" [hostname, nick]]+                    checkToken handle+    where hostname = "chat.deviantart.com"  checkToken :: Handle -> KevinState ()-checkToken handle = do-    s <- get-    io $ notice handle "Fetching token..."-    tok <- io $ getToken (s^.name) (s^.password)-    case tok of-        Just t -> do-            authtoken .= t-            io $ notice handle "Successfully authenticated."-        Nothing -> do-            io $ notice handle "Bad password, try again.\-                 \ (/quote pass yourpassword)"-            getAuthInfo handle True+checkToken handle = do s <- get+                       io $ notice handle "Fetching token..."+                       tok <- io $ getToken (s^.name) (s^.password)+                       case tok of Just t  -> do authtoken .= t+                                                 io $ notice handle "Successfully authenticated."+                                   Nothing -> do io $ notice handle "Bad password, try again. (/quote pass yourpassword)"+                                                 getAuthInfo handle True
Kevin/IRC/Protocol/Send.hs view
@@ -37,123 +37,82 @@ type Room = Str type Username = Str -sendJoin :: Username -> Room -> KevinIO ()-sendPart :: Username -> Room -> Maybe Str -> KevinIO ()-sendSetUserMode :: Username -> Room -> Int -> KevinIO ()-sendChangeUserMode :: Username -> Room -> Int -> Int -> KevinIO ()-sendNotice :: Str -> KevinIO ()-sendRoomNotice :: Room -> Str -> KevinIO ()-sendChanMsg, sendChanAction :: Username -> Room -> Str -> KevinIO ()-sendKick :: Username -> Username -> Room -> Maybe Str -> KevinIO ()-sendTopic :: Username -> Room -> Username -> Str -> Str -> KevinIO ()-sendChanMode :: Username -> Room -> KevinIO ()-sendUserList :: Username -> [User] -> Room -> KevinIO ()-sendPong :: T.Text -> KevinIO ()-sendNoticeClone :: Username -> Int -> Room -> KevinIO ()-sendNoticeUnclone :: Username -> Int -> Room -> KevinIO ()-sendWhoisReply :: Username-               -> Username-               -> Username-               -> [Room]-               -> Int-               -> Int-               -> KevinIO ()+sendJoin                    :: Username -> Room                                         -> KevinIO ()+sendPart                    :: Username -> Room -> Maybe Str                            -> KevinIO ()+sendSetUserMode             :: Username -> Room -> Int                                  -> KevinIO ()+sendChangeUserMode          :: Username -> Room -> Int -> Int                           -> KevinIO ()+sendNotice                  :: Str                                                      -> KevinIO ()+sendRoomNotice              :: Room -> Str                                              -> KevinIO ()+sendChanMsg, sendChanAction :: Username -> Room -> Str                                  -> KevinIO ()+sendKick                    :: Username -> Username -> Room -> Maybe Str                -> KevinIO ()+sendTopic                   :: Username -> Room -> Username -> Str -> Str               -> KevinIO ()+sendChanMode                :: Username -> Room                                         -> KevinIO ()+sendUserList                :: Username -> [User] -> Room                               -> KevinIO ()+sendPong                    :: T.Text                                                   -> KevinIO ()+sendNoticeClone             :: Username -> Int -> Room                                  -> KevinIO ()+sendNoticeUnclone           :: Username -> Int -> Room                                  -> KevinIO ()+sendWhoisReply              :: Username -> Username -> Username -> [Room] -> Int -> Int -> KevinIO () -sendJoin us rm =-    sendPacket $ printf "%s JOIN :%s" [getHost us, rm]+sendJoin us rm = sendPacket $ printf "%s JOIN :%s" [getHost us, rm] -sendPart us rm msg =-    sendPacket $ printf "%s PART %s%s" [getHost us, rm, maybeBody msg]+sendPart us rm msg = sendPacket $ printf "%s PART %s%s" [getHost us, rm, maybeBody msg] -sendSetUserMode us rm m = unless (T.null mode) $-    sendPacket $ printf "%s MODE %s +%s %s" [hostname, rm, mode, us]-    where mode = levelToMode m+sendSetUserMode us rm m = unless (T.null mode) $ sendPacket $ printf "%s MODE %s +%s %s" [hostname, rm, mode, us]+             where mode = levelToMode m -sendChangeUserMode us rm old new = unless (oldMode == newMode) $-    sendPacket $ printf "%s MODE %s %s" [hostname, rm, modesAndUser]-    where-        oldMode = levelToMode old-        newMode = levelToMode new-        modesAndUser = case (oldMode, newMode) of-            ("", "") -> T.concat ["-v ", us]-            ("", _) -> T.concat ["+", newMode, " ", us]-            (_, "") -> T.concat ["-", oldMode, " ", us]-            (a, b) -> T.concat ["-", a, "+", b, " ", us, " ", us]+sendChangeUserMode us rm old new = unless (oldMode == newMode) $ sendPacket $ printf "%s MODE %s %s" [hostname, rm, modesAndUser]+    where oldMode      = levelToMode old+          newMode      = levelToMode new+          modesAndUser = case (oldMode, newMode) of ("", "") -> T.concat ["-v ", us]+                                                    ("", _)  -> T.concat ["+", newMode, " ", us]+                                                    (_, "")  -> T.concat ["-", oldMode, " ", us]+                                                    (a, b)   -> T.concat ["-", a, "+", b, " ", us, " ", us]  -sendNotice =-    sendPacket . printf "NOTICE AUTH :%s" . return+sendNotice = sendPacket . printf "NOTICE AUTH :%s" . return -sendRoomNotice room n =-    sendPacket $ printf "%s NOTICE %s :%s" [hostname, room, n]+sendRoomNotice room n = sendPacket $ printf "%s NOTICE %s :%s" [hostname, room, n] -sendChanMsg sender room msg = mapM_ (\x ->-    sendPacket $ printf "%s PRIVMSG %s :%s" [getHost sender, room, x]-    ) . T.splitOn "\n" $ msg+sendChanMsg sender room msg = mapM_ (\x -> sendPacket $ printf "%s PRIVMSG %s :%s" [getHost sender, room, x]) . T.splitOn "\n" $ msg -sendChanAction sender room msg = mapM_ (\x ->-    sendPacket $ printf "%s PRIVMSG %s :\1ACTION %s\1"-        [getHost sender, room, x]-    ) . T.splitOn "\n" $ msg+sendChanAction sender room msg = mapM_ (\x -> sendPacket $ printf "%s PRIVMSG %s :\1ACTION %s\1" [getHost sender, room, x]) . T.splitOn "\n" $ msg -sendKick kickee kicker room msg =-    sendPacket $ printf "%s KICK %s %s%s"-        [getHost kicker, room, kickee, maybeBody msg]+sendKick kickee kicker room msg = sendPacket $ printf "%s KICK %s %s%s" [getHost kicker, room, kickee, maybeBody msg] -sendTopic us rm maker top startdate = do-    sendPacket $ printf "%s 332 %s %s :%s" [hostname, us, rm, top]-    sendPacket $ printf "%s 333 %s %s %s %s"-        [hostname, us, rm, maker, startdate]+sendTopic us rm maker top startdate = do sendPacket $ printf "%s 332 %s %s :%s" [hostname, us, rm, top]+                                         sendPacket $ printf "%s 333 %s %s %s %s" [hostname, us, rm, maker, startdate] -sendChanMode us rm = do-    sendPacket $ printf "%s 324 %s %s +t" [hostname, us, rm]-    sendPacket $ printf "%s 329 %s %s 767529000" [hostname, us, rm]+sendChanMode us rm = do sendPacket $ printf "%s 324 %s %s +t" [hostname, us, rm]+                        sendPacket $ printf "%s 329 %s %s 767529000" [hostname, us, rm] -sendUserList us uss rm = do-    mapM_ (\nms ->-        sendPacket $ printf "%s 353 %s = %s :%s"-            [hostname, us, rm, T.unwords nms]) chunkedNames-    sendPacket $ printf "%s 366 %s %s :End of /NAMES list." [hostname, us, rm]-    where-        names = map (\u ->-            T.concat [levelToSym $ privclassLevel u, username u]) uss-        chunkedNames = reverse . map reverse . subchunk' 432 names $ [[]]-        subchunk' n = fix (\f x y ->-            let hy = head y-                hx = head x-                ty = tail y-                tx = tail x-            in if null x-                then y-                else f tx $ if sum (map T.length hy) + T.length hx <= n-                    then (hx:hy):ty-                    else [hx]:y)+sendUserList us uss rm = do mapM_ (\nms -> sendPacket $ printf "%s 353 %s = %s :%s" [hostname, us, rm, T.unwords nms]) chunkedNames+                            sendPacket $ printf "%s 366 %s %s :End of /NAMES list." [hostname, us, rm]+    where names        = map (\u -> T.concat [levelToSym $ privclassLevel u, username u]) uss+          chunkedNames = reverse . map reverse . subchunk' 432 names $ [[]]+          subchunk' n  = fix (\f x y -> let hy = head y+                                            hx = head x+                                            ty = tail y+                                            tx = tail x+                                         in if null x+                                               then y+                                               else f tx $ if sum (map T.length hy) + T.length hx <= n+                                                              then (hx:hy):ty+                                                              else [hx]:y) -sendPong p =-    sendPacket $ printf "%s PONG chat.deviantart.com :%s" [hostname, p]+sendPong p = sendPacket $ printf "%s PONG chat.deviantart.com :%s" [hostname, p] -sendNoticeClone uname i rm =-    sendPacket $ printf "%s NOTICE %s :%s has joined again\-         \ (now joined %s times)" [hostname, rm, uname, T.pack $ show i]+sendNoticeClone uname i rm = sendPacket $ printf "%s NOTICE %s :%s has joined again (now joined %s times)" [hostname, rm, uname, T.pack $ show i] -sendNoticeUnclone uname i rm =-    sendPacket $ printf "%s NOTICE %s :%s has parted (now joined %s)"-        [hostname, rm, uname, times]-    where-        times | i == 1    = "once"-              | otherwise = T.pack (show i) <> " times"+sendNoticeUnclone uname i rm = sendPacket $ printf "%s NOTICE %s :%s has parted (now joined %s)" [hostname, rm, uname, times]+    where times | i == 1    = "once"+                | otherwise = T.pack (show i) <> " times" -sendWhoisReply me us rn rooms idle signon = do-    sendPacket $ printf "%s 311 %s %s %s chat.deviantart.com * :%s"-        [hostname, me, us, us, rn]-    sendPacket $ printf "%s 307 %s %s :is a registered nick" [hostname, me, us]-    sendPacket $ printf "%s 319 %s %s :%s"-        [hostname, me, us, T.intercalate " " . map (T.cons '#') $ rooms]-    sendPacket $ printf "%s 312 %s %s chat.deviantart.com :dAmn"-        [hostname, me, us]-    sendPacket $ printf "%s 317 %s %s %s %s :seconds idle, signon time"-        [hostname, me, us, T.pack $ show idle, T.pack $ show signon]-    sendPacket $ printf "%s 318 %s %s :End of /WHOIS list." [hostname, me, us]+sendWhoisReply me us rn rooms idle signon = do sendPacket $ printf "%s 311 %s %s %s chat.deviantart.com * :%s" [hostname, me, us, us, rn]+                                               sendPacket $ printf "%s 307 %s %s :is a registered nick" [hostname, me, us]+                                               sendPacket $ printf "%s 319 %s %s :%s" [hostname, me, us, T.intercalate " " . map (T.cons '#') $ rooms]+                                               sendPacket $ printf "%s 312 %s %s chat.deviantart.com :dAmn" [hostname, me, us]+                                               sendPacket $ printf "%s 317 %s %s %s %s :seconds idle, signon time" [hostname, me, us, T.pack $ show idle, T.pack $ show signon]+                                               sendPacket $ printf "%s 318 %s %s :End of /WHOIS list." [hostname, me, us]  levelToSym :: Int -> T.Text levelToSym x | x > 0  && x <= 35 = ""@@ -163,9 +122,8 @@              | otherwise         = ""  levelToMode :: Int -> T.Text-levelToMode x = case levelToSym x of-   "" -> ""-   "+" -> "v"-   "@" -> "o"-   "~" -> "q"-   _ -> error "levelToSym, what are you doing"+levelToMode x = case levelToSym x of ""  -> ""+                                     "+" -> "v"+                                     "@" -> "o"+                                     "~" -> "q"+                                     _   -> error "levelToSym, what are you doing"
Kevin/Protocol.hs view
@@ -15,51 +15,43 @@                  , handler_ id (return Nothing) ]  mkKevin :: Socket -> IO (Maybe Kevin)-mkKevin sock = flip catches watchInterrupt . withSocketsDo $ do-    (client, _, _) <- accept sock-    hSetBuffering client NoBuffering-    klogNow Blue "received a client"-    s <- execStateT (C.getAuthInfo client False) def-    damnSock <- connectTo "chat.deviantart.com" $ PortNumber 3900-    hSetBuffering damnSock NoBuffering-    logChan <- newChan-    damnChan <- newChan-    ircChan <- newChan-    return . Just $ Kevin damnSock-                          client-                          damnChan-                          ircChan-                          s-                          mempty-                          mempty-                          mempty-                          mempty-                          False-                          logChan+mkKevin sock = flip catches watchInterrupt . withSocketsDo+    $ do (client, _, _) <- accept sock+         hSetBuffering client NoBuffering+         klogNow Blue "received a client"+         s <- execStateT (C.getAuthInfo client False) def+         damnSock <- connectTo "chat.deviantart.com" $ PortNumber 3900+         hSetBuffering damnSock NoBuffering+         logChan <- newChan+         damnChan <- newChan+         ircChan <- newChan+         return . Just $ Kevin damnSock+                               client+                               damnChan+                               ircChan+                               s+                               mempty+                               mempty+                               mempty+                               mempty+                               False+                               logChan  mkListener :: Int -> IO Socket mkListener = listenOn . PortNumber . fromIntegral  kevinServer :: Int -> IO ()-kevinServer n = do-    sock <- mkListener n-    putStrLn $ "Listening on port " ++ show n-    forever $ do-        kev <- mkKevin sock-        case kev of-            Just k -> listen k-            Nothing -> return ()+kevinServer n = do sock <- mkListener n+                   putStrLn $ "Listening on port " ++ show n+                   forever $ do kev <- mkKevin sock+                                case kev of Just k -> listen k+                                            Nothing -> return ()  listen :: Kevin -> IO ()-listen k = do-    mvar <- newTVarIO k-    runLogger (logger k)-    runPrinter (dChan k) (damn k)-    runPrinter (iChan k) (irc k)-    forkIO . void $ runReaderT (bracket_ S.initialize-                                         (S.cleanup >> io (closeClient k))-                                         S.listen) mvar-    forkIO . void $ runReaderT (bracket_ (return ())-                                         (C.cleanup >> io (closeServer k))-                                         C.listen) mvar-    return ()+listen k = do mvar <- newTVarIO k+              runLogger (logger k)+              runPrinter (dChan k) (damn k)+              runPrinter (iChan k) (irc k)+              forkIO . void $ runReaderT (bracket_ S.initialize (S.cleanup >> io (closeClient k)) S.listen) mvar+              forkIO . void $ runReaderT (bracket_ (return ()) (C.cleanup >> io (closeServer k)) C.listen) mvar+              return ()
Kevin/Settings.hs view
@@ -7,9 +7,9 @@ data Settings = Settings { _name      :: Text                          , _password  :: Text                          , _authtoken :: Text-                         , _passed :: Bool-                         , _nicked :: Bool-                         , _usered :: Bool+                         , _passed    :: Bool+                         , _nicked    :: Bool+                         , _usered    :: Bool                          } deriving (Show)  makeClassy ''Settings
Kevin/Types.hs view
@@ -39,35 +39,35 @@  type Chatroom = T.Text -data User = User { username :: T.Text-                 , privclass :: T.Text+data User = User { username       :: T.Text+                 , privclass      :: T.Text                  , privclassLevel :: Int-                 , symbol :: T.Text-                 , realname :: T.Text-                 , typename :: T.Text-                 , gpc :: T.Text+                 , symbol         :: T.Text+                 , realname       :: T.Text+                 , typename       :: T.Text+                 , gpc            :: T.Text                  } deriving (Eq, Show) -type UserStore = M.Map Chatroom [User]+type UserStore      = M.Map Chatroom [User] -type Privclasses = M.Map T.Text Int+type Privclasses    = M.Map T.Text Int type PrivclassStore = M.Map Chatroom Privclasses-type Privclass = (T.Text, Int)+type Privclass      = (T.Text, Int) -type Title = T.Text-type TitleStore = M.Map Chatroom Title+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+data Kevin = Kevin { damn           :: Handle+                   , irc            :: Handle+                   , dChan          :: Chan T.Text+                   , iChan          :: Chan T.Text                    , _kevinSettings :: Settings-                   , _users :: UserStore-                   , _privclasses :: PrivclassStore-                   , _titles :: TitleStore-                   , _joining :: [T.Text]-                   , _loggedIn :: Bool-                   , logger :: Chan String+                   , _users         :: UserStore+                   , _privclasses   :: PrivclassStore+                   , _titles        :: TitleStore+                   , _joining       :: [T.Text]+                   , _loggedIn      :: Bool+                   , logger         :: Chan String                    }  makeLenses ''Kevin@@ -78,11 +78,10 @@ type KevinS = StateT Kevin STM  kevin :: KevinS a -> KevinIO a-kevin m = ask >>= \v -> liftIO $ atomically $ do-    s <- readTVar v-    (a, t) <- runStateT m s-    writeTVar v t-    return a+kevin m = ask >>= \v -> liftIO $ atomically $ do s <- readTVar v+                                                 (a, t) <- runStateT m s+                                                 writeTVar v t+                                                 return a  type KevinIO = ReaderT (TVar Kevin) IO 
Kevin/Util/Entity.hs view
@@ -18,57 +18,48 @@ decodeCharacter = entityNumeric <|> entityNamed <|> take 1  entityNumeric :: Parser T.Text-entityNumeric = do-    string "&#"-    entity <- (<>) <$> option "" (string "x") <*> takeWhile1 isHexDigit-    char ';'-    return . fromMaybe (T.concat ["&#", entity, ";"])-           $ (if "x" `T.isPrefixOf` entity -               then lookupHexEntity-               else lookupNumericEntity) entity+entityNumeric = do string "&#"+                   entity <- (<>) <$> option "" (string "x") <*> takeWhile1 isHexDigit+                   char ';'+                   return . fromMaybe (T.concat ["&#", entity, ";"]) $ (if "x" `T.isPrefixOf` entity +                                                                           then lookupHexEntity+                                                                           else lookupNumericEntity) entity  entityNamed :: Parser T.Text-entityNamed = do-    char '&'-    entity <- T.cons <$> letter <*> takeWhile1 isAlphaNum-    char ';'-    return . fromMaybe (T.concat ["&", entity, ";"])-           . lookupNamedEntity $ entity+entityNamed = do char '&'+                 entity <- T.cons <$> letter <*> takeWhile1 isAlphaNum+                 char ';'+                 return . fromMaybe (T.concat ["&", entity, ";"]) . lookupNamedEntity $ entity  decodeParser :: Parser T.Text decodeParser = T.concat <$> many1 decodeCharacter  entityDecode :: T.Text -> T.Text entityDecode "" = ""-entityDecode str = case parseOnly decodeParser str of-    Left err -> error $ "entityDecode: " ++ err-    Right s -> s+entityDecode str = case parseOnly decodeParser str of Left err -> error $ "entityDecode: " ++ err+                                                      Right s -> s  entityEncode :: T.Text -> T.Text entityEncode = T.pack . concat . entityEncodeS . T.unpack  entityEncodeS :: String -> [String]-entityEncodeS = fix (\f str -> case str of-    [] -> []-    (x:xs) -> if x < '\127' then [x]:f xs-                            else ("&#" ++ show (ord x) ++ ";"):f xs)+entityEncodeS = fix (\f str -> case str of [] -> []+                                           (x:xs) -> if x < '\127'+                                                        then [x]:f xs+                                                        else ("&#" ++ show (ord x) ++ ";"):f xs)  lookupNamedEntity :: T.Text -> Maybe T.Text lookupNamedEntity ent = (T.singleton . chr) <$> lookup ent namedEntities  lookupHexEntity :: T.Text -> Maybe T.Text-lookupHexEntity e = case R.hexadecimal $ T.cons '0' e of-    Right (n,_) -> do-        guard $ n < ord maxBound-        return . T.singleton . chr $ n-    Left _ -> Nothing+lookupHexEntity e = case R.hexadecimal $ T.cons '0' e of Right (n,_) -> do guard $ n < ord maxBound+                                                                           return . T.singleton . chr $ n+                                                         Left _ -> Nothing  lookupNumericEntity :: T.Text -> Maybe T.Text-lookupNumericEntity e = case R.decimal e of-    Right (n,_) -> do-        guard $ n < ord maxBound-        return . T.singleton . chr $ n-    Left _ -> Nothing+lookupNumericEntity e = case R.decimal e of Right (n,_) -> do guard $ n < ord maxBound+                                                              return . T.singleton . chr $ n+                                            Left _ -> Nothing  namedEntities :: [(T.Text, Int)] namedEntities = [ ("quot", 34), ("amp", 38), ("apos", 39), ("lt", 60)
Kevin/Util/Logger.hs view
@@ -42,8 +42,7 @@                          ++ "m" ++ rtrimmed                          ++ "\027[0m"     where-        rtrimmed = reverse . dropWhile (\x -> isSpace x || x == '\x0')-                 . reverse $ str+        rtrimmed = reverse . dropWhile (\x -> isSpace x || x == '\x0') . reverse $ str  klog_ :: Chan String -> Color -> String -> IO () klog_ ch col str = writeChan ch $ render col str
Kevin/Util/Tablump.hs view
@@ -13,15 +13,14 @@ import Text.Regex.PCRE.String  fromRight :: (Show a) => Either a b -> b-fromRight (Left x) = error $ "fromRight on Left " ++ show x+fromRight (Left x)  = error $ "fromRight on Left " ++ show x fromRight (Right a) = a  {-# NOINLINE regexReplace #-} regexReplace :: Regex -> ([String] -> String) -> String -> String-regexReplace find replace = fix (\f str -> case fromRight . unsafePerformIO-        $ regexec find str of-            Just (bef, _, af, matches) -> concat [bef, replace matches, f af]-            Nothing -> str)+regexReplace find replace = fix (\f str ->+    case fromRight . unsafePerformIO $ regexec find str of Just (bef, _, af, matches) -> concat [bef, replace matches, f af]+                                                           Nothing                    -> str)  {-# NOINLINE regexen #-} regexen :: [(Regex, [String] -> String)]@@ -29,53 +28,48 @@     first (fromRight . unsafePerformIO           . compile defaultCompOpt defaultExecOpt)      ) . reverse $ [-        "&b\t"      $$ const "\2",-        "&/b\t"     $$ const "\15",-        "&i\t"      $$ const "\22",-        "&/i\t"     $$ const "\15",-        "&u\t"      $$ const "\31",-        "&/u\t"     $$ const "\15",-        "&s\t"      $$ const "<s>",-        "&/s\t"     $$ const "</s>",-        "&sup\t"    $$ const "",-        "&/sup\t"   $$ const "",-        "&sub\t"    $$ const "",-        "&/sub\t"   $$ const "",-        "&code\t"   $$ const "",-        "&/code\t"  $$ const "",-        "&br\t"     $$ const "\n",-        "&ul\t"     $$ const "",-        "&/ul\t"    $$ const "",-        "&ol\t"     $$ const "",-        "&/ol\t"    $$ const "",-        "&li\t"     $$ const "- ",-        "&/li\t"    $$ const "\n",-        "&bcode\t"  $$ const "",-        "&/bcode\t" $$ const "",-        "&/a\t"     $$ const ")",-        "&/acro\t"  $$ const "</acronym>",-        "&/abbr\t"  $$ const "</abbr>",-        "&p\t"      $$ const "",-        "&/p\t"     $$ const "\n",-        "&emote\t(.+?)\t.+?\t.+?\t.+?\t.+?\t" $$ head,-        "&a\t(.+?)\t.*?\t" $$ \(x:_) -> printf "%s (" x,-        "&link\t(.+?)\t&\t" $$ head,-        "&link\t(.+?)\t(.+?)\t&\t" $$ \(x:y:_) -> printf "%s (%s)" x y,-        "&dev\t.+?\t(.+?)\t" $$ head,-        "&avatar\t(.+?)\t.+?\t" $$ \(x:_) -> printf ":icon%s:" x,-        "&thumb\t(.+?)\t.+?\t.+?\t.+?\t.+?\t.+?\t.+?\t" $$ \(x:_) ->-            printf ":thumb%s:" x,-        "&img\t(.+?)\t(.*?)\t(.*?)\t" $$ \(x:y:z:_) ->-            printf "<img src='%s' alt='%s' title='%s' />" x y z,-        "&iframe\t(.+?)\t(.*?)\t(.*?)\t" $$ \(x:y:z:_) ->-            printf "<iframe src='%s' width='%s' height='%s' />" x y z,-        "&acro\t(.+?)\t" $$ \(x:_) -> printf "<acronym title='%s'>" x,-        "&abbr\t(.+?)\t" $$ \(x:_) -> printf "<abbr title='%s'>" x,-        " ?<abbr title='colors:[0-9A-Fa-f]{6}:[0-9A-Fa-f]{6}'></abbr>"-            $$ const "",-        "^<abbr title='(.+?)'>.+?</abbr>:" $$ \(x:_) -> printf "%s:" x,-        "^[a-zA-Z0-9\\-_]+<abbr title='(.+?)'></abbr>:" $$ \(x:_) ->-            printf "%s:" x+        "&b\t"                                                         $$ const "\2",+        "&/b\t"                                                        $$ const "\15",+        "&i\t"                                                         $$ const "\22",+        "&/i\t"                                                        $$ const "\15",+        "&u\t"                                                         $$ const "\31",+        "&/u\t"                                                        $$ const "\15",+        "&s\t"                                                         $$ const "<s>",+        "&/s\t"                                                        $$ const "</s>",+        "&sup\t"                                                       $$ const "",+        "&/sup\t"                                                      $$ const "",+        "&sub\t"                                                       $$ const "",+        "&/sub\t"                                                      $$ const "",+        "&code\t"                                                      $$ const "",+        "&/code\t"                                                     $$ const "",+        "&br\t"                                                        $$ const "\n",+        "&ul\t"                                                        $$ const "",+        "&/ul\t"                                                       $$ const "",+        "&ol\t"                                                        $$ const "",+        "&/ol\t"                                                       $$ const "",+        "&li\t"                                                        $$ const "- ",+        "&/li\t"                                                       $$ const "\n",+        "&bcode\t"                                                     $$ const "",+        "&/bcode\t"                                                    $$ const "",+        "&/a\t"                                                        $$ const ")",+        "&/acro\t"                                                     $$ const "</acronym>",+        "&/abbr\t"                                                     $$ const "</abbr>",+        "&p\t"                                                         $$ const "",+        "&/p\t"                                                        $$ const "\n",+        "&emote\t(.+?)\t.+?\t.+?\t.+?\t.+?\t"                          $$ head,+        "&a\t(.+?)\t.*?\t"                                             $$ \(x:_) -> printf "%s (" x,+        "&link\t(.+?)\t&\t"                                            $$ head,+        "&link\t(.+?)\t(.+?)\t&\t"                                     $$ \(x:y:_) -> printf "%s (%s)" x y,+        "&dev\t.+?\t(.+?)\t"                                           $$ head,+        "&avatar\t(.+?)\t.+?\t"                                        $$ \(x:_) -> printf ":icon%s:" x,+        "&thumb\t.+?\t(.+?)\t.+?\t.+?\t.+?\t.+?\t"                     $$ \(x:_) -> printf "[thumb: %s]" x,+        "&img\t(.+?)\t(.*?)\t(.*?)\t"                                  $$ \(x:y:z:_) -> printf "<img src='%s' alt='%s' title='%s' />" x y z,+        "&iframe\t(.+?)\t(.*?)\t(.*?)\t"                               $$ \(x:y:z:_) -> printf "<iframe src='%s' width='%s' height='%s' />" x y z,+        "&acro\t(.+?)\t"                                               $$ \(x:_) -> printf "<acronym title='%s'>" x,+        "&abbr\t(.+?)\t"                                               $$ \(x:_) -> printf "<abbr title='%s'>" x,+        " ?<abbr title='colors:[0-9A-Fa-f]{6}:[0-9A-Fa-f]{6}'></abbr>" $$ const "",+        "^<abbr title='(.+?)'>.+?</abbr>:"                             $$ \(x:_) -> printf "%s:" x,+        "^[a-zA-Z0-9\\-_]+<abbr title='(.+?)'></abbr>:"                $$ \(x:_) -> printf "%s:" x     ]  tablumpDecode :: T.Text -> T.Text
Kevin/Util/Token.hs view
@@ -16,54 +16,61 @@ import Text.Printf  recvUntil :: TLSCtx -> B.ByteString -> IO B.ByteString-recvUntil ctx str = do-    line <- recvData ctx-    if str `B.isInfixOf` line-        then return line-        else fmap (line <>) $ recvUntil ctx str+recvUntil ctx str = do line <- recvData ctx+                       if str `B.isInfixOf` line+                          then return line+                          else fmap (line <>) $ recvUntil ctx str  concatHeaders :: [(String,String)] -> String concatHeaders = intercalate "\r\n" . map (\(x,y) -> x ++ ": " ++ y) +scrapeFormValue :: String -> B.ByteString -> B.ByteString+scrapeFormValue key bs = B.takeWhile (/='"') . B.drop 7 . head+                       . filter (\l -> "value=" `B.isPrefixOf` l) . B.words . head+                       . filter (\l -> B.pack key `B.isInfixOf` l) . B.lines $ bs++scrapeCookies :: B.ByteString -> B.ByteString+scrapeCookies bs = B.intercalate ";" . map snd+                 . filter ((== "Set-Cookie") . fst)+                 . map (second (B.drop 2 . B.takeWhile (/=';'))+                       . B.breakSubstring ": ")+                 . B.lines $ bs+ getToken :: T.Text -> T.Text -> IO (Maybe T.Text)-getToken uname pass = do-    let params = defaultParams { pCiphers = ciphersuite_all-                               , onCertificatesRecv =-                                   certificateChecks [return-                                     . certificateVerifyDomain-                                         "chat.deviantart.com"]-                               }-        headers :: [(String, String)]-        headers = [("Connection", "closed"),-                   ("Content-Type", "application/x-www-form-urlencoded")]-    gen <- makeSystem-    ctx <- connectionClient "www.deviantart.com" "443" params gen-    let payload = urlEncodeVars [ ("username", T.unpack uname)-                                , ("password", T.unpack pass)-                                , ("remember_me","1")-                                ]-    handshake ctx-    sendData ctx . LB.pack-        $ printf "POST /users/login HTTP/1.1\r\n%s\r\n\-            \Content-Length: %d\r\n\r\n%s"-            (concatHeaders $ ("Host", "www.deviantart.com"):headers)-            (length payload)-            payload-    bs <- recvData ctx-    if "wrong-password" `B.isInfixOf` bs-        then return Nothing-        else do-            let cookie = B.intercalate ";" . map snd-                       . filter ((== "Set-Cookie") . fst)-                       . map (second (B.drop 2 . B.takeWhile (/=';'))-                            . B.breakSubstring ": ")-                       . B.lines $ bs-                s = printf "GET /chat/Botdom HTTP/1.1\r\n%s\r\n\-                        \cookie: %s\r\n\r\n"-                        (concatHeaders [("Host", "chat.deviantart.com")])-                        (B.unpack cookie)-            sendData ctx $ LB.pack s-            bq <- recvUntil ctx "dAmnChat_Init"-            return . (Just . decodeUtf8 . B.take 32 . B.tail-                     . B.dropWhile (/='"') . B.dropWhile (/=',') . snd)-                   . B.breakSubstring "dAmn_Login" $ bq+getToken uname pass = do let params = defaultParams { pCiphers = ciphersuite_all+                                                    , onCertificatesRecv = certificateChecks [return . certificateVerifyDomain "chat.deviantart.com"] }+                             headers :: [(String, String)]+                             headers = [("Connection", "closed"), ("Content-Type", "application/x-www-form-urlencoded")]+                         gen <- makeSystem+                         ctv <- connectionClient "www.deviantart.com" "443" params gen+                         handshake ctv+                         sendData ctv . LB.pack $ "GET /users/login HTTP/1.1\r\nHost: www.deviantart.com\r\n\r\n"+                         bl <- recvUntil ctv "validate_key"+                         bye ctv++                         let payload = urlEncodeVars [ ("username", T.unpack uname)+                                                     , ("password", T.unpack pass)+                                                     , ("validate_token", B.unpack (scrapeFormValue "validate_token" bl))+                                                     , ("validate_key", B.unpack (scrapeFormValue "validate_key" bl))+                                                     , ("remember_me","1")+                                                     ]+                         ctx <- connectionClient "www.deviantart.com" "443" params gen+                         handshake ctx+                         sendData ctx . LB.pack $ printf "POST /users/login HTTP/1.1\r\n%s\r\n\+                                                         \cookie: %s\r\nContent-Length: %d\r\n\r\n%s"+                                                         (concatHeaders $ ("Host", "www.deviantart.com"):headers)+                                                         (B.unpack (scrapeCookies bl))+                                                         (length payload)+                                                         payload+                         bs <- recvData ctx+                         if "wrong-password" `B.isInfixOf` bs+                            then return Nothing+                            else do let s = printf "GET /chat/Botdom HTTP/1.1\r\n%s\r\n\+                                                \cookie: %s\r\n\r\n"+                                                (concatHeaders [("Host", "chat.deviantart.com")])+                                                (B.unpack (scrapeCookies bs))+                                    sendData ctx $ LB.pack s+                                    bq <- recvUntil ctx "dAmnChat_Init"+                                    return . (Just . decodeUtf8 . B.take 32 . B.tail+                                             . B.dropWhile (/='"') . B.dropWhile (/=',') . snd)+                                           . B.breakSubstring "dAmn_Login" $ bq
Main.hs view
@@ -9,30 +9,21 @@ data Flag = Port Int | Version | Help deriving (Eq)  opts :: [OptDescr Flag]-opts = [-        Option "p" ["port"] (ReqArg (Port . read) "number")-            $ "local port to run the server on (defaults to "-                ++ show defaultPort-                ++ ")",-        Option "h" ["help"] (NoArg Help) "print this message",-        Option "v" ["version"] (NoArg Version) "show kevin's version number"-       ]+opts = [ Option "p" ["port"] (ReqArg (Port . read) "number") $ "local port to run the server on (defaults to " ++ show defaultPort ++ ")"+       , Option "h" ["help"] (NoArg Help) "print this message"+       , Option "v" ["version"] (NoArg Version) "show kevin's version number" ]  header :: String header = "Usage: kevin [options...]"  getPort :: [Flag] -> Int getPort (Port x:_) = x-getPort (_:xs) = getPort xs-getPort [] = defaultPort+getPort (_:xs)     = getPort xs+getPort []         = defaultPort  main :: IO ()-main = do-    args <- getArgs-    case getOpt Permute opts args of-        (flags, _, []) -> case flags of-            f | Help `elem` f -> putStrLn $ usageInfo header opts-              | Version `elem` f -> putStrLn -                  $ "kevin version " ++ showVersion version-              | otherwise -> kevinServer $ getPort flags-        (_, _, msgs@(_:_)) -> putStrLn $ concat msgs ++ usageInfo header opts+main = do args <- getArgs+          case getOpt Permute opts args of (flags, _, [])     -> case flags of f | Help `elem` f    -> putStrLn $ usageInfo header opts+                                                                                 | Version `elem` f -> putStrLn $ "kevin version " ++ showVersion version+                                                                                 | otherwise        -> kevinServer $ getPort flags+                                           (_, _, msgs@(_:_)) -> putStrLn $ concat msgs ++ usageInfo header opts
kevin.cabal view
@@ -1,5 +1,5 @@ Name:             kevin-Version:          0.8.2+Version:          0.9.0 Synopsis:         a dAmn ↔ IRC proxy Description:      a dAmn ↔ IRC proxy License:          GPL@@ -18,7 +18,7 @@     Main-is:            Main.hs      Default-language:   Haskell2010-    +     Build-Depends:      attoparsec,                         base == 4.*,                         bytestring,@@ -36,7 +36,7 @@                         time,                         tls,                         tls-extra-                        +     Other-Modules:      Kevin,                         Kevin.Base,                         Kevin.Chatrooms,@@ -54,6 +54,6 @@                         Kevin.Util.Tablump,                         Kevin.Util.Token,                         Kevin.Version-                +     default-extensions: DeriveDataTypeable, ExistentialQuantification, FlexibleContexts, OverloadedStrings, ScopedTypeVariables, TemplateHaskell     ghc-options:        -Wall -fno-warn-unused-do-bind -threaded