packages feed

irc-fun-messages 0.1.0.1 → 0.2.0.0

raw patch · 6 files changed

+399/−197 lines, 6 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Network.IRC.Fun.Messages.Types: code :: GenericReply -> Int
- Network.IRC.Fun.Messages.Types: command :: GenericMessage -> Command
- Network.IRC.Fun.Messages.Types: parameters :: GenericMessage -> [Parameter]
- Network.IRC.Fun.Messages.Types: params :: GenericReply -> [Parameter]
- Network.IRC.Fun.Messages.Types: prefix :: GenericMessage -> Maybe Prefix
- Network.IRC.Fun.Messages.Types: sender :: GenericReply -> ServerName
- Network.IRC.Fun.Messages.Types: target :: GenericReply -> Target
+ Network.IRC.Fun.Messages.Types: PrivActionMessage :: MessageTarget -> String -> Message
+ Network.IRC.Fun.Messages.Types: [gmCommand] :: GenericMessage -> Command
+ Network.IRC.Fun.Messages.Types: [gmParams] :: GenericMessage -> [Parameter]
+ Network.IRC.Fun.Messages.Types: [gmPrefix] :: GenericMessage -> Maybe Prefix
+ Network.IRC.Fun.Messages.Types: [grCode] :: GenericReply -> Int
+ Network.IRC.Fun.Messages.Types: [grParams] :: GenericReply -> [Parameter]
+ Network.IRC.Fun.Messages.Types: [grSender] :: GenericReply -> ServerName
+ Network.IRC.Fun.Messages.Types: [grTarget] :: GenericReply -> Target
- Network.IRC.Fun.Messages.Types: ListMessage :: [ChannelName] -> (Maybe (Either ServerName Mask)) -> Message
+ Network.IRC.Fun.Messages.Types: ListMessage :: [ChannelName] -> (Maybe ServerName) -> Message
- Network.IRC.Fun.Messages.Types: MotdMessage :: (Maybe (Either ServerName Mask)) -> Message
+ Network.IRC.Fun.Messages.Types: MotdMessage :: (Maybe ServerName) -> Message

Files

