packages feed

simpleirc 0.1.0 → 0.2.0

raw patch · 6 files changed

+378/−312 lines, 6 filesdep ~networkPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: network

API changes (from Hackage documentation)

- Network.SimpleIRC.Types: Disconnect :: (IrcServer -> IO ()) -> IrcEvent
- Network.SimpleIRC.Types: Invite :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: IrcConfig :: String -> Int -> String -> String -> String -> [String] -> [IrcEvent] -> String -> IO String -> IrcConfig
- Network.SimpleIRC.Types: IrcMessage :: Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> ByteString -> ByteString -> Maybe ByteString -> Maybe [ByteString] -> ByteString -> IrcMessage
- Network.SimpleIRC.Types: IrcServer :: ByteString -> Int -> ByteString -> ByteString -> ByteString -> [ByteString] -> Map Unique IrcEvent -> Maybe Handle -> Maybe ThreadId -> Chan SIrcCommand -> Bool -> String -> IO String -> IrcServer
- Network.SimpleIRC.Types: Join :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Kick :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Mode :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Nick :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Notice :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Numeric :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Part :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Ping :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Privmsg :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: Quit :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: RawMsg :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: SIrcAddEvent :: (Unique, IrcEvent) -> SIrcCommand
- Network.SimpleIRC.Types: SIrcChangeEvents :: (Map Unique IrcEvent) -> SIrcCommand
- Network.SimpleIRC.Types: SIrcRemoveEvent :: Unique -> SIrcCommand
- Network.SimpleIRC.Types: Topic :: EventFunc -> IrcEvent
- Network.SimpleIRC.Types: cAddr :: IrcConfig -> String
- Network.SimpleIRC.Types: cCTCPTime :: IrcConfig -> IO String
- Network.SimpleIRC.Types: cCTCPVersion :: IrcConfig -> String
- Network.SimpleIRC.Types: cChannels :: IrcConfig -> [String]
- Network.SimpleIRC.Types: cEvents :: IrcConfig -> [IrcEvent]
- Network.SimpleIRC.Types: cNick :: IrcConfig -> String
- Network.SimpleIRC.Types: cPort :: IrcConfig -> Int
- Network.SimpleIRC.Types: cRealname :: IrcConfig -> String
- Network.SimpleIRC.Types: cUsername :: IrcConfig -> String
- Network.SimpleIRC.Types: data IrcConfig
- Network.SimpleIRC.Types: data IrcEvent
- Network.SimpleIRC.Types: data IrcMessage
- Network.SimpleIRC.Types: data IrcServer
- Network.SimpleIRC.Types: data SIrcCommand
- Network.SimpleIRC.Types: instance Show IrcEvent
- Network.SimpleIRC.Types: instance Show IrcMessage
- Network.SimpleIRC.Types: instance Typeable IrcMessage
- Network.SimpleIRC.Types: mChan :: IrcMessage -> Maybe ByteString
- Network.SimpleIRC.Types: mCode :: IrcMessage -> ByteString
- Network.SimpleIRC.Types: mHost :: IrcMessage -> Maybe ByteString
- Network.SimpleIRC.Types: mMsg :: IrcMessage -> ByteString
- Network.SimpleIRC.Types: mNick :: IrcMessage -> Maybe ByteString
- Network.SimpleIRC.Types: mOther :: IrcMessage -> Maybe [ByteString]
- Network.SimpleIRC.Types: mRaw :: IrcMessage -> ByteString
- Network.SimpleIRC.Types: mServer :: IrcMessage -> Maybe ByteString
- Network.SimpleIRC.Types: sAddr :: IrcServer -> ByteString
- Network.SimpleIRC.Types: sCTCPTime :: IrcServer -> IO String
- Network.SimpleIRC.Types: sCTCPVersion :: IrcServer -> String
- Network.SimpleIRC.Types: sChannels :: IrcServer -> [ByteString]
- Network.SimpleIRC.Types: sCmdChan :: IrcServer -> Chan SIrcCommand
- Network.SimpleIRC.Types: sDebug :: IrcServer -> Bool
- Network.SimpleIRC.Types: sEvents :: IrcServer -> Map Unique IrcEvent
- Network.SimpleIRC.Types: sListenThread :: IrcServer -> Maybe ThreadId
- Network.SimpleIRC.Types: sNickname :: IrcServer -> ByteString
- Network.SimpleIRC.Types: sPort :: IrcServer -> Int
- Network.SimpleIRC.Types: sRealname :: IrcServer -> ByteString
- Network.SimpleIRC.Types: sSock :: IrcServer -> Maybe Handle
- Network.SimpleIRC.Types: sUsername :: IrcServer -> ByteString
- Network.SimpleIRC.Types: type EventFunc = IrcServer -> IrcMessage -> IO ()
- Network.SimpleIRC.Utils: getChan :: IrcServer -> IrcMessage -> ByteString
+ Network.SimpleIRC.Core: Disconnect :: (IrcServer -> IO ()) -> IrcEvent
+ Network.SimpleIRC.Core: Invite :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: IrcConfig :: String -> Int -> String -> String -> String -> [String] -> [IrcEvent] -> String -> IO String -> IrcConfig
+ Network.SimpleIRC.Core: Join :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Kick :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Mode :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Nick :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Notice :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Numeric :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Part :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Ping :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Privmsg :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Quit :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: RawMsg :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: Topic :: EventFunc -> IrcEvent
+ Network.SimpleIRC.Core: cAddr :: IrcConfig -> String
+ Network.SimpleIRC.Core: cCTCPTime :: IrcConfig -> IO String
+ Network.SimpleIRC.Core: cCTCPVersion :: IrcConfig -> String
+ Network.SimpleIRC.Core: cChannels :: IrcConfig -> [String]
+ Network.SimpleIRC.Core: cEvents :: IrcConfig -> [IrcEvent]
+ Network.SimpleIRC.Core: cNick :: IrcConfig -> String
+ Network.SimpleIRC.Core: cPort :: IrcConfig -> Int
+ Network.SimpleIRC.Core: cRealname :: IrcConfig -> String
+ Network.SimpleIRC.Core: cUsername :: IrcConfig -> String
+ Network.SimpleIRC.Core: data IrcConfig
+ Network.SimpleIRC.Core: data IrcEvent
+ Network.SimpleIRC.Core: getAddress :: MIrc -> IO ByteString
+ Network.SimpleIRC.Core: getChannels :: MIrc -> IO [ByteString]
+ Network.SimpleIRC.Core: getDest :: MIrc -> IrcMessage -> IO ByteString
+ Network.SimpleIRC.Core: getNickname :: MIrc -> IO ByteString
+ Network.SimpleIRC.Core: getRealname :: MIrc -> IO ByteString
+ Network.SimpleIRC.Core: getUsername :: MIrc -> IO ByteString
+ Network.SimpleIRC.Core: instance Show IrcEvent
+ Network.SimpleIRC.Core: type EventFunc = MIrc -> IrcMessage -> IO ()
+ Network.SimpleIRC.Core: type MIrc = MVar IrcServer
+ Network.SimpleIRC.Messages: IrcMessage :: Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> Maybe ByteString -> ByteString -> ByteString -> Maybe ByteString -> Maybe [ByteString] -> ByteString -> IrcMessage
+ Network.SimpleIRC.Messages: data IrcMessage
+ Network.SimpleIRC.Messages: instance Show IrcMessage
+ Network.SimpleIRC.Messages: instance Typeable IrcMessage
+ Network.SimpleIRC.Messages: mChan :: IrcMessage -> Maybe ByteString
+ Network.SimpleIRC.Messages: mCode :: IrcMessage -> ByteString
+ Network.SimpleIRC.Messages: mHost :: IrcMessage -> Maybe ByteString
+ Network.SimpleIRC.Messages: mMsg :: IrcMessage -> ByteString
+ Network.SimpleIRC.Messages: mNick :: IrcMessage -> Maybe ByteString
+ Network.SimpleIRC.Messages: mOther :: IrcMessage -> Maybe [ByteString]
+ Network.SimpleIRC.Messages: mRaw :: IrcMessage -> ByteString
+ Network.SimpleIRC.Messages: mServer :: IrcMessage -> Maybe ByteString
+ Network.SimpleIRC.Messages: mUser :: IrcMessage -> Maybe ByteString
- Network.SimpleIRC.Core: addEvent :: IrcServer -> IrcEvent -> IO Unique
+ Network.SimpleIRC.Core: addEvent :: MIrc -> IrcEvent -> IO Unique
- Network.SimpleIRC.Core: changeEvents :: IrcServer -> [IrcEvent] -> IO ()
+ Network.SimpleIRC.Core: changeEvents :: MIrc -> [IrcEvent] -> IO ()
- Network.SimpleIRC.Core: connect :: IrcConfig -> Bool -> Bool -> IO (Either IOError IrcServer)
+ Network.SimpleIRC.Core: connect :: IrcConfig -> Bool -> Bool -> IO (Either IOError MIrc)
- Network.SimpleIRC.Core: disconnect :: IrcServer -> ByteString -> IO ()
+ Network.SimpleIRC.Core: disconnect :: MIrc -> ByteString -> IO ()
- Network.SimpleIRC.Core: remEvent :: IrcServer -> Unique -> IO ()
+ Network.SimpleIRC.Core: remEvent :: MIrc -> Unique -> IO ()
- Network.SimpleIRC.Core: sendCmd :: IrcServer -> Command -> IO ()
+ Network.SimpleIRC.Core: sendCmd :: MIrc -> Command -> IO ()
- Network.SimpleIRC.Core: sendMsg :: IrcServer -> ByteString -> ByteString -> IO ()
+ Network.SimpleIRC.Core: sendMsg :: MIrc -> ByteString -> ByteString -> IO ()
- Network.SimpleIRC.Core: sendRaw :: IrcServer -> ByteString -> IO ()
+ Network.SimpleIRC.Core: sendRaw :: MIrc -> ByteString -> IO ()

