aboutsummaryrefslogtreecommitdiff
path: root/roomserver/api/output.go
blob: 36d0625c7841cb510e27f7dbcb869debe5d3cd1e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// Copyright 2017 Vector Creations Ltd
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package api

import (
	"github.com/matrix-org/gomatrixserverlib"
)

// An OutputType is a type of roomserver output.
type OutputType string

const (
	// OutputTypeNewRoomEvent indicates that the event is an OutputNewRoomEvent
	OutputTypeNewRoomEvent OutputType = "new_room_event"
	// OutputTypeOldRoomEvent indicates that the event is an OutputOldRoomEvent
	OutputTypeOldRoomEvent OutputType = "old_room_event"
	// OutputTypeNewInviteEvent indicates that the event is an OutputNewInviteEvent
	OutputTypeNewInviteEvent OutputType = "new_invite_event"
	// OutputTypeRetireInviteEvent indicates that the event is an OutputRetireInviteEvent
	OutputTypeRetireInviteEvent OutputType = "retire_invite_event"
	// OutputTypeRedactedEvent indicates that the event is an OutputRedactedEvent
	//
	// This event is emitted when a redaction has been 'validated' (meaning both the redaction and the event to redact are known).
	// Redaction validation happens when the roomserver receives either:
	// - A redaction for which we have the event to redact.
	// - Any event for which we have a redaction.
	// When the roomserver receives an event, it will check against the redactions table to see if there is a matching redaction
	// for the event. If there is, it will mark the redaction as validated and emit this event. In the common case of a redaction
	// happening after receiving the event to redact, the roomserver will emit a OutputTypeNewRoomEvent of m.room.redaction
	// immediately followed by a OutputTypeRedactedEvent. In the uncommon case of receiving the redaction BEFORE the event to redact,
	// the roomserver will emit a OutputTypeNewRoomEvent of the event to redact immediately followed by a OutputTypeRedactedEvent.
	//
	// In order to honour redactions correctly, downstream components must ignore m.room.redaction events emitted via OutputTypeNewRoomEvent.
	// When downstream components receive an OutputTypeRedactedEvent they must:
	// - Pull out the event to redact from the database. They should have this because the redaction is validated.
	// - Redact the event and set the corresponding `unsigned` fields to indicate it as redacted.
	// - Replace the event in the database.
	OutputTypeRedactedEvent OutputType = "redacted_event"

	// OutputTypeNewPeek indicates that the kafka event is an OutputNewPeek
	OutputTypeNewPeek OutputType = "new_peek"
	// OutputTypeNewInboundPeek indicates that the kafka event is an OutputNewInboundPeek
	OutputTypeNewInboundPeek OutputType = "new_inbound_peek"
	// OutputTypeRetirePeek indicates that the kafka event is an OutputRetirePeek
	OutputTypeRetirePeek OutputType = "retire_peek"
)

// An OutputEvent is an entry in the roomserver output kafka log.
// Consumers should check the type field when consuming this event.
type OutputEvent struct {
	// What sort of event this is.
	Type OutputType `json:"type"`
	// The content of event with type OutputTypeNewRoomEvent
	NewRoomEvent *OutputNewRoomEvent `json:"new_room_event,omitempty"`
	// The content of event with type OutputTypeOldRoomEvent
	OldRoomEvent *OutputOldRoomEvent `json:"old_room_event,omitempty"`
	// The content of event with type OutputTypeNewInviteEvent
	NewInviteEvent *OutputNewInviteEvent `json:"new_invite_event,omitempty"`
	// The content of event with type OutputTypeRetireInviteEvent
	RetireInviteEvent *OutputRetireInviteEvent `json:"retire_invite_event,omitempty"`
	// The content of event with type OutputTypeRedactedEvent
	RedactedEvent *OutputRedactedEvent `json:"redacted_event,omitempty"`
	// The content of event with type OutputTypeNewPeek
	NewPeek *OutputNewPeek `json:"new_peek,omitempty"`
	// The content of event with type OutputTypeNewInboundPeek
	NewInboundPeek *OutputNewInboundPeek `json:"new_inbound_peek,omitempty"`
	// The content of event with type OutputTypeRetirePeek
	RetirePeek *OutputRetirePeek `json:"retire_peek,omitempty"`
}

