matrix-client-0.1.6.1: src/Network/Matrix/Events.hs
{-# LANGUAGE OverloadedStrings #-}
-- | Matrix event data type
module Network.Matrix.Events (
MessageTextType (..),
MessageText (..),
RoomMessage (..),
Event (..),
EventID (..),
eventType,
)
where
import Control.Applicative ((<|>))
import Control.Monad (mzero)
import Data.Aeson (FromJSON (..), Object, ToJSON (..), Value (Object, String), object, (.:), (.:?), (.=))
import Data.Aeson.Types (Pair)
import Data.Text (Text)
data MessageTextType
= TextType
| EmoteType
| NoticeType
deriving (Eq, Show)
instance FromJSON MessageTextType where
parseJSON (String name) = case name of
"m.text" -> pure TextType
"m.emote" -> pure EmoteType
"m.notice" -> pure NoticeType
_ -> mzero
parseJSON _ = mzero
instance ToJSON MessageTextType where
toJSON mt = String $ case mt of
TextType -> "m.text"
EmoteType -> "m.emote"
NoticeType -> "m.notice"
data MessageText = MessageText
{ mtBody :: Text
, mtType :: MessageTextType
, mtFormat :: Maybe Text
, mtFormattedBody :: Maybe Text
}
deriving (Show, Eq)
instance FromJSON MessageText where
parseJSON (Object v) =
MessageText
<$> v .: "body"
<*> v .: "msgtype"
<*> v .:? "format"
<*> v .:? "formatted_body"
parseJSON _ = mzero
messageTextAttr :: MessageText -> [Pair]
messageTextAttr msg =
["body" .= mtBody msg, "msgtype" .= mtType msg] <> format <> formattedBody
where
omitNull k = maybe [] (\v -> [k .= v])
format = omitNull "format" $ mtFormat msg
formattedBody = omitNull "formatted_body" $ mtFormattedBody msg
instance ToJSON MessageText where
toJSON = object . messageTextAttr
newtype RoomMessage
= RoomMessageText MessageText
deriving (Show, Eq)
roomMessageAttr :: RoomMessage -> [Pair]
roomMessageAttr rm = case rm of
RoomMessageText mt -> messageTextAttr mt
instance ToJSON RoomMessage where
toJSON msg = case msg of
RoomMessageText mt -> toJSON mt
instance FromJSON RoomMessage where
parseJSON x = RoomMessageText <$> parseJSON x
data RelatedMessage = RelatedMessage
{ rmMessage :: RoomMessage
, rmRelatedTo :: EventID
}
deriving (Show, Eq)
data Event
= EventRoomMessage RoomMessage
| -- | A reply defined by the parent event id and the reply message
EventRoomReply EventID RoomMessage
| -- | An edit defined by the original message and the new message
EventRoomEdit (EventID, RoomMessage) RoomMessage
| EventUnknown Object
deriving (Eq, Show)
instance ToJSON Event where
toJSON event = case event of
EventRoomMessage msg -> toJSON msg
EventRoomReply eventID msg ->
let replyAttr =
[ "m.relates_to"
.= object
[ "m.in_reply_to" .= toJSON eventID
]
]
in object $ replyAttr <> roomMessageAttr msg
EventRoomEdit (EventID eventID, msg) newMsg ->
let editAttr =
[ "m.relates_to"
.= object
[ "rel_type" .= ("m.replace" :: Text)
, "event_id" .= eventID
]
, "m.new_content" .= object (roomMessageAttr newMsg)
]
in object $ editAttr <> roomMessageAttr msg
EventUnknown v -> Object v
instance FromJSON Event where
parseJSON (Object content) =
parseRelated <|> parseMessage <|> pure (EventUnknown content)
where
parseMessage = EventRoomMessage <$> parseJSON (Object content)
parseRelated = do
relateM <- content .: "m.relates_to"
case relateM of
Object relate -> parseReply relate <|> parseReplace relate
_ -> mzero
parseReply relate =
EventRoomReply <$> relate .: "m.in_reply_to" <*> parseJSON (Object content)
parseReplace relate = do
rel_type <- relate .: "rel_type"
if rel_type == ("m.replace" :: Text)
then do
ev <- EventID <$> relate .: "event_id"
msg <- parseJSON (Object content)
EventRoomEdit (ev, msg) <$> content .: "m.new_content"
else mzero
parseJSON _ = mzero
eventType :: Event -> Text
eventType event = case event of
EventRoomMessage _ -> "m.room.message"
EventRoomReply _ _ -> "m.room.message"
EventRoomEdit _ _ -> "m.room.message"
EventUnknown _ -> error $ "Event is not implemented: " <> show event
newtype EventID = EventID {unEventID :: Text} deriving (Show, Eq, Ord)
instance FromJSON EventID where
parseJSON (Object v) = EventID <$> v .: "event_id"
parseJSON _ = mzero
instance ToJSON EventID where
toJSON (EventID v) = object ["event_id" .= v]