Connect internal Credentials, Room, Event

WARNING: contains a few syntax errors - still work in progress
pull/1/head
Bram van den Heuvel 2023-03-01 15:58:40 +01:00
parent f88c9604dc
commit 7e345c2b05
8 changed files with 377 additions and 56 deletions

View File

@ -7,8 +7,13 @@ This file combines the internal functions with the API endpoints to create a ful
-} -}
import Dict
import Internal.Api.All as Api
import Internal.Room as Room import Internal.Room as Room
import Internal.Event as Event
import Internal.Values.Credentials as Internal import Internal.Values.Credentials as Internal
import Internal.Values.Event as IEvent
import Internal.Values.Room as IRoom
{-| You can consider the `Credentials` type as a large ring of keys, {-| You can consider the `Credentials` type as a large ring of keys,
@ -50,3 +55,71 @@ getRoomById roomId credentials =
, versions = Internal.getVersions credentials , versions = Internal.getVersions credentials
} }
) )
{-| Insert an internal room type into the credentials.
-}
insertInternalRoom : IRoom.Room -> Credentials -> Credentials
insertInternalRoom = Internal.insertRoom
{-| Internal a full room type into the credentials. -}
insertRoom : Room.Room -> Credentials -> Credentials
insertRoom = Room.internalValue >> insertInternalRoom
{-| Update the Credentials type with new values -}
updateWith : Api.CredUpdate -> Credentials -> Credentials
updateWith credUpdate credentials =
case credUpdate of
Api.MultipleUpdates updates ->
List.foldl updateWith credentials updates
Api.GetEvent input output ->
case getRoomById input.roomId credentials of
Just room ->
output
|> IEvent.initFromGetEvent
|> Room.addInternalEvent
|> (|>) room
|> insertRoom
|> (|>) credentials
Nothing ->
credentials
Api.JoinedMembersToRoom _ _ ->
credentials -- TODO
Api.MessageEventSent _ _ ->
credentials -- TODO
Api.StateEventSent _ _ ->
credentials -- TODO
Api.SyncUpdate input output ->
let
rooms =
output.rooms
|> Maybe.map .join
|> Maybe.withDefault Dict.empty
|> Dict.toList
|> List.map
(\(roomId, jroom)->
case getRoomById roomId credentials of
-- Update existing room
Just room ->
room
|> Room.internalValue
|> IRoom.addEvents
-- Add new room
Nothing ->
jroom
)
in
credentials
Api.UpdateAccessToken token ->
Internal.addAccessToken token credentials
Api.UpdateVersions versions ->
Internal.addVersions versions credentials

159
src/Internal/Event.elm Normal file
View File