// Type of the OutputNewRoomEvent.
type OutputRoomEventType int

const (
	// The event is a timeline event and likely just happened.
	OutputRoomTimeline OutputRoomEventType = iota

	// The event is a state event and quite possibly happened in the past.
	OutputRoomState
)

// An OutputNewRoomEvent is written when the roomserver receives a new event.
// It contains the full matrix room event and enough information for a
// consumer to construct the current state of the room and the state before the
// event.
//
// When we talk about state in a matrix room we are talking about the state
// after a list of events. The current state is the state after the latest
// event IDs in the room. The state before an event is the state after its
// prev_events.
type OutputNewRoomEvent struct {
	// The Event.
	Event *gomatrixserverlib.HeaderedEvent `json:"event"`
	// Does the event completely rewrite the room state? If so, then AddsStateEventIDs
	// will contain the entire room state.
	RewritesState bool `json:"rewrites_state,omitempty"`
	// The latest events in the room after this event.
	// This can be used to set the prev events for new events in the room.
	// This also can be used to get the full current state after this event.
	LatestEventIDs []string `json:"latest_event_ids"`
	// The state event IDs that were added to the state of the room by this event.
	// Together with RemovesStateEventIDs this allows the receiver to keep an up to date
	// view of the current state of the room.
	AddsStateEventIDs []string `json:"adds_state_event_ids,omitempty"`
	// The state event IDs that were removed from the state of the room by this event.
	RemovesStateEventIDs []string `json:"removes_state_event_ids,omitempty"`
	// The ID of the event that was output before this event.
	// Or the empty string if this is the first event output for this room.
	// This is used by consumers to check if they can safely update their
	// current state using the delta supplied in AddsStateEventIDs and
	// RemovesStateEventIDs.
	//
	// If the LastSentEventID doesn't match what they were expecting it to be
	// they can use the LatestEventIDs to request the full current state.
	LastSentEventID string `json:"last_sent_event_id"`
	// The state event IDs that are part of the state at the event, but not
	// part of the current state. Together with the StateBeforeRemovesEventIDs
	// this can be used to construct the state before the event from the
	// current state. The StateBeforeAddsEventIDs and StateBeforeRemovesEventIDs
	// delta is applied after the AddsStateEventIDs and RemovesStateEventIDs.
	//
	// Consumers need to know the state at each event in order to determine
	// which users and servers are allowed to see the event. This information
	// is needed to apply the history visibility rules and to tell which
	// servers we need to push events to over federation.
	//
	// The state is given as a delta against the current state because they are
	// usually either the same state, or differ by just a couple of events.
	StateBeforeAddsEventIDs []string `json:"state_before_adds_event_ids,omitempty"`
	// The state event IDs that are part of the current state, but not part
	// of the state at the event.
	StateBeforeRemovesEventIDs []string `json:"state_before_removes_event_ids,omitempty"`
	// The server name to use to push this event to other servers.
	// Or empty if this event shouldn't be pushed to other servers.
	//
	// This is used by the federation sender component. We need to tell it what
	// event it needs to send because it can't tell on its own. Normally if an
	// event was created on this server then we are responsible for sending it.
	// However there are a couple of exceptions. The first is that when the
	// server joins a remote room through another matrix server, it is the job
	// of the other matrix server to send the event over federation. The second
	// is the reverse of the first, that is when a remote server joins a room
	// that we are in over federation using our server it is our responsibility
	// to send the join event to other matrix servers.
	//
	// We encode the server name that the event should be sent using here to
	// future proof the API for virtual hosting.
	SendAsServer string `json:"send_as_server"`
	// The transaction ID of the send request if sent by a local user and one
	// was specified
	TransactionID *TransactionID `json:"transaction_id,omitempty"`
	// The history visibility of the event.
	HistoryVisibility gomatrixserverlib.HistoryVisibility `json:"history_visibility"`
}

