{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TupleSections #-}

-- |
-- Module      : Network.IRC.Conduit.Internal
-- Copyright   : (c) 2016 Michael Walker
-- License     : MIT
-- Maintainer  : Michael Walker <mike@barrucadu.co.uk>
-- Stability   : experimental
-- Portability : BangPatterns, DeriveFunctor, OverloadedStrings, RankNTypes, TupleSections
--
-- Internal IRC conduit types and utilities. This module is NOT
-- considered to form part of the public interface of this library.
module Network.IRC.Conduit.Internal where

import           Control.Applicative   ((<$>))
import           Control.Arrow         ((&&&))
import           Data.ByteString       (ByteString, isSuffixOf, singleton,
                                        unpack)
import           Data.Char             (ord)
import           Data.Conduit          (ConduitM, await, yield)
import           Data.Maybe            (isJust, listToMaybe)
import           Data.Monoid           ((<>))
import           Data.Profunctor       (Choice)
import           Data.String           (fromString)
import           Network.IRC.CTCP      (CTCPByteString, getUnderlyingByteString,
                                        orCTCP)
import           Text.Read             (readMaybe)

import qualified Data.ByteString       as B
import qualified Data.ByteString.Char8 as B8
import qualified Network.IRC           as I

-- * Internal Lens synonyms

-- | See @<http://hackage.haskell.org/package/lens/docs/Control-Lens-Lens.html#t:Lens Control.Lens.Lens.Lens>@.
type Lens s t a b = forall f. Functor f => (a -> f b) -> s -> f t

-- | A @<http://hackage.haskell.org/package/lens/docs/Control-Lens-Type.html#t:Simple Simple>@ 'Lens'.
type Lens' s a = Lens s s a a

-- | See @<http://hackage.haskell.org/package/lens/docs/Control-Lens-Prism.html#t:Prism Control.Lens.Prism.Prism>@.
type Prism s t a b = forall p f. (Choice p, Applicative f) => p a (f b) -> p s (f t)

-- | A @<http://hackage.haskell.org/package/lens/docs/Control-Lens-Type.html#t:Simple Simple>@ 'Prism'.
type Prism' s a = Prism s s a a


-- *Conduits

-- |Split up incoming bytestrings into new lines.
chunked :: Monad m => ConduitM ByteString ByteString m ()
chunked :: forall (m :: * -> *).
Monad m =>
ConduitM ByteString ByteString m ()
chunked = forall {m :: * -> *}.
Monad m =>
ByteString -> ConduitT ByteString ByteString m ()
chunked' ByteString
""
  where
    chunked' :: ByteString -> ConduitT ByteString ByteString m ()
chunked' !ByteString
leftover = do
      -- Wait for a value from upstream
      Maybe ByteString
val <- forall (m :: * -> *) i o. Monad m => ConduitT i o m (Maybe i)
await

      case Maybe ByteString
val of
        Just ByteString
val' ->
          let
            carriage :: Word8
carriage = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ forall a. Enum a => a -> Int
fromEnum Char
'\r'
            newline :: Word8
newline  = forall a b. (Integral a, Num b) => a -> b
fromIntegral forall a b. (a -> b) -> a -> b
$ forall a. Enum a => a -> Int
fromEnum Char
'\n'

            -- Split on '\n's, removing any stray '\r's (line endings
            -- are usually '\r\n's, but this isn't certain).
            bytes :: ByteString
bytes    = (Word8 -> Bool) -> ByteString -> ByteString
B.filter (forall a. Eq a => a -> a -> Bool
/=Word8
carriage) forall a b. (a -> b) -> a -> b
$ ByteString
leftover forall a. Semigroup a => a -> a -> a
<> ByteString
val'
            splitted :: [ByteString]
splitted = Word8 -> ByteString -> [ByteString]
B.split Word8
newline ByteString
bytes

            -- If the last chunk ends with a '\n', then we have a
            -- complete message at the end, and can yield it
            -- immediately. Otherwise, store the partial message to
            -- prepend to the next bytestring received.
            ([ByteString]
toyield, ByteString
remainder)
              | Word8 -> ByteString
singleton Word8
newline ByteString -> ByteString -> Bool
`isSuffixOf` ByteString
bytes = ([ByteString]
splitted, ByteString
"")
              | Bool
otherwise = forall a. [a] -> [a]
init forall (a :: * -> * -> *) b c c'.
Arrow a =>
a b c -> a b c' -> a b (c, c')
&&& forall a. [a] -> a
last forall a b. (a -> b) -> a -> b
$ [ByteString]
splitted

          in do
            -- Yield all complete and nonempty messages, and loop.
            forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m ()
yield forall a b. (a -> b) -> a -> b
$ forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Bool
B.null) [ByteString]
toyield
            ByteString -> ConduitT ByteString ByteString m ()