NEWS view
@@ -3,6 +3,30 @@   +irc-fun-messages 0.2.0.0 -- 2015-10-17+======================================++General, build and documentation changes:++* Prefix some record field names, they had too generic names++New APIs, features and enhancements:++* Recognize action messages (/me)+* Implement parsing of a few more IRC messages, previously unimplemented++Bug fixes:++* (None)++Dependency changes:++* (None)+++++ irc-fun-messages 0.1.0.1 -- 2015-09-10 ====================================== 
irc-fun-messages.cabal view
@@ -1,5 +1,5 @@ name:                irc-fun-messages-version:             0.1.0.1+version:             0.2.0.0 synopsis:            Types and functions for working with the IRC protocol. description:   Another IRC library. It doesn't provide a client API though. It just provides@@ -9,8 +9,8 @@   The package is based on RFC 2812, with modifications and extensions to work   with Freenode.   .-  NOTE: The handling of IRC protocol messages is FAR from complete, but there-  is enough to e.g. create a basic IRC bot. Missing features are being added as+  NOTE: The handling of IRC protocol messages is incomplete, but there+  is enough to e.g. create an IRC bot. Missing features are being added as   needed while working on <http://rel4tion.org/projects/funbot FunBot>, and of   course patches are welcome. homepage:            http://rel4tion.org/projects/irc-fun-messages/@@ -43,6 +43,7 @@                      , Network.IRC.Fun.Messages.Internal.Tokens.Target                      , Network.IRC.Fun.Messages.Internal.Tokens.Wildcards                      , Network.IRC.Fun.Messages.Internal.Types+                     , Network.IRC.Fun.Messages.Internal.Util   -- other-extensions:       build-depends:       base              >=4.7 && <5                      , regex-applicative >=0.3.1
src/Network/IRC/Fun/Messages/Internal/Parse.hs view
@@ -28,17 +28,18 @@  import Data.Bits (testBit) import Data.Char (toUpper)-import Data.List (isSuffixOf)---import Data.Maybe (fromMaybe)+import Data.List (isSuffixOf, stripPrefix) import Network.IRC.Fun.Messages.Mask (parseMask) import Network.IRC.Fun.Messages.Modes (parseUserMode)+import Network.IRC.Fun.Messages.Internal.Types+import Network.IRC.Fun.Messages.Internal.Util+import Text.Regex.Applicative+import Text.Regex.Applicative.Common (decimal)+ import qualified Network.IRC.Fun.Messages.Internal.Tokens.Message as T import qualified Network.IRC.Fun.Messages.Internal.Tokens.Other as T import qualified Network.IRC.Fun.Messages.Internal.Tokens.Reply as T import qualified Network.IRC.Fun.Messages.Internal.Tokens.Target as T-import Network.IRC.Fun.Messages.Internal.Types-import Text.Regex.Applicative-import Text.Regex.Applicative.Common (decimal)  ------------------------------------------------------------------------------- -- Parsing@@ -54,12 +55,12 @@ messageIsReply :: GenericMessage -> Bool messageIsReply msg =     let prefOk =-            case prefix msg of+            case gmPrefix msg of                 Nothing         -> False                 Just (Server _) -> True                 Just (Nick {})  -> False         cmdOk =-            case command msg of+            case gmCommand msg of                 StringCommand _  -> False                 NumericCommand _ -> True     in  prefOk && cmdOk@@ -69,13 +70,13 @@ -- 'Left' an error message. messageToReply :: GenericMessage -> Either String GenericReply messageToReply msg =-    case (prefix msg, command msg, parameters msg) of+    case (gmPrefix msg, gmCommand msg, gmParams msg) of         (Just (Server s),   NumericCommand n, p : ps) ->             Right GenericReply-                { sender = s-                , code   = n-                , target = NickTarget p-                , params = ps+                { grSender = s+                , grCode   = n+                , grTarget = NickTarget p+                , grParams = ps                 }         (Just (Server _),   NumericCommand _, []) ->             Left "No parameters, must have at least one"@@ -101,183 +102,196 @@ -- Analysis ------------------------------------------------------------------------------- +mapLeft :: (a -> c) -> Either a b -> Either c b+mapLeft f (Left x)  = Left $ f x+mapLeft _ (Right y) = Right y+ analyzeMessage' :: Maybe Prefix                 -> String                 -> [Parameter]                 -> Either String Message-analyzeMessage' prefix cmd params =+analyzeMessage' _prefix cmd params = mapLeft show $     case map toUpper cmd of     -- Connection registration-        "PASS"     ->-            case params of-                [pass] -> Right $ PassMessage pass-                []     -> Left "Password missing"-                _      -> Left "Too many parameters"-        "NICK"     ->-            case params of-                [nick] -> Right $ NickMessage nick-                []     -> Left "Nickname missing"-                _      -> Left "Too many parameters"-        "USER"     ->-            case params of-                [user, mode, _, realname] ->-                    case match decimal mode :: Maybe Int of-                        (Just m) -> Right $ UserMessage user-                                            (testBit m 3)-                                            (testBit m 2)-                                            realname-                        Nothing  -> Left "<mode> should be a number"-                [_:_:_:_:_:_]             -> Left "Too many parameters"-                _                         -> Left "Too few parameters"-        "OPER"     ->-            case params of-                [name, pass] -> Right $ OperMessage name pass-                [_:_:_:_]    -> Left "Too many parameters"-                _            -> Left "Too few parameters"-        "MODE"     ->-            case params of-                (first:rest) ->-                    case (match T.nickname first, match T.channel first) of-                        (Just nick, _)       ->-                            case rest of-                                [mode] ->-                                    case parseUserMode mode of-                                        Just (remove, add) ->-                                            Right $ UserModeMessage nick-                                                                    remove-                                                                    add-                                        Nothing            ->-                                            Left "Invalid user mode string"-                                []    -> Left "Too few parameters"-                                _     -> Left "Too many parameters"-                        (Nothing, Just chan) -> Left "Not implemented yet"-                            {-case parseChannelModeParams rest of-                                Just X ->-                                Nothing ->-}-                        _                    -> Left "First parameter invalid"-                _         ->-                    Left "Too new parameters"-        "SERVICE"  ->-            case params of-                [nick, _, dist, _, _, info] ->-                    case parseMask dist of-                        Just m  -> Right $ ServiceMessage nick m info-                        Nothing -> Left "Distribution mask invalid"-                [_:_:_:_:_:_:_:_]           -> Left "Too many parameters"-                _                           -> Left "Too few parameters"-        "QUIT"     ->-            case params of-                []    -> Right $ QuitMessage Nothing-                [msg] -> Right $ QuitMessage (Just msg)-                _     -> Left "Too many parameters"-        "SQUIT"    ->-            case params of-                [server, comment] -> Right $ SQuitMessage server comment-                [_:_:_:_]         -> Left "Too many parameters"-                _                 -> Left "Too few parameters"+        "PASS"     -> req1 params $ \ pass -> Right $ PassMessage pass+        "NICK"     -> req1 params $ \ nick -> Right $ NickMessage nick+        "USER"     -> req4 params $ \ user mode _ realname ->+            case match decimal mode :: Maybe Int of+                Just m  ->+                    Right $+                    UserMessage user (testBit m 3) (testBit m 2) realname+                Nothing ->+                    Left $+                    InvalidArg+                        (Just 2)+                        (Just "mode")+                        (Just "Should be a number")+        "OPER"     -> req2 params $ \ name pass ->+                        Right $ OperMessage name pass+        "MODE"     -> min1 params $ \ first rest ->+            case (match T.nickname first, match T.channel first) of+                (Just nick, _)       ->+                    case rest of+                        [mode] ->+                            case parseUserMode mode of+                                Just (remove, add) ->+                                    Right $ UserModeMessage nick remove add+                                Nothing ->+                                    Left $+                                    InvalidArg (Just 1) (Just "mode") Nothing+                        l     -> Left $ WrongNumArgs (length l) Nothing+                (Nothing, Just _chan) ->+                    Left $ OtherError "Not implemented yet"+                    {-case parseChannelModeParams rest of+                        Just X ->+                        Nothing ->-}+                _                    ->+                    Left $ InvalidArg (Just 1) Nothing Nothing+        "SERVICE"  -> req6 params $ \ nick _ dist _ _ info ->+            case parseMask dist of+                Just m  -> Right $ ServiceMessage nick m info+                Nothing ->+                    Left $+                    InvalidArg (Just 3) (Just "distribution mask") Nothing+        "QUIT"     -> req0m1 params $ \ msg -> Right $ QuitMessage msg+        "SQUIT"    -> req2 params $ \ server comment ->+            Right $ SQuitMessage server comment     -- Channel operations-        "JOIN"     ->-            case params of-                ["0"]            -> Right $ JoinMessage Nothing-                [channels]       ->-                    let c = (:) <$> T.channel <*> many (sym ',' *> T.channel)-                    in  case match c channels of-                            Just cs -> Right $ JoinMessage $ Just (cs, [])-                            _       -> Left "Invalid parameters"-                [channels, keys] ->-                    let c = (:) <$> T.channel <*> many (sym ',' *> T.channel)-                        k = (:) <$> T.key     <*> many (sym ',' *> T.key)-                    in  case (match c channels, match k keys) of+        "JOIN"     -> req1m2 params $ \ chans mkeys ->+            case (chans, mkeys) of+                ("0", Nothing) -> Right $ JoinMessage Nothing+                _              ->+                    let mcs = match (listOf T.channel) chans+                        mks = maybe (Just []) (match $ listOf T.key) mkeys+                    in  case (mcs, mks) of+                            (Nothing, _)       ->+                                Left $+                                InvalidArg (Just 1) (Just "channels") Nothing+                            (Just _,  Nothing) ->+                                Left $+                                InvalidArg (Just 2) (Just "keys") Nothing                             (Just cs, Just ks) ->                                 Right $ JoinMessage $ Just (cs, ks)-                            _                  ->-                                Left "Invalid parameters"-                _                -> Left "Invalid parameters"-        "PART"     ->-            case params of-                [channels]      ->-                    case match c channels of-                        Just cs -> Right $ PartMessage cs Nothing-                        Nothing -> Left "Invalid channel list"-                [channels, msg] ->-                    case match c channels of-                        Just cs -> Right $ PartMessage cs $ Just msg-                        Nothing -> Left "Invalid channel list"-                _               -> Left "Wrong number of parameters"-                where-                c = (:) <$> T.channel <*> many (sym ',' *> T.channel)+        "PART"     -> req1m2 params $ \ chans msg ->+            case match (listOf T.channel) chans of+                Just cs -> Right $ PartMessage cs msg+                Nothing -> Left $ InvalidArg (Just 1) (Just "channels") Nothing         -- "MODE" -> Channel mode handled by MODE above-        "TOPIC"    ->-            case params of-                [channel]        -> Right $ TopicMessage channel Nothing-                [channel, topic] -> Right $ TopicMessage channel $ Just topic-                []               -> Left "No parameters given"-                _                -> Left "Too many parameters"-        "NAMES"    -> Left "Not implemented yet"-        "LIST"     -> Left "Not implemented yet"-        "INVITE"   -> Left "Not implemented yet"-        "KICK"     -> Left "Not implemented yet"-    -- Sending messages-        "PRIVMSG"  ->+        "TOPIC"    -> req1m2 params $ \ chan topic ->+            Right $ TopicMessage chan topic+        "NAMES"    ->             case params of-                [target, text] ->-                    case match T.msgto target of-                        Nothing -> Left $ "Invalid message target: " ++ target-                        Just t  -> Right $ PrivMsgMessage t text-                (_:_:_:_)      -> Left "Too many parameters"-                _              -> Left "Too few parameters"-        "NOTICE"   ->+                []              -> Right $ NamesMessage [] Nothing+                [chans]         ->+                    case match (listOf T.channel) chans of+                        Just cs -> Right $ NamesMessage cs Nothing+                        Nothing ->+                            Left $+                            InvalidArg (Just 1) (Just "channels") Nothing+                [chans, target] ->+                    case match (listOf T.channel) chans of+                        Just cs -> Right $ NamesMessage cs (Just target)+                        Nothing ->+                            Left $+                            InvalidArg (Just 1) (Just "channels") Nothing+                l               ->+                    Left $ WrongNumArgs (length l) (Just $ Between 0 2)+        "LIST"     ->             case params of-                [target, text] ->-                    case match T.msgto target of-                        Nothing -> Left $ "Invalid notice target: " ++ target-                        Just t  -> Right $ NoticeMessage t text-                (_:_:_:_)      -> Left "Too many parameters"-                _              -> Left "Too few parameters"+                []              -> Right $ ListMessage [] Nothing+                [chans]         ->+                    case match (listOf T.channel) chans of+                        Just cs -> Right $ ListMessage cs Nothing+                        Nothing ->+                            Left $+                            InvalidArg (Just 1) (Just "channels") Nothing+                [chans, target] ->+                    case match (listOf T.channel) chans of+                        Just cs -> Right $ ListMessage cs (Just target)+                        Nothing ->+                            Left $+                            InvalidArg (Just 1) (Just "channels") Nothing+                l               ->+                    Left $ WrongNumArgs (length l) (Just $ Between 0 2)+        "INVITE"   -> req2 params $ \ nick chan ->+            case (match T.nickname nick, match T.channel chan) of+                (Nothing, _)      ->+                    Left $ InvalidArg (Just 1) (Just "nickname") Nothing+                (Just _, Nothing) ->+                    Left $ InvalidArg (Just 2) (Just "channel") Nothing+                (Just n,  Just c) -> Right $ InviteMessage n c+        "KICK"     -> req2m3 params $ \ chans users comment ->+            let mcs = match (listOf T.channel) chans+                ucs = match (listOf T.nickname) users+            in  case (mcs, ucs) of+                    (Nothing, _)       ->+                        Left $ InvalidArg (Just 1) (Just "channels") Nothing+                    (Just _,  Nothing) ->+                        Left $ InvalidArg (Just 2) (Just "users") Nothing+                    (Just cs, Just us) ->+                        let lc = length cs+                            lu = length us+                        in  if lc == 1 || lc == lu+                                then Right $ KickMessage cs us comment+                                else Left $ OtherError "Invalid list lengths"+    -- Sending messages+        "PRIVMSG"  -> req2 params $ \ target text ->+            case match T.msgto target of+                Nothing ->+                    Left $ InvalidArg (Just 1) (Just "target") (Just target)+                Just t ->+                    Right $ case stripPrefix "\1ACTION " text of+                        Just s  ->+                            if not (null s) && last s == '\1'+                                then PrivActionMessage t $ init s+                                else PrivMsgMessage t text+                        Nothing -> PrivMsgMessage t text+        "NOTICE"   -> req2 params $ \ target text ->+            case match T.msgto target of+                Nothing ->+                    Left $ InvalidArg (Just 1) (Just "target") (Just target)+                Just t  ->+                    Right $ NoticeMessage t text     -- Server queries and commands-        "MOTD"     -> Left "Not implemented yet"-        "LUSERS"   -> Left "Not implemented yet"-        "VERSION"  -> Left "Not implemented yet"-        "STATS"    -> Left "Not implemented yet"-        "LINKS"    -> Left "Not implemented yet"-        "TIME"     -> Left "Not implemented yet"-        "CONNECT"  -> Left "Not implemented yet"-        "TRACE"    -> Left "Not implemented yet"-        "ADMIN"    -> Left "Not implemented yet"-        "INFO"     -> Left "Not implemented yet"+        "MOTD"     -> req0m1 params $ \ target -> Right $ MotdMessage target+        "LUSERS"   -> Left $ OtherError "Not implemented yet"+        "VERSION"  -> Left $ OtherError "Not implemented yet"+        "STATS"    -> Left $ OtherError "Not implemented yet"+        "LINKS"    -> Left $ OtherError "Not implemented yet"+        "TIME"     -> Left $ OtherError "Not implemented yet"+        "CONNECT"  -> Left $ OtherError "Not implemented yet"+        "TRACE"    -> Left $ OtherError "Not implemented yet"+        "ADMIN"    -> Left $ OtherError "Not implemented yet"+        "INFO"     -> req0m1 params $ \ target ->+            case target of+                Nothing -> Right $ InfoMessage Nothing+                Just t  ->+                    Right $ case match T.nickname t of+                        Just n  -> InfoMessage (Just $ NickTarget n)+                        Nothing -> InfoMessage (Just $ ServerTarget t)     -- Service query and commands-        "SERVLIST" -> Left "Not implemented yet"-        "SQUERY"   -> Left "Not implemented yet"+        "SERVLIST" -> Left $ OtherError "Not implemented yet"+        "SQUERY"   -> Left $ OtherError "Not implemented yet"     -- User based queries-        "WHO"      -> Left "Not implemented yet"-        "WHOIS"    -> Left "Not implemented yet"-        "WHOWAS"   -> Left "Not implemented yet"+        "WHO"      -> Left $ OtherError "Not implemented yet"+        "WHOIS"    -> Left $ OtherError "Not implemented yet"+        "WHOWAS"   -> Left $ OtherError "Not implemented yet"     -- Miscellaneous messages-        "KILL"     -> Left "Not implemented yet"-        "PING"     ->-            case params of-                []       -> Left "No parameters given"-                [s]      -> Right $ PingMessage s Nothing-                [s1, s2] -> Right $ PingMessage s1 (Just s2)-                _        -> Left "Too many parameters"-        "PONG"     ->-            case params of-                []       -> Left "No parameters given"-                [s]      -> Right $ PongMessage s Nothing-                [s1, s2] -> Right $ PongMessage s1 (Just s2)-                _        -> Left "Too many parameters"-        "ERROR"    -> Left "Not implemented yet"+        "KILL"     -> Left $ OtherError "Not implemented yet"+        "PING"     -> req1m2 params $ \ s1 s2 -> Right $ PingMessage s1 s2+        "PONG"     -> req1m2 params $ \ s1 s2 -> Right $ PongMessage s1 s2+        "ERROR"    -> Left $ OtherError "Not implemented yet"     -- Optional features-        "AWAY"     -> Left "Not implemented yet"-        "REHASH"   -> Left "Not implemented yet"-        "DIE"      -> Left "Not implemented yet"-        "RESTART"  -> Left "Not implemented yet"-        "SUMMON"   -> Left "Not implemented yet"-        "USERS"    -> Left "Not implemented yet"-        "WALLOPS"  -> Left "Not implemented yet"-        "USERHOST" -> Left "Not implemented yet"-        "ISON"     -> Left "Not implemented yet"-        _          -> Left "Unrecognized command"+        "AWAY"     -> req0m1 params $ \ text -> Right $ AwayMessage text+        "REHASH"   -> Left $ OtherError "Not implemented yet"+        "DIE"      -> Left $ OtherError "Not implemented yet"+        "RESTART"  -> Left $ OtherError "Not implemented yet"+        "SUMMON"   -> Left $ OtherError "Not implemented yet"+        "USERS"    -> Left $ OtherError "Not implemented yet"+        "WALLOPS"  -> Left $ OtherError "Not implemented yet"+        "USERHOST" -> Left $ OtherError "Not implemented yet"+        "ISON"     -> Left $ OtherError "Not implemented yet"+        _          -> Left $ OtherError "Unrecognized command"  -- | Read a generic message structure into specific message details. Return -- 'Left' an error description if analysis fails. If the message is detected@@ -287,10 +301,10 @@ analyzeMessage msg =     if messageIsReply msg         then fmap Left $ messageToReply msg-        else case command msg of+        else case gmCommand msg of             StringCommand c ->-                fmap (Right . SpecificMessage (prefix msg)) $-                analyzeMessage' (prefix msg) c (parameters msg)+                fmap (Right . SpecificMessage (gmPrefix msg)) $+                analyzeMessage' (gmPrefix msg) c (gmParams msg)             _               ->                 Left "Implementation error" @@ -313,8 +327,8 @@ -- 'Left' an error description if analysis fails. analyzeReply :: GenericReply -> Either String SpecificReply analyzeReply gr =-    fmap (SpecificReply (sender gr) (target gr)) $-    analyzeReply' (code gr) (target gr) (params gr)+    fmap (SpecificReply (grSender gr) (grTarget gr)) $+    analyzeReply' (grCode gr) (grTarget gr) (grParams gr)  -- | Read a generic reply structure into specific message or reply details. -- Return 'Left' an error description if analysis fails.
src/Network/IRC/Fun/Messages/Internal/Serialize.hs view
@@ -89,6 +89,8 @@         InviteMessage nn chan -> ("????", error "Not implemented")         KickMessage chans users s -> ("????", error "Not implemented")         PrivMsgMessage tgt text -> ("PRIVMSG", [showTarget tgt, text])+        PrivActionMessage tgt text ->+            ("PRIVMSG", [showTarget tgt, "\1ACTION " ++ text ++ "\1"])         NoticeMessage tgt text -> ("NOTICE", [showTarget tgt, text])         MotdMessage serv -> ("????", error "Not implemented")         LusersMessage serv -> ("????", error "Not implemented")@@ -162,13 +164,13 @@ -- generic message value. messageFromReply :: GenericReply -> GenericMessage messageFromReply reply = GenericMessage-    { prefix     = Just $ Server $ sender reply-    , command    = NumericCommand $ code reply-    , parameters = target' reply : params reply+    { gmPrefix  = Just $ Server $ grSender reply+    , gmCommand = NumericCommand $ grCode reply+    , gmParams  = target' reply : grParams reply     }     where-    target' (GenericReply { target = NickTarget n })   = n-    target' (GenericReply { target = ServerTarget s }) = s+    target' (GenericReply { grTarget = NickTarget n })   = n+    target' (GenericReply { grTarget = ServerTarget s }) = s  ------------------------------------------------------------------------------- -- Serializing@@ -198,9 +200,9 @@ -- | Write a message into an IRC protocol message formatted string. serializeMessage :: GenericMessage -> String serializeMessage msg = concat-    [ maybe "" (\ p -> ':' : serializePrefix p ++ " ") $ prefix msg-    , serializeCommand $ command msg-    , serializeParams $ parameters msg+    [ maybe "" (\ p -> ':' : serializePrefix p ++ " ") $ gmPrefix msg+    , serializeCommand $ gmCommand msg+    , serializeParams $ gmParams msg     , "\r\n"     ] 
src/Network/IRC/Fun/Messages/Internal/Types.hs view
@@ -45,6 +45,8 @@     , SpecificMessage (..)     , MessageTarget (..)     , Message (..)+    , AnalysisError (..)+    , ArgSpec (..)       -- * Modes     , UserMode (..)     , ChannelMode (..)@@ -179,9 +181,9 @@ -- | A generic IRC message. It consists of a sender prefix, command name -- (string or numeric) and optionally a list of parameters. data GenericMessage = GenericMessage-    { prefix     :: Maybe Prefix-    , command    :: Command-    , parameters :: [Parameter]+    { gmPrefix  :: Maybe Prefix+    , gmCommand :: Command+    , gmParams  :: [Parameter]     }     deriving Show @@ -251,14 +253,15 @@     | ChannelModeMessage [ChannelMode] [ChannelMode]     | TopicMessage ChannelName (Maybe ChannelTopic)     | NamesMessage [ChannelName] (Maybe ServerName)-    | ListMessage [ChannelName] (Maybe (Either ServerName Mask))+    | ListMessage [ChannelName] (Maybe ServerName)     | InviteMessage NickName ChannelName     | KickMessage [ChannelName] [UserName] (Maybe Comment) -- Sending messages     | PrivMsgMessage MessageTarget String+    | PrivActionMessage MessageTarget String     | NoticeMessage MessageTarget String -- Server queries and commands-    | MotdMessage (Maybe (Either ServerName Mask))+    | MotdMessage (Maybe ServerName)     | LusersMessage (Maybe (Mask, Maybe ServerName))     | VersionMessage (Maybe (Either ServerName Mask))     | StatsMessage (Maybe (Query, Maybe (Either ServerName Mask)))@@ -688,10 +691,10 @@  -- | TODO data GenericReply = GenericReply-    { sender     :: ServerName-    , code       :: Int-    , target     :: Target-    , params     :: [Parameter]+    { grSender :: ServerName+    , grCode   :: Int+    , grTarget :: Target+    , grParams :: [Parameter]     }     deriving Show @@ -879,3 +882,19 @@  -- data ChannelPrivacy = Secret | Private | Public deriving (Eq, Show)++--+data ArgSpec+    = Exactly Int+    | AtLeast Int+    | AtMost Int+    | Between Int Int+    | OneOf [Int]+    deriving (Show)++--+data AnalysisError+    = WrongNumArgs Int (Maybe ArgSpec)+    | InvalidArg (Maybe Int) (Maybe String) (Maybe String)+    | OtherError String+    deriving (Show)
+ src/Network/IRC/Fun/Messages/Internal/Util.hs view
@@ -0,0 +1,142 @@+{- This file is part of irc-fun-messages.+ -+ - Written in 2015 by fr33domlover <fr33domlover@rel4tion.org>.+ -+ - ♡ Copying is an act of love. Please copy, reuse and share.+ -+ - The author(s) have dedicated all copyright and related and neighboring+ - rights to this software to the public domain worldwide. This software is+ - distributed without any warranty.+ -+ - You should have received a copy of the CC0 Public Domain Dedication along+ - with this software. If not, see+ - <http://creativecommons.org/publicdomain/zero/1.0/>.+ -}++module Network.IRC.Fun.Messages.Internal.Util+    ( listOf+    , req0+    , req1+    , req2+    , req3+    , req4+    , req5+    , req6+    , req0m1+    , req1m2+    , req2m3+    , req3m4+    , min1+    )+where++import Network.IRC.Fun.Messages.Internal.Types+import Text.Regex.Applicative++listOf :: Regex a -> Regex [a]+listOf re = (:) <$> re <*> many (sym ',' *> re)++req0 :: [Parameter]+     -> Either AnalysisError Message+     -> Either AnalysisError Message+req0 [] f = f+req0 l  _ = Left $ WrongNumArgs (length l) (Just $ Exactly 0)++req1 :: [Parameter]+     -> (Parameter -> Either AnalysisError Message)+     -> Either AnalysisError Message+req1 [p1] f = f p1+req1 l    _ = Left $ WrongNumArgs (length l) (Just $ Exactly 1)++req2 :: [Parameter]+     -> (Parameter -> Parameter -> Either AnalysisError Message)+     -> Either AnalysisError Message+req2 [p1, p2] f = f p1 p2+req2 l        _ = Left $ WrongNumArgs (length l) (Just $ Exactly 2)++req3 :: [Parameter]+     -> (Parameter -> Parameter -> Parameter -> Either AnalysisError Message)+     -> Either AnalysisError Message+req3 [p1, p2, p3] f = f p1 p2 p3+req3 l            _ = Left $ WrongNumArgs (length l) (Just $ Exactly 3)++req4 :: [Parameter]+     -> (  Parameter+        -> Parameter+        -> Parameter+        -> Parameter+        -> Either AnalysisError Message+        )+     -> Either AnalysisError Message+req4 [p1, p2, p3, p4] f = f p1 p2 p3 p4+req4 l            _     = Left $ WrongNumArgs (length l) (Just $ Exactly 4)++req5 :: [Parameter]+     -> (  Parameter+        -> Parameter+        -> Parameter+        -> Parameter+        -> Parameter+        -> Either AnalysisError Message+        )+     -> Either AnalysisError Message+req5 [p1, p2, p3, p4, p5] f = f p1 p2 p3 p4 p5+req5 l                    _ = Left $ WrongNumArgs (length l) (Just $ Exactly 5)++req6 :: [Parameter]+     -> (  Parameter+        -> Parameter+        -> Parameter+        -> Parameter+        -> Parameter+        -> Parameter+        -> Either AnalysisError Message+        )+     -> Either AnalysisError Message+req6 [p1, p2, p3, p4, p5,p6] f = f p1 p2 p3 p4 p5 p6+req6 l                       _ = Left $+                                 WrongNumArgs (length l) (Just $ Exactly 6)++req0m1 :: [Parameter]+       -> (Maybe Parameter -> Either AnalysisError Message)+       -> Either AnalysisError Message+req0m1 [] f   = f Nothing+req0m1 [p1] f = f $ Just p1+req0m1 l    _ = Left $ WrongNumArgs (length l) (Just $ Between 0 1)++req1m2 :: [Parameter]+       -> (Parameter -> Maybe Parameter -> Either AnalysisError Message)+       -> Either AnalysisError Message+req1m2 [p1]     f = f p1 Nothing+req1m2 [p1, p2] f = f p1 (Just p2)+req1m2 l        _ = Left $ WrongNumArgs (length l) (Just $ Between 1 2)++req2m3 :: [Parameter]+       -> (  Parameter+          -> Parameter+          -> Maybe Parameter+          -> Either AnalysisError Message+          )+       -> Either AnalysisError Message+req2m3 [p1, p2]     f = f p1 p2 Nothing+req2m3 [p1, p2, p3] f = f p1 p2 (Just p3)+req2m3 l            _ = Left $ WrongNumArgs (length l) (Just $ Between 2 3)++req3m4 :: [Parameter]+       -> (  Parameter+          -> Parameter+          -> Parameter+          -> Maybe Parameter+          -> Either AnalysisError Message+          )+       -> Either AnalysisError Message+req3m4 [p1, p2, p3]     f = f p1 p2 p3 Nothing+req3m4 [p1, p2, p3, p4] f = f p1 p2 p3 (Just p4)+req3m4 l                _ = Left $+                            WrongNumArgs (length l) (Just $ Between 3 4)++min1 :: [Parameter]+     -> (Parameter -> [Parameter] -> Either AnalysisError Message)+     -> Either AnalysisError Message+min1 []      _ = Left $ WrongNumArgs 0 (Just $ AtLeast 1)+min1 (p1:ps) f = f p1 ps