@ -0,0 +1,159 @@
module Internal.Event exposing (..)
{-| This module represents the event type in the Matrix API.
Users can use this type to reply to events, to link them, look for other events,
resend other events or forward them elsewhere.
-}
import Internal.Api.GetEvent.Main as GetEvent
import Internal.Api.GetEvent.V1.SpecObjects as GetEventSO
import Internal.Api.PreApi.Objects.Versions as V
import Internal.Api.Sync.V1.SpecObjects as SyncSO
import Internal.Tools.LoginValues exposing (AccessToken)
import Internal.Tools.Timestamp exposing (Timestamp)
import Internal.Values.Event as Internal
import Json.Encode as E
{-| The central event type. This type will be used by the user and will be directly interacted with.
-}
type Event
= Event
{ event : Internal.Event
, accessToken : AccessToken
, baseUrl : String
, versions : Maybe V.Versions
}
{-| Using the credentials' background information and an internal event type,
create an interactive event type.
-}
init : { accessToken : AccessToken, baseUrl : String, versions : Maybe V.Versions } -> Internal.Event -> Event
init { accessToken, baseUrl, versions } event =
Event
{ event = event
, accessToken = accessToken
, baseUrl = baseUrl
, versions = versions
}
{-| Create an internal event type from an API endpoint event object.
This function is placed in this file to respect file hierarchy and avoid circular imports.
-}
initFromGetEvent : GetEvent.EventOutput -> Internal.Event
initFromGetEvent output =
Internal.init
{ content = output.content
, eventId = output.eventId
, originServerTs = output.originServerTs
, roomId = output.roomId
, sender = output.sender
, stateKey = output.stateKey
, contentType = output.contentType
, unsigned =
output.unsigned
|> Maybe.map
(\(GetEventSO.UnsignedData data) ->
{ age = data.age
, prevContent = data.prevContent
, redactedBecause = Maybe.map initFromGetEvent data.redactedBecause
, transactionId = data.transactionId
}
)
}
{-| Create an internal event type from an API endpoint event object.
This function is placed in this file to respect file hierarchy and avoid circular imports.
-}
initFromClientEventWithoutRoomId : String -> SyncSO.ClientEventWithoutRoomId -> Internal.Event
initFromClientEventWithoutRoomId rId output =
Internal.init
{ content = output.content
, eventId = output.eventId
, originServerTs = output.originServerTs
, roomId = rId
, sender = output.sender
, stateKey = output.stateKey
, contentType = output.contentType
, unsigned =
output.unsigned
|> Maybe.map
(\(SyncSO.UnsignedData data) ->
{ age = data.age
, prevContent = data.prevContent
, redactedBecause = Maybe.map (initFromClientEventWithoutRoomId roomId) data.redactedBecause
, transactionId = data.transactionId
}
)
}
{-| Get the internal event type that is hidden in the interactive event type.
-}
internalValue : Event -> Internal.Event
internalValue (Event { event }) =
event
{- GETTER FUNCTIONS -}
content : Event -> E.Value
content =
internalValue >> Internal.content
eventId : Event -> String
eventId =
internalValue >> Internal.eventId
originServerTs : Event -> Timestamp
originServerTs =
internalValue >> Internal.originServerTs
roomId : Event -> String
roomId =
internalValue >> Internal.roomId
sender : Event -> String
sender =
internalValue >> Internal.sender
stateKey : Event -> Maybe String
stateKey =
internalValue >> Internal.stateKey
contentType : Event -> String
contentType =
internalValue >> Internal.contentType
age : Event -> Maybe Int
age =
internalValue >> Internal.age
redactedBecause : Event -> Maybe Event
redactedBecause (Event data) =
data.event
|> Internal.redactedBecause
|> Maybe.map
(\event ->
Event { data | event = event }
)
transactionId : Event -> Maybe String
transactionId =
internalValue >> Internal.transactionId

View File

@ -5,14 +5,22 @@ module Internal.Room exposing (..)
import Internal.Api.All as Api import Internal.Api.All as Api
import Internal.Api.PreApi.Objects.Versions as V import Internal.Api.PreApi.Objects.Versions as V
import Internal.Event as Event exposing (Event)
import Internal.Tools.Exceptions as X import Internal.Tools.Exceptions as X
import Internal.Tools.LoginValues exposing (AccessToken) import Internal.Tools.LoginValues exposing (AccessToken)
import Internal.Values.Event as IEvent
import Internal.Values.Room as Internal import Internal.Values.Room as Internal
import Json.Encode as E import Json.Encode as E
import Task exposing (Task) import Task exposing (Task)
{-| The Room type. {-| The `Room` type represents a Matrix Room. It contains context information
such as the `accessToken` that allows the retrieval of new information from
the Matrix API if necessary.
The `Room` type contains utilities to inquire about the room and send messages
to it.
-} -}
type Room type Room
= Room = Room
@ -23,6 +31,24 @@ type Room
} }
{-| Adds an internal event to the `Room`. An internal event is a custom event
that has been generated by the client.
-}
addInternalEvent : IEvent.Event -> Room -> Room
addInternalEvent ievent (Room ({ room } as data)) =
Room { data | room = Internal.addEvent ievent room }
{-| Adds an `Event` object to the `Room`. An `Event` is a value from the
`Internal.Event` module that is used to represent an event in a Matrix room.
-}
addEvent : Event -> Room -> Room
addEvent =
Event.internalValue >> addInternalEvent
{-| Creates a new `Room` object with the given parameters.
-}
init : { accessToken : AccessToken, baseUrl : String, versions : Maybe V.Versions } -> Internal.Room -> Room init : { accessToken : AccessToken, baseUrl : String, versions : Maybe V.Versions } -> Internal.Room -> Room
init { accessToken, baseUrl, versions } room = init { accessToken, baseUrl, versions } room =
Room Room
@ -33,13 +59,22 @@ init { accessToken, baseUrl, versions } room =
} }
{-| Get the room's id. {-| Retrieves the `Internal.Room` type contained within the given `Room`.
-}
internalValue : Room -> Internal.Room
internalValue (Room { room }) =
room
{-| Retrieves the ID of the Matrix room associated with the given `Room`.
-} -}
roomId : Room -> String roomId : Room -> String
roomId (Room { room }) = roomId =
Internal.roomId room internalValue >> Internal.roomId
{-| Sends a new event to the Matrix room associated with the given `Room`.
-}
sendEvent : Room -> { eventType : String, content : E.Value } -> Task X.Error Api.CredUpdate sendEvent : Room -> { eventType : String, content : E.Value } -> Task X.Error Api.CredUpdate
sendEvent (Room { room, accessToken, baseUrl, versions }) { eventType, content } = sendEvent (Room { room, accessToken, baseUrl, versions }) { eventType, content } =
Api.sendMessageEvent Api.sendMessageEvent
@ -53,6 +88,8 @@ sendEvent (Room { room, accessToken, baseUrl, versions }) { eventType, content }
} }
{-| Sends a new text message to the Matrix room associated with the given `Room`.
-}
sendMessage : Room -> String -> Task X.Error Api.CredUpdate sendMessage : Room -> String -> Task X.Error Api.CredUpdate
sendMessage (Room { room, accessToken, baseUrl, versions }) text = sendMessage (Room { room, accessToken, baseUrl, versions }) text =
Api.sendMessageEvent Api.sendMessageEvent

View File

@ -21,6 +21,17 @@ empty hash =
Hashdict { hash = hash, values = Dict.empty } Hashdict { hash = hash, values = Dict.empty }
fromList : (a -> String) -> List a -> Hashdict a
fromList hash xs =
Hashdict
{ hash = hash
, values =
xs
|> List.map (\x -> ( hash x, x ))
|> Dict.fromList
}
get : String -> Hashdict a -> Maybe a get : String -> Hashdict a -> Maybe a
get k (Hashdict h) = get k (Hashdict h) =
Dict.get k h.values Dict.get k h.values
@ -36,6 +47,14 @@ keys (Hashdict h) =
Dict.keys h.values Dict.keys h.values
union : Hashdict a -> Hashdict a -> Hashdict a
union (Hashdict h1) (Hashdict h2) =
Hashdict
{ hash = h1.hash
, values = Dict.union h1.values h2.values
}
values : Hashdict a -> List a values : Hashdict a -> List a
values (Hashdict h) = values (Hashdict h) =
Dict.values h.values Dict.values h.values

View File

@ -22,23 +22,23 @@ HTTP call that needs that value.
import Task exposing (Task) import Task exposing (Task)
{-| A ValueGetter type takes care of values that MIGHT be available. {-| A ValueGetter x type takes care of values that MIGHT be available.
If a value is not available, then the task can be used to get a new value. If a value is not available, then the task can be used to get a new value.
-} -}
type alias ValueGetter a = type alias ValueGetter x a =
{ value : Maybe a, getValue : Task x a } { value : Maybe a, getValue : Task x a }
{-| Convert a `ValueGetter` type to a task. If a previous value has already been given, {-| Convert a `ValueGetter` type to a task. If a previous value has already been given,
then use that value. Otherwise, use the `getValue` task to get a new value. then use that value. Otherwise, use the `getValue` task to get a new value.
-} -}
toTask : ValueGetter a -> Task x a toTask : ValueGetter x a -> Task x a
toTask { value, getValue } = toTask { value, getValue } =
Maybe.map Task.succeed value Maybe.map Task.succeed value
|> Maybe.withDefault getValue |> Maybe.withDefault getValue
withInfo : (a -> Task x result) -> ValueGetter a -> Task x result withInfo : (a -> Task x result) -> ValueGetter x a -> Task x result
withInfo task info1 = withInfo task info1 =
Task.andThen Task.andThen
(\a -> (\a ->
@ -49,8 +49,8 @@ withInfo task info1 =
withInfo2 : withInfo2 :
(a -> b -> Task x result) (a -> b -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> Task x result -> Task x result
withInfo2 task info1 info2 = withInfo2 task info1 info2 =
Task.andThen Task.andThen
@ -66,9 +66,9 @@ withInfo2 task info1 info2 =
withInfo3 : withInfo3 :
(a -> b -> c -> Task x result) (a -> b -> c -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> ValueGetter c -> ValueGetter x c
-> Task x result -> Task x result
withInfo3 task info1 info2 info3 = withInfo3 task info1 info2 info3 =
Task.andThen Task.andThen
@ -88,10 +88,10 @@ withInfo3 task info1 info2 info3 =
withInfo4 : withInfo4 :
(a -> b -> c -> d -> Task x result) (a -> b -> c -> d -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> ValueGetter c -> ValueGetter x c
-> ValueGetter d -> ValueGetter x d
-> Task x result -> Task x result
withInfo4 task info1 info2 info3 info4 = withInfo4 task info1 info2 info3 info4 =
Task.andThen Task.andThen
@ -115,11 +115,11 @@ withInfo4 task info1 info2 info3 info4 =
withInfo5 : withInfo5 :
(a -> b -> c -> d -> e -> Task x result) (a -> b -> c -> d -> e -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> ValueGetter c -> ValueGetter x c
-> ValueGetter d -> ValueGetter x d
-> ValueGetter e -> ValueGetter x e
-> Task x result -> Task x result
withInfo5 task info1 info2 info3 info4 info5 = withInfo5 task info1 info2 info3 info4 info5 =
Task.andThen Task.andThen
@ -147,12 +147,12 @@ withInfo5 task info1 info2 info3 info4 info5 =
withInfo6 : withInfo6 :
(a -> b -> c -> d -> e -> f -> Task x result) (a -> b -> c -> d -> e -> f -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> ValueGetter c -> ValueGetter x c
-> ValueGetter d -> ValueGetter x d
-> ValueGetter e -> ValueGetter x e
-> ValueGetter f -> ValueGetter x f
-> Task x result -> Task x result
withInfo6 task info1 info2 info3 info4 info5 info6 = withInfo6 task info1 info2 info3 info4 info5 info6 =
Task.andThen Task.andThen
@ -184,13 +184,13 @@ withInfo6 task info1 info2 info3 info4 info5 info6 =
withInfo7 : withInfo7 :
(a -> b -> c -> d -> e -> f -> g -> Task x result) (a -> b -> c -> d -> e -> f -> g -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> ValueGetter c -> ValueGetter x c
-> ValueGetter d -> ValueGetter x d
-> ValueGetter e -> ValueGetter x e
-> ValueGetter f -> ValueGetter x f
-> ValueGetter g -> ValueGetter x g
-> Task x result -> Task x result
withInfo7 task info1 info2 info3 info4 info5 info6 info7 = withInfo7 task info1 info2 info3 info4 info5 info6 info7 =
Task.andThen Task.andThen
@ -226,14 +226,14 @@ withInfo7 task info1 info2 info3 info4 info5 info6 info7 =
withInfo8 : withInfo8 :
(a -> b -> c -> d -> e -> f -> g -> h -> Task x result) (a -> b -> c -> d -> e -> f -> g -> h -> Task x result)
-> ValueGetter a -> ValueGetter x a
-> ValueGetter b -> ValueGetter x b
-> ValueGetter c -> ValueGetter x c
-> ValueGetter d -> ValueGetter x d
-> ValueGetter e -> ValueGetter x e
-> ValueGetter f -> ValueGetter x f
-> ValueGetter g -> ValueGetter x g
-> ValueGetter h -> ValueGetter x h
-> Task x result -> Task x result
withInfo8 task info1 info2 info3 info4 info5 info6 info7 info8 = withInfo8 task info1 info2 info3 info4 info5 info6 info7 info8 =
Task.andThen Task.andThen

View File

@ -14,6 +14,20 @@ type Credentials
= Credentials { access : AccessToken, baseUrl : String, rooms : Hashdict Room, versions : Maybe V.Versions } = Credentials { access : AccessToken, baseUrl : String, rooms : Hashdict Room, versions : Maybe V.Versions }
{-| Add a new access token based on prior information.
-}
addAccessToken : String -> Credentials -> Credentials
addAccessToken token (Credentials ({ access } as data)) =
Credentials { data | access = Login.addToken token access }
{-| Add the list of versions that is supported by the homeserver.
-}
addVersions : V.Versions -> Credentials -> Credentials
addVersions versions (Credentials data) =
Credentials { data | versions = Just versions }
{-| Get the stringed access token the Credentials type is using, if any. {-| Get the stringed access token the Credentials type is using, if any.
-} -}
getAccessToken : Credentials -> Maybe String getAccessToken : Credentials -> Maybe String

View File

@ -23,6 +23,27 @@ type Event
} }
init :
{ content : E.Value
, eventId : String
, originServerTs : Timestamp
, roomId : String
, sender : String
, stateKey : Maybe String
, contentType : String
, unsigned :
Maybe
{ age : Maybe Int
, prevContent : Maybe E.Value
, redactedBecause : Maybe Event
, transactionId : Maybe String
}
}
-> Event
init =
Event
{- GETTER FUNCTIONS -} {- GETTER FUNCTIONS -}
@ -74,12 +95,6 @@ redactedBecause (Event e) =
|> Maybe.andThen .redactedBecause |> Maybe.andThen .redactedBecause
age : Event -> Maybe Int
age (Event e) =
e.unsigned
|> Maybe.andThen .age
transactionId : Event -> Maybe String transactionId : Event -> Maybe String
transactionId (Event e) = transactionId (Event e) =
e.unsigned e.unsigned

View File

@ -1,6 +1,7 @@
module Internal.Values.Room exposing (..) module Internal.Values.Room exposing (..)
import Dict exposing (Dict) import Dict exposing (Dict)
import Internal.Tools.Hashdict as Hashdict exposing (Hashdict)
import Internal.Tools.SpecEnums exposing (SessionDescriptionType(..)) import Internal.Tools.SpecEnums exposing (SessionDescriptionType(..))
import Internal.Values.Event as Event exposing (BlindEvent, Event) import Internal.Values.Event as Event exposing (BlindEvent, Event)
import Internal.Values.StateManager exposing (StateManager) import Internal.Values.StateManager exposing (StateManager)
@ -12,12 +13,19 @@ type Room
= Room = Room
{ accountData : Dict String E.Value { accountData : Dict String E.Value
, ephemeral : List BlindEvent , ephemeral : List BlindEvent
, events : Dict String Event , events : Hashdict Event
, roomId : String , roomId : String
, timeline : Timeline , timeline : Timeline
} }
{-| Add the data of a single event to the hashdict of events.
-}
addEvent : Event -> Room -> Room
addEvent event (Room ({ events } as room)) =
Room { room | events = Hashdict.insert event events }
{-| Add new events as the most recent events. {-| Add new events as the most recent events.
-} -}
addEvents : addEvents :
@ -31,11 +39,7 @@ addEvents :
addEvents ({ events } as data) (Room room) = addEvents ({ events } as data) (Room room) =
Room Room
{ room { room
| events = | events = List.foldl Hashdict.insert room.events events
events
|> List.map (\e -> ( Event.eventId e, e ))
|> Dict.fromList
|> (\x -> Dict.union x room.events)
, timeline = Timeline.addNewEvents data room.timeline , timeline = Timeline.addNewEvents data room.timeline
} }
@ -44,7 +48,7 @@ addEvents ({ events } as data) (Room room) =
-} -}
getEventById : String -> Room -> Maybe Event getEventById : String -> Room -> Maybe Event
getEventById eventId (Room room) = getEventById eventId (Room room) =
Dict.get eventId room.events Hashdict.get eventId room.events
{-| Get the room's id. {-| Get the room's id.