chunked' ByteString
remainder

        Maybe ByteString
Nothing -> forall (m :: * -> *) a. Monad m => a -> m a
return ()

-- *Type synonyms
type ChannelName a = a
type NickName    a = a
type ServerName  a = a
type Reason      a = Maybe a
type IsModeSet     = Bool
type ModeFlag    a = a
type ModeArg     a = a
type NumericArg  a = a

-- |The target of a message. Will be a nick or channel name.
type Target      a = a

type IrcEvent   = Event ByteString
type IrcSource  = Source ByteString
type IrcMessage = Message ByteString

-- *Messages

-- |A decoded IRC message + source.
data Event a = Event
    { forall a. Event a -> ByteString
_raw     :: ByteString
    -- ^The message as a bytestring.
    , forall a. Event a -> Source a
_source  :: Source a
    -- ^The source of the message (user, channel, or server).
    , forall a. Event a -> Message a
_message :: Message a
    -- ^The decoded message. This will never be a 'RawMsg'.
    }
    deriving (Event (NickName a) -> Event (NickName a) -> Bool
forall a.
Eq (NickName a) =>
Event (NickName a) -> Event (NickName a) -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Event (NickName a) -> Event (NickName a) -> Bool
$c/= :: forall a.
Eq (NickName a) =>
Event (NickName a) -> Event (NickName a) -> Bool
== :: Event (NickName a) -> Event (NickName a) -> Bool
$c== :: forall a.
Eq (NickName a) =>
Event (NickName a) -> Event (NickName a) -> Bool
Eq, forall a b. a -> Event b -> Event a
forall a b. (a -> b) -> Event a -> Event b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Event b -> Event a
$c<$ :: forall a b. a -> Event b -> Event a
fmap :: forall a b. (a -> b) -> Event a -> Event b
$cfmap :: forall a b. (a -> b) -> Event a -> Event b
Functor, Int -> Event (NickName a) -> ShowS
forall a. Show (NickName a) => Int -> Event (NickName a) -> ShowS
forall a. Show (NickName a) => [Event (NickName a)] -> ShowS
forall a. Show (NickName a) => Event (NickName a) -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Event (NickName a)] -> ShowS
$cshowList :: forall a. Show (NickName a) => [Event (NickName a)] -> ShowS
show :: Event (NickName a) -> String
$cshow :: forall a. Show (NickName a) => Event (NickName a) -> String
showsPrec :: Int -> Event (NickName a) -> ShowS
$cshowsPrec :: forall a. Show (NickName a) => Int -> Event (NickName a) -> ShowS
Show)

-- |The source of an IRC message.
data Source a = User (NickName a)
              -- ^The message comes directly from a user.
              | Channel (ChannelName a) (NickName a)
              -- ^The message comes from a user in a channel.
              | Server (ServerName a)
              -- ^The message comes directly from the server.
              deriving (Source (NickName a) -> Source (NickName a) -> Bool
forall a.
Eq (NickName a) =>
Source (NickName a) -> Source (NickName a) -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Source (NickName a) -> Source (NickName a) -> Bool
$c/= :: forall a.
Eq (NickName a) =>
Source (NickName a) -> Source (NickName a) -> Bool
== :: Source (NickName a) -> Source (NickName a) -> Bool
$c== :: forall a.
Eq (NickName a) =>
Source (NickName a) -> Source (NickName a) -> Bool
Eq, forall a b. a -> Source b -> Source a
forall a b. (a -> b) -> Source a -> Source b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Source b -> Source a
$c<$ :: forall a b. a -> Source b -> Source a
fmap :: forall a b. (a -> b) -> Source a -> Source b
$cfmap :: forall a b. (a -> b) -> Source a -> Source b
Functor, Int -> Source (NickName a) -> ShowS
forall a. Show (NickName a) => Int -> Source (NickName a) -> ShowS
forall a. Show (NickName a) => [Source (NickName a)] -> ShowS
forall a. Show (NickName a) => Source (NickName a) -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Source (NickName a)] -> ShowS
$cshowList :: forall a. Show (NickName a) => [Source (NickName a)] -> ShowS
show :: Source (NickName a) -> String
$cshow :: forall a. Show (NickName a) => Source (NickName a) -> String
showsPrec :: Int -> Source (NickName a) -> ShowS
$cshowsPrec :: forall a. Show (NickName a) => Int -> Source (NickName a) -> ShowS
Show)

-- |A decoded IRC message.
data Message a = Privmsg (Target a) (Either CTCPByteString a)
               -- ^A message, either from a user or to a channel the
               -- client is in. CTCPs are distinguished by starting
               -- and ending with a \\001 (SOH).
               | Notice (Target a) (Either CTCPByteString a)
               -- ^Like a privmsg, but should not provoke an automatic
               -- response.
               | Nick (NickName a)
               -- ^Someone has updated their nick.
               | Join (ChannelName a)
               -- ^Someone has joined a channel.
               | Part (ChannelName a) (Reason a)
               -- ^Someone has left a channel.
               | Quit (Reason a)
               -- ^Someone has left the network.
               | Mode (Target a) IsModeSet [ModeFlag a] [ModeArg a]
               -- ^Someone has set some channel modes or user modes.
               | Topic (ChannelName a) a
               -- ^Someone has set the topic of a channel.
               | Invite (ChannelName a) (NickName a)
               -- ^The client has been invited to a channel.
               | Kick (ChannelName a) (NickName a) (Reason a)
               -- ^Someone has been kicked from a channel.
               | Ping (ServerName a) (Maybe (ServerName a))
               -- ^The client has received a server ping, and should
               -- send a pong asap.
               | Pong (ServerName a)
               -- ^A pong sent to the named server.
               | Numeric Int [NumericArg a]
               -- ^One of the many server numeric responses.
               | RawMsg a
               -- ^Never produced by decoding, but can be used to send
               -- arbitrary bytestrings to the IRC server. Naturally,
               -- this should only be used when you are confident that
               -- the produced bytestring will be a valid IRC message.
               deriving (Message (Target a) -> Message (Target a) -> Bool
forall a.
Eq (Target a) =>
Message (Target a) -> Message (Target a) -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Message (Target a) -> Message (Target a) -> Bool
$c/= :: forall a.
Eq (Target a) =>
Message (Target a) -> Message (Target a) -> Bool
== :: Message (Target a) -> Message (Target a) -> Bool
$c== :: forall a.
Eq (Target a) =>
Message (Target a) -> Message (Target a) -> Bool
Eq, forall a b. a -> Message b -> Message a
forall a b. (a -> b) -> Message a -> Message b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: forall a b. a -> Message b -> Message a
$c<$ :: forall a b. a -> Message b -> Message a
fmap :: forall a b. (a -> b) -> Message a -> Message b
$cfmap :: forall a b. (a -> b) -> Message a -> Message b
Functor, Int -> Message (Target a) -> ShowS
forall a. Show (Target a) => Int -> Message (Target a) -> ShowS
forall a. Show (Target a) => [Message (Target a)] -> ShowS
forall a. Show (Target a) => Message (Target a) -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Message (Target a)] -> ShowS
$cshowList :: forall a. Show (Target a) => [Message (Target a)] -> ShowS
show :: Message (Target a) -> String
$cshow :: forall a. Show (Target a) => Message (Target a) -> String
showsPrec :: Int -> Message (Target a) -> ShowS
$cshowsPrec :: forall a. Show (Target a) => Int -> Message (Target a) -> ShowS
Show)

-- *Decoding messages

fromByteString :: ByteString -> Either ByteString IrcEvent
fromByteString :: ByteString -> Either ByteString IrcEvent
fromByteString ByteString
bs = forall b a. b -> (a -> b) -> Maybe a -> b
maybe (forall a b. a -> Either a b
Left ByteString
bs) (forall a b. b -> Either a b
Right forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b c. (a -> b -> c) -> (a, b) -> c
uncurry (forall a. ByteString -> Source a -> Message a -> Event a
Event ByteString
bs)) (ByteString -> Maybe (Source ByteString, Message ByteString)
attemptDecode ByteString
bs)

