diff --git a/client.go b/client.go index 6b60f9751..902056f3d 100644 --- a/client.go +++ b/client.go @@ -1043,6 +1043,22 @@ func (cli *Client) SetDisplayName(ctx context.Context, displayName string) (err return } +// UnstableSetProfileField sets an arbitrary MSC4133 profile field. See https://github.com/matrix-org/matrix-spec-proposals/pull/4133 +func (cli *Client) UnstableSetProfileField(ctx context.Context, key string, value any) (err error) { + urlPath := cli.BuildClientURL("unstable", "uk.tcpip.msc4133", "profile", cli.UserID, key) + _, err = cli.MakeRequest(ctx, http.MethodPut, urlPath, map[string]any{ + key: value, + }, nil) + return +} + +// UnstableDeleteProfileField deletes an arbitrary MSC4133 profile field. See https://github.com/matrix-org/matrix-spec-proposals/pull/4133 +func (cli *Client) UnstableDeleteProfileField(ctx context.Context, key string) (err error) { + urlPath := cli.BuildClientURL("unstable", "uk.tcpip.msc4133", "profile", cli.UserID, key) + _, err = cli.MakeRequest(ctx, http.MethodDelete, urlPath, nil, nil) + return +} + // GetAvatarURL gets the avatar URL of the user with the specified MXID. See https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseridavatar_url func (cli *Client) GetAvatarURL(ctx context.Context, mxid id.UserID) (url id.ContentURI, err error) { urlPath := cli.BuildClientURL("v3", "profile", mxid, "avatar_url") diff --git a/responses.go b/responses.go index 7312f0996..a2e5c2b86 100644 --- a/responses.go +++ b/responses.go @@ -4,6 +4,7 @@ import ( "bytes" "encoding/json" "fmt" + "maps" "reflect" "strconv" "strings" @@ -155,8 +156,44 @@ type RespUserDisplayName struct { } type RespUserProfile struct { - DisplayName string `json:"displayname"` - AvatarURL id.ContentURI `json:"avatar_url"` + DisplayName string `json:"displayname,omitempty"` + AvatarURL id.ContentURI `json:"avatar_url,omitempty"` + Extra map[string]any `json:"-"` +} + +type marshalableUserProfile RespUserProfile + +func (r *RespUserProfile) UnmarshalJSON(data []byte) error { + err := json.Unmarshal(data, &r.Extra) + if err != nil { + return err + } + r.DisplayName, _ = r.Extra["displayname"].(string) + avatarURL, _ := r.Extra["avatar_url"].(string) + if avatarURL != "" { + r.AvatarURL, _ = id.ParseContentURI(avatarURL) + } + delete(r.Extra, "displayname") + delete(r.Extra, "avatar_url") + return nil +} + +func (r *RespUserProfile) MarshalJSON() ([]byte, error) { + if len(r.Extra) == 0 { + return json.Marshal((*marshalableUserProfile)(r)) + } + marshalMap := maps.Clone(r.Extra) + if r.DisplayName != "" { + marshalMap["displayname"] = r.DisplayName + } else { + delete(marshalMap, "displayname") + } + if !r.AvatarURL.IsEmpty() { + marshalMap["avatar_url"] = r.AvatarURL.String() + } else { + delete(marshalMap, "avatar_url") + } + return json.Marshal(r.Extra) } type RespMutualRooms struct {