From 677c6aeb2e03d8282d402f49a7db4225d1424920 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Wed, 14 Dec 2022 12:16:11 +0000 Subject: [PATCH] core: types for timed and live messages (#1572) * core: types for timed and live messages * add protocol tests --- src/Simplex/Chat.hs | 24 ++++++------- src/Simplex/Chat/Protocol.hs | 30 +++++++++------- tests/ProtocolTests.hs | 68 ++++++++++++++++++++++-------------- 3 files changed, 70 insertions(+), 52 deletions(-) diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index af19e8f42d..c4b6ec162e 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -323,7 +323,7 @@ processChatCommand = \case pure (fileInvitation, ciFile, ft) prepareMsg :: Maybe FileInvitation -> m (MsgContainer, Maybe (CIQuote 'CTDirect)) prepareMsg fileInvitation_ = case quotedItemId_ of - Nothing -> pure (MCSimple (ExtMsgContent mc fileInvitation_), Nothing) + Nothing -> pure (MCSimple (extMsgContent mc fileInvitation_), Nothing) Just quotedItemId -> do CChatItem _ qci@ChatItem {meta = CIMeta {itemTs, itemSharedMsgId}, formattedText, file} <- withStore $ \db -> getDirectChatItem db userId chatId quotedItemId @@ -331,7 +331,7 @@ processChatCommand = \case let msgRef = MsgRef {msgId = itemSharedMsgId, sentAt = itemTs, sent, memberId = Nothing} qmc = quoteContent origQmc file quotedItem = CIQuote {chatDir = qd, itemId = Just quotedItemId, sharedMsgId = itemSharedMsgId, sentAt = itemTs, content = qmc, formattedText} - pure (MCQuote QuotedMsg {msgRef, content = qmc} (ExtMsgContent mc fileInvitation_), Just quotedItem) + pure (MCQuote QuotedMsg {msgRef, content = qmc} (extMsgContent mc fileInvitation_), Just quotedItem) where quoteData :: ChatItem c d -> m (MsgContent, CIQDirection 'CTDirect, Bool) quoteData ChatItem {meta = CIMeta {itemDeleted = True}} = throwChatError CEInvalidQuote @@ -372,7 +372,7 @@ processChatCommand = \case _ -> pure () prepareMsg :: Maybe FileInvitation -> GroupMember -> m (MsgContainer, Maybe (CIQuote 'CTGroup)) prepareMsg fileInvitation_ membership = case quotedItemId_ of - Nothing -> pure (MCSimple (ExtMsgContent mc fileInvitation_), Nothing) + Nothing -> pure (MCSimple (extMsgContent mc fileInvitation_), Nothing) Just quotedItemId -> do CChatItem _ qci@ChatItem {meta = CIMeta {itemTs, itemSharedMsgId}, formattedText, file} <- withStore $ \db -> getGroupChatItem db user chatId quotedItemId @@ -380,7 +380,7 @@ processChatCommand = \case let msgRef = MsgRef {msgId = itemSharedMsgId, sentAt = itemTs, sent, memberId = Just memberId} qmc = quoteContent origQmc file quotedItem = CIQuote {chatDir = qd, itemId = Just quotedItemId, sharedMsgId = itemSharedMsgId, sentAt = itemTs, content = qmc, formattedText} - pure (MCQuote QuotedMsg {msgRef, content = qmc} (ExtMsgContent mc fileInvitation_), Just quotedItem) + pure (MCQuote QuotedMsg {msgRef, content = qmc} (extMsgContent mc fileInvitation_), Just quotedItem) where quoteData :: ChatItem c d -> GroupMember -> m (MsgContent, CIQDirection 'CTGroup, Bool, GroupMember) quoteData ChatItem {meta = CIMeta {itemDeleted = True}} _ = throwChatError CEInvalidQuote @@ -423,7 +423,7 @@ processChatCommand = \case CChatItem SMDSnd ChatItem {meta = CIMeta {itemSharedMsgId}, content = ciContent} -> do case (ciContent, itemSharedMsgId) of (CISndMsgContent _, Just itemSharedMId) -> do - (SndMessage {msgId}, _) <- sendDirectContactMessage ct (XMsgUpdate itemSharedMId mc) + (SndMessage {msgId}, _) <- sendDirectContactMessage ct (XMsgUpdate itemSharedMId mc Nothing) updCi <- withStore $ \db -> updateDirectChatItem db userId contactId itemId (CISndMsgContent mc) $ Just msgId setActive $ ActiveC c pure . CRChatItemUpdated $ AChatItem SCTDirect SMDSnd (DirectChat ct) updCi @@ -437,7 +437,7 @@ processChatCommand = \case CChatItem SMDSnd ChatItem {meta = CIMeta {itemSharedMsgId}, content = ciContent} -> do case (ciContent, itemSharedMsgId) of (CISndMsgContent _, Just itemSharedMId) -> do - SndMessage {msgId} <- sendGroupMessage gInfo ms (XMsgUpdate itemSharedMId mc) + SndMessage {msgId} <- sendGroupMessage gInfo ms (XMsgUpdate itemSharedMId mc Nothing) updCi <- withStore $ \db -> updateGroupChatItem db user groupId itemId (CISndMsgContent mc) msgId setActive $ ActiveG gName pure . CRChatItemUpdated $ AChatItem SCTGroup SMDSnd (GroupChat gInfo) updCi @@ -866,7 +866,7 @@ processChatCommand = \case forM_ cts $ \ct -> void ( do - (sndMsg, _) <- sendDirectContactMessage ct (XMsgNew $ MCSimple (ExtMsgContent mc Nothing)) + (sndMsg, _) <- sendDirectContactMessage ct (XMsgNew $ MCSimple (extMsgContent mc Nothing)) saveSndChatItem user (CDDirectSnd ct) sndMsg (CISndMsgContent mc) Nothing Nothing ) `catchError` (toView . CRChatError) @@ -1792,7 +1792,7 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage = updateChatLock "directMessage" event case event of XMsgNew mc -> newContentMessage ct mc msg msgMeta - XMsgUpdate sharedMsgId mContent -> messageUpdate ct sharedMsgId mContent msg msgMeta + XMsgUpdate sharedMsgId mContent _ -> messageUpdate ct sharedMsgId mContent msg msgMeta XMsgDel sharedMsgId -> messageDelete ct sharedMsgId msg msgMeta -- TODO discontinue XFile XFile fInv -> processFileInvitation' ct fInv msg msgMeta @@ -1846,7 +1846,7 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage = withStore' (\db -> getUserContactLinkById db userId userContactLinkId) >>= \case Just (UserContactLink {autoAccept = Just AutoAccept {autoReply = mc_}}, groupId_) -> do forM_ mc_ $ \mc -> do - (msg, _) <- sendDirectContactMessage ct (XMsgNew $ MCSimple (ExtMsgContent mc Nothing)) + (msg, _) <- sendDirectContactMessage ct (XMsgNew $ MCSimple (extMsgContent mc Nothing)) ci <- saveSndChatItem user (CDDirectSnd ct) msg (CISndMsgContent mc) Nothing Nothing toView . CRNewChatItem $ AChatItem SCTDirect SMDSnd (DirectChat ct) ci forM_ groupId_ $ \groupId -> do @@ -1998,7 +1998,7 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage = withAckMessage agentConnId cmdId msgMeta $ case event of XMsgNew mc -> newGroupContentMessage gInfo m mc msg msgMeta - XMsgUpdate sharedMsgId mContent -> groupMessageUpdate gInfo m sharedMsgId mContent msg msgMeta + XMsgUpdate sharedMsgId mContent _ -> groupMessageUpdate gInfo m sharedMsgId mContent msg msgMeta XMsgDel sharedMsgId -> groupMessageDelete gInfo m sharedMsgId msg -- TODO discontinue XFile XFile fInv -> processGroupFileInvitation' gInfo m fInv msg msgMeta @@ -2302,7 +2302,7 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage = newContentMessage ct@Contact {localDisplayName = c, contactUsed, chatSettings} mc msg msgMeta = do unless contactUsed $ withStore' $ \db -> updateContactUsed db user ct checkIntegrityCreateItem (CDDirectRcv ct) msgMeta - let ExtMsgContent content fileInvitation_ = mcExtMsgContent mc + let ExtMsgContent content fileInvitation_ _ _ = mcExtMsgContent mc if isVoice content && not (featureAllowed SCFVoice forContact ct) then do void $ newChatItem (CIRcvChatFeatureRejected CFVoice) Nothing @@ -2371,7 +2371,7 @@ processAgentMessage (Just user@User {userId}) corrId agentConnId agentMessage = newGroupContentMessage :: GroupInfo -> GroupMember -> MsgContainer -> RcvMessage -> MsgMeta -> m () newGroupContentMessage gInfo@GroupInfo {chatSettings} m@GroupMember {localDisplayName = c} mc msg msgMeta = do - let (ExtMsgContent content fInv_) = mcExtMsgContent mc + let (ExtMsgContent content fInv_ _ _) = mcExtMsgContent mc if isVoice content && not (groupFeatureAllowed SGFVoice gInfo) then void $ newChatItem (CIRcvGroupFeatureRejected GFVoice) Nothing else do diff --git a/src/Simplex/Chat/Protocol.hs b/src/Simplex/Chat/Protocol.hs index f45d8a07c6..0b7a31a662 100644 --- a/src/Simplex/Chat/Protocol.hs +++ b/src/Simplex/Chat/Protocol.hs @@ -179,7 +179,7 @@ instance StrEncoding AChatMessage where data ChatMsgEvent (e :: MsgEncoding) where XMsgNew :: MsgContainer -> ChatMsgEvent 'Json - XMsgUpdate :: SharedMsgId -> MsgContent -> ChatMsgEvent 'Json + XMsgUpdate :: {msgId :: SharedMsgId, content :: MsgContent, live :: Maybe Bool} -> ChatMsgEvent 'Json XMsgDel :: SharedMsgId -> ChatMsgEvent 'Json XMsgDeleted :: ChatMsgEvent 'Json XFile :: FileInvitation -> ChatMsgEvent 'Json -- TODO discontinue @@ -356,7 +356,7 @@ msgContentTag = \case MCFile {} -> MCFile_ MCUnknown {tag} -> MCUnknown_ tag -data ExtMsgContent = ExtMsgContent MsgContent (Maybe FileInvitation) +data ExtMsgContent = ExtMsgContent {content :: MsgContent, file :: Maybe FileInvitation, ttl :: Maybe Int, live :: Maybe Bool} deriving (Eq, Show) parseMsgContainer :: J.Object -> JT.Parser MsgContainer @@ -365,7 +365,10 @@ parseMsgContainer v = <|> (v .: "forward" >>= \f -> (if f then MCForward else MCSimple) <$> mc) <|> MCSimple <$> mc where - mc = ExtMsgContent <$> v .: "content" <*> v .:? "file" + mc = ExtMsgContent <$> v .: "content" <*> v .:? "file" <*> v .:? "ttl" <*> v .:? "live" + +extMsgContent :: MsgContent -> Maybe FileInvitation -> ExtMsgContent +extMsgContent mc file = ExtMsgContent mc file Nothing Nothing instance FromJSON MsgContent where parseJSON (J.Object v) = @@ -395,13 +398,12 @@ unknownMsgType = "unknown message type" msgContainerJSON :: MsgContainer -> J.Object msgContainerJSON = \case - MCQuote qm (ExtMsgContent c file) -> JM.fromList $ withFile ["quote" .= qm, "content" .= c] file - MCForward (ExtMsgContent c file) -> JM.fromList $ withFile ["forward" .= True, "content" .= c] file - MCSimple (ExtMsgContent c file) -> JM.fromList $ withFile ["content" .= c] file + MCQuote qm mc -> o $ ("quote" .= qm) : msgContent mc + MCForward mc -> o $ ("forward" .= True) : msgContent mc + MCSimple mc -> o $ msgContent mc where - withFile l = \case - Nothing -> l - Just f -> l <> ["file" .= f] + o = JM.fromList + msgContent (ExtMsgContent c file ttl live) = ("file" .=? file) $ ("ttl" .=? ttl) $ ("live" .=? live) ["content" .= c] instance ToJSON MsgContent where toJSON = \case @@ -551,7 +553,7 @@ instance StrEncoding ACMEventTag where toCMEventTag :: ChatMsgEvent e -> CMEventTag e toCMEventTag msg = case msg of XMsgNew _ -> XMsgNew_ - XMsgUpdate _ _ -> XMsgUpdate_ + XMsgUpdate {} -> XMsgUpdate_ XMsgDel _ -> XMsgDel_ XMsgDeleted -> XMsgDeleted_ XFile _ -> XFile_ @@ -636,7 +638,7 @@ appJsonToCM AppMessageJson {msgId, event, params} = do msg :: CMEventTag 'Json -> Either String (ChatMsgEvent 'Json) msg = \case XMsgNew_ -> XMsgNew <$> JT.parseEither parseMsgContainer params - XMsgUpdate_ -> XMsgUpdate <$> p "msgId" <*> p "content" + XMsgUpdate_ -> XMsgUpdate <$> p "msgId" <*> p "content" <*> opt "live" XMsgDel_ -> XMsgDel <$> p "msgId" XMsgDeleted_ -> pure XMsgDeleted XFile_ -> XFile <$> p "file" @@ -670,6 +672,9 @@ appJsonToCM AppMessageJson {msgId, event, params} = do XOk_ -> pure XOk XUnknown_ t -> pure $ XUnknown t params +(.=?) :: ToJSON v => JT.Key -> Maybe v -> [(J.Key, J.Value)] -> [(J.Key, J.Value)] +key .=? value = maybe id ((:) . (key .=)) value + chatToAppMessage :: forall e. MsgEncodingI e => ChatMessage e -> AppMessage e chatToAppMessage ChatMessage {msgId, chatMsgEvent} = case encoding @e of SBinary -> @@ -680,14 +685,13 @@ chatToAppMessage ChatMessage {msgId, chatMsgEvent} = case encoding @e of tag = toCMEventTag chatMsgEvent o :: [(J.Key, J.Value)] -> J.Object o = JM.fromList - key .=? value = maybe id ((:) . (key .=)) value toBody :: ChatMsgEvent 'Binary -> (Maybe SharedMsgId, ByteString) toBody = \case BFileChunk (SharedMsgId msgId') chunk -> (Nothing, smpEncode (msgId', IFC chunk)) params :: ChatMsgEvent 'Json -> J.Object params = \case XMsgNew container -> msgContainerJSON container - XMsgUpdate msgId' content -> o ["msgId" .= msgId', "content" .= content] + XMsgUpdate msgId' content live -> o $ ("live" .=? live) ["msgId" .= msgId', "content" .= content] XMsgDel msgId' -> o ["msgId" .= msgId'] XMsgDeleted -> JM.empty XFile fileInv -> o ["file" .= fileInv] diff --git a/tests/ProtocolTests.hs b/tests/ProtocolTests.hs index 87a8998d8c..829c4b3880 100644 --- a/tests/ProtocolTests.hs +++ b/tests/ProtocolTests.hs @@ -53,6 +53,12 @@ testE2ERatchetParams = E2ERatchetParamsUri supportedE2EEncryptVRange testDhPubKe testConnReq :: ConnectionRequestUri 'CMInvitation testConnReq = CRInvitationUri connReqData testE2ERatchetParams +quotedMsg :: QuotedMsg +quotedMsg = + QuotedMsg + (MsgRef (Just $ SharedMsgId "\5\6\7\8") (systemToUTCTime $ MkSystemTime 1 1) True Nothing) + $ MCText "hello there!" + (==##) :: MsgEncodingI e => ByteString -> ChatMessage e -> Expectation s ==## msg = do strDecode s `shouldBe` Right msg @@ -95,55 +101,63 @@ decodeChatMessageTest :: Spec decodeChatMessageTest = describe "Chat message encoding/decoding" $ do it "x.msg.new simple text" $ "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" - #==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing)) + #==# XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing)) + it "x.msg.new simple text - timed message TTL" $ + "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"ttl\":3600}}" + #==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing (Just 3600) Nothing)) + it "x.msg.new simple text - live message" $ + "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"live\":true}}" + #==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing Nothing (Just True))) it "x.msg.new simple link" $ "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"https://simplex.chat\",\"type\":\"link\",\"preview\":{\"description\":\"SimpleX Chat\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA\",\"title\":\"SimpleX Chat\",\"uri\":\"https://simplex.chat\"}}}}" - #==# XMsgNew (MCSimple (ExtMsgContent (MCLink "https://simplex.chat" $ LinkPreview {uri = "https://simplex.chat", title = "SimpleX Chat", description = "SimpleX Chat", image = ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA"}) Nothing)) + #==# XMsgNew (MCSimple (extMsgContent (MCLink "https://simplex.chat" $ LinkPreview {uri = "https://simplex.chat", title = "SimpleX Chat", description = "SimpleX Chat", image = ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgA"}) Nothing)) it "x.msg.new simple image" $ "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}}" - #==# XMsgNew (MCSimple (ExtMsgContent (MCImage "" $ ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=") Nothing)) + #==# XMsgNew (MCSimple (extMsgContent (MCImage "" $ ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=") Nothing)) it "x.msg.new simple image with text" $ "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"here's an image\",\"type\":\"image\",\"image\":\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=\"}}}" - #==# XMsgNew (MCSimple (ExtMsgContent (MCImage "here's an image" $ ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=") Nothing)) + #==# XMsgNew (MCSimple (extMsgContent (MCImage "here's an image" $ ImageData "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAIAQMAAAD+wSzIAAAABlBMVEX///+/v7+jQ3Y5AAAADklEQVQI12P4AIX8EAgALgAD/aNpbtEAAAAASUVORK5CYII=") Nothing)) it "x.msg.new chat message " $ "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" - ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (ExtMsgContent (MCText "hello") Nothing))) + ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew (MCSimple (extMsgContent (MCText "hello") Nothing))) it "x.msg.new quote" $ "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}}}}" ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") - ( XMsgNew - ( MCQuote - ( QuotedMsg - (MsgRef (Just $ SharedMsgId "\5\6\7\8") (systemToUTCTime $ MkSystemTime 1 1) True Nothing) - $ MCText "hello there!" - ) - ( ExtMsgContent - (MCText "hello to you too") - Nothing - ) - ) - ) + (XMsgNew (MCQuote quotedMsg (extMsgContent (MCText "hello to you too") Nothing))) + it "x.msg.new quote - timed message TTL" $ + "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"ttl\":3600}}" + ##==## ChatMessage + (Just $ SharedMsgId "\1\2\3\4") + (XMsgNew (MCQuote quotedMsg (ExtMsgContent (MCText "hello to you too") Nothing (Just 3600) Nothing))) + it "x.msg.new quote - live message" $ + "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"live\":true}}" + ##==## ChatMessage + (Just $ SharedMsgId "\1\2\3\4") + (XMsgNew (MCQuote quotedMsg (ExtMsgContent (MCText "hello to you too") Nothing Nothing (Just True)))) it "x.msg.new forward" $ "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true}}" - ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing)) + ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (extMsgContent (MCText "hello") Nothing)) + it "x.msg.new forward - timed message TTL" $ + "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"ttl\":3600}}" + ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing (Just 3600) Nothing)) + it "x.msg.new forward - live message" $ + "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"live\":true}}" + ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") Nothing Nothing (Just True))) it "x.msg.new simple text with file" $ "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}" - #==# XMsgNew (MCSimple (ExtMsgContent (MCText "hello") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}))) + #==# XMsgNew (MCSimple (extMsgContent (MCText "hello") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}))) it "x.msg.new simple file with file" $ "{\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"\",\"type\":\"file\"},\"file\":{\"fileSize\":12345,\"fileName\":\"file.txt\"}}}" - #==# XMsgNew (MCSimple (ExtMsgContent (MCFile "") (Just FileInvitation {fileName = "file.txt", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}))) + #==# XMsgNew (MCSimple (extMsgContent (MCFile "") (Just FileInvitation {fileName = "file.txt", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}))) it "x.msg.new quote with file" $ "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello to you too\",\"type\":\"text\"},\"quote\":{\"content\":{\"text\":\"hello there!\",\"type\":\"text\"},\"msgRef\":{\"msgId\":\"BQYHCA==\",\"sent\":true,\"sentAt\":\"1970-01-01T00:00:01.000000001Z\"}},\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}" ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") ( XMsgNew ( MCQuote - ( QuotedMsg - (MsgRef (Just $ SharedMsgId "\5\6\7\8") (systemToUTCTime $ MkSystemTime 1 1) True Nothing) - $ MCText "hello there!" - ) - ( ExtMsgContent + quotedMsg + ( extMsgContent (MCText "hello to you too") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}) ) @@ -151,10 +165,10 @@ decodeChatMessageTest = describe "Chat message encoding/decoding" $ do ) it "x.msg.new forward with file" $ "{\"msgId\":\"AQIDBA==\",\"event\":\"x.msg.new\",\"params\":{\"content\":{\"text\":\"hello\",\"type\":\"text\"},\"forward\":true,\"file\":{\"fileSize\":12345,\"fileName\":\"photo.jpg\"}}}" - ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (ExtMsgContent (MCText "hello") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}))) + ##==## ChatMessage (Just $ SharedMsgId "\1\2\3\4") (XMsgNew $ MCForward (extMsgContent (MCText "hello") (Just FileInvitation {fileName = "photo.jpg", fileSize = 12345, fileConnReq = Nothing, fileInline = Nothing}))) it "x.msg.update" $ "{\"event\":\"x.msg.update\",\"params\":{\"msgId\":\"AQIDBA==\", \"content\":{\"text\":\"hello\",\"type\":\"text\"}}}" - #==# XMsgUpdate (SharedMsgId "\1\2\3\4") (MCText "hello") + #==# XMsgUpdate (SharedMsgId "\1\2\3\4") (MCText "hello") Nothing it "x.msg.del" $ "{\"event\":\"x.msg.del\",\"params\":{\"msgId\":\"AQIDBA==\"}}" #==# XMsgDel (SharedMsgId "\1\2\3\4")