From d03aea1f3f473c062a790156396134c719970d96 Mon Sep 17 00:00:00 2001 From: Bram van den Heuvel Date: Wed, 15 Mar 2023 19:39:48 +0100 Subject: [PATCH] Add exposed Room type --- src/Internal/Room.elm | 86 +++++++++++++++++++++++---- src/Matrix/Room.elm | 119 ++++++++++++++++++++++++++++++++++++++ src/Matrix/RoomInvite.elm | 1 - 3 files changed, 194 insertions(+), 12 deletions(-) create mode 100644 src/Matrix/Room.elm diff --git a/src/Internal/Room.elm b/src/Internal/Room.elm index feb737d..52d56f6 100644 --- a/src/Internal/Room.elm +++ b/src/Internal/Room.elm @@ -136,21 +136,58 @@ roomId = {-| Sends a new event to the Matrix room associated with the given `Room`. -} -sendEvent : Room -> { eventType : String, content : E.Value } -> Task X.Error VaultUpdate -sendEvent (Room { context, room }) { eventType, content } = - Api.sendMessageEvent - { content = content - , eventType = eventType - , extraTransactionNoise = "content-value:" - , roomId = Internal.roomId room - } - context +sendEvent : { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> Task X.Error VaultUpdate +sendEvent { eventType, content, stateKey } (Room { context, room }) = + case stateKey of + Nothing -> + Api.sendMessageEvent + { content = content + , eventType = eventType + , extraTransactionNoise = "send-one-message" + , roomId = Internal.roomId room + } + context + + Just s -> + Api.sendStateEvent + { content = content + , eventType = eventType + , stateKey = s + , roomId = Internal.roomId room + } + context + + +sendEvents : List { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> List (Task X.Error VaultUpdate) +sendEvents events (Room { context, room }) = + List.indexedMap Tuple.pair events + |> List.map + (\( i, { eventType, content, stateKey } ) -> + case stateKey of + Nothing -> + Api.sendMessageEvent + { content = content + , eventType = eventType + , extraTransactionNoise = "send-message-" ++ String.fromInt i + , roomId = Internal.roomId room + } + context + + Just s -> + Api.sendStateEvent + { content = content + , eventType = eventType + , stateKey = s + , roomId = Internal.roomId room + } + context + ) {-| Sends a new text message to the Matrix room associated with the given `Room`. -} -sendMessage : Room -> String -> Task X.Error VaultUpdate -sendMessage (Room { context, room }) text = +sendMessage : String -> Room -> Task X.Error VaultUpdate +sendMessage text (Room { context, room }) = Api.sendMessageEvent { content = E.object @@ -162,3 +199,30 @@ sendMessage (Room { context, room }) text = , roomId = Internal.roomId room } context + + +sendMessages : List String -> Room -> List (Task X.Error VaultUpdate) +sendMessages pieces (Room { context, room }) = + pieces + |> List.indexedMap Tuple.pair + |> List.map + (\( i, piece ) -> + Api.sendMessageEvent + { content = + E.object + [ ( "msgtype", E.string "m.text" ) + , ( "body", E.string piece ) + ] + , eventType = "m.room.message" + , extraTransactionNoise = "literal-message-" ++ String.fromInt i ++ ":" ++ piece + , roomId = Internal.roomId room + } + context + ) + + +{-| Leave this room. +-} +leave : Room -> Task X.Error VaultUpdate +leave ((Room { context }) as r) = + Api.leave { roomId = roomId r, reason = Nothing } context diff --git a/src/Matrix/Room.elm b/src/Matrix/Room.elm new file mode 100644 index 0000000..8c27802 --- /dev/null +++ b/src/Matrix/Room.elm @@ -0,0 +1,119 @@ +module Matrix.Room exposing (..) + +{-| -} + +import Internal.Api.VaultUpdate exposing (VaultUpdate) +import Internal.Event as Event +import Internal.Room as Internal +import Internal.Tools.Exceptions as X +import Json.Encode as E +import Task exposing (Task) + + +{-| The `Room` type represents a Matrix Room that the user has joined. +It contains context information that allows the retrieval of new information from +the Matrix API if necessary. +-} +type alias Room = + Internal.Room + + +{-| Get the most recent events from this room. +-} +mostRecentEvents : Room -> List Event.Event +mostRecentEvents = + Internal.mostRecentEvents + + +{-| Get the Matrix room id of a room. +-} +roomId : Room -> String +roomId = + Internal.roomId + + +{-| Send an unformatted text message to a room. + + task = + room + |> sendMessage "Hello, world!" + |> Task.attempt toMsg + +**Hint:** are you trying to send multiple messages at the same time? You might want to use `sendMessages` instead. + +-} +sendMessage : String -> Room -> Task X.Error VaultUpdate +sendMessage = + Internal.sendMessage + + +{-| Send multiple unformatted text messages to a room. + +**Why this function?** If you send the same message too quickly again, the Matrix API might get confused and think it's the same message. +This way, you will lose messages! + +If you're intending to send the same message multiple times, this function will emphasize that these messages are not the same. + + -- SAFE + Task.sequence [ sendMessage "Hello, world!", sendMessage "hi mom!" ] + + -- NOT SAFE + Task.sequence [ sendMessage "Hello, world!", sendMessage "Hello world!" ] + + -- SAFE + Task.sequence <| sendMessages [ "Hello, world!", "hi mom!" ] + + Task.sequence <| sendMessages [ "Hello, world!", "Hello, world!" ] + +-} +sendMessages : List String -> Room -> List (Task X.Error VaultUpdate) +sendMessages = + Internal.sendMessages + + +{-| Send a custom event to the Matrix room. + +Keep in mind that this function is not safe to use if you're sending exactly the same messages multiple times: + + -- SAFE + Task.sequence + [ sendOneEvent { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + , sendOneEvent { content = E.int 0, eventType = "com.example.foo", stateKey = Nothing } room + ] + + -- NOT SAFE + Task.sequence + [ sendOneEvent { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + , sendOneEvent { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + ] + +-} +sendOneEvent : { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> Task X.Error VaultUpdate +sendOneEvent = + Internal.sendEvent + + +{-| Send multiple events to the same room. + +If you send the same event twice to the same room too close together, the Matrix API will sometimes think that it's the same event. +This function ensures that every messages is treated separately. + +Keep in mind that this function doesn't send the events in order, it just makes them safe to send at the same time. + + -- NOT SAFE + [ sendOneEvent { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + , sendOneEvent { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + ] + |> Task.sequence + + -- SAFE + [ { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + , { content = E.object [], eventType = "com.example.foo", stateKey = Nothing } room + ] + |> sendMultipleEvents + |> Task.sequence + +-} +sendMultipleEvents : List { content : E.Value, eventType : String, stateKey : Maybe String } -> Room -> List (Task X.Error VaultUpdate) +sendMultipleEvents = + Internal.sendEvents diff --git a/src/Matrix/RoomInvite.elm b/src/Matrix/RoomInvite.elm index 50bffbe..88bcd3d 100644 --- a/src/Matrix/RoomInvite.elm +++ b/src/Matrix/RoomInvite.elm @@ -22,7 +22,6 @@ want to see. import Internal.Api.VaultUpdate exposing (VaultUpdate) import Internal.Invite as Internal -import Internal.Room exposing (Room) import Internal.Tools.Exceptions as X import Internal.Values.RoomInvite as IR import Json.Encode as E