Events ====== Receiving live updates on a client ---------------------------------- Clients can receive new events by long-polling the home server. This will hold open the HTTP connection for a short period of time waiting for new events, returning early if an event occurs. This is called the `Event Stream`_. All events which are visible to the client will appear in the event stream. When the request returns, an ``end`` token is included in the response. This token can be used in the next request to continue where the client left off. .. TODO-spec How do we filter the event stream? Do we ever return multiple events in a single request? Don't we get lots of request setup RTT latency if we only do one event per request? Do we ever support streaming requests? Why not websockets? When the client first logs in, they will need to initially synchronise with their home server. This is achieved via the |initialSync|_ API. This API also returns an ``end`` token which can be used with the event stream. .. TODO-spec We *HAVE* to clarify the difference between "state events" and "non-state events" here (or somewhere like it) Common event fields ------------------- All events MUST have the following fields: ``event_id`` Type: String. Description: Represents the globally unique ID for this event. ``type`` Type: String. Description: Contains the event type, e.g. ``m.room.message`` ``content`` Type: JSON Object. Description: Contains the content of the event. When interacting with the REST API, this is the HTTP body. ``room_id`` Type: String. Description: Contains the ID of the room associated with this event. ``user_id`` Type: String. Description: Contains the fully-qualified ID of the user who *sent* this event. State events have the additional fields: ``state_key`` Type: String. Description: Contains the state key for this state event. If there is no state key for this state event, this will be an empty string. The presence of ``state_key`` makes this event a state event. ``required_power_level`` Type: Integer. Description: Contains the minimum power level a user must have before they can update this event. ``prev_content`` Type: JSON Object. Description: Optional. Contains the previous ``content`` for this event. If there is no previous content, this key will be missing. .. TODO-spec How do "age" and "ts" fit in to all this? Which do we expose? Room Events ----------- .. NOTE:: This section is a work in progress. This specification outlines several standard event types, all of which are prefixed with ``m.`` ``m.room.name`` Summary: Set the human-readable name for the room. Type: State event JSON format: ``{ "name" : "string" }`` Example: ``{ "name" : "My Room" }`` Description: A room has an opaque room ID which is not human-friendly to read. A room alias is human-friendly, but not all rooms have room aliases. The room name is a human-friendly string designed to be displayed to the end-user. The room name is not *unique*, as multiple rooms can have the same room name set. The room name can also be set when creating a room using |createRoom|_ with the ``name`` key. ``m.room.topic`` Summary: Set a topic for the room. Type: State event JSON format: ``{ "topic" : "string" }`` Example: ``{ "topic" : "Welcome to the real world." }`` Description: A topic is a short message detailing what is currently being discussed in the room. It can also be used as a way to display extra information about the room, which may not be suitable for the room name. The room topic can also be set when creating a room using |createRoom|_ with the ``topic`` key. ``m.room.member`` Summary: The current membership state of a user in the room. Type: State event JSON format: ``{ "membership" : "enum[ invite|join|leave|ban ]" }`` Example: ``{ "membership" : "join" }`` Description: Adjusts the membership state for a user in a room. It is preferable to use the membership APIs (``/rooms//invite`` etc) when performing membership actions rather than adjusting the state directly as there are a restricted set of valid transformations. For example, user A cannot force user B to join a room, and trying to force this state change directly will fail. See the `Rooms`_ section for how to use the membership APIs. ``m.room.create`` Summary: The first event in the room. Type: State event JSON format: ``{ "creator": "string"}`` Example: ``{ "creator": "@user:example.com" }`` Description: This is the first event in a room and cannot be changed. It acts as the root of all other events. ``m.room.join_rules`` Summary: Descripes how/if people are allowed to join. Type: State event JSON format: ``{ "join_rule": "enum [ public|knock|invite|private ]" }`` Example: ``{ "join_rule": "public" }`` Description: TODO-doc : Use docs/models/rooms.rst ``m.room.power_levels`` Summary: Defines the power levels of users in the room. Type: State event JSON format: ``{ "": , ..., "default": }`` Example: ``{ "@user:example.com": 5, "@user2:example.com": 10, "default": 0 }`` Description: If a user is in the list, then they have the associated power level. Otherwise they have the default level. If not ``default`` key is supplied, it is assumed to be 0. ``m.room.add_state_level`` Summary: Defines the minimum power level a user needs to add state. Type: State event JSON format: ``{ "level": }`` Example: ``{ "level": 5 }`` Description: To add a new piece of state to the room a user must have the given power level. This does not apply to updating current state, which is goverened by the ``required_power_level`` event key. ``m.room.send_event_level`` Summary: Defines the minimum power level a user needs to send an event. Type: State event JSON format: ``{ "level": }`` Example: ``{ "level": 0 }`` Description: To send a new event into the room a user must have at least this power level. This allows ops to make the room read only by increasing this level, or muting individual users by lowering their power level below this threshold. ``m.room.ops_levels`` Summary: Defines the minimum power levels that a user must have before they can kick and/or ban other users. Type: State event JSON format: ``{ "ban_level": , "kick_level": , "redact_level": }`` Example: ``{ "ban_level": 5, "kick_level": 5 }`` Description: This defines who can ban and/or kick people in the room. Most of the time ``ban_level`` will be greater than or equal to ``kick_level`` since banning is more severe than kicking. ``m.room.aliases`` Summary: These state events are used to inform the room about what room aliases it has. Type: State event JSON format: ``{ "aliases": ["string", ...] }`` Example: ``{ "aliases": ["#foo:example.com"] }`` Description: This event is sent by a homeserver directly to inform of changes to the list of aliases it knows about for that room. As a special-case, the ``state_key`` of the event is the homeserver which owns the room alias. For example, an event might look like:: { "type": "m.room.aliases", "event_id": "012345678ab", "room_id": "!xAbCdEfG:example.com", "state_key": "example.com", "content": { "aliases": ["#foo:example.com"] } } The event contains the full list of aliases now stored by the home server that emitted it; additions or deletions are not explicitly mentioned as being such. The entire set of known aliases for the room is then the union of the individual lists declared by all such keys, one from each home server holding at least one alias. Clients `should` check the validity of any room alias given in this list before presenting it to the user as trusted fact. The lists given by this event should be considered simply as advice on which aliases might exist, for which the client can perform the lookup to confirm whether it receives the correct room ID. ``m.room.message`` Summary: A message. Type: Non-state event JSON format: ``{ "msgtype": "string" }`` Example: ``{ "msgtype": "m.text", "body": "Testing" }`` Description: This event is used when sending messages in a room. Messages are not limited to be text. The ``msgtype`` key outlines the type of message, e.g. text, audio, image, video, etc. Whilst not required, the ``body`` key SHOULD be used with every kind of ``msgtype`` as a fallback mechanism when a client cannot render the message. For more information on the types of messages which can be sent, see `m.room.message msgtypes`_. ``m.room.message.feedback`` Summary: A receipt for a message. Type: Non-state event JSON format: ``{ "type": "enum [ delivered|read ]", "target_event_id": "string" }`` Example: ``{ "type": "delivered", "target_event_id": "e3b2icys" }`` Description: Feedback events are events sent to acknowledge a message in some way. There are two supported acknowledgements: ``delivered`` (sent when the event has been received) and ``read`` (sent when the event has been observed by the end-user). The ``target_event_id`` should reference the ``m.room.message`` event being acknowledged. ``m.room.redaction`` Summary: Indicates a previous event has been redacted. Type: Non-state event JSON format: ``{ "reason": "string" }`` Description: Events can be redacted by either room or server admins. Redacting an event means that all keys not required by the protocol are stripped off, allowing admins to remove offensive or illegal content that may have been attached to any event. This cannot be undone, allowing server owners to physically delete the offending data. There is also a concept of a moderator hiding a non-state event, which can be undone, but cannot be applied to state events. The event that has been redacted is specified in the ``redacts`` event level key. m.room.message msgtypes ~~~~~~~~~~~~~~~~~~~~~~~ .. TODO-spec How a client should handle unknown message types. Each ``m.room.message`` MUST have a ``msgtype`` key which identifies the type of message being sent. Each type has their own required and optional keys, as outlined below: ``m.text`` Required keys: - ``body`` : "string" - The body of the message. Optional keys: None. Example: ``{ "msgtype": "m.text", "body": "I am a fish" }`` ``m.emote`` Required keys: - ``body`` : "string" - The emote action to perform. Optional keys: None. Example: ``{ "msgtype": "m.emote", "body": "tries to come up with a witty explanation" }`` ``m.image`` Required keys: - ``url`` : "string" - The URL to the image. Optional keys: - ``info`` : "string" - info : JSON object (ImageInfo) - The image info for image referred to in ``url``. - ``thumbnail_url`` : "string" - The URL to the thumbnail. - ``thumbnail_info`` : JSON object (ImageInfo) - The image info for the image referred to in ``thumbnail_url``. - ``body`` : "string" - The alt text of the image, or some kind of content description for accessibility e.g. "image attachment". ImageInfo: Information about an image:: { "size" : integer (size of image in bytes), "w" : integer (width of image in pixels), "h" : integer (height of image in pixels), "mimetype" : "string (e.g. image/jpeg)", } ``m.audio`` Required keys: - ``url`` : "string" - The URL to the audio. Optional keys: - ``info`` : JSON object (AudioInfo) - The audio info for the audio referred to in ``url``. - ``body`` : "string" - A description of the audio e.g. "Bee Gees - Stayin' Alive", or some kind of content description for accessibility e.g. "audio attachment". AudioInfo: Information about a piece of audio:: { "mimetype" : "string (e.g. audio/aac)", "size" : integer (size of audio in bytes), "duration" : integer (duration of audio in milliseconds), } ``m.video`` Required keys: - ``url`` : "string" - The URL to the video. Optional keys: - ``info`` : JSON object (VideoInfo) - The video info for the video referred to in ``url``. - ``body`` : "string" - A description of the video e.g. "Gangnam style", or some kind of content description for accessibility e.g. "video attachment". VideoInfo: Information about a video:: { "mimetype" : "string (e.g. video/mp4)", "size" : integer (size of video in bytes), "duration" : integer (duration of video in milliseconds), "w" : integer (width of video in pixels), "h" : integer (height of video in pixels), "thumbnail_url" : "string (URL to image)", "thumbanil_info" : JSON object (ImageInfo) } ``m.location`` Required keys: - ``geo_uri`` : "string" - The geo URI representing the location. Optional keys: - ``thumbnail_url`` : "string" - The URL to a thumnail of the location being represented. - ``thumbnail_info`` : JSON object (ImageInfo) - The image info for the image referred to in ``thumbnail_url``. - ``body`` : "string" - A description of the location e.g. "Big Ben, London, UK", or some kind of content description for accessibility e.g. "location attachment". The following keys can be attached to any ``m.room.message``: Optional keys: - ``sender_ts`` : integer - A timestamp (ms resolution) representing the wall-clock time when the message was sent from the client. Events on Change of Profile Information ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Because the profile displayname and avatar information are likely to be used in many places of a client's display, changes to these fields cause an automatic propagation event to occur, informing likely-interested parties of the new values. This change is conveyed using two separate mechanisms: - a ``m.room.member`` event is sent to every room the user is a member of, to update the ``displayname`` and ``avatar_url``. - a presence status update is sent, again containing the new values of the ``displayname`` and ``avatar_url`` keys, in addition to the required ``presence`` key containing the current presence state of the user. Both of these should be done automatically by the home server when a user successfully changes their displayname or avatar URL fields. Additionally, when home servers emit room membership events for their own users, they should include the displayname and avatar URL fields in these events so that clients already have these details to hand, and do not have to perform extra roundtrips to query it. Voice over IP ------------- Matrix can also be used to set up VoIP calls. This is part of the core specification, although is still in a very early stage. Voice (and video) over Matrix is based on the WebRTC standards. Call events are sent to a room, like any other event. This means that clients must only send call events to rooms with exactly two participants as currently the WebRTC standard is based around two-party communication. Events ~~~~~~ ``m.call.invite`` This event is sent by the caller when they wish to establish a call. Required keys: - ``call_id`` : "string" - A unique identifier for the call - ``offer`` : "offer object" - The session description - ``version`` : "integer" - The version of the VoIP specification this message adheres to. This specification is version 0. - ``lifetime`` : "integer" - The time in milliseconds that the invite is valid for. Once the invite age exceeds this value, clients should discard it. They should also no longer show the call as awaiting an answer in the UI. Optional keys: None. Example: ``{ "version" : 0, "call_id": "12345", "offer": { "type" : "offer", "sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]" } }`` ``Offer Object`` Required keys: - ``type`` : "string" - The type of session description, in this case 'offer' - ``sdp`` : "string" - The SDP text of the session description ``m.call.candidates`` This event is sent by callers after sending an invite and by the callee after answering. Its purpose is to give the other party additional ICE candidates to try using to communicate. Required keys: - ``call_id`` : "string" - The ID of the call this event relates to - ``version`` : "integer" - The version of the VoIP specification this messages adheres to. his specification is version 0. - ``candidates`` : "array of candidate objects" - Array of object describing the candidates. ``Candidate Object`` Required Keys: - ``sdpMid`` : "string" - The SDP media type this candidate is intended for. - ``sdpMLineIndex`` : "integer" - The index of the SDP 'm' line this candidate is intended for - ``candidate`` : "string" - The SDP 'a' line of the candidate ``m.call.answer`` Required keys: - ``call_id`` : "string" - The ID of the call this event relates to - ``version`` : "integer" - The version of the VoIP specification this messages - ``answer`` : "answer object" - Object giving the SDK answer ``Answer Object`` Required keys: - ``type`` : "string" - The type of session description. 'answer' in this case. - ``sdp`` : "string" - The SDP text of the session description ``m.call.hangup`` Sent by either party to signal their termination of the call. This can be sent either once the call has has been established or before to abort the call. Required keys: - ``call_id`` : "string" - The ID of the call this event relates to - ``version`` : "integer" - The version of the VoIP specification this messages Message Exchange ~~~~~~~~~~~~~~~~ A call is set up with messages exchanged as follows: :: Caller Callee m.call.invite -----------> m.call.candidate --------> [more candidates events] User answers call <------ m.call.answer [...] <------ m.call.hangup Or a rejected call: :: Caller Callee m.call.invite -----------> m.call.candidate --------> [more candidates events] User rejects call <------- m.call.hangup Calls are negotiated according to the WebRTC specification. Glare ~~~~~ This specification aims to address the problem of two users calling each other at roughly the same time and their invites crossing on the wire. It is a far better experience for the users if their calls are connected if it is clear that their intention is to set up a call with one another. In Matrix, calls are to rooms rather than users (even if those rooms may only contain one other user) so we consider calls which are to the same room. The rules for dealing with such a situation are as follows: - If an invite to a room is received whilst the client is preparing to send an invite to the same room, the client should cancel its outgoing call and instead automatically accept the incoming call on behalf of the user. - If an invite to a room is received after the client has sent an invite to the same room and is waiting for a response, the client should perform a lexicographical comparison of the call IDs of the two calls and use the lesser of the two calls, aborting the greater. If the incoming call is the lesser, the client should accept this call on behalf of the user. The call setup should appear seamless to the user as if they had simply placed a call and the other party had accepted. Thusly, any media stream that had been setup for use on a call should be transferred and used for the call that replaces it.