func (o *OutputNewRoomEvent) NeededStateEventIDs() ([]*gomatrixserverlib.HeaderedEvent, []string) {
	addsStateEvents := make([]*gomatrixserverlib.HeaderedEvent, 0, 1)
	missingEventIDs := make([]string, 0, len(o.AddsStateEventIDs))
	for _, eventID := range o.AddsStateEventIDs {
		if eventID != o.Event.EventID() {
			missingEventIDs = append(missingEventIDs, eventID)
		} else {
			addsStateEvents = append(addsStateEvents, o.Event)
		}
	}
	return addsStateEvents, missingEventIDs
}

// An OutputOldRoomEvent is written when the roomserver receives an old event.
// This will typically happen as a result of getting either missing events
// or backfilling. Downstream components may wish to send these events to
// clients when it is advantageous to do so, but with the consideration that
// the event is likely a historic event.
//
// Old events do not update forward extremities or the current room state,
// therefore they must not be treated as if they do. Downstream components
// should build their current room state up from OutputNewRoomEvents only.
type OutputOldRoomEvent struct {
	// The Event.
	Event             *gomatrixserverlib.HeaderedEvent    `json:"event"`
	HistoryVisibility gomatrixserverlib.HistoryVisibility `json:"history_visibility"`
}

// An OutputNewInviteEvent is written whenever an invite becomes active.
// Invite events can be received outside of an existing room so have to be
// tracked separately from the room events themselves.
type OutputNewInviteEvent struct {
	// The room version of the invited room.
	RoomVersion gomatrixserverlib.RoomVersion `json:"room_version"`
	// The "m.room.member" invite event.
	Event *gomatrixserverlib.HeaderedEvent `json:"event"`
}

// An OutputRetireInviteEvent is written whenever an existing invite is no longer
// active. An invite stops being active if the user joins the room or if the
// invite is rejected by the user.
type OutputRetireInviteEvent struct {
	// The ID of the "m.room.member" invite event.
	EventID string
	// The target user ID of the "m.room.member" invite event that was retired.
	TargetUserID string
	// Optional event ID of the event that replaced the invite.
	// This can be empty if the invite was rejected locally and we were unable
	// to reach the server that originally sent the invite.
	RetiredByEventID string
	// The "membership" of the user after retiring the invite. One of "join"
	// "leave" or "ban".
	Membership string
}

// An OutputRedactedEvent is written whenever a redaction has been /validated/.
// Downstream components MUST redact the given event ID if they have stored the
// event JSON. It is guaranteed that this event ID has been seen before.
type OutputRedactedEvent struct {
	// The event ID that was redacted
	RedactedEventID string
	// The value of `unsigned.redacted_because` - the redaction event itself
	RedactedBecause *gomatrixserverlib.HeaderedEvent
}

// An OutputNewPeek is written whenever a user starts peeking into a room
// using a given device.
type OutputNewPeek struct {
	RoomID   string
	UserID   string
	DeviceID string
}

// An OutputNewInboundPeek is written whenever a server starts peeking into a room
type OutputNewInboundPeek struct {
	RoomID string
	PeekID string
	// the event ID at which the peek begins (so we can avoid
	// a race between tracking the state returned by /peek and emitting subsequent
	// peeked events)
	LatestEventID string
	ServerName    gomatrixserverlib.ServerName
	// how often we told the peeking server to renew the peek
	RenewalInterval int64
}

// An OutputRetirePeek is written whenever a user stops peeking into a room.
type OutputRetirePeek struct {
	RoomID   string
	UserID   string
	DeviceID string
}