-- |Attempt to decode a ByteString into a message, returning a Nothing
-- if either the source or the message can't be determined.
attemptDecode :: ByteString -> Maybe (IrcSource, IrcMessage)
attemptDecode :: ByteString -> Maybe (Source ByteString, Message ByteString)
attemptDecode ByteString
bs = ByteString -> Maybe Message
I.decode ByteString
bs forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Message -> Maybe (Source ByteString, Message ByteString)
decode'
  where
    decode' :: Message -> Maybe (Source ByteString, Message ByteString)
decode' Message
msg = case Message
msg of
      -- Disambiguate PRIVMSG and NOTICE source by checking the first
      -- character of the target
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"PRIVMSG" [ByteString
t, ByteString
m] | ByteString -> Bool
isChan ByteString
t  -> forall a. a -> Maybe a
Just (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
t ByteString
n, ByteString -> ByteString -> Message ByteString
privmsg ByteString
t ByteString
m)
                                                           | Bool
otherwise -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
User ByteString
n,      ByteString -> ByteString -> Message ByteString
privmsg ByteString
t ByteString
m)

      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"NOTICE"  [ByteString
t, ByteString
m] | ByteString -> Bool
isChan ByteString
t  -> forall a. a -> Maybe a
Just (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
t ByteString
n, ByteString -> ByteString -> Message ByteString
notice ByteString
t ByteString
m)
                                                           | Bool
otherwise -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
User ByteString
n,      ByteString -> ByteString -> Message ByteString
notice ByteString
t ByteString
m)

      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"NICK"   [ByteString
n']    -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
User ByteString
n,      forall a. Target a -> Message (Target a)
Nick ByteString
n')
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"JOIN"   [ByteString
c]     -> forall a. a -> Maybe a
Just (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
c ByteString
n, forall a. Target a -> Message (Target a)
Join ByteString
c)
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"PART"   (ByteString
c:[ByteString]
r)   -> forall a. a -> Maybe a
Just (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
c ByteString
n, forall a. Target a -> Reason (Target a) -> Message (Target a)
Part ByteString
c   forall a b. (a -> b) -> a -> b
$ forall a. [a] -> Maybe a
listToMaybe [ByteString]
r)
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"QUIT"   [ByteString]
r       -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
User ByteString
n,      forall a. Reason (Target a) -> Message (Target a)
Quit     forall a b. (a -> b) -> a -> b
$ forall a. [a] -> Maybe a
listToMaybe [ByteString]
r)
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"KICK"   (ByteString
c:ByteString
u:[ByteString]
r) -> forall a. a -> Maybe a
Just (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
c ByteString
n, forall a.
Target a -> Target a -> Reason (Target a) -> Message (Target a)
Kick ByteString
c ByteString
u forall a b. (a -> b) -> a -> b
$ forall a. [a] -> Maybe a
listToMaybe [ByteString]
r)
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"INVITE" [ByteString
_, ByteString
c]  -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
User ByteString
n,      forall a. Target a -> Target a -> Message (Target a)
Invite ByteString
c ByteString
n)
      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"TOPIC"  [ByteString
c, ByteString
t]  -> forall a. a -> Maybe a
Just (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
c ByteString
n, forall a. Target a -> Target a -> Message (Target a)
Topic  ByteString
c ByteString
t)

      I.Message (Just (I.NickName ByteString
n Maybe ByteString
_ Maybe ByteString
_)) ByteString
"MODE" (ByteString
t:ByteString
fs:[ByteString]
as) | ByteString
n forall a. Eq a => a -> a -> Bool
== ByteString
t    -> (forall a. NickName a -> Source (NickName a)
User ByteString
n,)      forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString
-> ByteString -> [ByteString] -> Maybe (Message ByteString)
mode ByteString
t ByteString
fs [ByteString]
as
                                                           | Bool
otherwise -> (forall a. NickName a -> NickName a -> Source (NickName a)
Channel ByteString
t ByteString
n,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ByteString
-> ByteString -> [ByteString] -> Maybe (Message ByteString)
mode ByteString
t ByteString
fs [ByteString]
as

      I.Message (Just (I.Server ByteString
s)) ByteString
"PING" (ByteString
s1:[ByteString]
s2) -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
Server ByteString
s,  forall a. Target a -> Reason (Target a) -> Message (Target a)
Ping ByteString
s1 forall a b. (a -> b) -> a -> b
$ forall a. [a] -> Maybe a
listToMaybe [ByteString]
s2)
      I.Message Maybe Prefix
