simpleirc 0.2.0 → 0.2.1
raw patch · 3 files changed
+120/−57 lines, 3 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
- Network.SimpleIRC.Core: getDest :: MIrc -> IrcMessage -> IO ByteString
+ Network.SimpleIRC.Core: cPass :: IrcConfig -> Maybe String
+ Network.SimpleIRC.Core: cPingTimeoutInterval :: IrcConfig -> Int
+ Network.SimpleIRC.Core: getPort :: MIrc -> IO Int
+ Network.SimpleIRC.Core: reconnect :: MIrc -> IO (Either IOError MIrc)
+ Network.SimpleIRC.Messages: mOrigin :: IrcMessage -> Maybe ByteString
- Network.SimpleIRC.Core: Disconnect :: (IrcServer -> IO ()) -> IrcEvent
+ Network.SimpleIRC.Core: Disconnect :: (MIrc -> IO ()) -> IrcEvent
- Network.SimpleIRC.Core: IrcConfig :: String -> Int -> String -> String -> String -> [String] -> [IrcEvent] -> String -> IO String -> IrcConfig
+ Network.SimpleIRC.Core: IrcConfig :: String -> Int -> String -> Maybe String -> String -> String -> [String] -> [IrcEvent] -> String -> IO String -> Int -> IrcConfig
- Network.SimpleIRC.Messages: IrcMessage :: Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> ByteString -> ByteString -> Maybe ByteString -> Maybe [ByteString] -> ByteString -> IrcMessage
+ Network.SimpleIRC.Messages: IrcMessage :: Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> ByteString -> ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe [ByteString] -> ByteString -> IrcMessage
Files
- Network/SimpleIRC/Core.hs +93/−42
- Network/SimpleIRC/Messages.hs +25/−13
- simpleirc.cabal +2/−2
Network/SimpleIRC/Core.hs view
@@ -20,6 +20,7 @@ -- * Functions , connect , disconnect+ , reconnect , sendRaw , sendMsg , sendCmd@@ -28,13 +29,11 @@ , remEvent , defaultConfig - -- * Utils- , getDest- -- * Accessors , getChannels , getNickname , getAddress+ , getPort , getUsername , getRealname ) where@@ -42,6 +41,7 @@ import Network import System.IO import Data.Maybe+import Data.List (delete) import Data.Char (isNumber) import Control.Monad import Control.Concurrent@@ -50,12 +50,13 @@ import Network.SimpleIRC.Messages import Data.Unique import System.IO.Error+import System.Timeout import Data.Time import System.Locale import qualified Data.ByteString.Char8 as B import qualified Data.Map as Map -internalEvents = [joinChans, pong, onJoin]+internalEvents = [joinChans, pong, trackChanges] internalNormEvents = [Privmsg ctcpHandler] type MIrc = MVar IrcServer@@ -64,12 +65,14 @@ { cAddr :: String -- ^ Server address to connect to , cPort :: Int -- ^ Server port to connect to , cNick :: String -- ^ Nickname+ , cPass :: Maybe String -- ^ Optional server password , cUsername :: String -- ^ Username , cRealname :: String -- ^ Realname , cChannels :: [String] -- ^ List of channels to join on connect , cEvents :: [IrcEvent] -- ^ Events to bind , cCTCPVersion :: String -- ^ What to send on CTCP VERSION , cCTCPTime :: IO String -- ^ What to send on CTCP TIME+ , cPingTimeoutInterval :: Int -- The time between server messages that causes ping timeout } data SIrcCommand =@@ -81,6 +84,7 @@ { sAddr :: B.ByteString , sPort :: Int , sNickname :: B.ByteString+ , sPassword :: Maybe B.ByteString , sUsername :: B.ByteString , sRealname :: B.ByteString , sChannels :: [B.ByteString]@@ -93,6 +97,7 @@ -- Other info , sCTCPVersion :: String , sCTCPTime :: IO String+ , sPingTimeoutInterval :: Int } -- When adding events here, remember add them in callEvents and in eventFunc@@ -112,7 +117,7 @@ | Nick EventFunc -- ^ NICK | Notice EventFunc -- ^ NOTICE | RawMsg EventFunc -- ^ This event gets called on every message received- | Disconnect (IrcServer -> IO ()) -- ^ This event gets called whenever the+ | Disconnect (MIrc -> IO ()) -- ^ This event gets called whenever the -- connection with the server is dropped instance Show IrcEvent where@@ -174,6 +179,38 @@ write s $ "QUIT :" `B.append` quitMsg return () +-- |Reconnects to the server.+reconnect :: MIrc -> IO (Either IOError MIrc)+reconnect mServer = try $ do+ server <- readMVar mServer+ + h <- connectTo (B.unpack $ sAddr server) (PortNumber $ fromIntegral $ sPort server)+ hSetBuffering h NoBuffering+ modifyMVar_ mServer (\s -> return $ s {sSock = Just h})++ -- Initialize connection with the server+ withMVar mServer greetServer++ -- Restart the listen loop.+ listenId <- forkIO (listenLoop mServer)+ cmdId <- forkIO (execCmdsLoop mServer)+ modifyMVar_ mServer (\s -> return $ s {sListenThread = Just listenId,+ sCmdThread = Just cmdId})+ return mServer++{-+-- |Reconnects to the server.+reconnect :: MIrc -> IO (Either IOError MIrc)+reconnect server = do+ s <- readMVar server+ + let conf = IrcConfig (B.unpack $ sAddr s) (sPort s) + (B.unpack $ sNickname s) (B.unpack $ sUsername s) + (B.unpack $ sRealname s) (map (B.unpack) (sChannels s)) + (elems $ sEvents s) (sCTCPVersion s) (sCTCPTime s)+ connect conf True (sDebug s)+-}+ genUnique :: IrcEvent -> IO (Unique, IrcEvent) genUnique e = do u <- newUnique@@ -189,19 +226,23 @@ uniqueEvents <- genUniqueMap $ internalNormEvents ++ cEvents config return $ IrcServer (B.pack $ cAddr config) (cPort config)- (B.pack $ cNick config) (B.pack $ cUsername config) + (B.pack $ cNick config) (B.pack `fmap` cPass config) (B.pack $ cUsername config) (B.pack $ cRealname config) (map B.pack $ cChannels config) uniqueEvents (Just h) Nothing Nothing cmdChan debug- (cCTCPVersion config) (cCTCPTime config)+ (cCTCPVersion config) (cCTCPTime config) (cPingTimeoutInterval config) greetServer :: IrcServer -> IO IrcServer greetServer server = do+ case mpass of+ Nothing -> return ()+ Just pass -> write server $ "PASS " `B.append` pass write server $ "NICK " `B.append` nick write server $ "USER " `B.append` user `B.append` " " `B.append` user `B.append` " " `B.append` addr `B.append` " :" `B.append` real return server where nick = sNickname server+ mpass = sPassword server user = sUsername server real = sRealname server addr = sAddr server@@ -228,14 +269,15 @@ server <- readMVar s let h = fromJust $ sSock server- eof <- hIsEOF h+ eof <- timeout (sPingTimeoutInterval server) $ hIsEOF h -- If EOF then we are disconnected- if eof + if (eof /= Just False) then do let comp = (\a -> a `eqEvent` (Disconnect undefined)) events = Map.filter comp (sEvents server)- eventCall = (\obj -> (eventFuncD $ snd obj) server)+ eventCall = (\obj -> (eventFuncD $ snd obj) s)+ modifyMVar_ s (\serv -> return $ serv {sSock = Nothing}) debugWrite server $ B.pack $ show $ length $ Map.toList events mapM_ eventCall (Map.toList events) else do@@ -251,9 +293,12 @@ putMVar s newServ -- Put the MVar back. + let parsed = (parse line) -- Call the events- callEvents s (parse line)+ callEvents s parsed + -- Call the RawMsg Events.+ events s (RawMsg undefined) parsed listenLoop s@@ -280,15 +325,32 @@ pingMsg = mMsg msg code = mCode msg --- TODO: Nick and Channels tracking. KICK, PART and NICK.-onJoin :: IrcServer -> IrcMessage -> IO IrcServer-onJoin server msg+trackChanges :: IrcServer -> IrcMessage -> IO IrcServer+trackChanges server msg | code == "JOIN" = do let nick = fromJust $ mNick msg chan = mMsg msg if nick == sNickname server then return server { sChannels = chan:(sChannels server) } else return server+ | code == "NICK" = do+ let nick = fromJust $ mNick msg+ newNick = mMsg msg+ if nick == sNickname server+ then return server { sNickname = newNick }+ else return server + | code == "KICK" = do + let nick = (fromJust $ mOther msg) !! 0+ chan = fromJust $ mChan msg+ if nick == sNickname server+ then return server { sChannels = delete chan (sChannels server) }+ else return server+ | code == "PART" = do + let nick = fromJust $ mNick msg+ chan = mMsg msg+ if nick == sNickname server+ then return server { sChannels = delete chan (sChannels server) }+ else return server | otherwise = return server where code = mCode msg@@ -299,28 +361,26 @@ | msg == "\x01VERSION\x01" = do server <- readMVar mServ - chan <- getDest mServ iMsg sendCmd mServ- (MNotice chan ("\x01VERSION " `B.append`+ (MNotice origin ("\x01VERSION " `B.append` B.pack (sCTCPVersion server) `B.append` "\x01"))+ | msg == "\x01TIME\x01" = do server <- readMVar mServ time <- sCTCPTime server- chan <- getDest mServ iMsg sendCmd mServ- (MNotice chan ("\x01TIME " `B.append`+ (MNotice origin ("\x01TIME " `B.append` (B.pack time) `B.append` "\x01")) | "\x01PING " `B.isPrefixOf` msg = do server <- readMVar mServ - chan <- getDest mServ iMsg sendCmd mServ- (MNotice chan msg)+ (MNotice origin msg) | otherwise = return ()- where msg = mMsg iMsg-+ where msg = mMsg iMsg+ origin = fromJust $ mOrigin iMsg -- Event code events :: MIrc -> IrcEvent -> IrcMessage -> IO () events mServ event msg = do@@ -370,8 +430,8 @@ | B.all isNumber (mCode msg) = events mServ (Numeric undefined) msg - | otherwise =- events mServ (RawMsg undefined) msg+ | otherwise = return ()+ (Privmsg _) `eqEvent` (Privmsg _) = True (Numeric _) `eqEvent` (Numeric _) = True@@ -466,32 +526,18 @@ defaultConfig = IrcConfig { cPort = 6667+ , cPass = Nothing , cUsername = "simpleirc" , cRealname = "SimpleIRC Bot" , cChannels = [] , cEvents = [] , cCTCPVersion = "SimpleIRC v0.2" , cCTCPTime = fmap (formatTime defaultTimeLocale "%c") getZonedTime+ , cPingTimeoutInterval = 350 * 10^6 } --- Utils---- |Gets the destination, i.e if the IrcMessage was sent--- directly to you returns the senders nick otherwise the channel.-getDest :: MIrc -> IrcMessage -> IO B.ByteString-getDest mIrc m = do- s <- readMVar mIrc- - if sNickname s == chan- then return (fromJust $ mNick m)- else return chan- - where chan = fromJust $ mChan m- -- MIrc Accessors -- |Returns a list of channels currently joined.--- --- Currently this is not updated on KICK or PART. getChannels :: MIrc -> IO [B.ByteString] getChannels mIrc = do s <- readMVar mIrc@@ -499,8 +545,6 @@ return $ sChannels s -- |Returns the current nickname.--- --- Currently this is not updated on NICK. getNickname :: MIrc -> IO B.ByteString getNickname mIrc = do s <- readMVar mIrc@@ -513,6 +557,13 @@ s <- readMVar mIrc return $ sAddr s ++-- |Returns the address+getPort :: MIrc -> IO Int+getPort mIrc = do+ s <- readMVar mIrc+ + return $ sPort s -- |Returns the User name getUsername :: MIrc -> IO B.ByteString
Network/SimpleIRC/Messages.hs view
@@ -20,6 +20,7 @@ import Data.Maybe import qualified Data.ByteString.Char8 as B import Control.Arrow+import Control.Applicative import Data.Typeable -- PING :asimov.freenode.net@@ -55,6 +56,7 @@ , mCode :: B.ByteString , mMsg :: B.ByteString , mChan :: Maybe B.ByteString+ , mOrigin :: Maybe B.ByteString -- ^ Origin of the message, this is mNick if a message was sent directly to the bot, otherwise if it got sent to the channel it's mChan. , mOther :: Maybe [B.ByteString] , mRaw :: B.ByteString } deriving (Show, Typeable)@@ -63,13 +65,14 @@ parse :: B.ByteString -> IrcMessage parse txt = case length split of - 2 -> parse2 split txt- 3 -> parse3 split txt- 4 -> parse4 split txt - 5 -> parse5 split txt- _ -> parseOther split txt+ 2 -> parse2 split noCarriage+ 3 -> parse3 split noCarriage+ 4 -> parse4 split noCarriage + 5 -> parse5 split noCarriage+ _ -> parseOther split noCarriage - where split = smartSplit (takeCarriageRet txt)+ where noCarriage = takeCarriageRet txt+ split = smartSplit noCarriage -- Nick, Host, Server parseFirst :: B.ByteString -> (Maybe B.ByteString, Maybe B.ByteString, Maybe B.ByteString, Maybe B.ByteString)@@ -82,31 +85,40 @@ else (Just nick, Nothing, Just user_host, Nothing) else (Nothing, Nothing, Nothing, Just $ dropColon first) +getOrigin :: Maybe B.ByteString -> B.ByteString -> B.ByteString+getOrigin (Just nick) chan =+ if "#" `B.isPrefixOf` chan || "&" `B.isPrefixOf` chan || "+" `B.isPrefixOf` chan+ || "!" `B.isPrefixOf` chan+ then chan+ else nick+getOrigin Nothing chan = chan+ parse2 :: [B.ByteString] -> B.ByteString -> IrcMessage parse2 (code:msg:_) = IrcMessage Nothing Nothing Nothing Nothing code- (dropColon msg) Nothing Nothing+ (dropColon msg) Nothing Nothing Nothing parse3 :: [B.ByteString] -> B.ByteString -> IrcMessage parse3 (first:code:msg:_) = let (nick, user, host, server) = parseFirst first- in IrcMessage nick user host server code (dropColon msg) Nothing Nothing+ in IrcMessage nick user host server code (dropColon msg) Nothing Nothing Nothing parse4 :: [B.ByteString] -> B.ByteString -> IrcMessage parse4 (first:code:chan:msg:_) = let (nick, user, host, server) = parseFirst first in IrcMessage nick user host server code- (dropColon msg) (Just chan) Nothing+ (dropColon msg) (Just chan) (Just $ getOrigin nick chan) Nothing parse5 :: [B.ByteString] -> B.ByteString -> IrcMessage-parse5 (server:code:nick:chan:msg:_) =- IrcMessage (Just nick) Nothing Nothing (Just server) code- (dropColon msg) (Just chan) Nothing+parse5 (first:code:chan:other:msg:_) =+ let (nick, user, host, server) = parseFirst first+ in IrcMessage nick user host server code+ (dropColon msg) (Just chan) (Just $ getOrigin nick chan) (Just [other]) parseOther :: [B.ByteString] -> B.ByteString -> IrcMessage parseOther (server:code:nick:chan:other) = IrcMessage (Just nick) Nothing Nothing (Just server) code- (B.unwords other) (Just chan) (Just other)+ (B.unwords other) (Just chan) (Just $ getOrigin (Just nick) chan) (Just other) smartSplit :: B.ByteString -> [B.ByteString] smartSplit txt =
simpleirc.cabal view
@@ -1,5 +1,5 @@ Name: simpleirc-Version: 0.2.0+Version: 0.2.1 Category: Network, IRC Synopsis: Simple IRC Library Maintainer: Dominik Picheta <dominikpicheta@googlemail.com>@@ -21,7 +21,7 @@ Source-repository this Type: git Location: git://github.com/dom96/SimpleIRC.git- tag: v0.2.0+ tag: v0.2.1 Library Build-depends: