irc-fun-client-0.1.0.0: src/Network/IRC/Fun/Client/IO.hs
{- This file is part of irc-fun-client.
-
- 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.Client.IO
( Connection (..)
, Handle
, ircConnect
, ircDisconnect
, hPutIrcRaw
, hPutIrcGeneric
, hPutIrc
, hGetIrcRaw
, hGetIrcGenericOnce
, hGetIrcGeneric
, hGetIrcOnce
, hGetIrc
)
where
import Control.Exception (bracketOnError)
import Data.Maybe (fromMaybe)
import Network.IRC.Fun.Messages
import Network.IRC.Fun.Messages.Types
import Network.Socket
import System.IO
-- | Details of the connection to IRC.
data Connection = Connection
{ -- | IRC Server address, e.g. @"irc.freenode.net"@
server :: String
-- | IRC server port, @6667@ should be a safe default
, port :: Int
-- | Whether to make an encrypted connection via TLS (not implemented)
, tls :: Bool
-- | IRC nickname for the bot, e.g. @"funbot"@
, nick :: String
-- | Connection password, use if the nickname is registered
, password :: Maybe String
}
deriving (Eq, Show)
-- | Connect to an IRC server using the given connection parameters, and return
-- a handle to the open socket. This just opens a TCP connection, without
-- sending any IRC commands.
ircConnect :: Connection -> IO Handle
ircConnect conn = do
let hints = defaultHints
{ addrSocketType = Stream
, addrFlags = [AI_ADDRCONFIG]
}
addrs <- getAddrInfo (Just hints)
(Just $ server conn)
(Just $ show $ port conn)
let addr = head addrs
bracketOnError
(socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
(\ sock -> close sock >> putStrLn "Connection failed") $
\ sock -> do
connect sock (addrAddress addr)
handle <- socketToHandle sock ReadWriteMode
hSetBuffering handle LineBuffering
encoding <- mkTextEncoding "UTF-8//TRANSLIT"
hSetEncoding handle encoding
hSetNewlineMode handle (NewlineMode CRLF CRLF)
return handle
-- | Disconnect from IRC by closing the client's side of the connection. This
-- function is mainly provided for completeness. You should probably use the
-- QUIT command of IRC to quit the network in a manner coordinated with the
-- server.
ircDisconnect :: Handle -> IO ()
ircDisconnect = hClose
-- | Send an IRC command, given in string form, to the server. The given
-- command string shouldn't contain any newlines.
hPutIrcRaw :: Handle -> String -> IO ()
hPutIrcRaw = hPutStrLn
-- | Send an IRC message represented in generic form to the server.
hPutIrcGeneric :: Handle -> GenericMessage -> IO ()
hPutIrcGeneric h = hPutIrcRaw h . serializeMessage
-- | Send an IRC message to the server.
hPutIrc :: Handle -> Message -> IO ()
hPutIrc h = hPutIrcGeneric h . buildMessage . SpecificMessage Nothing
-- | Receive an IRC message, given in string form, from the server. The
-- resulting string won't contain newlines.
hGetIrcRaw :: Handle -> IO String
hGetIrcRaw = hGetLine
-- | Receive an IRC message in generic form from the server. If parsing the
-- message read from the server fails, 'Nothing' is returned.
hGetIrcGenericOnce :: Handle -> IO (Maybe GenericMessage)
hGetIrcGenericOnce h = hGetIrcRaw h >>= return . parseMessage
-- | Receive the next valid (successfully parsed) IRC message in generic form
-- from the server.
hGetIrcGeneric :: Handle -> IO GenericMessage
hGetIrcGeneric h = hGetIrcGenericOnce h >>= maybe (hGetIrcGeneric h) return
-- | Receive an IRC message from the server. If parsing the message read from
-- the server fails, 'Nothing' is returned.
hGetIrcOnce :: Handle -> IO (Maybe (Either SpecificReply SpecificMessage))
hGetIrcOnce h = hGetIrcGenericOnce h >>=
return . maybe Nothing ((either (const Nothing) Just) . analyze)
-- | Receive the next valid (successfully parsed) IRC message from the server.
hGetIrc :: Handle -> IO (Either SpecificReply SpecificMessage)
hGetIrc h = hGetIrcOnce h >>= maybe (hGetIrc h) return