Nothing             ByteString
"PING" (ByteString
s1:[ByteString]
s2) -> forall a. a -> Maybe a
Just (forall a. NickName a -> Source (NickName a)
Server ByteString
s1, forall a. Target a -> Reason (Target a) -> Message (Target a)
Ping ByteString
s1 forall a b. (a -> b) -> a -> b
$ forall a. [a] -> Maybe a
listToMaybe [ByteString]
s2)

      I.Message (Just (I.Server ByteString
s)) ByteString
n [ByteString]
args | ByteString -> Bool
isNumeric ByteString
n -> (forall a. NickName a -> Source (NickName a)
Server ByteString
s,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {a}.
ByteString -> [NumericArg a] -> Maybe (Message (NumericArg a))
numeric ByteString
n [ByteString]
args

      Message
_ -> forall a. Maybe a
Nothing

    -- An IRC channel name can start with '#', '&', '+', or '!', all
    -- of which have different meanings. However, most servers only
    -- support '#'.
    isChan :: ByteString -> Bool
isChan ByteString
t = Int -> ByteString -> ByteString
B.take Int
1 ByteString
t forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` [ByteString
"#", ByteString
"&", ByteString
"+", ByteString
"!"]

    -- Check if the message looks like a ctcp or not, and produce the appropriate message type.
    privmsg :: ByteString -> ByteString -> Message ByteString
privmsg ByteString
t = forall a.
Target a -> Either CTCPByteString (Target a) -> Message (Target a)
Privmsg ByteString
t forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a b. b -> Either a b
Right forall a.
(ByteString -> a) -> (CTCPByteString -> a) -> ByteString -> a
`orCTCP` forall a b. a -> Either a b
Left)
    notice :: ByteString -> ByteString -> Message ByteString
notice  ByteString
t = forall a.
Target a -> Either CTCPByteString (Target a) -> Message (Target a)
Notice  ByteString
t forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a b. b -> Either a b
Right forall a.
(ByteString -> a) -> (CTCPByteString -> a) -> ByteString -> a
`orCTCP` forall a b. a -> Either a b
Left)

    -- Decode a set of mode changes
    mode :: ByteString
-> ByteString -> [ByteString] -> Maybe (Message ByteString)
mode ByteString
t ByteString
fs [ByteString]
as = case ByteString -> [Word8]
unpack ByteString
fs of
      (Word8
f:[Word8]
fs') | Word8
f forall a. Eq a => a -> a -> Bool
== forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
ord Char
'+') -> forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a.
Target a -> Bool -> [Target a] -> [Target a] -> Message (Target a)
Mode ByteString
t Bool
True  (forall a b. (a -> b) -> [a] -> [b]
map Word8 -> ByteString
singleton [Word8]
fs') [ByteString]
as
              | Word8
f forall a. Eq a => a -> a -> Bool
== forall a b. (Integral a, Num b) => a -> b
fromIntegral (Char -> Int
ord Char
'-') -> forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a.
Target a -> Bool -> [Target a] -> [Target a] -> Message (Target a)
Mode ByteString
t Bool
False (forall a b. (a -> b) -> [a] -> [b]
map Word8 -> ByteString
singleton [Word8]
fs') [ByteString]
as
      [Word8]
_ -> forall a. Maybe a
Nothing

    -- Parse the number in a numeric response
    isNumeric :: ByteString -> Bool
isNumeric = forall a. Maybe a -> Bool
isJust forall b c a. (b -> c) -> (a -> b) -> a -> c
. (forall a. Read a => String -> Maybe a
readMaybe :: String -> Maybe Int) forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> String
B8.unpack
    numeric :: ByteString -> [NumericArg a] -> Maybe (Message (NumericArg a))
numeric ByteString
n [NumericArg a]
args = forall a b c. (a -> b -> c) -> b -> a -> c
flip forall a. Int -> [Target a] -> Message (Target a)
Numeric [NumericArg a]
args forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a. Read a => String -> Maybe a
readMaybe (ByteString -> String
B8.unpack ByteString
n)

-- *Encoding messages

-- |Encode an IRC message into a single bytestring suitable for
-- sending to the server.
toByteString :: IrcMessage -> ByteString
toByteString :: Message ByteString -> ByteString
toByteString (Privmsg ByteString
t (Left CTCPByteString
ctcpbs))  = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PRIVMSG" [ByteString
t, CTCPByteString -> ByteString
getUnderlyingByteString CTCPByteString
ctcpbs]
toByteString (Privmsg ByteString
t (Right ByteString
bs))     = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PRIVMSG" [ByteString
t, ByteString
bs]
toByteString (Notice  ByteString
t (Left CTCPByteString
ctcpbs))  = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"NOTICE"  [ByteString
t, CTCPByteString -> ByteString
getUnderlyingByteString CTCPByteString
ctcpbs]
toByteString (Notice  ByteString
t (Right ByteString
bs))     = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"NOTICE"  [ByteString
t, ByteString
bs]
toByteString (Nick ByteString
n)                   = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"NICK"    [ByteString
n]
toByteString (Join ByteString
c)                   = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"JOIN"    [ByteString
c]
toByteString (Part ByteString
c (Just ByteString
r))          = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PART"    [ByteString
c, ByteString
r]
toByteString (Part ByteString
c Maybe ByteString
Nothing)           = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PART"    [ByteString
c]
toByteString (Quit (Just ByteString
r))            = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"QUIT"    [ByteString
r]
toByteString (Quit Maybe ByteString
Nothing)             = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"QUIT"    []
toByteString (Mode ByteString
t Bool
True  [ByteString]
ms [ByteString]
as)       = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"MODE"    forall a b. (a -> b) -> a -> b
$ ByteString
t forall a. a -> [a] -> [a]
: (ByteString
"+" forall a. Semigroup a => a -> a -> a
<> [ByteString] -> ByteString
B.concat [ByteString]
ms) forall a. a -> [a] -> [a]
: [ByteString]
as
toByteString (Mode ByteString
t Bool
False [ByteString]
ms [ByteString]
as)       = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"MODE"    forall a b. (a -> b) -> a -> b
$ ByteString
t forall a. a -> [a] -> [a]
: (ByteString
"-" forall a. Semigroup a => a -> a -> a
<> [ByteString] -> ByteString
B.concat [ByteString]
ms) forall a. a -> [a] -> [a]
: [ByteString]
as
toByteString (Invite ByteString
c ByteString
n)               = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"INVITE"  [ByteString
c, ByteString
n]
toByteString (Topic ByteString
c ByteString
bs)               = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"TOPIC"   [ByteString
c, ByteString
bs]
toByteString (Kick ByteString
c ByteString
n (Just ByteString
r))        = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"KICK"    [ByteString
c, ByteString
n, ByteString
r]
toByteString (Kick ByteString
c ByteString
n Maybe ByteString
Nothing)         = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"KICK"    [ByteString
c, ByteString
n]
toByteString (Ping ByteString
s1 (Just ByteString
s2))        = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PING"    [ByteString
s1, ByteString
s2]
toByteString (Ping ByteString
s1 Maybe ByteString
Nothing)          = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PING"    [ByteString
s1]
toByteString (Pong ByteString
s)                   = ByteString -> [ByteString] -> ByteString
mkMessage ByteString
"PONG"    [ByteString
s]
toByteString (Numeric Int
n [ByteString]
as)             = ByteString -> [ByteString] -> ByteString
mkMessage (forall a. IsString a => String -> a
fromString forall a b. (a -> b) -> a -> b
$ forall a. Show a => a -> String
show Int
n) [ByteString]
as
toByteString (RawMsg ByteString
bs)                = ByteString
bs

mkMessage :: ByteString -> [ByteString] -> ByteString
mkMessage :: ByteString -> [ByteString] -> ByteString
mkMessage ByteString
cmd = Message -> ByteString
I.encode forall b c a. (b -> c) -> (a -> b) -> a -> c
. Maybe Prefix -> ByteString -> [ByteString] -> Message
I.Message forall a. Maybe a
Nothing ByteString
cmd

-- |Construct a raw message.
rawMessage :: ByteString
           -- ^The command
           -> [ByteString]
           -- ^The arguments
           -> IrcMessage
rawMessage :: ByteString -> [ByteString] -> Message ByteString
rawMessage ByteString
cmd = forall a. Target a -> Message (Target a)
RawMsg forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> [ByteString] -> ByteString
mkMessage ByteString
cmd