|
|
|
@ -6,6 +6,7 @@ module Api exposing ( changePassword, ClientError(..) |
|
|
|
|
, postLogin, rawAccessToAPI, refreshAccessToken |
|
|
|
|
, ServerError(..), getWhoAmI |
|
|
|
|
) |
|
|
|
|
import Bytes as B |
|
|
|
|
import Dict |
|
|
|
|
import Http |
|
|
|
|
import Json.Decode as D |
|
|
|
@ -14,7 +15,7 @@ import Process |
|
|
|
|
import Task exposing (Task) |
|
|
|
|
import Time |
|
|
|
|
|
|
|
|
|
import SpecObjects |
|
|
|
|
import SpecObjects as S |
|
|
|
|
|
|
|
|
|
{----------------------------} |
|
|
|
|
{----- PUBLIC FUNCTIONS -----} |
|
|
|
@ -391,7 +392,7 @@ type ClientError |
|
|
|
|
| ServerDoesntFollowJSONSpec |
|
|
|
|
| ServerDoesntReturnBaseURL |
|
|
|
|
| CouldntGetTimestamp |
|
|
|
|
| InvalidInputAccordingToSpec |
|
|
|
|
| InvalidInputAccordingToSpec String |
|
|
|
|
|
|
|
|
|
{-| Potential error codes that the server may return. If the error is not a |
|
|
|
|
default one described in the Matrix Spec, it will be a `CustomServerError` |
|
|
|
@ -519,25 +520,23 @@ Gets discovery information about the domain. |
|
|
|
|
|
|
|
|
|
https://spec.matrix.org/v1.4/client-server-api/#getwell-knownmatrixclient |
|
|
|
|
-} |
|
|
|
|
getWellKnownMatrixClientTask : String -> Task CommunicationError |
|
|
|
|
{ baseURL : String |
|
|
|
|
, identityServer : Maybe String |
|
|
|
|
} |
|
|
|
|
getWellKnownMatrixClientTask : String -> Task |
|
|
|
|
CommunicationError |
|
|
|
|
WellKnownMatrixClient |
|
|
|
|
getWellKnownMatrixClientTask hostName = |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "GET" |
|
|
|
|
, url = withURL hostName "/.well-known/matrix/client" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = |
|
|
|
|
D.map2 |
|
|
|
|
(\burl ids -> { baseURL = burl, identityServer = ids }) |
|
|
|
|
(D.at ["m.homeserver", "base_url"] D.string) |
|
|
|
|
(D.at ["m.identity_server", "base_url"] D.string |
|
|
|
|
|> D.maybe |
|
|
|
|
) |
|
|
|
|
, decoder = wellKnownMatrixClientDecoder |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type alias WellKnownMatrixClient = S.DiscoveryInformation |
|
|
|
|
|
|
|
|
|
wellKnownMatrixClientDecoder : D.Decoder WellKnownMatrixClient |
|
|
|
|
wellKnownMatrixClientDecoder = S.discoveryInformationDecoder |
|
|
|
|
|
|
|
|
|
{-| GET /_matrix/client/versions |
|
|
|
|
|
|
|
|
|
Gets the versions of the specification supported by the server. |
|
|
|
@ -546,24 +545,33 @@ https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientversions |
|
|
|
|
-} |
|
|
|
|
getSpecVersionSupportTask : String -> Task |
|
|
|
|
CommunicationError |
|
|
|
|
{ versions : List String |
|
|
|
|
, unstableFeatures : Dict.Dict |
|
|
|
|
String |
|
|
|
|
Bool |
|
|
|
|
} |
|
|
|
|
SpecVersionSupport |
|
|
|
|
getSpecVersionSupportTask baseURL = |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "GET" |
|
|
|
|
, url = withURL baseURL "/_matrix/client/versions" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = |
|
|
|
|
D.map2 |
|
|
|
|
(\ver unF -> { versions = ver, unstableFeatures = unF }) |
|
|
|
|
(D.field "versions" (D.list D.string)) |
|
|
|
|
(D.field "unstable_features" (D.dict D.bool)) |
|
|
|
|
, decoder = specVersionSupportDecoder |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
type alias SpecVersionSupport = { versions : List String |
|
|
|
|
, unstableFeatures : Dict.Dict |
|
|
|
|
String |
|
|
|
|
Bool |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
specVersionSupportDecoder : D.Decoder SpecVersionSupport |
|
|
|
|
specVersionSupportDecoder = |
|
|
|
|
D.map2 |
|
|
|
|
(\ver unF -> |
|
|
|
|
{ versions = ver |
|
|
|
|
, unstableFeatures = Maybe.withDefault Dict.empty unF |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "versions" (D.list D.string) ) |
|
|
|
|
(D.maybe <| D.field "unstable_features" (D.dict D.bool) ) |
|
|
|
|
|
|
|
|
|
{-| GET /_matrix/client/v1/register/m.login.registration_token/validity |
|
|
|
|
|
|
|
|
|
Queries the server to determine if a given registration token is still valid |
|
|
|
@ -579,16 +587,37 @@ getRegistrationTokenValidityTask : { baseURL : String |
|
|
|
|
CommunicationError |
|
|
|
|
Bool |
|
|
|
|
getRegistrationTokenValidityTask data = |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "GET" |
|
|
|
|
, url = "https://" ++ data.baseURL ++ |
|
|
|
|
"/_matrix/client/v1/register/m.login.registration_token" ++ |
|
|
|
|
"/validity?token=" ++ data.token |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = D.field "valid" D.bool |
|
|
|
|
} |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
( |
|
|
|
|
if S.validateCharacters "A-Za-z0-9._~-" data.token then |
|
|
|
|
if String.length data.token <= 64 then |
|
|
|
|
Task.succeed () |
|
|
|
|
else |
|
|
|
|
InvalidInputAccordingToSpec "Invalid token: too long" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
else |
|
|
|
|
InvalidInputAccordingToSpec "Invalid token: invalid characters" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
) |
|
|
|
|
|> Task.andThen |
|
|
|
|
(\_ -> |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "GET" |
|
|
|
|
, url = "https://" ++ data.baseURL ++ |
|
|
|
|
"/_matrix/client/v1/register/m.login.registration_token" ++ |
|
|
|
|
"/validity?token=" ++ data.token |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = registrationTokenValidityDecoder |
|
|
|
|
} |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type alias RegistrationTokenValidity = Bool |
|
|
|
|
|
|
|
|
|
registrationTokenValidityDecoder : D.Decoder RegistrationTokenValidity |
|
|
|
|
registrationTokenValidityDecoder = D.field "valid" D.bool |
|
|
|
|
|
|
|
|
|
{-| GET /_matrix/client/v3/login |
|
|
|
|
|
|
|
|
@ -599,26 +628,26 @@ https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3login |
|
|
|
|
-} |
|
|
|
|
getLoginTypesTask : { baseURL : String |
|
|
|
|
, timeoutAfter : Int |
|
|
|
|
} -> Task CommunicationError (Maybe (List (Maybe String))) |
|
|
|
|
} -> Task CommunicationError LoginTypes |
|
|
|
|
getLoginTypesTask data = |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "GET" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/login" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = |
|
|
|
|
-- NOTE: It does not make sense that the `flows` key would not be |
|
|
|
|
-- returned. I might write an MSC for this in the near future. |
|
|
|
|
-- D.field "flows" (D.list (D.field "type" D.string)) |
|
|
|
|
D.string |
|
|
|
|
|> D.field "type" |
|
|
|
|
|> D.maybe |
|
|
|
|
|> D.list |
|
|
|
|
|> D.field "flows" |
|
|
|
|
|> D.maybe |
|
|
|
|
, decoder = loginTypesDecoder |
|
|
|
|
} |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
|
|
|
|
|
type alias LoginTypes = List S.LoginFlow |
|
|
|
|
|
|
|
|
|
loginTypesDecoder : D.Decoder LoginTypes |
|
|
|
|
loginTypesDecoder = |
|
|
|
|
D.list S.loginFlowDecoder |
|
|
|
|
|> D.field "flows" |
|
|
|
|
|> D.maybe |
|
|
|
|
|> D.map (Maybe.withDefault []) |
|
|
|
|
|
|
|
|
|
{-| POST /_matrix/client/v3/login |
|
|
|
|
|
|
|
|
|
Authenticates the user, and issues an access token they can use to authorize |
|
|
|
@ -641,24 +670,23 @@ postLoginTask : { baseURL : String |
|
|
|
|
, token : Maybe String |
|
|
|
|
, loginType : String |
|
|
|
|
, timeoutAfter : Int |
|
|
|
|
} -> Task |
|
|
|
|
CommunicationError |
|
|
|
|
{ access_token : String |
|
|
|
|
, device_id : String |
|
|
|
|
, expires_at : Maybe Time.Posix |
|
|
|
|
, refresh_token : Maybe String |
|
|
|
|
, user_id : String |
|
|
|
|
, well_known_baseURL : String |
|
|
|
|
, well_known_identityServer : Maybe String |
|
|
|
|
} |
|
|
|
|
} -> Task CommunicationError Login |
|
|
|
|
postLoginTask data = |
|
|
|
|
( case (data.loginType, data.token, data.password) of |
|
|
|
|
("m.login.password", _, Just _) -> |
|
|
|
|
Time.now |
|
|
|
|
("m.login.token", Just _, _) -> |
|
|
|
|
Time.now |
|
|
|
|
("m.login.password", _, Nothing) -> |
|
|
|
|
InvalidInputAccordingToSpec "Invalid login type" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
("m.login.token", Nothing, _) -> |
|
|
|
|
InvalidInputAccordingToSpec "Invalid login type" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
_ -> |
|
|
|
|
InvalidInputAccordingToSpec |
|
|
|
|
InvalidInputAccordingToSpec "Invalid login type" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
) |
|
|
|
@ -683,30 +711,42 @@ postLoginTask data = |
|
|
|
|
, ( "token" , Maybe.map E.string data.token ) |
|
|
|
|
, ( "type" , Just <| E.string data.loginType ) |
|
|
|
|
] |
|
|
|
|
, decoder = D.map7 |
|
|
|
|
(\a d e r u wb wi -> |
|
|
|
|
{ access_token = a |
|
|
|
|
, device_id = d |
|
|
|
|
, expires_at = e |
|
|
|
|
|> Maybe.map ((+) (Time.posixToMillis now)) |
|
|
|
|
|> Maybe.map Time.millisToPosix |
|
|
|
|
, refresh_token= r |
|
|
|
|
, user_id = u |
|
|
|
|
, well_known_baseURL = wb |
|
|
|
|
, well_known_identityServer = wi |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "access_token" D.string ) |
|
|
|
|
( D.field "device_id" D.string ) |
|
|
|
|
( D.maybe <| D.field "expires_in_ms" D.int ) |
|
|
|
|
( D.maybe <| D.field "refresh_token" D.string ) |
|
|
|
|
( D.field "user_id" D.string ) |
|
|
|
|
( D.at ["well_known", "m.homeserver", "base_url"] D.string ) |
|
|
|
|
( D.maybe <| D.at ["well_known", "m.homeserver", "base_url"] D.string ) |
|
|
|
|
, decoder = loginDecoder now |
|
|
|
|
} |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type alias Login = { access_token : String |
|
|
|
|
, device_id : String |
|
|
|
|
, expires_at : Maybe Time.Posix |
|
|
|
|
, refresh_token : Maybe String |
|
|
|
|
, user_id : String |
|
|
|
|
, well_known_baseURL : String |
|
|
|
|
, well_known_identityServer : Maybe String |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
loginDecoder : Time.Posix -> D.Decoder Login |
|
|
|
|
loginDecoder t = |
|
|
|
|
D.map7 |
|
|
|
|
(\a d e r u wb wi -> |
|
|
|
|
{ access_token = a |
|
|
|
|
, device_id = d |
|
|
|
|
, expires_at = e |
|
|
|
|
|> Maybe.map ((+) (Time.posixToMillis now)) |
|
|
|
|
|> Maybe.map Time.millisToPosix |
|
|
|
|
, refresh_token= r |
|
|
|
|
, user_id = u |
|
|
|
|
, well_known_baseURL = wb |
|
|
|
|
, well_known_identityServer = wi |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "access_token" D.string ) |
|
|
|
|
( D.field "device_id" D.string ) |
|
|
|
|
( D.maybe <| D.field "expires_in_ms" D.int ) |
|
|
|
|
( D.maybe <| D.field "refresh_token" D.string ) |
|
|
|
|
( D.field "user_id" D.string ) |
|
|
|
|
( D.at ["well_known", "m.homeserver", "base_url"] D.string ) |
|
|
|
|
( D.maybe <| D.at ["well_known", "m.homeserver", "base_url"] D.string ) |
|
|
|
|
|
|
|
|
|
{-| POST /_matrix/client/v3/refresh |
|
|
|
|
|
|
|
|
@ -732,10 +772,7 @@ refreshAccessTokenTask : { baseURL : String |
|
|
|
|
, refreshToken : String |
|
|
|
|
} -> Task |
|
|
|
|
CommunicationError |
|
|
|
|
{ accessToken : String |
|
|
|
|
, refreshToken : String |
|
|
|
|
, expiresAt : Maybe Time.Posix |
|
|
|
|
} |
|
|
|
|
RefreshAccessToken |
|
|
|
|
refreshAccessTokenTask data = |
|
|
|
|
-- NOTE: Fun fact, apparently it's not required to provide an access |
|
|
|
|
-- token in the body of the request. |
|
|
|
@ -762,28 +799,37 @@ refreshAccessTokenTask data = |
|
|
|
|
) |
|
|
|
|
] |
|
|
|
|
|> Just |
|
|
|
|
, decoder = |
|
|
|
|
D.map3 |
|
|
|
|
(\acs ref exp -> |
|
|
|
|
{ accessToken = acs |
|
|
|
|
, refreshToken = Maybe.withDefault |
|
|
|
|
data.refreshToken |
|
|
|
|
ref |
|
|
|
|
, expiresAt = Maybe.map |
|
|
|
|
(\t -> |
|
|
|
|
now |
|
|
|
|
|> Time.posixToMillis |
|
|
|
|
|> (+) t |
|
|
|
|
|> Time.millisToPosix |
|
|
|
|
) |
|
|
|
|
exp |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "access_token" D.string ) |
|
|
|
|
( D.maybe <| D.field "refresh_token" D.string ) |
|
|
|
|
( D.maybe <| D.field "expires_in_ms" D.int ) |
|
|
|
|
} |
|
|
|
|
, decoder = refreshAccessTokenDecoder data.refreshToken now |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
type alias RefreshAccessToken = |
|
|
|
|
{ accessToken : String |
|
|
|
|
, refreshToken : String |
|
|
|
|
, expiresAt : Maybe Time.Posix |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
refreshAccessTokenDecoder : String -> Time.Posix -> D.Decoder RefreshAccessToken |
|
|
|
|
refreshAccessTokenDecoder token now = |
|
|
|
|
D.map3 |
|
|
|
|
(\acs ref exp -> |
|
|
|
|
{ accessToken = acs |
|
|
|
|
, refreshToken = Maybe.withDefault |
|
|
|
|
token |
|
|
|
|
ref |
|
|
|
|
, expiresAt = Maybe.map |
|
|
|
|
(\t -> |
|
|
|
|
now |
|
|
|
|
|> Time.posixToMillis |
|
|
|
|
|> (+) t |
|
|
|
|
|> Time.millisToPosix |
|
|
|
|
) |
|
|
|
|
exp |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "access_token" D.string ) |
|
|
|
|
( D.maybe <| D.field "refresh_token" D.string ) |
|
|
|
|
( D.maybe <| D.field "expires_in_ms" D.int ) |
|
|
|
|
|
|
|
|
|
{-| POST /_matrix/client/v3/logout |
|
|
|
|
|
|
|
|
@ -802,8 +848,7 @@ logoutAccessTokenTask data = |
|
|
|
|
, method = "POST" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/logout" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = |
|
|
|
|
D.succeed () |
|
|
|
|
, decoder = D.succeed () |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{-| POST /_matrix/client/v3/logout/all |
|
|
|
@ -824,8 +869,7 @@ logoutAllAccessTokensTask data = |
|
|
|
|
, method = "POST" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/logout/all" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = |
|
|
|
|
D.succeed () |
|
|
|
|
, decoder = D.succeed () |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
{-| POST /_matrix/client/v3/account/deactivate |
|
|
|
@ -844,27 +888,39 @@ https://spec.matrix.org/v1.4/client-server-api/#post_matrixclientv3accountdeacti |
|
|
|
|
-} |
|
|
|
|
deactivateUserTask : { baseURL : String |
|
|
|
|
, accessToken : String |
|
|
|
|
, auth : Maybe S.AuthenticationData |
|
|
|
|
, idServer : Maybe String |
|
|
|
|
, timeoutAfter : Int |
|
|
|
|
} -> Task CommunicationError String |
|
|
|
|
} -> Task CommunicationError DeactivateUser |
|
|
|
|
deactivateUserTask data = |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = withAccessToken data.accessToken |
|
|
|
|
, method = "POST" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/account/deactivate" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = D.field "id_server_unbind_result" D.string |
|
|
|
|
, body = maybeObject |
|
|
|
|
[ ( "auth" |
|
|
|
|
, Maybe.map S.encodeAuthenticationData data.auth |
|
|
|
|
) |
|
|
|
|
, ( "id_server" |
|
|
|
|
, Maybe.map E.string data.idServer |
|
|
|
|
) |
|
|
|
|
] |
|
|
|
|
, decoder = deactivateUserDecoder |
|
|
|
|
} |
|
|
|
|
|> Task.andThen |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
|
|
|
|
|
type DeactivateUser = Success | NoSupport |
|
|
|
|
|
|
|
|
|
deactivateUserDecoder : D.Decoder DeactivateUser |
|
|
|
|
deactivateUserDecoder = |
|
|
|
|
D.field "id_server_unbind_result" D.string |
|
|
|
|
|> D.andThen |
|
|
|
|
(\result -> |
|
|
|
|
case result of |
|
|
|
|
"success" -> |
|
|
|
|
Task.succeed result |
|
|
|
|
"no-support" -> |
|
|
|
|
Task.succeed result |
|
|
|
|
_ -> |
|
|
|
|
Task.fail (SDKException ServerDoesntFollowJSONSpec) |
|
|
|
|
"success" -> D.succeed Success |
|
|
|
|
"no-support" -> D.succeed NoSupport |
|
|
|
|
_ -> D.fail "Invalid unbind indicator." |
|
|
|
|
) |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
|
|
|
|
|
{-| POST /_matrix/client/v3/account/password |
|
|
|
|
|
|
|
|
@ -925,40 +981,39 @@ validateEmailTask : { baseURL : String |
|
|
|
|
, idServer : Maybe String |
|
|
|
|
, nextLink : Maybe String |
|
|
|
|
, sendAttempt : Int |
|
|
|
|
} -> Task |
|
|
|
|
CommunicationError |
|
|
|
|
{ sid : String, submit_url : Maybe String } |
|
|
|
|
} -> Task CommunicationError S.RequestTokenResponse |
|
|
|
|
validateEmailTask data = |
|
|
|
|
( case (data.idAccessToken, data.idServer) of |
|
|
|
|
(Nothing, Just _) -> |
|
|
|
|
InvalidInputAccordingToSpec |
|
|
|
|
InvalidInputAccordingToSpec "ID Access token is required when an ID server is supplied" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
_ -> |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "POST" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/account/password/email/requestToken" |
|
|
|
|
, body = maybeObject |
|
|
|
|
[ ( "client_secret", Just <| E.string data.clientSecret ) |
|
|
|
|
, ( "email", Just <| E.string data.email ) |
|
|
|
|
, ( "id_access_token", Maybe.map E.string data.idAccessToken ) |
|
|
|
|
, ( "id_server", Maybe.map E.string data.idServer ) |
|
|
|
|
, ( "next_link", Maybe.map E.string data.nextLink ) |
|
|
|
|
, ( "send_attempt", Just <| E.int data.sendAttempt ) |
|
|
|
|
] |
|
|
|
|
, decoder = D.map2 |
|
|
|
|
(\sid surl -> { sid = sid, submit_url = surl }) |
|
|
|
|
( D.field "sid" D.string ) |
|
|
|
|
( D.maybe <| D.field "submit_url" D.string) |
|
|
|
|
} |
|
|
|
|
if S.validateCharacters "0-9a-zA-Z.=_-" data.clientSecret then |
|
|
|
|
if String.length data.clientSecret <= 255 then |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = [] |
|
|
|
|
, method = "POST" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/account/password/email/requestToken" |
|
|
|
|
, body = maybeObject |
|
|
|
|
[ ( "client_secret" , Just <| E.string data.clientSecret ) |
|
|
|
|
, ( "email" , Just <| E.string data.email ) |
|
|
|
|
, ( "id_access_token", Maybe.map E.string data.idAccessToken ) |
|
|
|
|
, ( "id_server" , Maybe.map E.string data.idServer ) |
|
|
|
|
, ( "next_link" , Maybe.map E.string data.nextLink ) |
|
|
|
|
, ( "send_attempt" , Just <| E.int data.sendAttempt ) |
|
|
|
|
] |
|
|
|
|
, decoder = S.requestTokenResponseDecoder |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
InvalidInputAccordingToSpec "Client secret is longer than allowed" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
else |
|
|
|
|
InvalidInputAccordingToSpec "Client secret contains invalid characters" |
|
|
|
|
|> SDKException |
|
|
|
|
|> Task.fail |
|
|
|
|
) |
|
|
|
|
-- -- TODO: Validate function output to see whether the session ID follows |
|
|
|
|
-- -- the spec of consisting entirely of the characters [0-9a-zA-Z.=_-] |
|
|
|
|
-- |> Task.andThen |
|
|
|
|
-- (\result -> |
|
|
|
|
|
|
|
|
|
-- ) |
|
|
|
|
|
|
|
|
|
{- POST /_matrix/client/v3/account/password/msisdn/requestToken |
|
|
|
|
|
|
|
|
@ -1058,30 +1113,32 @@ https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3accountwhoami |
|
|
|
|
getWhoAmITask : { baseURL : String |
|
|
|
|
, accessToken : String |
|
|
|
|
, timeoutAfter : Int |
|
|
|
|
} -> Task |
|
|
|
|
CommunicationError |
|
|
|
|
{ device_id : String |
|
|
|
|
, is_guest : Bool |
|
|
|
|
, user_id : String |
|
|
|
|
} |
|
|
|
|
} -> Task CommunicationError WhoAmI |
|
|
|
|
|
|
|
|
|
getWhoAmITask data = |
|
|
|
|
rawAccessToAPITask |
|
|
|
|
{ headers = withAccessToken data.accessToken |
|
|
|
|
, method = "GET" |
|
|
|
|
, url = withURL data.baseURL "/_matrix/client/v3/account/whoami" |
|
|
|
|
, body = Nothing |
|
|
|
|
, decoder = D.map3 |
|
|
|
|
(\dv gs uid -> { device_id = dv |
|
|
|
|
, is_guest = Maybe.withDefault False gs |
|
|
|
|
, user_id = uid |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "device_id" D.string ) |
|
|
|
|
( D.maybe <| D.field "is_guest" D.bool ) |
|
|
|
|
( D.field "user_id" D.string ) |
|
|
|
|
, decoder = whoAmIDecoder |
|
|
|
|
} |
|
|
|
|
|> retryDuringRatelimits data.timeoutAfter |
|
|
|
|
|
|
|
|
|
type alias WhoAmI = { device_id : String, is_guest : Bool, user_id : String } |
|
|
|
|
|
|
|
|
|
whoAmIDecoder : D.Decoder WhoAmI |
|
|
|
|
whoAmIDecoder = |
|
|
|
|
D.map3 |
|
|
|
|
(\dv gs uid -> { device_id = dv |
|
|
|
|
, is_guest = Maybe.withDefault False gs |
|
|
|
|
, user_id = uid |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
( D.field "device_id" D.string ) |
|
|
|
|
( D.maybe <| D.field "is_guest" D.bool ) |
|
|
|
|
( D.field "user_id" D.string ) |
|
|
|
|
|
|
|
|
|
{- GET /_matrix/client/v3/capabilities |
|
|
|
|
|
|
|
|
|
Gets information about the server’s supported feature set and other relevant capabilities. |
|
|
|
@ -1304,15 +1361,7 @@ decodeTaskAs decoder = |
|
|
|
|
-} |
|
|
|
|
maybeObject : List (String, Maybe E.Value) -> Maybe E.Value |
|
|
|
|
maybeObject = |
|
|
|
|
List.filterMap |
|
|
|
|
(\(name, value) -> |
|
|
|
|
case value of |
|
|
|
|
Just v -> |
|
|
|
|
Just (name, v) |
|
|
|
|
_ -> |
|
|
|
|
Nothing |
|
|
|
|
) |
|
|
|
|
>> E.object |
|
|
|
|
S.maybeObject |
|
|
|
|
>> Just |
|
|
|
|
|
|
|
|
|
{-| Raise an error when a specific error code is raised. |
|
|
|
|