summary refs log tree commit diff
path: root/vendor/maunium.net/go/mautrix/responses.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/maunium.net/go/mautrix/responses.go')
-rw-r--r--vendor/maunium.net/go/mautrix/responses.go625
1 files changed, 625 insertions, 0 deletions
diff --git a/vendor/maunium.net/go/mautrix/responses.go b/vendor/maunium.net/go/mautrix/responses.go
new file mode 100644
index 0000000..26aaac7
--- /dev/null
+++ b/vendor/maunium.net/go/mautrix/responses.go
@@ -0,0 +1,625 @@
+package mautrix
+
+import (
+	"bytes"
+	"encoding/json"
+	"fmt"
+	"reflect"
+	"strconv"
+	"strings"
+
+	"github.com/tidwall/gjson"
+	"github.com/tidwall/sjson"
+	"go.mau.fi/util/jsontime"
+
+	"maunium.net/go/mautrix/event"
+	"maunium.net/go/mautrix/id"
+)
+
+// RespWhoami is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3accountwhoami
+type RespWhoami struct {
+	UserID   id.UserID   `json:"user_id"`
+	DeviceID id.DeviceID `json:"device_id"`
+}
+
+// RespCreateFilter is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3useruseridfilter
+type RespCreateFilter struct {
+	FilterID string `json:"filter_id"`
+}
+
+// RespJoinRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidjoin
+type RespJoinRoom struct {
+	RoomID id.RoomID `json:"room_id"`
+}
+
+// RespLeaveRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidleave
+type RespLeaveRoom struct{}
+
+// RespForgetRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidforget
+type RespForgetRoom struct{}
+
+// RespInviteUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidinvite
+type RespInviteUser struct{}
+
+// RespKickUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidkick
+type RespKickUser struct{}
+
+// RespBanUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidban
+type RespBanUser struct{}
+
+// RespUnbanUser is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3roomsroomidunban
+type RespUnbanUser struct{}
+
+// RespTyping is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidtypinguserid
+type RespTyping struct{}
+
+// RespPresence is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3presenceuseridstatus
+type RespPresence struct {
+	Presence        event.Presence `json:"presence"`
+	LastActiveAgo   int            `json:"last_active_ago"`
+	StatusMsg       string         `json:"status_msg"`
+	CurrentlyActive bool           `json:"currently_active"`
+}
+
+// RespJoinedRooms is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3joined_rooms
+type RespJoinedRooms struct {
+	JoinedRooms []id.RoomID `json:"joined_rooms"`
+}
+
+// RespJoinedMembers is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidjoined_members
+type RespJoinedMembers struct {
+	Joined map[id.UserID]JoinedMember `json:"joined"`
+}
+
+type JoinedMember struct {
+	DisplayName string `json:"display_name,omitempty"`
+	AvatarURL   string `json:"avatar_url,omitempty"`
+}
+
+// RespMessages is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidmessages
+type RespMessages struct {
+	Start string         `json:"start"`
+	Chunk []*event.Event `json:"chunk"`
+	State []*event.Event `json:"state"`
+	End   string         `json:"end,omitempty"`
+}
+
+// RespContext is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3roomsroomidcontexteventid
+type RespContext struct {
+	End          string         `json:"end"`
+	Event        *event.Event   `json:"event"`
+	EventsAfter  []*event.Event `json:"events_after"`
+	EventsBefore []*event.Event `json:"events_before"`
+	Start        string         `json:"start"`
+	State        []*event.Event `json:"state"`
+}
+
+// RespSendEvent is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#put_matrixclientv3roomsroomidsendeventtypetxnid
+type RespSendEvent struct {
+	EventID id.EventID `json:"event_id"`
+}
+
+// RespMediaConfig is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixmediav3config
+type RespMediaConfig struct {
+	UploadSize int64 `json:"m.upload.size,omitempty"`
+}
+
+// RespMediaUpload is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixmediav3upload
+type RespMediaUpload struct {
+	ContentURI id.ContentURI `json:"content_uri"`
+}
+
+// RespCreateMXC is the JSON response for https://spec.matrix.org/v1.7/client-server-api/#post_matrixmediav1create
+type RespCreateMXC struct {
+	ContentURI      id.ContentURI      `json:"content_uri"`
+	UnusedExpiresAt jsontime.UnixMilli `json:"unused_expires_at,omitempty"`
+
+	UnstableUploadURL string `json:"com.beeper.msc3870.upload_url,omitempty"`
+
+	// Beeper extensions for uploading unique media only once
+	BeeperUniqueID    string             `json:"com.beeper.unique_id,omitempty"`
+	BeeperCompletedAt jsontime.UnixMilli `json:"com.beeper.completed_at,omitempty"`
+}
+
+// RespPreviewURL is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixmediav3preview_url
+type RespPreviewURL = event.LinkPreview
+
+// RespUserInteractive is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#user-interactive-authentication-api
+type RespUserInteractive struct {
+	Flows     []UIAFlow                `json:"flows,omitempty"`
+	Params    map[AuthType]interface{} `json:"params,omitempty"`
+	Session   string                   `json:"session,omitempty"`
+	Completed []string                 `json:"completed,omitempty"`
+
+	ErrCode string `json:"errcode,omitempty"`
+	Error   string `json:"error,omitempty"`
+}
+
+type UIAFlow struct {
+	Stages []AuthType `json:"stages,omitempty"`
+}
+
+// HasSingleStageFlow returns true if there exists at least 1 Flow with a single stage of stageName.
+func (r RespUserInteractive) HasSingleStageFlow(stageName AuthType) bool {
+	for _, f := range r.Flows {
+		if len(f.Stages) == 1 && f.Stages[0] == stageName {
+			return true
+		}
+	}
+	return false
+}
+
+// RespUserDisplayName is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3profileuseriddisplayname
+type RespUserDisplayName struct {
+	DisplayName string `json:"displayname"`
+}
+
+type RespUserProfile struct {
+	DisplayName string        `json:"displayname"`
+	AvatarURL   id.ContentURI `json:"avatar_url"`
+}
+
+// RespRegisterAvailable is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv3registeravailable
+type RespRegisterAvailable struct {
+	Available bool `json:"available"`
+}
+
+// RespRegister is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3register
+type RespRegister struct {
+	AccessToken string      `json:"access_token,omitempty"`
+	DeviceID    id.DeviceID `json:"device_id,omitempty"`
+	UserID      id.UserID   `json:"user_id"`
+
+	RefreshToken string `json:"refresh_token,omitempty"`
+	ExpiresInMS  int64  `json:"expires_in_ms,omitempty"`
+
+	// Deprecated: homeserver should be parsed from the user ID
+	HomeServer string `json:"home_server,omitempty"`
+}
+
+type LoginFlow struct {
+	Type AuthType `json:"type"`
+}
+
+// RespLoginFlows is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3login
+type RespLoginFlows struct {
+	Flows []LoginFlow `json:"flows"`
+}
+
+func (rlf *RespLoginFlows) FirstFlowOfType(flowTypes ...AuthType) *LoginFlow {
+	for _, flow := range rlf.Flows {
+		for _, flowType := range flowTypes {
+			if flow.Type == flowType {
+				return &flow
+			}
+		}
+	}
+	return nil
+}
+
+func (rlf *RespLoginFlows) HasFlow(flowType ...AuthType) bool {
+	return rlf.FirstFlowOfType(flowType...) != nil
+}
+
+// RespLogin is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3login
+type RespLogin struct {
+	AccessToken string           `json:"access_token"`
+	DeviceID    id.DeviceID      `json:"device_id"`
+	UserID      id.UserID        `json:"user_id"`
+	WellKnown   *ClientWellKnown `json:"well_known,omitempty"`
+}
+
+// RespLogout is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3logout
+type RespLogout struct{}
+
+// RespCreateRoom is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#post_matrixclientv3createroom
+type RespCreateRoom struct {
+	RoomID id.RoomID `json:"room_id"`
+}
+
+type RespMembers struct {
+	Chunk []*event.Event `json:"chunk"`
+}
+
+type LazyLoadSummary struct {
+	Heroes             []id.UserID `json:"m.heroes,omitempty"`
+	JoinedMemberCount  *int        `json:"m.joined_member_count,omitempty"`
+	InvitedMemberCount *int        `json:"m.invited_member_count,omitempty"`
+}
+
+type SyncEventsList struct {
+	Events []*event.Event `json:"events,omitempty"`
+}
+
+type SyncTimeline struct {
+	SyncEventsList
+	Limited   bool   `json:"limited,omitempty"`
+	PrevBatch string `json:"prev_batch,omitempty"`
+}
+
+// RespSync is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3sync
+type RespSync struct {
+	NextBatch string `json:"next_batch"`
+
+	AccountData SyncEventsList `json:"account_data"`
+	Presence    SyncEventsList `json:"presence"`
+	ToDevice    SyncEventsList `json:"to_device"`
+
+	DeviceLists    DeviceLists       `json:"device_lists"`
+	DeviceOTKCount OTKCount          `json:"device_one_time_keys_count"`
+	FallbackKeys   []id.KeyAlgorithm `json:"device_unused_fallback_key_types"`
+
+	Rooms RespSyncRooms `json:"rooms"`
+}
+
+type RespSyncRooms struct {
+	Leave  map[id.RoomID]*SyncLeftRoom    `json:"leave,omitempty"`
+	Join   map[id.RoomID]*SyncJoinedRoom  `json:"join,omitempty"`
+	Invite map[id.RoomID]*SyncInvitedRoom `json:"invite,omitempty"`
+	Knock  map[id.RoomID]*SyncKnockedRoom `json:"knock,omitempty"`
+}
+
+type marshalableRespSync RespSync
+
+var syncPathsToDelete = []string{"account_data", "presence", "to_device", "device_lists", "device_one_time_keys_count", "rooms"}
+
+// marshalAndDeleteEmpty marshals a JSON object, then uses gjson to delete empty objects at the given gjson paths.
+func marshalAndDeleteEmpty(marshalable interface{}, paths []string) ([]byte, error) {
+	data, err := json.Marshal(marshalable)
+	if err != nil {
+		return nil, err
+	}
+	for _, path := range paths {
+		res := gjson.GetBytes(data, path)
+		if res.IsObject() && len(res.Raw) == 2 {
+			data, err = sjson.DeleteBytes(data, path)
+			if err != nil {
+				return nil, fmt.Errorf("failed to delete empty %s: %w", path, err)
+			}
+		}
+	}
+	return data, nil
+}
+
+func (rs *RespSync) MarshalJSON() ([]byte, error) {
+	return marshalAndDeleteEmpty((*marshalableRespSync)(rs), syncPathsToDelete)
+}
+
+type DeviceLists struct {
+	Changed []id.UserID `json:"changed,omitempty"`
+	Left    []id.UserID `json:"left,omitempty"`
+}
+
+type OTKCount struct {
+	Curve25519       int `json:"curve25519,omitempty"`
+	SignedCurve25519 int `json:"signed_curve25519,omitempty"`
+
+	// For appservice OTK counts only: the user ID in question
+	UserID   id.UserID   `json:"-"`
+	DeviceID id.DeviceID `json:"-"`
+}
+
+type SyncLeftRoom struct {
+	Summary  LazyLoadSummary `json:"summary"`
+	State    SyncEventsList  `json:"state"`
+	Timeline SyncTimeline    `json:"timeline"`
+}
+
+type marshalableSyncLeftRoom SyncLeftRoom
+
+var syncLeftRoomPathsToDelete = []string{"summary", "state", "timeline"}
+
+func (slr SyncLeftRoom) MarshalJSON() ([]byte, error) {
+	return marshalAndDeleteEmpty((marshalableSyncLeftRoom)(slr), syncLeftRoomPathsToDelete)
+}
+
+type BeeperInboxPreviewEvent struct {
+	EventID   id.EventID         `json:"event_id"`
+	Timestamp jsontime.UnixMilli `json:"origin_server_ts"`
+	Event     *event.Event       `json:"event,omitempty"`
+}
+
+type SyncJoinedRoom struct {
+	Summary     LazyLoadSummary `json:"summary"`
+	State       SyncEventsList  `json:"state"`
+	Timeline    SyncTimeline    `json:"timeline"`
+	Ephemeral   SyncEventsList  `json:"ephemeral"`
+	AccountData SyncEventsList  `json:"account_data"`
+
+	UnreadNotifications *UnreadNotificationCounts `json:"unread_notifications,omitempty"`
+	// https://github.com/matrix-org/matrix-spec-proposals/pull/2654
+	MSC2654UnreadCount *int `json:"org.matrix.msc2654.unread_count,omitempty"`
+	// Beeper extension
+	BeeperInboxPreview *BeeperInboxPreviewEvent `json:"com.beeper.inbox.preview,omitempty"`
+}
+
+type UnreadNotificationCounts struct {
+	HighlightCount    int `json:"highlight_count"`
+	NotificationCount int `json:"notification_count"`
+}
+
+type marshalableSyncJoinedRoom SyncJoinedRoom
+
+var syncJoinedRoomPathsToDelete = []string{"summary", "state", "timeline", "ephemeral", "account_data"}
+
+func (sjr SyncJoinedRoom) MarshalJSON() ([]byte, error) {
+	return marshalAndDeleteEmpty((marshalableSyncJoinedRoom)(sjr), syncJoinedRoomPathsToDelete)
+}
+
+type SyncInvitedRoom struct {
+	Summary LazyLoadSummary `json:"summary"`
+	State   SyncEventsList  `json:"invite_state"`
+}
+
+type marshalableSyncInvitedRoom SyncInvitedRoom
+
+var syncInvitedRoomPathsToDelete = []string{"summary"}
+
+func (sir SyncInvitedRoom) MarshalJSON() ([]byte, error) {
+	return marshalAndDeleteEmpty((marshalableSyncInvitedRoom)(sir), syncInvitedRoomPathsToDelete)
+}
+
+type SyncKnockedRoom struct {
+	State SyncEventsList `json:"knock_state"`
+}
+
+type RespTurnServer struct {
+	Username string   `json:"username"`
+	Password string   `json:"password"`
+	TTL      int      `json:"ttl"`
+	URIs     []string `json:"uris"`
+}
+
+type RespAliasCreate struct{}
+type RespAliasDelete struct{}
+type RespAliasResolve struct {
+	RoomID  id.RoomID `json:"room_id"`
+	Servers []string  `json:"servers"`
+}
+type RespAliasList struct {
+	Aliases []id.RoomAlias `json:"aliases"`
+}
+
+type RespUploadKeys struct {
+	OneTimeKeyCounts OTKCount `json:"one_time_key_counts"`
+}
+
+type RespQueryKeys struct {
+	Failures        map[string]interface{}                   `json:"failures,omitempty"`
+	DeviceKeys      map[id.UserID]map[id.DeviceID]DeviceKeys `json:"device_keys"`
+	MasterKeys      map[id.UserID]CrossSigningKeys           `json:"master_keys"`
+	SelfSigningKeys map[id.UserID]CrossSigningKeys           `json:"self_signing_keys"`
+	UserSigningKeys map[id.UserID]CrossSigningKeys           `json:"user_signing_keys"`
+}
+
+type RespClaimKeys struct {
+	Failures    map[string]interface{}                                `json:"failures,omitempty"`
+	OneTimeKeys map[id.UserID]map[id.DeviceID]map[id.KeyID]OneTimeKey `json:"one_time_keys"`
+}
+
+type RespUploadSignatures struct {
+	Failures map[string]interface{} `json:"failures,omitempty"`
+}
+
+type RespKeyChanges struct {
+	Changed []id.UserID `json:"changed"`
+	Left    []id.UserID `json:"left"`
+}
+
+type RespSendToDevice struct{}
+
+// RespDevicesInfo is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devices
+type RespDevicesInfo struct {
+	Devices []RespDeviceInfo `json:"devices"`
+}
+
+// RespDeviceInfo is the JSON response for https://spec.matrix.org/v1.2/client-server-api/#get_matrixclientv3devicesdeviceid
+type RespDeviceInfo struct {
+	DeviceID    id.DeviceID `json:"device_id"`
+	DisplayName string      `json:"display_name"`
+	LastSeenIP  string      `json:"last_seen_ip"`
+	LastSeenTS  int64       `json:"last_seen_ts"`
+}
+
+// Deprecated: MSC2716 was abandoned
+type RespBatchSend struct {
+	StateEventIDs []id.EventID `json:"state_event_ids"`
+	EventIDs      []id.EventID `json:"event_ids"`
+
+	InsertionEventID     id.EventID `json:"insertion_event_id"`
+	BatchEventID         id.EventID `json:"batch_event_id"`
+	BaseInsertionEventID id.EventID `json:"base_insertion_event_id"`
+
+	NextBatchID id.BatchID `json:"next_batch_id"`
+}
+
+type RespBeeperBatchSend struct {
+	EventIDs []id.EventID `json:"event_ids"`
+}
+
+// RespCapabilities is the JSON response for https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3capabilities
+type RespCapabilities struct {
+	RoomVersions    *CapRoomVersions `json:"m.room_versions,omitempty"`
+	ChangePassword  *CapBooleanTrue  `json:"m.change_password,omitempty"`
+	SetDisplayname  *CapBooleanTrue  `json:"m.set_displayname,omitempty"`
+	SetAvatarURL    *CapBooleanTrue  `json:"m.set_avatar_url,omitempty"`
+	ThreePIDChanges *CapBooleanTrue  `json:"m.3pid_changes,omitempty"`
+
+	Custom map[string]interface{} `json:"-"`
+}
+
+type serializableRespCapabilities RespCapabilities
+
+func (rc *RespCapabilities) UnmarshalJSON(data []byte) error {
+	res := gjson.GetBytes(data, "capabilities")
+	if !res.Exists() || !res.IsObject() {
+		return nil
+	}
+	if res.Index > 0 {
+		data = data[res.Index : res.Index+len(res.Raw)]
+	} else {
+		data = []byte(res.Raw)
+	}
+	err := json.Unmarshal(data, (*serializableRespCapabilities)(rc))
+	if err != nil {
+		return err
+	}
+	err = json.Unmarshal(data, &rc.Custom)
+	if err != nil {
+		return err
+	}
+	// Remove non-custom capabilities from the custom map so that they don't get overridden when serializing back
+	for _, field := range reflect.VisibleFields(reflect.TypeOf(rc).Elem()) {
+		jsonTag := strings.Split(field.Tag.Get("json"), ",")[0]
+		if jsonTag != "-" && jsonTag != "" {
+			delete(rc.Custom, jsonTag)
+		}
+	}
+	return nil
+}
+
+func (rc *RespCapabilities) MarshalJSON() ([]byte, error) {
+	marshalableCopy := make(map[string]interface{}, len(rc.Custom))
+	val := reflect.ValueOf(rc).Elem()
+	for _, field := range reflect.VisibleFields(val.Type()) {
+		jsonTag := strings.Split(field.Tag.Get("json"), ",")[0]
+		if jsonTag != "-" && jsonTag != "" {
+			fieldVal := val.FieldByIndex(field.Index)
+			if !fieldVal.IsNil() {
+				marshalableCopy[jsonTag] = fieldVal.Interface()
+			}
+		}
+	}
+	if rc.Custom != nil {
+		for key, value := range rc.Custom {
+			marshalableCopy[key] = value
+		}
+	}
+	var buf bytes.Buffer
+	buf.WriteString(`{"capabilities":`)
+	err := json.NewEncoder(&buf).Encode(marshalableCopy)
+	if err != nil {
+		return nil, err
+	}
+	buf.WriteByte('}')
+	return buf.Bytes(), nil
+}
+
+type CapBoolean struct {
+	Enabled bool `json:"enabled"`
+}
+
+type CapBooleanTrue CapBoolean
+
+// IsEnabled returns true if the capability is either enabled explicitly or not specified (nil)
+func (cb *CapBooleanTrue) IsEnabled() bool {
+	// Default to true when
+	return cb == nil || cb.Enabled
+}
+
+type CapBooleanFalse CapBoolean
+
+// IsEnabled returns true if the capability is enabled explicitly. If it's not specified, this returns false.
+func (cb *CapBooleanFalse) IsEnabled() bool {
+	return cb != nil && cb.Enabled
+}
+
+type CapRoomVersionStability string
+
+const (
+	CapRoomVersionStable   CapRoomVersionStability = "stable"
+	CapRoomVersionUnstable CapRoomVersionStability = "unstable"
+)
+
+type CapRoomVersions struct {
+	Default   string                             `json:"default"`
+	Available map[string]CapRoomVersionStability `json:"available"`
+}
+
+func (vers *CapRoomVersions) IsStable(version string) bool {
+	if vers == nil || vers.Available == nil {
+		val, err := strconv.Atoi(version)
+		return err == nil && val > 0
+	}
+	return vers.Available[version] == CapRoomVersionStable
+}
+
+func (vers *CapRoomVersions) IsAvailable(version string) bool {
+	if vers == nil || vers.Available == nil {
+		return false
+	}
+	_, available := vers.Available[version]
+	return available
+}
+
+// RespHierarchy is the JSON response for https://spec.matrix.org/v1.4/client-server-api/#get_matrixclientv1roomsroomidhierarchy
+type RespHierarchy struct {
+	NextBatch string            `json:"next_batch,omitempty"`
+	Rooms     []ChildRoomsChunk `json:"rooms"`
+}
+
+type ChildRoomsChunk struct {
+	AvatarURL        id.ContentURI           `json:"avatar_url,omitempty"`
+	CanonicalAlias   id.RoomAlias            `json:"canonical_alias,omitempty"`
+	ChildrenState    []StrippedStateWithTime `json:"children_state"`
+	GuestCanJoin     bool                    `json:"guest_can_join"`
+	JoinRule         event.JoinRule          `json:"join_rule,omitempty"`
+	Name             string                  `json:"name,omitempty"`
+	NumJoinedMembers int                     `json:"num_joined_members"`
+	RoomID           id.RoomID               `json:"room_id"`
+	RoomType         event.RoomType          `json:"room_type"`
+	Topic            string                  `json:"topic,omitempty"`
+	WorldReadble     bool                    `json:"world_readable"`
+}
+
+type StrippedStateWithTime struct {
+	event.StrippedState
+	Timestamp jsontime.UnixMilli `json:"origin_server_ts"`
+}
+
+type RespAppservicePing struct {
+	DurationMS int64 `json:"duration_ms"`
+}
+
+type RespBeeperMergeRoom RespCreateRoom
+
+type RespBeeperSplitRoom struct {
+	RoomIDs map[string]id.RoomID `json:"room_ids"`
+}
+
+type RespTimestampToEvent struct {
+	EventID   id.EventID         `json:"event_id"`
+	Timestamp jsontime.UnixMilli `json:"origin_server_ts"`
+}
+
+type RespRoomKeysVersionCreate struct {
+	Version id.KeyBackupVersion `json:"version"`
+}
+
+type RespRoomKeysVersion[A any] struct {
+	Algorithm id.KeyBackupAlgorithm `json:"algorithm"`
+	AuthData  A                     `json:"auth_data"`
+	Count     int                   `json:"count"`
+	ETag      string                `json:"etag"`
+	Version   id.KeyBackupVersion   `json:"version"`
+}
+
+type RespRoomKeys[S any] struct {
+	Rooms map[id.RoomID]RespRoomKeyBackup[S] `json:"rooms"`
+}
+
+type RespRoomKeyBackup[S any] struct {
+	Sessions map[id.SessionID]RespKeyBackupData[S] `json:"sessions"`
+}
+
+type RespKeyBackupData[S any] struct {
+	FirstMessageIndex int  `json:"first_message_index"`
+	ForwardedCount    int  `json:"forwarded_count"`
+	IsVerified        bool `json:"is_verified"`
+	SessionData       S    `json:"session_data"`
+}
+
+type RespRoomKeysUpdate struct {
+	Count int    `json:"count"`
+	ETag  string `json:"etag"`
+}