Files

Network/SimpleIRC.hs view
@@ -15,15 +15,9 @@      -- * Messages   , module Network.SimpleIRC.Messages-  -    -- * Utils-  , module Network.SimpleIRC.Utils-  -    -- * Types-  , module Network.SimpleIRC.Types++   ) where  import Network.SimpleIRC.Core import Network.SimpleIRC.Messages-import Network.SimpleIRC.Utils-import Network.SimpleIRC.Types
Network/SimpleIRC/Core.hs view
@@ -11,8 +11,14 @@ {-# LANGUAGE OverloadedStrings #-} module Network.SimpleIRC.Core   ( +    -- * Types+    MIrc +  , EventFunc+  , IrcConfig(..)+  , IrcEvent(..)+         -- * Functions-    connect+  , connect   , disconnect   , sendRaw   , sendMsg@@ -21,6 +27,16 @@   , changeEvents   , remEvent   , defaultConfig+  +   -- * Utils+  , getDest+  +   -- * Accessors+  , getChannels+  , getNickname+  , getAddress+  , getUsername+  , getRealname   ) where    import Network@@ -30,9 +46,8 @@ import Control.Monad import Control.Concurrent import Control.Concurrent.Chan+import Control.Concurrent.MVar import Network.SimpleIRC.Messages-import Network.SimpleIRC.Types-import Network.SimpleIRC.Utils import Data.Unique import System.IO.Error import Data.Time@@ -43,15 +58,90 @@ internalEvents     = [joinChans, pong, onJoin] internalNormEvents = [Privmsg ctcpHandler] +type MIrc = MVar IrcServer++data IrcConfig = IrcConfig+  { cAddr     :: String   -- ^ Server address to connect to+  , cPort     :: Int      -- ^ Server port to connect to+  , cNick     :: String   -- ^ Nickname+  , 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+  }++data SIrcCommand =+    SIrcAddEvent (Unique, IrcEvent)+  | SIrcChangeEvents (Map.Map Unique IrcEvent)+  | SIrcRemoveEvent Unique++data IrcServer = IrcServer+  { sAddr         :: B.ByteString+  , sPort         :: Int+  , sNickname     :: B.ByteString+  , sUsername     :: B.ByteString+  , sRealname     :: B.ByteString+  , sChannels     :: [B.ByteString]+  , sEvents       :: Map.Map Unique IrcEvent+  , sSock         :: Maybe Handle+  , sListenThread :: Maybe ThreadId+  , sCmdThread    :: Maybe ThreadId+  , sCmdChan      :: Chan SIrcCommand+  , sDebug        :: Bool+  -- Other info+  , sCTCPVersion  :: String+  , sCTCPTime     :: IO String+  }++-- When adding events here, remember add them in callEvents and in eventFunc+-- AND also in the Show instance and Eq instance++data IrcEvent = +    Privmsg EventFunc -- ^ PRIVMSG+  | Numeric EventFunc -- ^ Numeric, 001, 002, 372 etc.+  | Ping EventFunc    -- ^ PING+  | Join EventFunc    -- ^ JOIN+  | Part EventFunc    -- ^ PART+  | Mode EventFunc    -- ^ MODE+  | Topic EventFunc   -- ^ TOPIC+  | Invite EventFunc  -- ^ INVITE+  | Kick EventFunc    -- ^ KICK+  | Quit EventFunc    -- ^ QUIT+  | 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+                                    --   connection with the server is dropped+  +instance Show IrcEvent where+  show (Privmsg _) = "IrcEvent - Privmsg"+  show (Numeric _) = "IrcEvent - Numeric"+  show (Ping    _) = "IrcEvent - Ping"+  show (Join    _) = "IrcEvent - Join"+  show (Part    _) = "IrcEvent - Part"+  show (Mode    _) = "IrcEvent - Mode"+  show (Topic   _) = "IrcEvent - Topic"+  show (Invite  _) = "IrcEvent - Invite"+  show (Kick    _) = "IrcEvent - Kick"+  show (Quit    _) = "IrcEvent - Quit"+  show (Nick    _) = "IrcEvent - Nick"+  show (Notice  _) = "IrcEvent - Notice"+  show (RawMsg  _) = "IrcEvent - RawMsg"+  show (Disconnect  _) = "IrcEvent - Disconnect"++type EventFunc = (MIrc -> IrcMessage -> IO ())+ -- |Connects to a server connect :: IrcConfig       -- ^ Configuration            -> Bool         -- ^ Run in a new thread            -> Bool         -- ^ Print debug messages-           -> IO (Either IOError IrcServer) -- ^ IrcServer instance+           -> IO (Either IOError MIrc) -- ^ IrcServer instance connect config threaded debug = try $ do-  if debug-    then B.putStrLn $ "Connecting to " `B.append` (B.pack $ cAddr config)-    else return ()+  (when debug $+    B.putStrLn $ "Connecting to " `B.append` B.pack (cAddr config))+     h <- connectTo (cAddr config) (PortNumber $ fromIntegral $ cPort config)   hSetBuffering h NoBuffering   @@ -61,21 +151,28 @@   -- Initialize connection with the server   greetServer server   -  -- Start listening+  -- Create a new MVar+  res <- newMVar server+  +  -- Start the loops, listen and exec cmds   if threaded-    then do listenId <- forkIO (listenLoop server)-            return server {sListenThread = Just listenId}-    else do listenLoop server-            return server-    +    then do listenId <- forkIO (listenLoop res)+            cmdId <- forkIO (execCmdsLoop res)+            modifyMVar_ res (\srv -> return $ srv {sListenThread = Just listenId}) +            return res+    else do listenLoop res+            return res+ -- |Sends a QUIT command to the server.-disconnect :: IrcServer+disconnect :: MIrc               -> B.ByteString -- ^ Quit message               -> IO () disconnect server quitMsg = do-  write server $ "QUIT :" `B.append` quitMsg+  s <- readMVar server+  +  let h = fromJust $ sSock s+  write s $ "QUIT :" `B.append` quitMsg   return ()-  where h = fromJust $ sSock server  genUnique :: IrcEvent -> IO (Unique, IrcEvent) genUnique e = do@@ -89,12 +186,12 @@  toServer :: IrcConfig -> Handle -> Chan SIrcCommand -> Bool -> IO IrcServer toServer config h cmdChan debug = do-  uniqueEvents <- genUniqueMap $ internalNormEvents ++ (cEvents config)+  uniqueEvents <- genUniqueMap $ internalNormEvents ++ cEvents config    return $ IrcServer (B.pack $ cAddr config) (cPort config)               (B.pack $ cNick config) (B.pack $ cUsername config)                (B.pack $ cRealname config) (map B.pack $ cChannels config) -              uniqueEvents (Just h) Nothing cmdChan debug+              uniqueEvents (Just h) Nothing Nothing cmdChan debug               (cCTCPVersion config) (cCTCPTime config)  greetServer :: IrcServer -> IO IrcServer@@ -109,54 +206,70 @@         real = sRealname server         addr = sAddr server --- TODO: I think this should execute all commands that are available.-execCmds :: IrcServer -> IO IrcServer-execCmds server = do-  empty <- isEmptyChan $ sCmdChan server-  if not $ empty -    then do cmd <- readChan $ sCmdChan server-            case cmd of (SIrcAddEvent uEvent) -> return server {sEvents = Map.insert (fst uEvent) (snd uEvent) (sEvents server)}-                        (SIrcChangeEvents events) -> return server {sEvents = events}-                        (SIrcRemoveEvent key) -> return server {sEvents = Map.delete key (sEvents server)}-    else return server+execCmdsLoop :: MIrc -> IO ()+execCmdsLoop mIrc = do+  server <- readMVar mIrc+  cmd <- readChan $ sCmdChan server+  case cmd of (SIrcAddEvent uEvent)     -> do+                swapMVar mIrc (server {sEvents = +                  (uncurry Map.insert uEvent) (sEvents server)}) +                execCmdsLoop mIrc+              (SIrcChangeEvents events) -> do+                swapMVar mIrc (server {sEvents = events}) +                execCmdsLoop mIrc+              (SIrcRemoveEvent key)     -> do+                swapMVar mIrc (server {sEvents = +                  Map.delete key (sEvents server)})+                execCmdsLoop mIrc -listenLoop :: IrcServer -> IO ()++listenLoop :: MIrc -> IO () listenLoop s = do-  server <- execCmds s+  server <- readMVar s    let h = fromJust $ sSock server   eof <- hIsEOF h+     -- If EOF then we are disconnected   if eof      then do       let comp   = (\a -> a `eqEvent` (Disconnect undefined))           events = Map.filter comp (sEvents server)           eventCall = (\obj -> (eventFuncD $ snd obj) server)-      debugWrite s $ B.pack $ show $ length $ Map.toList events-      mapM eventCall (Map.toList events)-      return ()+      debugWrite server $ B.pack $ show $ length $ Map.toList events+      mapM_ eventCall (Map.toList events)     else do       line <- B.hGetLine h-      debugWrite s $ (B.pack ">> ") `B.append` line       -      newServ <- foldM (\s f -> f s (parse line)) server internalEvents+      server1 <- takeMVar s       -      callEvents newServ (parse line)+      -- Print the received line.+      debugWrite server1 $ (B.pack ">> ") `B.append` line+      +      -- Call the internal events+      newServ <- foldM (\sr f -> f sr (parse line)) server1 internalEvents+      +      putMVar s newServ -- Put the MVar back.+      +      -- Call the events+      callEvents s (parse line) -      listenLoop newServ+      ++      listenLoop s      -- Internal Events - They can edit the server joinChans :: IrcServer -> IrcMessage -> IO IrcServer-joinChans server msg = do+joinChans server msg =   if code == "001"-    then do mapM (\chan -> write server $ "JOIN " `B.append` chan) (sChannels server)+    then do mapM_ (\chan -> write server $ "JOIN " `B.append` chan) (sChannels server)             return server {sChannels = []}     else return server   where h    = fromJust $ sSock server         code = mCode msg  pong :: IrcServer -> IrcMessage -> IO IrcServer-pong server msg = do+pong server msg =   if code == "PING"     then do       write server $ "PONG :" `B.append` pingMsg@@ -167,6 +280,7 @@         pingMsg = mMsg msg         code    = mCode msg +-- TODO: Nick and Channels tracking. KICK, PART and NICK. onJoin :: IrcServer -> IrcMessage -> IO IrcServer onJoin server msg   | code == "JOIN" = do@@ -181,68 +295,83 @@  -- Internal normal events ctcpHandler :: EventFunc-ctcpHandler server iMsg-  | msg == "\x01VERSION\x01" =-    sendCmd server+ctcpHandler mServ iMsg+  | msg == "\x01VERSION\x01" = do+    server <- readMVar mServ+    +    chan <- getDest mServ iMsg+    sendCmd mServ       (MNotice chan ("\x01VERSION " `B.append`-        (B.pack $ sCTCPVersion server) `B.append` "\x01"))+        B.pack (sCTCPVersion server) `B.append` "\x01"))   | msg == "\x01TIME\x01" = do+    server <- readMVar mServ+         time <- sCTCPTime server-    sendCmd server+    chan <- getDest mServ iMsg+    sendCmd mServ       (MNotice chan ("\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)+   | otherwise = return ()-  where msg  = mMsg iMsg-        chan = getChan server iMsg+  where msg = mMsg iMsg+ -- Event code-events :: IrcServer -> IrcEvent -> IrcMessage -> IO ()-events server event msg = do-  mapM eventCall (Map.toList events)-  return ()-  where comp   = (\a -> a `eqEvent` event)-        events = Map.filter comp (sEvents server)-        eventCall = (\obj -> (eventFunc $ snd obj) server msg)+events :: MIrc -> IrcEvent -> IrcMessage -> IO ()+events mServ event msg = do+  server <- readMVar mServ+  let comp   = (`eqEvent` event)+      events = Map.filter comp (sEvents server)+      eventCall = (\obj -> (eventFunc $ snd obj) mServ msg) -callEvents :: IrcServer -> IrcMessage -> IO ()-callEvents server msg-  | mCode msg == "PRIVMSG"     = do-    events server (Privmsg undefined) msg+  mapM_ eventCall (Map.toList events)+++callEvents :: MIrc -> IrcMessage -> IO ()+callEvents mServ msg+  | mCode msg == "PRIVMSG"     =+    events mServ (Privmsg undefined) msg     -  | mCode msg == "PING"        = do-    events server (Ping undefined) msg+  | mCode msg == "PING"        =+    events mServ (Ping undefined) msg -  | mCode msg == "JOIN"        = do-    events server (Join undefined) msg+  | mCode msg == "JOIN"        =+    events mServ (Join undefined) msg   -  | mCode msg == "PART"        = do-    events server (Part undefined) msg+  | mCode msg == "PART"        =+    events mServ (Part undefined) msg -  | mCode msg == "MODE"        = do-    events server (Mode undefined) msg+  | mCode msg == "MODE"        =+    events mServ (Mode undefined) msg -  | mCode msg == "TOPIC"       = do-    events server (Topic undefined) msg+  | mCode msg == "TOPIC"       =+    events mServ (Topic undefined) msg -  | mCode msg == "INVITE"      = do-    events server (Invite undefined) msg+  | mCode msg == "INVITE"      =+    events mServ (Invite undefined) msg -  | mCode msg == "KICK"        = do-    events server (Kick undefined) msg+  | mCode msg == "KICK"        =+    events mServ (Kick undefined) msg -  | mCode msg == "QUIT"        = do-    events server (Quit undefined) msg+  | mCode msg == "QUIT"        =+    events mServ (Quit undefined) msg -  | mCode msg == "NICK"        = do-    events server (Nick undefined) msg+  | mCode msg == "NICK"        =+    events mServ (Nick undefined) msg -  | mCode msg == "NOTICE"      = do-    events server (Notice undefined) msg+  | mCode msg == "NOTICE"      =+    events mServ (Notice undefined) msg -  | B.all isNumber (mCode msg) = do-    events server (Numeric undefined) msg+  | B.all isNumber (mCode msg) =+    events mServ (Numeric undefined) msg   -  | otherwise                = do-    events server (RawMsg undefined) msg+  | otherwise                =+    events mServ (RawMsg undefined) msg  (Privmsg _) `eqEvent` (Privmsg _) = True (Numeric _) `eqEvent` (Numeric _) = True@@ -278,48 +407,55 @@ eventFuncD (Disconnect  f) = f  -- |Sends a raw command to the server-sendRaw :: IrcServer -> B.ByteString -> IO ()-sendRaw server msg = write server msg+sendRaw :: MIrc -> B.ByteString -> IO ()+sendRaw mServ msg = do+  server <- readMVar mServ+  write server msg  -- |Sends a message to a channel  -- |Please note: As of now this function doesn't provide flood control.--- So be careful.-sendMsg :: IrcServer +-- So be careful with \\n.+sendMsg :: MIrc             -> B.ByteString -- ^ Channel            -> B.ByteString -- ^ Message            -> IO ()-sendMsg server chan msg = do-  mapM (s) lins-  return ()+sendMsg mServ chan msg =+  mapM_ s lins   where lins = B.lines msg-        s m = sendCmd server (MPrivmsg chan m)-sendCmd :: IrcServer+        s m = sendCmd mServ (MPrivmsg chan m)+        +        +sendCmd :: MIrc            -> Command -- Command to send            -> IO ()-sendCmd server cmd =-  sendRaw server (showCommand cmd)+sendCmd mServ cmd = sendRaw mServ (showCommand cmd) -addEvent :: IrcServer -> IrcEvent -> IO Unique-addEvent s event = do+addEvent :: MIrc -> IrcEvent -> IO Unique+addEvent mIrc event = do+  s <- readMVar mIrc+   u <- newUnique   writeChan (sCmdChan s) (SIrcAddEvent (u, event))   return u -changeEvents :: IrcServer -> [IrcEvent] -> IO ()-changeEvents s events = do++changeEvents :: MIrc -> [IrcEvent] -> IO ()+changeEvents mIrc events = do+  s <- readMVar mIrc+   uniqueEvents <- genUniqueMap events   writeChan (sCmdChan s) (SIrcChangeEvents uniqueEvents) -remEvent :: IrcServer -> Unique -> IO ()-remEvent s uniq = do+remEvent :: MIrc -> Unique -> IO ()+remEvent mIrc uniq = do+  s <- readMVar mIrc+   writeChan (sCmdChan s) (SIrcRemoveEvent uniq)  debugWrite :: IrcServer -> B.ByteString -> IO ()-debugWrite s msg = do-  if sDebug s-    then B.putStrLn msg-    else return ()+debugWrite s msg =+  (when (sDebug s) $ B.putStrLn msg)  write :: IrcServer -> B.ByteString -> IO () write s msg = do@@ -334,7 +470,61 @@   , cRealname = "SimpleIRC Bot"   , cChannels = []   , cEvents   = []-  , cCTCPVersion = "SimpleIRC v0.1"-  , cCTCPTime    =  getZonedTime >>= -    (\t -> return $ formatTime defaultTimeLocale "%c" t)+  , cCTCPVersion = "SimpleIRC v0.2"+  , cCTCPTime    = fmap (formatTime defaultTimeLocale "%c") getZonedTime   }+  +-- 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+  +  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+  +  return $ sNickname s+  +-- |Returns the address+getAddress :: MIrc -> IO B.ByteString+getAddress mIrc = do+  s <- readMVar mIrc+  +  return $ sAddr s  ++-- |Returns the User name+getUsername :: MIrc -> IO B.ByteString+getUsername mIrc = do+  s <- readMVar mIrc+  +  return $ sUsername s  ++-- |Returns the Real name+getRealname :: MIrc -> IO B.ByteString+getRealname mIrc = do+  s <- readMVar mIrc+  +  return $ sRealname s  +
Network/SimpleIRC/Messages.hs view
@@ -7,18 +7,20 @@ -- Stability : provisional -- Portability : portable ----- Messages module+-- Messages (parsing) module ---{-# LANGUAGE OverloadedStrings #-}-module Network.SimpleIRC.Messages -  ( Command(..)+{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}+module Network.SimpleIRC.Messages+  ( IrcMessage(..)+  , Command(..)   , parse-  , showCommand  -  ) +  , showCommand+  ) where import Data.Maybe-import Network.SimpleIRC.Types import qualified Data.ByteString.Char8 as B+import Control.Arrow+import Data.Typeable  -- PING :asimov.freenode.net -- :haskellTestBot!~test@host86-177-151-242.range86-177.btcentralplus.com JOIN :#()@@ -30,89 +32,106 @@ -- :asimov.freenode.net 332 haskellTestBot #() :Parenthesis  -- :asimov.freenode.net 333 haskellTestBot #() Raynes!~macr0@unaffiliated/raynes 1281221819-+   data Command = Command-  | MPrivmsg B.ByteString B.ByteString              -- ^ PRIVMSG #chan :msg-  | MJoin    B.ByteString (Maybe B.ByteString)      -- ^ JOIN #chan key-  | MPart    B.ByteString B.ByteString              -- ^ PART #chan :msg-  | MMode    B.ByteString B.ByteString -      (Maybe B.ByteString)                          -- ^ MODE #chan +o user-  | MTopic   B.ByteString (Maybe B.ByteString)      -- ^ TOPIC #chan :topic-  | MInvite  B.ByteString B.ByteString              -- ^ INVITE user #chan-  | MKick    B.ByteString B.ByteString B.ByteString -- ^ KICK #chan user :msg-  | MQuit    B.ByteString                           -- ^ QUIT :msg-  | MNick    B.ByteString                           -- ^ NICK newnick-  | MNotice  B.ByteString B.ByteString              -- ^ NOTICE usr/#chan :msg-  | MAction  B.ByteString B.ByteString              -- ^ PRIVMSG usr/#chan :\\x01ACTION msg\\x01+  | MPrivmsg B.ByteString B.ByteString                      -- ^ PRIVMSG #chan :msg+  | MJoin    B.ByteString (Maybe B.ByteString)              -- ^ JOIN #chan key+  | MPart    B.ByteString B.ByteString                      -- ^ PART #chan :msg+  | MMode    B.ByteString B.ByteString (Maybe B.ByteString) -- ^ MODE #chan +o user+  | MTopic   B.ByteString (Maybe B.ByteString)              -- ^ TOPIC #chan :topic+  | MInvite  B.ByteString B.ByteString                      -- ^ INVITE user #chan+  | MKick    B.ByteString B.ByteString B.ByteString         -- ^ KICK #chan user :msg+  | MQuit    B.ByteString                                   -- ^ QUIT :msg+  | MNick    B.ByteString                                   -- ^ NICK newnick+  | MNotice  B.ByteString B.ByteString                      -- ^ NOTICE usr/#chan :msg+  | MAction  B.ByteString B.ByteString                      -- ^ PRIVMSG usr/#chan :ACTION msg   deriving (Eq, Read, Show) +data IrcMessage = IrcMessage+  { mNick   :: Maybe B.ByteString+  , mUser   :: Maybe B.ByteString+  , mHost   :: Maybe B.ByteString+  , mServer :: Maybe B.ByteString+  , mCode   :: B.ByteString+  , mMsg    :: B.ByteString+  , mChan   :: Maybe B.ByteString+  , mOther  :: Maybe [B.ByteString]+  , mRaw    :: B.ByteString+  } deriving (Show, Typeable)+ -- |Parse a raw IRC message 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-                       otherwise -> (parseOther split) txt-                       +  case length split of +    2 -> parse2 split txt+    3 -> parse3 split txt+    4 -> parse4 split txt +    5 -> parse5 split txt+    _ -> parseOther split txt+     where split = smartSplit (takeCarriageRet txt) -parse4 :: [B.ByteString] -> (B.ByteString -> IrcMessage)-parse4 (first:code:chan:msg:_) = -  let (nick, host, server) = parseFirst first-  in IrcMessage nick host server (code)-       (dropColon msg) (Just chan) Nothing- -- Nick, Host, Server-parseFirst :: B.ByteString -> (Maybe B.ByteString, Maybe B.ByteString, Maybe B.ByteString)+parseFirst :: B.ByteString -> (Maybe B.ByteString, Maybe B.ByteString, Maybe B.ByteString, Maybe B.ByteString) parseFirst first =    if '!' `B.elem` first-    then let (nick, host) = B.break (== '!') (dropColon first)-         in (Just nick, Just host, Nothing)-    else (Nothing, Nothing, Just $ dropColon first) --dropColon :: B.ByteString -> B.ByteString-dropColon xs =-  if B.take 1 xs == (B.pack ":")-    then B.drop 1 xs-    else xs+    then let (nick, user_host) = B.break (== '!') (dropColon first)+         in if '@' `B.elem` user_host+               then let (user, host) = second B.tail $ B.break (== '@') $ B.tail user_host+                    in (Just nick, Just user, Just host, Nothing)+               else (Just nick, Nothing, Just user_host, Nothing)+    else (Nothing, Nothing, Nothing, Just $ dropColon first)  -parse2 :: [B.ByteString] -> (B.ByteString -> IrcMessage)+parse2 :: [B.ByteString] -> B.ByteString -> IrcMessage parse2 (code:msg:_) =-  IrcMessage Nothing Nothing Nothing (code)+  IrcMessage Nothing Nothing Nothing Nothing code     (dropColon msg) Nothing Nothing     -parse3 :: [B.ByteString] -> (B.ByteString -> IrcMessage)+parse3 :: [B.ByteString] -> B.ByteString -> IrcMessage parse3 (first:code:msg:_) =-  let (nick, host, server) = parseFirst first-  in IrcMessage nick host server (code) (dropColon msg) Nothing Nothing+  let (nick, user, host, server) = parseFirst first+  in IrcMessage nick user host server code (dropColon msg) Nothing Nothing   -parse5 :: [B.ByteString] -> (B.ByteString -> IrcMessage)+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++parse5 :: [B.ByteString] -> B.ByteString -> IrcMessage parse5 (server:code:nick:chan:msg:_) =-  IrcMessage (Just nick) Nothing (Just server) (code)+  IrcMessage (Just nick) Nothing Nothing (Just server) code     (dropColon msg) (Just chan) Nothing -parseOther :: [B.ByteString] -> (B.ByteString -> IrcMessage)+parseOther :: [B.ByteString] -> B.ByteString -> IrcMessage parseOther (server:code:nick:chan:other) =-  IrcMessage (Just nick) Nothing (Just server) (code)+  IrcMessage (Just nick) Nothing Nothing (Just server) code     (B.unwords other) (Just chan) (Just other)  smartSplit :: B.ByteString -> [B.ByteString]-smartSplit txt-  | ':' `B.elem` (dropColon txt) =-    let (first, msg) = B.break (== ':') (dropColon txt)-    in (B.words $ takeLast first) ++ [msg]-  | otherwise = B.words $ txt+smartSplit txt = +  case B.breakSubstring (B.pack " :") (dropColon txt) of+    (x,y) | B.null y -> +              B.words txt+          | otherwise -> +              let (_, msg) = B.break (== ':') y+              in B.words x ++ [msg]  takeLast :: B.ByteString -> B.ByteString takeLast xs = B.take (B.length xs - 1) xs  takeCarriageRet :: B.ByteString -> B.ByteString takeCarriageRet xs = -  if B.drop (B.length xs - 1) xs == (B.pack "\r")+  if B.drop (B.length xs - 1) xs == B.pack "\r"     then takeLast xs     else xs +dropColon :: B.ByteString -> B.ByteString+dropColon xs =+  if B.take 1 xs == B.pack ":"+    then B.drop 1 xs+    else xs+ showCommand :: Command -> B.ByteString showCommand (MPrivmsg chan msg)             = "PRIVMSG " `B.append` chan `B.append`                                               " :" `B.append` msg@@ -141,3 +160,4 @@ showCommand (MAction  chan msg)             = showCommand $ MPrivmsg chan                                               ("\x01 ACTION " `B.append` msg                                               `B.append` "\x01")+
− Network/SimpleIRC/Types.hs
@@ -1,106 +0,0 @@-{-# LANGUAGE DeriveDataTypeable #-}-module Network.SimpleIRC.Types-  ( -    -- * Datatypes-    IrcConfig(..)-  , IrcServer(..)-  , SIrcCommand(..)-  , IrcEvent(..)-  , EventFunc-  , IrcMessage(..)-  ) where-import qualified Data.ByteString.Char8 as B-import Control.Concurrent (ThreadId)-import Control.Concurrent.Chan (Chan)-import System.IO (Handle)-import Data.Unique (Unique)-import Data.Map (Map)-import Data.Typeable--data IrcConfig = IrcConfig-  { cAddr     :: String   -- ^ Server address to connect to-  , cPort     :: Int      -- ^ Server port to connect to-  , cNick     :: String   -- ^ Nickname-  , 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-  }---data SIrcCommand =-    SIrcAddEvent (Unique, IrcEvent)-  | SIrcChangeEvents (Map Unique IrcEvent)-  | SIrcRemoveEvent Unique-  -data IrcServer = IrcServer-  { sAddr         :: B.ByteString-  , sPort         :: Int-  , sNickname     :: B.ByteString-  , sUsername     :: B.ByteString-  , sRealname     :: B.ByteString-  , sChannels     :: [B.ByteString]-  , sEvents       :: Map Unique IrcEvent-  , sSock         :: Maybe Handle-  , sListenThread :: Maybe ThreadId-  , sCmdChan      :: Chan SIrcCommand-  , sDebug        :: Bool-  -- Other info-  , sCTCPVersion  :: String-  , sCTCPTime     :: IO String-  }--type EventFunc = (IrcServer -> IrcMessage -> IO ())---- When adding events here, remember add them in callEvents and in eventFunc--- AND also in the Show instance and Eq instance--data IrcEvent = -    Privmsg EventFunc -- ^ PRIVMSG-  | Numeric EventFunc -- ^ Numeric, 001, 002, 372 etc.-  | Ping EventFunc    -- ^ PING-  | Join EventFunc    -- ^ JOIN-  | Part EventFunc    -- ^ PART-  | Mode EventFunc    -- ^ MODE-  | Topic EventFunc   -- ^ TOPIC-  | Invite EventFunc  -- ^ INVITE-  | Kick EventFunc    -- ^ KICK-  | Quit EventFunc    -- ^ QUIT-  | 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-                                    --   connection with the server is dropped-  -  -instance Show IrcEvent where-  show (Privmsg _) = "IrcEvent - Privmsg"-  show (Numeric _) = "IrcEvent - Numeric"-  show (Ping    _) = "IrcEvent - Ping"-  show (Join    _) = "IrcEvent - Join"-  show (Part    _) = "IrcEvent - Part"-  show (Mode    _) = "IrcEvent - Mode"-  show (Topic   _) = "IrcEvent - Topic"-  show (Invite  _) = "IrcEvent - Invite"-  show (Kick    _) = "IrcEvent - Kick"-  show (Quit    _) = "IrcEvent - Quit"-  show (Nick    _) = "IrcEvent - Nick"-  show (Notice  _) = "IrcEvent - Notice"-  show (RawMsg  _) = "IrcEvent - RawMsg"-  show (Disconnect  _) = "IrcEvent - Disconnect"-  -data IrcMessage = IrcMessage-  { mNick   :: Maybe B.ByteString-  , mHost   :: Maybe B.ByteString-  , mServer :: Maybe B.ByteString-  , mCode   :: B.ByteString-  , mMsg    :: B.ByteString-  , mChan   :: Maybe B.ByteString-  , mOther  :: Maybe [B.ByteString]-  , mRaw    :: B.ByteString-  } deriving (Show, Typeable)-  -  -   
− Network/SimpleIRC/Utils.hs
@@ -1,29 +0,0 @@--- |--- Module : Network.SimpleIRC.Core--- Copyright : (c) Dominik Picheta 2010--- License : BSD3------ Maintainer : morfeusz8@gmail.com--- Stability : provisional--- Portability : portable------ Utils module-----module Network.SimpleIRC.Utils-  ( -    -- * Functions-    getChan-  ) where-import Network.SimpleIRC.Types-import qualified Data.ByteString.Char8 as B-import Data.Maybe (fromJust)-  --- |If the IrcMessage was sent directly to you returns the nick otherwise the channel.-getChan :: IrcServer -> IrcMessage -> B.ByteString-getChan s m =-  if sNickname s == chan-    then (fromJust $ mNick m)-    else chan-  -  where chan = fromJust $ mChan m
simpleirc.cabal view
@@ -1,8 +1,7 @@ Name:          simpleirc-Version:       0.1.0+Version:       0.2.0 Category:      Network, IRC Synopsis:      Simple IRC Library-Package-URL:   http://code.haskell.org/fastirc/ Maintainer:    Dominik Picheta <dominikpicheta@googlemail.com> Author:        Dominik Picheta <dominikpicheta@googlemail.com> Copyright:     (c) 2010 Dominik Picheta@@ -22,13 +21,13 @@ Source-repository this   Type:     git   Location: git://github.com/dom96/SimpleIRC.git-  tag:      V0.1.0+  tag:      v0.2.0  Library   Build-depends:     base >= 4 && < 5,     bytestring >= 0.9.1.7,-    network >= 2.2.1.7,+    network >= 2.2.1.5,     containers >= 0.3.0.0,     time >= 1.1.4,     old-locale >= 1.0.0.2@@ -36,5 +35,3 @@     Network.SimpleIRC     Network.SimpleIRC.Core     Network.SimpleIRC.Messages-    Network.SimpleIRC.Utils-    Network.SimpleIRC.Types