irc-fun-client (empty) → 0.1.0.0
raw patch · 13 files changed
+730/−0 lines, 13 filesdep +basedep +irc-fun-messagesdep +networksetup-changed
Dependencies added: base, irc-fun-messages, network
Files
- AUTHORS +1/−0
- COPYING +121/−0
- ChangeLog +17/−0
- INSTALL +13/−0
- NEWS +23/−0
- README.md +14/−0
- Setup.hs +2/−0
- irc-fun-client.cabal +37/−0
- src/Network/IRC/Fun/Client.hs +45/−0
- src/Network/IRC/Fun/Client/Commands.hs +128/−0
- src/Network/IRC/Fun/Client/Events.hs +161/−0
- src/Network/IRC/Fun/Client/IO.hs +122/−0
- src/Network/IRC/Fun/Client/Util.hs +46/−0
+ AUTHORS view
@@ -0,0 +1,1 @@+fr33domlover <fr33domlover@riseup.net>
+ COPYING view
@@ -0,0 +1,121 @@+Creative Commons Legal Code++CC0 1.0 Universal++ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED+ HEREUNDER.++Statement of Purpose++The laws of most jurisdictions throughout the world automatically confer+exclusive Copyright and Related Rights (defined below) upon the creator+and subsequent owner(s) (each and all, an "owner") of an original work of+authorship and/or a database (each, a "Work").++Certain owners wish to permanently relinquish those rights to a Work for+the purpose of contributing to a commons of creative, cultural and+scientific works ("Commons") that the public can reliably and without fear+of later claims of infringement build upon, modify, incorporate in other+works, reuse and redistribute as freely as possible in any form whatsoever+and for any purposes, including without limitation commercial purposes.+These owners may contribute to the Commons to promote the ideal of a free+culture and the further production of creative, cultural and scientific+works, or to gain reputation or greater distribution for their Work in+part through the use and efforts of others.++For these and/or other purposes and motivations, and without any+expectation of additional consideration or compensation, the person+associating CC0 with a Work (the "Affirmer"), to the extent that he or she+is an owner of Copyright and Related Rights in the Work, voluntarily+elects to apply CC0 to the Work and publicly distribute the Work under its+terms, with knowledge of his or her Copyright and Related Rights in the+Work and the meaning and intended legal effect of CC0 on those rights.++1. Copyright and Related Rights. A Work made available under CC0 may be+protected by copyright and related or neighboring rights ("Copyright and+Related Rights"). Copyright and Related Rights include, but are not+limited to, the following:++ i. the right to reproduce, adapt, distribute, perform, display,+ communicate, and translate a Work;+ ii. moral rights retained by the original author(s) and/or performer(s);+iii. publicity and privacy rights pertaining to a person's image or+ likeness depicted in a Work;+ iv. rights protecting against unfair competition in regards to a Work,+ subject to the limitations in paragraph 4(a), below;+ v. rights protecting the extraction, dissemination, use and reuse of data+ in a Work;+ vi. database rights (such as those arising under Directive 96/9/EC of the+ European Parliament and of the Council of 11 March 1996 on the legal+ protection of databases, and under any national implementation+ thereof, including any amended or successor version of such+ directive); and+vii. other similar, equivalent or corresponding rights throughout the+ world based on applicable law or treaty, and any national+ implementations thereof.++2. Waiver. To the greatest extent permitted by, but not in contravention+of, applicable law, Affirmer hereby overtly, fully, permanently,+irrevocably and unconditionally waives, abandons, and surrenders all of+Affirmer's Copyright and Related Rights and associated claims and causes+of action, whether now known or unknown (including existing as well as+future claims and causes of action), in the Work (i) in all territories+worldwide, (ii) for the maximum duration provided by applicable law or+treaty (including future time extensions), (iii) in any current or future+medium and for any number of copies, and (iv) for any purpose whatsoever,+including without limitation commercial, advertising or promotional+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each+member of the public at large and to the detriment of Affirmer's heirs and+successors, fully intending that such Waiver shall not be subject to+revocation, rescission, cancellation, termination, or any other legal or+equitable action to disrupt the quiet enjoyment of the Work by the public+as contemplated by Affirmer's express Statement of Purpose.++3. Public License Fallback. Should any part of the Waiver for any reason+be judged legally invalid or ineffective under applicable law, then the+Waiver shall be preserved to the maximum extent permitted taking into+account Affirmer's express Statement of Purpose. In addition, to the+extent the Waiver is so judged Affirmer hereby grants to each affected+person a royalty-free, non transferable, non sublicensable, non exclusive,+irrevocable and unconditional license to exercise Affirmer's Copyright and+Related Rights in the Work (i) in all territories worldwide, (ii) for the+maximum duration provided by applicable law or treaty (including future+time extensions), (iii) in any current or future medium and for any number+of copies, and (iv) for any purpose whatsoever, including without+limitation commercial, advertising or promotional purposes (the+"License"). The License shall be deemed effective as of the date CC0 was+applied by Affirmer to the Work. Should any part of the License for any+reason be judged legally invalid or ineffective under applicable law, such+partial invalidity or ineffectiveness shall not invalidate the remainder+of the License, and in such case Affirmer hereby affirms that he or she+will not (i) exercise any of his or her remaining Copyright and Related+Rights in the Work or (ii) assert any associated claims and causes of+action with respect to the Work, in either case contrary to Affirmer's+express Statement of Purpose.++4. Limitations and Disclaimers.++ a. No trademark or patent rights held by Affirmer are waived, abandoned,+ surrendered, licensed or otherwise affected by this document.+ b. Affirmer offers the Work as-is and makes no representations or+ warranties of any kind concerning the Work, express, implied,+ statutory or otherwise, including without limitation warranties of+ title, merchantability, fitness for a particular purpose, non+ infringement, or the absence of latent or other defects, accuracy, or+ the present or absence of errors, whether or not discoverable, all to+ the greatest extent permissible under applicable law.+ c. Affirmer disclaims responsibility for clearing rights of other persons+ that may apply to the Work or any use thereof, including without+ limitation any person's Copyright and Related Rights in the Work.+ Further, Affirmer disclaims responsibility for obtaining any necessary+ consents, permissions or other rights required for any use of the+ Work.+ d. Affirmer understands and acknowledges that Creative Commons is not a+ party to this document and has no duty or obligation with respect to+ this CC0 or use of the Work.
+ ChangeLog view
@@ -0,0 +1,17 @@+The changes are recorded by the version control system, Darcs. To see a log+quickly from the terminal, run:++ $ darcs changes --repo http://dev.rel4tion.org/fr33domlover/irc-fun-client++There is also a web interface at <http://darcs.rel4tion.org> which, among other+things, can display the history log.++To see the log in a local clone, first get a copy of the repository if you+haven't yet:++ $ darcs get http://dev.rel4tion.org/fr33domlover/irc-fun-client++Then move into the newly created directory and run darcs:++ $ cd irc-fun-client+ $ darcs changes
+ INSTALL view
@@ -0,0 +1,13 @@+Install from Hackage:++ $ cabal install irc-fun-client++Install from unpacked release tarball or source repo:++ $ cd irc-fun-client+ $ cabal install++Just play with it without installing:++ $ cabal build+ $ cabal repl
+ NEWS view
@@ -0,0 +1,23 @@+This file lists the user-visible interesting changes between releases. For a+full list of changes to the source, see the ChangeLog.++++irc-fun-client 0.1.0.0 -- 2015-08-09+====================================++General, build and documentation changes:++* (This is the first release, so everything is new)++New APIs, features and enhancements:++* (This is the first release, so everything is a new feature)++Bug fixes:++* (This is just the first release, many bugs haven't been discovered yet)++Dependency changes:++* (This is the first release)
+ README.md view
@@ -0,0 +1,14 @@+See the .cabal file for more info and link to project website the version+control.++The official download location is Hackage:++<http://hackage.haskell.org/package/irc-fun-client>++This library is free software, and is committed to software freedom. It is+released to the public domain using the CC0 Public Domain Dedication. For the+boring "legal" details see the file 'COPYING'.++See the file 'INSTALL' for hints on installation. The file 'ChangeLog' explains+how to see the history log of the changes done in the code. 'NEWS' provides a+friendly overview of the changes for each release.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ irc-fun-client.cabal view
@@ -0,0 +1,37 @@+name: irc-fun-client+version: 0.1.0.0+synopsis: Another library for writing IRC clients.+description:+ This is an IRC client library that uses @irc-fun-messages@ library package+ for working with the IRC protocol. It provides functions for sending messages+ and for receiving them, with the IRC protocol messages abstracted by+ convenient command and event datatypes.+homepage: http://rel4tion.org/projects/irc-fun-client/+bug-reports: http://rel4tion.org/projects/irc-fun-client/tickets/+license: PublicDomain+license-file: COPYING+author: fr33domlover+maintainer: fr33domlover@riseup.net+copyright: ♡ Copying is an act of love. Please copy, reuse and share.+category: Network, IRC+build-type: Simple+extra-source-files: AUTHORS ChangeLog COPYING INSTALL NEWS README.md+cabal-version: >=1.10++source-repository head+ type: darcs+ location: http://dev.rel4tion.org/fr33domlover/irc-fun-client++library+ exposed-modules: Network.IRC.Fun.Client+ , Network.IRC.Fun.Client.Commands+ , Network.IRC.Fun.Client.Events+ , Network.IRC.Fun.Client.IO+ , Network.IRC.Fun.Client.Util+ -- other-modules:+ -- other-extensions: + build-depends: base >=4.7 && <5+ , irc-fun-messages+ , network >=2.3+ hs-source-dirs: src+ default-language: Haskell2010
+ src/Network/IRC/Fun/Client.hs view
@@ -0,0 +1,45 @@+{- 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/>.+ -}++-- | Here is an overview of the library, by module:+--+-- * "IO": Provides functions for connecting to an IRC server, and for sending+-- and receiving IRC messages.+-- * "Commands": Provides functions for sending IRC commands to the server.+-- * "Events": Provides an Event type and functions for receiving IRC events.+-- These events are an abstraction on top of IRC messages.+-- * "Util": Miscellaneous helper functions which could be useful to clients+--+-- If you're writing an IRC client, here is a suggestion for how to begin:+--+-- (1) Use functions from "IO" module to connect to an IRC server+-- (2) Use functions from "Commands" module to send common IRC commands+-- (3) Write an events loop which listens to the IRC server input and responds,+-- and a UI component which reacts to user-initiated commands. Use the+-- "Events" module to receive high-level events. If you have a specific+-- need to receive IRC messages directly, use functions from the "IO"+-- module.+module Network.IRC.Fun.Client+ ( module Network.IRC.Fun.Client.Commands+ , module Network.IRC.Fun.Client.Events+ , module Network.IRC.Fun.Client.IO+ , module Network.IRC.Fun.Client.Util+ )+where++import Network.IRC.Fun.Client.Commands+import Network.IRC.Fun.Client.Events+import Network.IRC.Fun.Client.IO+import Network.IRC.Fun.Client.Util
+ src/Network/IRC/Fun/Client/Commands.hs view
@@ -0,0 +1,128 @@+{- 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/>.+ -}++-- | This module provides convenient functions for performing common IRC client+-- actions. Some of them simply send a single IRC message, while othere send a+-- sequence of several messages.+module Network.IRC.Fun.Client.Commands+ ( ircLogin+ , ircPong+ , ircJoin+ , ircJoinMulti+ , ircPart+ , ircPartMulti+ , ircPartAll+ , ircSendToUser+ , ircSendToChannel+ , ircQuit+ )+where++import Control.Monad (unless)+import Data.Maybe (isNothing)+import Network.IRC.Fun.Messages+import Network.IRC.Fun.Messages.Types+import Network.IRC.Fun.Client.IO++-- | Log in as an IRC user with nickname and optional password, using the given+-- connection parameters.+ircLogin :: Handle -- ^ Handle to the open socket, returned from+ -- 'ircConnect'+ -> Connection -- ^ Connection configuration+ -> Bool -- ^ Whether you want to be invisible (mode @+i@)+ -> Bool -- ^ Whether you want to see wallops (mode @+w@)+ -> IO ()+ircLogin h conn i w = do+ let pass = password conn+ pass' = case pass of+ Just p -> p+ Nothing -> ""+ unless (isNothing pass) $ hPutIrc h $ PassMessage pass'+ hPutIrc h $ UserMessage (nick conn) i w (nick conn)+ hPutIrc h $ NickMessage $ nick conn++-- | IRC servers send PING messages at regular intervals to test the presence+-- of an active client, at least if no other activity is detected on the+-- connection. The server closes the connection automatically if a PONG+-- response isn't sent from the client within a certain amount of time.+--+-- Therefore, an IRC client usually listens to these PINGs and sends back PONG+-- messages. This function sends a PONG. The parameters should simply be the+-- ones received in the PING message.+ircPong :: Handle -- ^ Handle to the open socket+ -> String -- ^ Server name+ -> Maybe String -- ^ Optional server to forward to+ -> IO ()+ircPong h s ms = hPutIrc h $ PongMessage s ms++-- | Join an IRC channel.+ircJoin :: Handle -- ^ Handle to the open socket+ -> String -- ^ Channel name+ -> Maybe String -- ^ Optional channel key (password)+ -> IO ()+ircJoin h chan key = ircJoinMulti h [(chan, key)]++-- | Join one or more IRC channels.+ircJoinMulti :: Handle -- ^ Handle to the open socket+ -> [(String, Maybe String)] -- ^ List of channels and optional+ -- keys+ -> IO ()+ircJoinMulti _ [] = return ()+ircJoinMulti h l = do+ let nokey = [ chan | (chan, Nothing) <- l ]+ (chans, keys) = unzip [ (chan, key) | (chan, (Just key)) <- l ]+ unless (null nokey) $ hPutIrc h $ JoinMessage $ Just (nokey, [])+ unless (null chans) $ hPutIrc h $ JoinMessage $ Just (chans, keys)++-- | Leave an IRC channel.+ircPart :: Handle+ -> String+ -> Maybe String+ -> IO ()+ircPart h chan = ircPartMulti h [chan]++-- | Leave one or more IRC channels.+ircPartMulti :: Handle+ -> [String]+ -> Maybe String+ -> IO ()+ircPartMulti _ [] _ = return ()+ircPartMulti h chans msg = hPutIrc h $ PartMessage chans msg++-- | Leave all IRC channels you joined.+ircPartAll :: Handle -> IO ()+ircPartAll h = hPutIrc h $ JoinMessage Nothing++-- | Send a private message to an IRC user.+ircSendToUser :: Handle -- ^ Handle to the open socket+ -> String -- ^ The user's nickname+ -> String -- ^ The message to send+ -> IO ()+ircSendToUser h nick msg =+ hPutIrc h $ PrivMsgMessage (UserTarget (Just nick) Nothing Nothing) msg++-- | Send a message to an IRC channel.+ircSendToChannel :: Handle -- ^ Handle to the open socket+ -> String -- ^ The channel name+ -> String -- ^ The message to send+ -> IO ()+ircSendToChannel h chan msg =+ hPutIrc h $ PrivMsgMessage (ChannelTarget chan) msg++-- | Finish the IRC session, asking the server to close the connection.+ircQuit :: Handle -- ^ Handle to the open socket+ -> Maybe String -- ^ Optional message, e.g. the reason you quit+ -> IO ()+ircQuit h m = hPutIrc h $ QuitMessage m
+ src/Network/IRC/Fun/Client/Events.hs view
@@ -0,0 +1,161 @@+{- 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/>.+ -}++-- | This module allows you to identify IRC events. Events are a wrapper around+-- IRC messages, which provide a logical layer convenient to work with (while+-- IRC messages have their lower level aspects, being plain protocol messages).+module Network.IRC.Fun.Client.Events+ ( Event (..)+ , ChannelPrivacy (..)+ , Privilege (..)+ , detectEvents+ , hGetIrcEventsOnce+ , hGetIrcEvents+ )+where++import Control.Monad (unless)+import Data.Maybe (fromMaybe, isNothing)+import Network.IRC.Fun.Client.IO (Handle, hGetIrcOnce)+import Network.IRC.Fun.Messages+import Network.IRC.Fun.Messages.Types++-- | An event triggered by an IRC message sent from the server.+data Event+ -- | A ping was sent to the bot. The parameters are the server name and+ -- optionally a server to forward the response to. They can be passed as-is+ -- directly to the PONG response.+ = Ping String (Maybe String)+ -- | One or more users have been kicked from a channel for an optionally+ -- given reason. Parameters: Channel, nicknames, reason.+ | Kick String [String] (Maybe String)+ -- | A user joined a channel. Parameters: Channel, nickname.+ | Join String String+ -- | A user left a channel, optionally with a given reason. Parameters:+ -- Channel, nickname, reason.+ | Part String String (Maybe String)+ -- | A user left the network, optonally for the given reason. Parameters:+ -- Nickname, reason.+ | Quit String (Maybe String)+ -- | TODO+ | Mode+ -- | Message sent in a channel by a user. The last parameter indicates+ -- whether the message is actually a notice. If yes, the client shouldn't+ -- automatically send a response. Parameters: Channel, nickname, message,+ -- whether notice.+ | ChannelMessage String String String Bool+ -- | A private message sent specifically to the client from a user. The+ -- last parameter indicates whether the message is actually a notice. If+ -- yes, the client shouldn't send any automatic response. Parameters:+ -- Nickname, message, whether notice.+ | PrivateMessage String String Bool+ -- | Channel topic change. Parameterss: Channel, nickname of user who+ -- changed the topic, new topic content.+ | Topic String String String+ -- | The client has been invited to a channel by another user. Parameters:+ -- Channel, nickname of inviting user.+ | Invite String String+ -- | The server sent a list of nicknames present in a channel. Parameters:+ -- Channel privacy mode, channel name, list of users. Each list item is a+ -- pair of a user privilege level in the channel, and the user's nickname.+ | Names ChannelPrivacy String [(Privilege, String)]+ -- Unrecognized or unimplemented event. The parameter contains (possibly+ -- empty) input.+ | OtherEvent String+ deriving Show++detectMessageEvents :: SpecificMessage -> Either String [Event]+detectMessageEvents sm@(SpecificMessage mpref msg) =+ case msg of+ QuitMessage reason ->+ if null sender+ then err+ else one $ Quit sender reason+ JoinMessage (Just ([chan], [])) ->+ if null sender+ then err+ else one $ Join chan sender+ JoinMessage _ -> err+ PartMessage [chan] reason ->+ if null sender+ then err+ else one $ Part chan sender reason+ PartMessage _ _ -> err+ TopicMessage [] _ -> err+ TopicMessage chan topic ->+ if null sender+ then other+ else one $ Topic chan sender $ fromMaybe "" topic+ PingMessage s ms -> one $ Ping s ms+ KickMessage [] _ _ -> err+ KickMessage _ [] _ -> err+ KickMessage [chan] nicks comment ->+ one $ Kick chan nicks comment+ KickMessage chans nicks comment ->+ if length chans == length nicks+ then some $+ map (\ (c, n) -> Kick c [n] comment) $ zip chans nicks+ else err+ PrivMsgMessage _ [] -> other+ PrivMsgMessage (ChannelTarget chan) text ->+ if null sender+ then other+ else one $ ChannelMessage chan sender text False+ PrivMsgMessage (UserTarget _ _ _) text ->+ if null sender+ then err+ else one $ PrivateMessage sender text False+ PrivMsgMessage (MaskTarget _) _ -> other+ _ -> other+ where+ other = Right [OtherEvent $ show sm]+ one e = Right [e]+ some = Right+ err = Left $ show sm+ sender =+ case mpref of+ Just (Nick n _ _) -> n+ Nothing -> ""++detectReplyEvents :: SpecificReply -> Either String [Event]+detectReplyEvents sr@(SpecificReply _sender _target rpl) =+ case rpl of+ NamesReply priv chan pnicks -> one $ Names priv chan pnicks+ _ -> other+ where+ other = Right [OtherEvent $ show sr]+ one e = Right [e]+ err = Left $ show sr++-- | Try to generate events from an IRC message. If it fails, 'Left' an error+-- message is returned. Otherwise, 'Right' a list of generated events is+-- returned.+detectEvents :: Either SpecificReply SpecificMessage+ -> Either String [Event]+detectEvents = either detectReplyEvents detectMessageEvents++-- | Receive IRC events. If parsing and detecting the events fails, 'Nothing'+-- is returned.+hGetIrcEventsOnce :: Handle -> IO (Maybe [Event])+hGetIrcEventsOnce h =+ hGetIrcOnce h >>= return . \ r ->+ case r of+ Nothing -> Nothing+ Just spec -> either (const Nothing) Just $ detectEvents spec++-- | Receive the next valid (successfully parsed and detected) series of IRC+-- events.+hGetIrcEvents :: Handle -> IO [Event]+hGetIrcEvents h = hGetIrcEventsOnce h >>= maybe (hGetIrcEvents h) return
+ src/Network/IRC/Fun/Client/IO.hs view
@@ -0,0 +1,122 @@+{- 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
+ src/Network/IRC/Fun/Client/Util.hs view
@@ -0,0 +1,46 @@+{- 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.Util+ ( mentions+ )+where++import Data.Char (isAlphaNum)+import Data.List (isPrefixOf)++isNameChar :: Char -> Bool+isNameChar c = isAlphaNum c || c `elem` "-_|"++-- | Check if a message mentions a given nickname.+--+-- This isn't the same as substring search, since the nickname could appear+-- inside a longer word. For example, \"ron\" isn't really mentioned in+-- \"asynchronic\". Therefore, this function is a bit smarter about this,+-- trying to capturing real mentions, and works quite well in practice.+mentions :: String -- ^ Message content+ -> String -- ^ Nickname+ -> Bool+msg `mentions` nick = m msg nick True+ where+ m "" _ _ = False+ m msg@(c:cs) nick previnc =+ if not previnc && nick `isPrefixOf` msg+ then case drop (length nick) msg of+ "" -> True+ (d:ds) -> if not (isNameChar d)+ then True+ else m cs nick $ isNameChar c+ else m cs nick $ isNameChar c