irc-fun-client-0.2.0.0: src/Network/IRC/Fun/Client/Events.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/>.
-}
-- | 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
-- | A user's nickname has changed. Parameters: Old nick, new nick.
| NickChange String String
-- | 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
NickMessage newnick ->
if null sender
then err
else one $ NickChange sender newnick
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