Merge branch 'master' into human-id-rules
commit
c9f6534d84
@ -0,0 +1,8 @@
|
||||
scripts/gen
|
||||
scripts/continuserv/continuserv
|
||||
scripts/speculator/speculator
|
||||
templating/out
|
||||
*.pyc
|
||||
supporting-docs/_site
|
||||
supporting-docs/.sass-cache
|
||||
api/node_modules
|
@ -0,0 +1,41 @@
|
||||
.. This file is automatically processed by the templating system. To make it
|
||||
.. happy, you MUST use '=' as the title underline and you MUST stick the version
|
||||
.. in the title. The version MUST follow the numbering format
|
||||
.. "v<num>.<num>.<num>" - You cannot use a-z. If the templating system fails to
|
||||
.. find the right info, it will be treated as a test failure and so will show up
|
||||
.. in Jenkins. Comments like this are ignored by both RST and the templating
|
||||
.. system. Add the newest release notes beneath this comment.
|
||||
|
||||
Specification changes in v0.2.0 (2015-10-02)
|
||||
============================================
|
||||
|
||||
This update fundamentally restructures the specification. The specification has
|
||||
been split into more digestible "modules" which each describe a particular
|
||||
function (e.g. typing). This was done in order make the specification easier to
|
||||
maintain and help define which modules are mandatory for certain types
|
||||
of clients. Types of clients along with the mandatory modules can be found in a
|
||||
new "Feature Profiles" section. This update also begins to aggressively
|
||||
standardise on using Swagger and JSON Schema to document HTTP endpoints and
|
||||
Events respectively. It also introduces a number of new concepts to Matrix.
|
||||
|
||||
Additions:
|
||||
- New section: Feature Profiles.
|
||||
- New section: Receipts.
|
||||
- New section: Room history visibility.
|
||||
- New event: ``m.receipt``.
|
||||
- New event: ``m.room.canonical_alias``
|
||||
- New event: ``m.room.history_visibility``
|
||||
- New keys: ``/createRoom`` - allows room "presets" using ``preset`` and
|
||||
``initial_state`` keys.
|
||||
- New endpoint: ``/tokenrefresh`` - Related to refreshing access tokens.
|
||||
|
||||
Modifications:
|
||||
- Convert most of the older HTTP APIs to Swagger documentation.
|
||||
- Convert most of the older event formats to JSON Schema.
|
||||
- Move selected client-server sections to be "Modules".
|
||||
|
||||
Specification changes in v0.1.0 (2015-06-01)
|
||||
============================================
|
||||
- First numbered release.
|
||||
- Restructure the format of Event information. Add more information.
|
||||
- Restructure the format of the Client-Server HTTP APIs.
|
@ -0,0 +1,30 @@
|
||||
This repository contains the documentation for Matrix.
|
||||
|
||||
Structure
|
||||
=========
|
||||
|
||||
- ``api`` : Contains the HTTP API specification.
|
||||
- ``drafts`` : Contains documents which will make it into the specification
|
||||
and/or supporting documentation at some point in the future.
|
||||
- ``event-schemas`` : Contains the `JSON Schema`_ for all Matrix events
|
||||
contained in the specification, along with example JSON files.
|
||||
- ``meta`` : Contains documents outlining the processes involved when writing
|
||||
documents, e.g. documentation style, guidelines.
|
||||
- ``scripts`` : Contains scripts to generate formatted versions of the
|
||||
documentation, typically HTML.
|
||||
- ``specification`` : Contains the specification split up into sections.
|
||||
- ``supporting-docs`` : Contains additional documents which explain design
|
||||
decisions, examples, use cases, etc.
|
||||
- ``templating`` : Contains the templates and templating system used to
|
||||
generate the spec.
|
||||
|
||||
Contributing
|
||||
============
|
||||
|
||||
Known issues with the specification are represented as JIRA issues at
|
||||
https://matrix.org/jira/browse/SPEC
|
||||
|
||||
If you want to ask more about the specification, or have suggestions for
|
||||
improvements, join us on ``#matrix-dev:matrix.org`` via https://matrix.org/beta.
|
||||
|
||||
.. _JSON Schema: http://json-schema.org/
|
@ -0,0 +1,118 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
|
||||
|
||||
def import_error(module, package, debian, error):
|
||||
sys.stderr.write((
|
||||
"Error importing %(module)s: %(error)r\n"
|
||||
"To install %(module)s run:\n"
|
||||
" pip install %(package)s\n"
|
||||
"or on Debian run:\n"
|
||||
" sudo apt-get install python-%(debian)s\n"
|
||||
) % locals())
|
||||
if __name__ == '__main__':
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
import jsonschema
|
||||
except ImportError as e:
|
||||
import_error("jsonschema", "jsonschema", "jsonschema", e)
|
||||
raise
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError as e:
|
||||
import_error("yaml", "PyYAML", "yaml", e)
|
||||
raise
|
||||
|
||||
|
||||
def check_parameter(filepath, request, parameter):
|
||||
schema = parameter.get("schema")
|
||||
example = None
|
||||
try:
|
||||
example_json = schema.get('example')
|
||||
if example_json and not schema.get("format") == "byte":
|
||||
example = json.loads(example_json)
|
||||
except Exception as e:
|
||||
raise ValueError("Error parsing JSON example request for %r" % (
|
||||
request
|
||||
), e)
|
||||
fileurl = "file://" + os.path.abspath(filepath)
|
||||
if example and schema:
|
||||
try:
|
||||
print ("Checking request schema for: %r %r" % (
|
||||
filepath, request
|
||||
))
|
||||
# Setting the 'id' tells jsonschema where the file is so that it
|
||||
# can correctly resolve relative $ref references in the schema
|
||||
schema['id'] = fileurl
|
||||
jsonschema.validate(example, schema)
|
||||
except Exception as e:
|
||||
raise ValueError("Error validating JSON schema for %r" % (
|
||||
request
|
||||
), e)
|
||||
|
||||
|
||||
def check_response(filepath, request, code, response):
|
||||
example = None
|
||||
try:
|
||||
example_json = response.get('examples', {}).get('application/json')
|
||||
if example_json:
|
||||
example = json.loads(example_json)
|
||||
except Exception as e:
|
||||
raise ValueError("Error parsing JSON example response for %r %r" % (
|
||||
request, code
|
||||
), e)
|
||||
schema = response.get('schema')
|
||||
fileurl = "file://" + os.path.abspath(filepath)
|
||||
if example and schema:
|
||||
try:
|
||||
print ("Checking response schema for: %r %r %r" % (
|
||||
filepath, request, code
|
||||
))
|
||||
# Setting the 'id' tells jsonschema where the file is so that it
|
||||
# can correctly resolve relative $ref references in the schema
|
||||
schema['id'] = fileurl
|
||||
jsonschema.validate(example, schema)
|
||||
except Exception as e:
|
||||
raise ValueError("Error validating JSON schema for %r %r" % (
|
||||
request, code
|
||||
), e)
|
||||
|
||||
|
||||
def check_swagger_file(filepath):
|
||||
with open(filepath) as f:
|
||||
swagger = yaml.load(f)
|
||||
|
||||
for path, path_api in swagger.get('paths', {}).items():
|
||||
|
||||
for method, request_api in path_api.items():
|
||||
request = "%s %s" % (method.upper(), path)
|
||||
for parameter in request_api.get('parameters', ()):
|
||||
if parameter['in'] == 'body':
|
||||
check_parameter(filepath, request, parameter)
|
||||
|
||||
try:
|
||||
responses = request_api['responses']
|
||||
except KeyError:
|
||||
raise ValueError("No responses for %r" % (request,))
|
||||
for code, response in responses.items():
|
||||
check_response(filepath, request, code, response)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
paths = sys.argv[1:]
|
||||
if not paths:
|
||||
paths = []
|
||||
for (root, dirs, files) in os.walk(os.curdir):
|
||||
for filename in files:
|
||||
if filename.endswith(".yaml"):
|
||||
paths.append(os.path.join(root, filename))
|
||||
for path in paths:
|
||||
try:
|
||||
check_swagger_file(path)
|
||||
except Exception as e:
|
||||
raise ValueError("Error checking file %r" % (path,), e)
|
@ -0,0 +1,127 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Content Repository API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
basePath: /_matrix/media/v1
|
||||
produces:
|
||||
- application/json
|
||||
- "*/*"
|
||||
paths:
|
||||
"/upload":
|
||||
post:
|
||||
summary: Upload some content to the content repository.
|
||||
produces: ["application/json"]
|
||||
parameters:
|
||||
- in: header
|
||||
name: Content-Type
|
||||
type: string
|
||||
description: The content type of the file being uploaded
|
||||
x-example: "Content-Type: audio/mpeg"
|
||||
- in: body
|
||||
name: "<content>"
|
||||
description: The content to be uploaded.
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
example: "<bytes>"
|
||||
format: byte
|
||||
responses:
|
||||
200:
|
||||
description: The MXC URI for the uploaded content.
|
||||
schema:
|
||||
type: object
|
||||
required: ["content_uri"]
|
||||
properties:
|
||||
content_uri:
|
||||
type: string
|
||||
description: "The MXC URI to the uploaded content."
|
||||
examples:
|
||||
"application/json": |-
|
||||
{
|
||||
"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"
|
||||
}
|
||||
"/download/{serverName}/{mediaId}":
|
||||
get:
|
||||
summary: "Download content from the content repository."
|
||||
produces: ["*/*"]
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: serverName
|
||||
x-example: matrix.org
|
||||
required: true
|
||||
description: |
|
||||
The server name from the ``mxc://`` URI (the authoritory component)
|
||||
- in: path
|
||||
type: string
|
||||
name: mediaId
|
||||
x-example: ascERGshawAWawugaAcauga
|
||||
required: true
|
||||
description: |
|
||||
The media ID from the ``mxc://`` URI (the path component)
|
||||
responses:
|
||||
200:
|
||||
description: "The content that was previously uploaded."
|
||||
headers:
|
||||
Content-Type:
|
||||
description: "The content type of the file that was previously uploaded."
|
||||
type: "string"
|
||||
Content-Disposition:
|
||||
description: "The name of the file that was previously uploaded, if set."
|
||||
type: "string"
|
||||
schema:
|
||||
type: file
|
||||
"/thumbnail/{serverName}/{mediaId}":
|
||||
get:
|
||||
summary: "Download a thumbnail of the content from the content repository."
|
||||
produces: ["image/jpeg", "image/png"]
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: serverName
|
||||
required: true
|
||||
x-example: matrix.org
|
||||
description: |
|
||||
The server name from the ``mxc://`` URI (the authoritory component)
|
||||
- in: path
|
||||
type: string
|
||||
name: mediaId
|
||||
x-example: ascERGshawAWawugaAcauga
|
||||
required: true
|
||||
description: |
|
||||
The media ID from the ``mxc://`` URI (the path component)
|
||||
- in: query
|
||||
type: integer
|
||||
x-example: 64
|
||||
name: width
|
||||
description: |-
|
||||
The *desired* width of the thumbnail. The actual thumbnail may not
|
||||
match the size specified.
|
||||
- in: query
|
||||
type: integer
|
||||
x-example: 64
|
||||
name: height
|
||||
description: |-
|
||||
The *desired* height of the thumbnail. The actual thumbnail may not
|
||||
match the size specified.
|
||||
- in: query
|
||||
type: string
|
||||
enum: ["crop", "scale"]
|
||||
name: method
|
||||
x-example: "scale"
|
||||
description: The desired resizing method.
|
||||
responses:
|
||||
200:
|
||||
description: "A thumbnail of the requested content."
|
||||
headers:
|
||||
Content-Type:
|
||||
description: "The content type of the thumbnail."
|
||||
type: "string"
|
||||
enum: ["image/jpeg", "image/png"]
|
||||
schema:
|
||||
type: file
|
||||
|
||||
|
@ -0,0 +1 @@
|
||||
v1-event-schema/core-event-schema
|
@ -0,0 +1,10 @@
|
||||
type: object
|
||||
description: A Matrix-level Error
|
||||
properties:
|
||||
errcode:
|
||||
type: string
|
||||
description: An error code.
|
||||
error:
|
||||
type: string
|
||||
description: A human-readable error message.
|
||||
required: ["errcode"]
|
@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["event_match", "profile_tag", "contains_display_name", "room_member_count"]
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"rule_id": {
|
||||
"type": "string"
|
||||
},
|
||||
"actions": {
|
||||
"items": {
|
||||
"type": ["object", "string"]
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"content": {
|
||||
"items": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "push_rule.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"override": {
|
||||
"items": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "push_rule.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"sender": {
|
||||
"items": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "push_rule.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"underride": {
|
||||
"items": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "push_rule.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
},
|
||||
"room": {
|
||||
"items": {
|
||||
"type": "object",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "push_rule.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"type": "array"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Directory API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1/directory
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/room/{roomAlias}":
|
||||
put:
|
||||
summary: Create a new mapping from room alias to room ID.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomAlias
|
||||
description: The room alias to set.
|
||||
required: true
|
||||
- in: body
|
||||
name: roomInfo
|
||||
description: Information about this room alias.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
room_id:
|
||||
type: string
|
||||
description: The room ID to set.
|
||||
responses:
|
||||
200:
|
||||
description: The mapping was created.
|
||||
schema:
|
||||
type: object # empty json object
|
||||
get:
|
||||
summary: Get the room ID corresponding to this room alias.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomAlias
|
||||
description: The room alias.
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: The room ID and other information for this alias.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
room_id:
|
||||
type: string
|
||||
description: The room ID for this room alias.
|
||||
servers:
|
||||
type: array
|
||||
description: A list of servers that are aware of this room ID.
|
||||
items:
|
||||
type: string
|
||||
description: A server which is aware of this room ID.
|
||||
404:
|
||||
description: There is no mapped room ID for this room alias.
|
||||
delete:
|
||||
summary: Remove a mapping of room alias to room ID.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomAlias
|
||||
description: The room alias to remove.
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: The mapping was removed.
|
||||
schema:
|
||||
type: object # empty json object
|
||||
|
@ -0,0 +1,147 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Registration and Login API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/login":
|
||||
post:
|
||||
summary: Authenticates the user.
|
||||
description: |-
|
||||
Authenticates the user by password, and issues an access token they can
|
||||
use to authorize themself in subsequent requests.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"username": "cheeky_monkey",
|
||||
"password": "ilovebananas"
|
||||
}
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: The fully qualified user ID or just local part of the user ID, to log in.
|
||||
password:
|
||||
type: string
|
||||
description: The user's password.
|
||||
required: ["username", "password"]
|
||||
responses:
|
||||
200:
|
||||
description: The user has been authenticated.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"user_id": "@cheeky_monkey:matrix.org",
|
||||
"access_token": "abc123",
|
||||
"home_server": "matrix.org"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
user_id:
|
||||
type: string
|
||||
description: The fully-qualified Matrix ID that has been registered.
|
||||
access_token:
|
||||
type: string
|
||||
description: |-
|
||||
An access token for the account.
|
||||
This access token can then be used to authorize other requests.
|
||||
The access token may expire at some point, and if so, it SHOULD come with a ``refresh_token``.
|
||||
There is no specific error message to indicate that a request has failed because
|
||||
an access token has expired; instead, if a client has reason to believe its
|
||||
access token is valid, and it receives an auth error, they should attempt to
|
||||
refresh for a new token on failure, and retry the request with the new token.
|
||||
refresh_token:
|
||||
type: string
|
||||
# TODO: Work out how to linkify /tokenrefresh
|
||||
description: |-
|
||||
(optional) A ``refresh_token`` may be exchanged for a new ``access_token`` using the /tokenrefresh API endpoint.
|
||||
home_server:
|
||||
type: string
|
||||
description: The hostname of the Home Server on which the account has been registered.
|
||||
403:
|
||||
description: |-
|
||||
The login attempt failed. For example, the password may have been incorrect.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"errcode": "M_FORBIDDEN"}
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
"/tokenrefresh":
|
||||
post:
|
||||
summary: Exchanges a refresh token for an access token.
|
||||
description: |-
|
||||
Exchanges a refresh token for a new access token.
|
||||
This is intended to be used if the access token has expired.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: body
|
||||
name: body
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"refresh_token": "a1b2c3"
|
||||
}
|
||||
properties:
|
||||
refresh_token:
|
||||
type: string
|
||||
description: The refresh token which was issued by the server.
|
||||
required: ["refresh_token"]
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
The refresh token was accepted, and a new access token has been issued.
|
||||
The passed refresh token is no longer valid and cannot be used.
|
||||
A new refresh token will have been returned unless some policy does
|
||||
not allow the user to continue to renew their session.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"access_token": "bearwithme123",
|
||||
"refresh_token": "exchangewithme987"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
access_token:
|
||||
type: string
|
||||
description: |-
|
||||
An access token for the account.
|
||||
This access token can then be used to authorize other requests.
|
||||
The access token may expire at some point, and if so, it SHOULD come with a ``refresh_token``.
|
||||
refresh_token:
|
||||
type: string
|
||||
description: (optional) A ``refresh_token`` may be exchanged for a new ``access_token`` using the TODO Linkify /tokenrefresh API endpoint.
|
||||
403:
|
||||
description: |-
|
||||
The exchange attempt failed. For example, the refresh token may have already been used.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"errcode": "M_FORBIDDEN"}
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
@ -0,0 +1,234 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Room Membership API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/rooms/{roomId}/join":
|
||||
post:
|
||||
summary: Start the requesting user participating in a particular room.
|
||||
description: |-
|
||||
This API starts a user participating in a particular room, if that user
|
||||
is allowed to participate in that room. After this call, the client is
|
||||
allowed to see all current state events in the room, and all subsequent
|
||||
events associated with the room until the user leaves the room.
|
||||
|
||||
After a user has joined a room, the room will appear as an entry in the
|
||||
response of the |initialSync| API.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room identifier or room alias to join.
|
||||
required: true
|
||||
x-example: "#monkeys:matrix.org"
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
The room has been joined.
|
||||
|
||||
The joined room ID must be returned in the ``room_id`` field.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"room_id": "!d41d8cd:matrix.org"}
|
||||
schema:
|
||||
type: object
|
||||
403:
|
||||
description: |-
|
||||
You do not have permission to join the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejection are:
|
||||
- The room is invite-only and the user was not invited.
|
||||
- The user has been banned from the room.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"errcode": "M_FORBIDDEN", "error": "You are not invited to this room."}
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
x-alias:
|
||||
canonical-link: "post-matrix-client-api-v1-rooms-roomid-join"
|
||||
aliases:
|
||||
- /join/{roomId}
|
||||
|
||||
# With an extra " " to disambiguate from the 3pid invite endpoint
|
||||
# The extra space makes it sort first for what I'm sure is a good reason.
|
||||
"/rooms/{roomId}/invite ":
|
||||
post:
|
||||
summary: Invite a user to participate in a particular room.
|
||||
description: |-
|
||||
*Note that there are two forms of this API, which are documented separately.
|
||||
This version of the API requires that the inviter knows the Matrix
|
||||
identifier of the invitee.*
|
||||
|
||||
This API invites a user to participate in a particular room.
|
||||
They do not start participating in the room until they actually join the
|
||||
room.
|
||||
|
||||
Only users currently in a particular room can invite other users to
|
||||
join that room.
|
||||
|
||||
If the user was invited to the room, the home server will append a
|
||||
``m.room.member`` event to the room.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room identifier (not alias) to which to invite the user.
|
||||
required: true
|
||||
x-example: "!d41d8cd:matrix.org"
|
||||
- in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"user_id": "@cheeky_monkey:matrix.org"
|
||||
}
|
||||
properties:
|
||||
user_id:
|
||||
type: string
|
||||
description: The fully qualified user ID of the invitee.
|
||||
required: ["user_id"]
|
||||
responses:
|
||||
200:
|
||||
description: The user has been invited to join the room.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object
|
||||
403:
|
||||
description: |-
|
||||
You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:
|
||||
- The invitee has been banned from the room.
|
||||
- The invitee is already a member of the room.
|
||||
- The inviter is not currently in the room.
|
||||
- The inviter's power level is insufficient to invite users to the room.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
|
||||
"/rooms/{roomId}/invite":
|
||||
post:
|
||||
summary: Invite a user to participate in a particular room.
|
||||
description: |-
|
||||
*Note that there are two forms of this API, which are documented separately.
|
||||
This version of the API does not require that the inviter know the Matrix
|
||||
identifier of the invitee, and instead relies on third party identifiers.
|
||||
The homeserver uses an identity server to perform the mapping from
|
||||
third party identifier to a Matrix identifier.*
|
||||
|
||||
This API invites a user to participate in a particular room.
|
||||
They do not start participating in the room until they actually join the
|
||||
room.
|
||||
|
||||
Only users currently in a particular room can invite other users to
|
||||
join that room.
|
||||
|
||||
If the identity server did know the Matrix user identifier for the
|
||||
third party identifier, the home server will append a ``m.room.member``
|
||||
event to the room.
|
||||
|
||||
If the identity server does not know a Matrix user identifier for the
|
||||
passed third party identifier, the homeserver will issue an invitation
|
||||
which can be accepted upon providing proof of ownership of the third
|
||||
party identifier. This is achieved by the identity server generating a
|
||||
token, which it gives to the inviting homeserver. The homeserver will
|
||||
add an ``m.room.third_party_invite`` event into the graph for the room,
|
||||
containing that token.
|
||||
|
||||
When the invitee binds the invited third party identifier to a Matrix
|
||||
user ID, the identity server will give the user a list of pending
|
||||
invitations, each containing:
|
||||
|
||||
- The room ID to which they were invited
|
||||
|
||||
- The token given to the homeserver
|
||||
|
||||
- A signature of the token, signed with the identity server's private key
|
||||
|
||||
- The matrix user ID who invited them to the room
|
||||
|
||||
If a token is requested from the identity server, the home server will
|
||||
append a ``m.room.third_party_invite`` event to the room.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room identifier (not alias) to which to invite the user.
|
||||
required: true
|
||||
x-example: "!d41d8cd:matrix.org"
|
||||
- in: body
|
||||
name: body
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"id_server": "matrix.org",
|
||||
"medium": "email",
|
||||
"address": "cheeky@monkey.com",
|
||||
"display_name": "A very cheeky monkey"
|
||||
}
|
||||
properties:
|
||||
id_server:
|
||||
type: string
|
||||
description: The hostname+port of the identity server which should be used for third party identifier lookups.
|
||||
medium:
|
||||
type: string
|
||||
# TODO: Link to identity service spec when it eixsts
|
||||
description: The kind of address being passed in the address field, for example ``email``.
|
||||
address:
|
||||
type: string
|
||||
description: The invitee's third party identifier.
|
||||
display_name:
|
||||
type: string
|
||||
description: A user-friendly string describing who has been invited. It should not contain the address of the invitee, to avoid leaking mappings between third party identities and matrix user IDs.
|
||||
required: ["id_server", "medium", "address", "display_name"]
|
||||
responses:
|
||||
200:
|
||||
description: The user has been invited to join the room.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object
|
||||
403:
|
||||
description: |-
|
||||
You do not have permission to invite the user to the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:
|
||||
- The invitee has been banned from the room.
|
||||
- The invitee is already a member of the room.
|
||||
- The inviter is not currently in the room.
|
||||
- The inviter's power level is insufficient to invite users to the room.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"errcode": "M_FORBIDDEN", "error": "@cheeky_monkey:matrix.org is banned from the room"}
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
@ -0,0 +1,208 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Presence API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/presence/{userId}/status":
|
||||
put:
|
||||
summary: Update this user's presence state.
|
||||
description: |-
|
||||
This API sets the given user's presence state. When setting the status,
|
||||
the activity time is updated to reflect that activity; the client does
|
||||
not need to specify the ``last_active_ago`` field. You cannot set the
|
||||
presence state of another user.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose presence state to update.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
- in: body
|
||||
name: presenceState
|
||||
description: The updated presence state.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"presence": "online",
|
||||
"status_msg": "I am here."
|
||||
}
|
||||
properties:
|
||||
presence:
|
||||
type: string
|
||||
enum: ["online", "offline", "unavailable", "free_for_chat"]
|
||||
description: The new presence state.
|
||||
status_msg:
|
||||
type: string
|
||||
description: "The status message to attach to this state."
|
||||
required: ["presence"]
|
||||
responses:
|
||||
200:
|
||||
description: The new presence state was set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
get:
|
||||
summary: Get this user's presence state.
|
||||
description: |-
|
||||
Get the given user's presence state.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose presence state to get.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The presence state for this user.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"presence": "unavailable",
|
||||
"last_active_ago": 420845,
|
||||
"status_msg": null
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
presence:
|
||||
type: string
|
||||
enum: ["online", "offline", "unavailable", "free_for_chat"]
|
||||
description: This user's presence.
|
||||
last_active_ago:
|
||||
type: integer
|
||||
description: |-
|
||||
The length of time in milliseconds since an action was performed
|
||||
by this user.
|
||||
status_msg:
|
||||
type: [string, "null"]
|
||||
description: The state message for this user if one was set.
|
||||
404:
|
||||
description: |-
|
||||
There is no presence state for this user. This user may not exist or
|
||||
isn't exposing presence information to you.
|
||||
"/presence/list/{userId}":
|
||||
post:
|
||||
summary: Add or remove users from this presence list.
|
||||
description: |-
|
||||
Adds or removes users from this presence list.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose presence list is being modified.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
- in: body
|
||||
name: presence_diff
|
||||
description: The modifications to make to this presence list.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"invite": [
|
||||
"@bob:matrix.org"
|
||||
],
|
||||
"drop": [
|
||||
"@alice:matrix.org"
|
||||
]
|
||||
}
|
||||
properties:
|
||||
invite:
|
||||
type: array
|
||||
description: A list of user IDs to add to the list.
|
||||
items:
|
||||
type: string
|
||||
description: A list of user IDs.
|
||||
drop:
|
||||
type: array
|
||||
description: A list of user IDs to remove from the list.
|
||||
items:
|
||||
type: string
|
||||
description: A list of user IDs.
|
||||
responses:
|
||||
200:
|
||||
description: The list was updated.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
get:
|
||||
summary: Get presence events for this presence list.
|
||||
description: |-
|
||||
Retrieve a list of presence events for every user on this list.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose presence list should be retrieved.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: A list of presence events for this list.
|
||||
examples:
|
||||
application/json: |-
|
||||
[
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://matrix.org/AfwefuigfWEfhuiPP",
|
||||
"displayname": "Alice Margatroid",
|
||||
"last_active_ago": 395,
|
||||
"presence": "offline",
|
||||
"user_id": "@alice:matrix.org"
|
||||
},
|
||||
"type": "m.presence"
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://matrix.org/FWEhuiwegfWEfhuiwf",
|
||||
"displayname": "Marisa Kirisame",
|
||||
"last_active_ago": 16874,
|
||||
"presence": "online",
|
||||
"user_id": "@marisa:matrix.org"
|
||||
},
|
||||
"type": "m.presence"
|
||||
}
|
||||
]
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
title: PresenceEvent
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/event.json"
|
@ -0,0 +1,195 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Profile API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/profile/{userId}/displayname":
|
||||
put:
|
||||
summary: Set the user's display name.
|
||||
description: |-
|
||||
This API sets the given user's display name. You must have permission to
|
||||
set this user's display name, e.g. you need to have their ``access_token``.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose display name to set.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
- in: body
|
||||
name: displayName
|
||||
description: The display name info.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"displayname": "Alice Margatroid"
|
||||
}
|
||||
properties:
|
||||
displayname:
|
||||
type: string
|
||||
description: The new display name for this user.
|
||||
responses:
|
||||
200:
|
||||
description: The display name was set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
get:
|
||||
summary: Get the user's display name.
|
||||
description: |-
|
||||
Get the user's display name. This API may be used to fetch the user's
|
||||
own displayname or to query the name of other users; either locally or
|
||||
on remote homeservers.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose display name to get.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The display name for this user.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"displayname": "Alice Margatroid"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
displayname:
|
||||
type: string
|
||||
description: The user's display name if they have set one.
|
||||
404:
|
||||
description: There is no display name for this user or this user does not exist.
|
||||
"/profile/{userId}/avatar_url":
|
||||
put:
|
||||
summary: Set the user's avatar URL.
|
||||
description: |-
|
||||
This API sets the given user's avatar URL. You must have permission to
|
||||
set this user's avatar URL, e.g. you need to have their ``access_token``.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose avatar URL to set.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
- in: body
|
||||
name: avatar_url
|
||||
description: The avatar url info.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"avatar_url": "mxc://matrix.org/wefh34uihSDRGhw34"
|
||||
}
|
||||
properties:
|
||||
avatar_url:
|
||||
type: string
|
||||
description: The new avatar URL for this user.
|
||||
responses:
|
||||
200:
|
||||
description: The avatar URL was set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
get:
|
||||
summary: Get the user's avatar URL.
|
||||
description: |-
|
||||
Get the user's avatar URL. This API may be used to fetch the user's
|
||||
own avatar URL or to query the URL of other users; either locally or
|
||||
on remote homeservers.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose avatar URL to get.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The avatar URL for this user.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"avatar_url": "mxc://matrix.org/SDGdghriugerRg"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
avatar_url:
|
||||
type: string
|
||||
description: The user's avatar URL if they have set one.
|
||||
404:
|
||||
description: There is no avatar URL for this user or this user does not exist.
|
||||
"/profile/{userId}":
|
||||
get:
|
||||
summary: Get this user's profile information.
|
||||
description: |-
|
||||
Get the combined profile information for this user. This API may be used
|
||||
to fetch the user's own profile information or other users; either
|
||||
locally or on remote homeservers. This API may return keys which are not
|
||||
limited to ``displayname`` or ``avatar_url``.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user whose avatar URL to get.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The avatar URL for this user.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"avatar_url": "mxc://matrix.org/SDGdghriugerRg",
|
||||
"displayname": "Alice Margatroid"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
avatar_url:
|
||||
type: string
|
||||
description: The user's avatar URL if they have set one.
|
||||
displayname:
|
||||
type: string
|
||||
description: The user's display name if they have set one.
|
||||
404:
|
||||
description: There is no profile information for this user or this user does not exist.
|
@ -0,0 +1,192 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Push Notification API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/push/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
paths:
|
||||
"/notify":
|
||||
post:
|
||||
summary: Notify a push gateway about an event.
|
||||
description: |-
|
||||
This endpoint is invoked by HTTP pushers to notify a push gateway about
|
||||
an event.
|
||||
*NB: Notifications are sent to the URL configured when the pusher is
|
||||
created. This means that the HTTP path may be different depending on the
|
||||
push gateway.*
|
||||
parameters:
|
||||
- in: body
|
||||
name: notification
|
||||
description: Information about the push notification.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"notification": {
|
||||
"id": "$3957tyerfgewrf384",
|
||||
"room_id": "!slw48wfj34rtnrf:example.com",
|
||||
"type": "m.room.message",
|
||||
"sender": "@exampleuser:matrix.org",
|
||||
"sender_display_name": "Major Tom",
|
||||
"room_name": "Mission Control",
|
||||
"room_alias": "#exampleroom:matrix.org",
|
||||
"prio": "high",
|
||||
"content": {
|
||||
"msgtype": "m.text",
|
||||
"body": "I'm floating in a most peculiar way."
|
||||
}
|
||||
},
|
||||
"counts": {
|
||||
"unread" : 2,
|
||||
"missed_calls": 1
|
||||
},
|
||||
"devices": [
|
||||
{
|
||||
"app_id": "org.matrix.matrixConsole.ios",
|
||||
"pushkey": "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/",
|
||||
"pushkey_ts": 12345678,
|
||||
"data" : {},
|
||||
"tweaks": {
|
||||
"sound": "bing"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
required: ["notification", "counts", "devices"]
|
||||
properties:
|
||||
notification:
|
||||
type: object
|
||||
description: Information about the push notification
|
||||
required: ["id", "room_id", "type", "sender"]
|
||||
properties:
|
||||
id:
|
||||
type: string
|
||||
description: |-
|
||||
An identifier for this notification that may be used to
|
||||
detect duplicate notification requests. This is not
|
||||
necessarily the ID of the event that triggered the
|
||||
notification.
|
||||
room_id:
|
||||
type: string
|
||||
description: The ID of the room in which this event occurred.
|
||||
type:
|
||||
type: string
|
||||
description: The type of the event as in the event's ``type`` field.
|
||||
sender:
|
||||
type: string
|
||||
description: The sender of the event as in the corresponding event field.
|
||||
sender_display_name:
|
||||
type: string
|
||||
description: |-
|
||||
The current display name of the sender in the room in which
|
||||
the event occurred.
|
||||
room_name:
|
||||
type: string
|
||||
description: The name of the room in which the event occurred.
|
||||
room_alias:
|
||||
type: string
|
||||
description: An alias to display for the room in which the event occurred.
|
||||
user_is_target:
|
||||
type: boolean
|
||||
description: |-
|
||||
This is true if the user receiving the notification is the
|
||||
subject of a member event (i.e. the ``state_key`` of the
|
||||
member event is equal to the user's Matrix ID).
|
||||
prio:
|
||||
type: string
|
||||
enum: ["high", "low"]
|
||||
description: |-
|
||||
The priority of the notification. If omitted, ``high`` is
|
||||
assumed. This may be used by push gateways to deliver less
|
||||
time-sensitive notifications in a way that will preserve
|
||||
battery power on mobile devices.
|
||||
content:
|
||||
type: object
|
||||
title: EventContent
|
||||
description: |-
|
||||
The ``content`` field from the event, if present. If the
|
||||
event had no content field, this field is omitted.
|
||||
counts:
|
||||
type: object
|
||||
description: |-
|
||||
This is a dictionary of the current number of unacknowledged
|
||||
communications for the recipient user. Counts whose value is
|
||||
zero are omitted.
|
||||
properties:
|
||||
unread:
|
||||
type: integer
|
||||
description: |-
|
||||
The number of unread messages a user has across all of the
|
||||
rooms they are a member of.
|
||||
missed_calls:
|
||||
type: integer
|
||||
description: |-
|
||||
The number of unacknowledged missed calls a user has
|
||||
across all rooms of which they are a member.
|
||||
devices:
|
||||
type: array
|
||||
title: Devices
|
||||
description: |-
|
||||
This is an array of devices that the notification should be sent to.
|
||||
items:
|
||||
type: object
|
||||
properties:
|
||||
app_id:
|
||||
type: string
|
||||
description: |-
|
||||
The app_id given when the pusher was created.
|
||||
pushkey:
|
||||
type: string
|
||||
description: The pushkey given when the pusher was created.
|
||||
pushkey_ts:
|
||||
type: integer
|
||||
description: |-
|
||||
The unix timestamp (in seconds) when the
|
||||
pushkey was last updated.
|
||||
data:
|
||||
type: object
|
||||
title: PusherData
|
||||
description: |-
|
||||
A dictionary of additional pusher-specific data. For
|
||||
'http' pushers, this is the data dictionary passed in at
|
||||
pusher creation minus the ``url`` key.
|
||||
tweaks:
|
||||
type: object
|
||||
title: Tweaks
|
||||
description: |-
|
||||
A dictionary of customisations made to the way this
|
||||
notification is to be presented. These are added by push rules.
|
||||
responses:
|
||||
200:
|
||||
description: A list of rejected push keys.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"rejected": [ "V2h5IG9uIGVhcnRoIGRpZCB5b3UgZGVjb2RlIHRoaXM/" ]
|
||||
}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
properties:
|
||||
rejected:
|
||||
type: array
|
||||
description: |-
|
||||
A list of all pushkeys given in the notification request that
|
||||
are not valid. These could have been rejected by an upstream
|
||||
gateway because they have expired or have never been valid.
|
||||
Homeservers must cease sending notification requests for these
|
||||
pushkeys and remove the associated pushers. It may not
|
||||
necessarily be the notification in the request that failed:
|
||||
it could be that a previous notification to the same pushkey
|
||||
failed.
|
||||
items:
|
||||
type: string
|
||||
description: A pushkey
|
||||
|
@ -0,0 +1,144 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Push API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/pushers/set":
|
||||
post:
|
||||
summary: Modify a pusher for this user on the homeserver.
|
||||
description: |-
|
||||
This endpoint allows the creation, modification and deletion of `pushers`_
|
||||
for this user ID. The behaviour of this endpoint varies depending on the
|
||||
values in the JSON body.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: body
|
||||
name: pusher
|
||||
description: The pusher information
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"lang": "en",
|
||||
"kind": "http",
|
||||
"app_display_name": "Mat Rix",
|
||||
"device_display_name": "iPhone 9",
|
||||
"app_id": "com.example.app.ios",
|
||||
"profile_tag": "4bea66906d0111e59d70feff819cdc9f",
|
||||
"pushkey": "APA91bHPRgkF3JUikC4ENAHEeMrd41Zxv3hVZjC9KtT8OvPVGJ-hQMRKRrZuJAEcl7B338qju59zJMjw2DELjzEvxwYv7hH5Ynpc1ODQ0aT4U4OFEeco8ohsN5PjL1iC2dNtk2BAokeMCg2ZXKqpc8FXKmhX94kIxQ",
|
||||
"data": {
|
||||
"url": "https://push-gateway.location.here"
|
||||
},
|
||||
"append": false
|
||||
}
|
||||
properties:
|
||||
pushkey:
|
||||
type: string
|
||||
description: |-
|
||||
This is a unique identifier for this pusher. The value you
|
||||
should use for this is the routing or destination address
|
||||
information for the notification, for example, the APNS token
|
||||
for APNS or the Registration ID for GCM. If your notification
|
||||
client has no such concept, use any unique identifier.
|
||||
Max length, 512 bytes.
|
||||
kind:
|
||||
type: string
|
||||
enum: ["http", null]
|
||||
description: |-
|
||||
The kind of pusher to configure. ``"http"`` makes a pusher that
|
||||
sends HTTP pokes. ``null`` deletes the pusher.
|
||||
profile_tag:
|
||||
type: string
|
||||
description: |-
|
||||
This is a string that determines what set of device rules will
|
||||
be matched when evaluating push rules for this pusher. It is
|
||||
an arbitrary string. Multiple devices may use the same
|
||||
``profile_tag``. It is advised that when an app's data is
|
||||
copied or restored to a different device, this value remain
|
||||
the same. Client apps should offer ways to change the
|
||||
``profile_tag``, optionally copying rules from the old
|
||||
profile tag. Max length, 32 bytes.
|
||||
app_id:
|
||||
type: string
|
||||
description: |-
|
||||
This is a reverse-DNS style identifier for the application.
|
||||
It is recommended that this end with the platform, such that
|
||||
different platform versions get different app identifiers.
|
||||
Max length, 64 chars.
|
||||
app_display_name:
|
||||
type: string
|
||||
description: |-
|
||||
A string that will allow the user to identify what application
|
||||
owns this pusher.
|
||||
device_display_name:
|
||||
type: string
|
||||
description: |-
|
||||
A string that will allow the user to identify what device owns
|
||||
this pusher.
|
||||
lang:
|
||||
type: string
|
||||
description: |-
|
||||
The preferred language for receiving notifications (e.g. 'en'
|
||||
or 'en-US')
|
||||
data:
|
||||
type: object
|
||||
description: |-
|
||||
A dictionary of information for the pusher implementation
|
||||
itself. If ``kind`` is ``http``, this should contain ``url``
|
||||
which is the URL to use to send notifications to.
|
||||
properties:
|
||||
url:
|
||||
type: string
|
||||
description: |-
|
||||
Required if ``kind`` is ``http``. The URL to use to send
|
||||
notifications to.
|
||||
append:
|
||||
type: boolean
|
||||
description: |-
|
||||
If true, the homeserver should add another pusher with the
|
||||
given pushkey and App ID in addition to any others with
|
||||
different user IDs. Otherwise, the Home Server must remove any
|
||||
other pushers with the same App ID and pushkey for different
|
||||
users. The default is ``false``.
|
||||
required: ['profile_tag', 'kind', 'app_id', 'app_display_name',
|
||||
'device_display_name', 'pushkey', 'lang', 'data']
|
||||
responses:
|
||||
200:
|
||||
description: The pusher was set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
400:
|
||||
description: One or more of the pusher values were invalid.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"error": "Missing parameters: lang, data",
|
||||
"errcode": "M_MISSING_PARAM"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
|
@ -0,0 +1,488 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Push Rules API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/pushrules/":
|
||||
get:
|
||||
summary: Retrieve all push rulesets.
|
||||
description: |-
|
||||
Retrieve all push rulesets for this user. Clients can "drill-down" on
|
||||
the rulesets by suffixing a ``scope`` to this path e.g.
|
||||
``/pushrules/global/``. This will return a subset of this data under the
|
||||
specified key e.g. the ``global`` key.
|
||||
security:
|
||||
- accessToken: []
|
||||
responses:
|
||||
200:
|
||||
description: All the push rulesets for this user.
|
||||
schema:
|
||||
type: object
|
||||
required: ["device", "global"]
|
||||
properties:
|
||||
device:
|
||||
type: object
|
||||
title: Devices
|
||||
description: A dictionary of profile tags to rulesets.
|
||||
additionalProperties:
|
||||
x-pattern: "$PROFILE_TAG"
|
||||
type: object
|
||||
description: The ruleset for this profile tag.
|
||||
title: Ruleset
|
||||
allOf: [
|
||||
"$ref": "definitions/push_ruleset.json"
|
||||
]
|
||||
global:
|
||||
type: object
|
||||
description: The global ruleset.
|
||||
title: Ruleset
|
||||
allOf: [
|
||||
"$ref": "definitions/push_ruleset.json"
|
||||
]
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"device": {},
|
||||
"global": {
|
||||
"content": [
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"pattern": "alice",
|
||||
"rule_id": ".m.rule.contains_user_name"
|
||||
}
|
||||
],
|
||||
"override": [
|
||||
{
|
||||
"actions": [
|
||||
"dont_notify"
|
||||
],
|
||||
"conditions": [],
|
||||
"default": true,
|
||||
"enabled": false,
|
||||
"rule_id": ".m.rule.master"
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"dont_notify"
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "content.msgtype",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.notice"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.suppress_notices"
|
||||
}
|
||||
],
|
||||
"room": [],
|
||||
"sender": [],
|
||||
"underride": [
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "ring"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "type",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.call.invite"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.call"
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight"
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"kind": "contains_display_name"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.contains_display_name"
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"is": "2",
|
||||
"kind": "room_member_count"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.room_one_to_one"
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "sound",
|
||||
"value": "default"
|
||||
},
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "type",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.room.member"
|
||||
},
|
||||
{
|
||||
"key": "content.membership",
|
||||
"kind": "event_match",
|
||||
"pattern": "invite"
|
||||
},
|
||||
{
|
||||
"key": "state_key",
|
||||
"kind": "event_match",
|
||||
"pattern": "@alice:example.com"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.invite_for_me"
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "type",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.room.member"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.member_event"
|
||||
},
|
||||
{
|
||||
"actions": [
|
||||
"notify",
|
||||
{
|
||||
"set_tweak": "highlight",
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"conditions": [
|
||||
{
|
||||
"key": "type",
|
||||
"kind": "event_match",
|
||||
"pattern": "m.room.message"
|
||||
}
|
||||
],
|
||||
"default": true,
|
||||
"enabled": true,
|
||||
"rule_id": ".m.rule.message"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
"/pushrules/{scope}/{kind}/{ruleId}":
|
||||
get:
|
||||
summary: Retrieve a push rule.
|
||||
description: |-
|
||||
Retrieve a single specified push rule.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: scope
|
||||
required: true
|
||||
x-example: "global"
|
||||
description: |-
|
||||
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||
rules or device rules for the given ``profile_tag``.
|
||||
- in: path
|
||||
type: string
|
||||
name: kind
|
||||
required: true
|
||||
x-example: room
|
||||
enum: ["override", "underride", "sender", "room", "content"]
|
||||
description: |
|
||||
The kind of rule
|
||||
- in: path
|
||||
type: string
|
||||
name: ruleId
|
||||
required: true
|
||||
x-example: "#spam:example.com"
|
||||
description: |
|
||||
The identifier for the rule.
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
The specific push rule. This will also include keys specific to the
|
||||
rule itself such as the rule's ``actions`` and ``conditions`` if set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"actions": [
|
||||
"dont_notify"
|
||||
],
|
||||
"rule_id": "#spam:matrix.org",
|
||||
"enabled": true
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
description: The push rule.
|
||||
title: PushRule
|
||||
allOf: [
|
||||
"$ref": "definitions/push_rule.json"
|
||||
]
|
||||
delete:
|
||||
summary: Delete a push rule.
|
||||
description: |-
|
||||
This endpoint removes the push rule defined in the path.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: scope
|
||||
required: true
|
||||
x-example: "global"
|
||||
description: |-
|
||||
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||
rules or device rules for the given ``profile_tag``.
|
||||
- in: path
|
||||
type: string
|
||||
name: kind
|
||||
required: true
|
||||
x-example: room
|
||||
enum: ["override", "underride", "sender", "room", "content"]
|
||||
description: |
|
||||
The kind of rule
|
||||
- in: path
|
||||
type: string
|
||||
name: ruleId
|
||||
required: true
|
||||
x-example: "#spam:example.com"
|
||||
description: |
|
||||
The identifier for the rule.
|
||||
responses:
|
||||
200:
|
||||
description: The push rule was deleted.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
put:
|
||||
summary: Add or change a push rule.
|
||||
description: |-
|
||||
This endpoint allows the creation, modification and deletion of pushers
|
||||
for this user ID. The behaviour of this endpoint varies depending on the
|
||||
values in the JSON body.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: scope
|
||||
required: true
|
||||
x-example: "global"
|
||||
description: |-
|
||||
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||
rules or device rules for the given ``profile_tag``.
|
||||
- in: path
|
||||
type: string
|
||||
name: kind
|
||||
required: true
|
||||
x-example: room
|
||||
enum: ["override", "underride", "sender", "room", "content"]
|
||||
description: |
|
||||
The kind of rule
|
||||
- in: path
|
||||
type: string
|
||||
name: ruleId
|
||||
required: true
|
||||
x-example: "#spam:example.com"
|
||||
description: |
|
||||
The identifier for the rule.
|
||||
- in: query
|
||||
type: string
|
||||
name: before
|
||||
required: false
|
||||
x-example: someRuleId
|
||||
description: |-
|
||||
Use 'before' with a ``rule_id`` as its value to make the new rule the
|
||||
next-most important rule with respect to the given rule.
|
||||
- in: query
|
||||
type: string
|
||||
name: after
|
||||
required: false
|
||||
x-example: anotherRuleId
|
||||
description: |-
|
||||
This makes the new rule the next-less important rule relative to the
|
||||
given rule.
|
||||
- in: body
|
||||
name: pushrule
|
||||
description: |-
|
||||
The push rule data. Additional top-level keys may be present depending
|
||||
on the parameters for the rule ``kind``.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"pattern": "cake*lie",
|
||||
"actions": ["notify"]
|
||||
}
|
||||
properties:
|
||||
actions:
|
||||
type: array
|
||||
description: |-
|
||||
The action(s) to perform when the conditions for this rule are met.
|
||||
items:
|
||||
type: string
|
||||
enum: ["notify", "dont_notify", "coalesce", "set_tweak"]
|
||||
# TODO: type: object e.g. {"set_sound":"beeroclock.wav"} :/
|
||||
conditions:
|
||||
type: array
|
||||
description: |-
|
||||
The conditions that must hold true for an event in order for a
|
||||
rule to be applied to an event. A rule with no conditions
|
||||
always matches.
|
||||
items:
|
||||
type: object
|
||||
title: conditions
|
||||
allOf: [ "$ref": "definitions/push_condition.json" ]
|
||||
required: ["actions"]
|
||||
responses:
|
||||
200:
|
||||
description: The pusher was set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
400:
|
||||
description: There was a problem configuring this push rule.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"error": "before/after rule not found: someRuleId",
|
||||
"errcode": "M_UNKNOWN"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
"/pushrules/{scope}/{kind}/{ruleId}/enabled":
|
||||
put:
|
||||
summary: "Enable or disable a push rule."
|
||||
description: |-
|
||||
This endpoint allows clients to enable or disable the specified push rule.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: scope
|
||||
required: true
|
||||
x-example: "global"
|
||||
description: |-
|
||||
Either ``global`` or ``device/<profile_tag>`` to specify global
|
||||
rules or device rules for the given ``profile_tag``.
|
||||
- in: path
|
||||
type: string
|
||||
name: kind
|
||||
required: true
|
||||
x-example: room
|
||||
enum: ["override", "underride", "sender", "room", "content"]
|
||||
description: |
|
||||
The kind of rule
|
||||
- in: path
|
||||
type: string
|
||||
name: ruleId
|
||||
required: true
|
||||
x-example: "#spam:example.com"
|
||||
description: |
|
||||
The identifier for the rule.
|
||||
- in: body
|
||||
name: <body>
|
||||
description: |
|
||||
Whether the push rule is enabled or not.
|
||||
required: true
|
||||
schema:
|
||||
type: boolean
|
||||
example: |-
|
||||
true
|
||||
responses:
|
||||
200:
|
||||
description: The push rule was enabled or disabled.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
@ -0,0 +1,448 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Rooms API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/rooms/{roomId}/state/{eventType}/{stateKey}":
|
||||
get:
|
||||
summary: Get the state identified by the type and key.
|
||||
description: |-
|
||||
Looks up the contents of a state event in a room. If the user is
|
||||
joined to the room then the state is taken from the current
|
||||
state of the room. If the user has left the room then the state is
|
||||
taken from the state of the room when they left.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room to look up the state in.
|
||||
required: true
|
||||
x-example: "!636q39766251:example.com"
|
||||
- in: path
|
||||
type: string
|
||||
name: eventType
|
||||
description: The type of state to look up.
|
||||
required: true
|
||||
x-example: "m.room.name"
|
||||
- in: path
|
||||
type: string
|
||||
name: stateKey
|
||||
description: The key of the state to look up. Defaults to the empty string.
|
||||
required: true
|
||||
x-example: ""
|
||||
responses:
|
||||
200:
|
||||
description: The content of the state event.
|
||||
examples:
|
||||
application/json: |-
|
||||
{"name": "Example room name"}
|
||||
schema:
|
||||
type: object
|
||||
404:
|
||||
description: The room has no state with the given type or key.
|
||||
403:
|
||||
description: >
|
||||
You aren't a member of the room and weren't previously a
|
||||
member of the room.
|
||||
|
||||
"/rooms/{roomId}/state":
|
||||
get:
|
||||
summary: Get all state events in the current state of a room.
|
||||
description: |-
|
||||
Get the state events for the current state of a room.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room to look up the state for.
|
||||
required: true
|
||||
x-example: "!636q39766251:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The current state of the room
|
||||
examples:
|
||||
application/json: |-
|
||||
[
|
||||
{
|
||||
"age": 7148266897,
|
||||
"content": {
|
||||
"join_rule": "public"
|
||||
},
|
||||
"event_id": "$14259997323TLwtb:example.com",
|
||||
"origin_server_ts": 1425999732392,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.join_rules",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 6547561012,
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
|
||||
"displayname": null,
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1426600438280zExKY:example.com",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 1426600438277,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "@alice:example.com",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 7148267200,
|
||||
"content": {
|
||||
"creator": "@alice:example.com"
|
||||
},
|
||||
"event_id": "$14259997320KhbwJ:example.com",
|
||||
"origin_server_ts": 1425999732089,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 1622568720,
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1431525430134MxlLX:example.com",
|
||||
"origin_server_ts": 1431525430569,
|
||||
"replaces_state": "$142652023736BSXcM:example.com",
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "@bob:example.com",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@bob:example.com"
|
||||
},
|
||||
{
|
||||
"age": 7148267004,
|
||||
"content": {
|
||||
"ban": 50,
|
||||
"events": {
|
||||
"m.room.name": 100,
|
||||
"m.room.power_levels": 100
|
||||
},
|
||||
"events_default": 0,
|
||||
"kick": 50,
|
||||
"redact": 50,
|
||||
"state_default": 50,
|
||||
"users": {
|
||||
"@alice:example.com": 100
|
||||
},
|
||||
"users_default": 0
|
||||
},
|
||||
"event_id": "$14259997322mqfaq:example.com",
|
||||
"origin_server_ts": 1425999732285,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.power_levels",
|
||||
"user_id": "@alice:example.com"
|
||||
}
|
||||
]
|
||||
schema:
|
||||
type: array
|
||||
title: RoomState
|
||||
description: |-
|
||||
If the user is a member of the room this will be the
|
||||
current state of the room as a list of events. If the user
|
||||
has left the room then this will be the state of the room
|
||||
when they left as a list of events.
|
||||
items:
|
||||
title: StateEvent
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/state_event.json"
|
||||
403:
|
||||
description: >
|
||||
You aren't a member of the room and weren't previously a
|
||||
member of the room.
|
||||
|
||||
"/rooms/{roomId}/initialSync":
|
||||
get:
|
||||
summary: Snapshot the current state of a room and its most recent messages.
|
||||
description: |-
|
||||
Get a copy of the current state and the most recent messages in a room.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room to get the data.
|
||||
required: true
|
||||
x-example: "!636q39766251:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: The current state of the room
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"membership": "join",
|
||||
"messages": {
|
||||
"chunk": [
|
||||
{
|
||||
"age": 343513403,
|
||||
"content": {
|
||||
"body": "foo",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14328044851tzTJS:example.com",
|
||||
"origin_server_ts": 1432804485886,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 343511809,
|
||||
"content": {
|
||||
"body": "bar",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14328044872spjFg:example.com",
|
||||
"origin_server_ts": 1432804487480,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@bob:example.com"
|
||||
}
|
||||
],
|
||||
"end": "s3456_9_0",
|
||||
"start": "t44-3453_9_0"
|
||||
},
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state": [
|
||||
{
|
||||
"age": 7148266897,
|
||||
"content": {
|
||||
"join_rule": "public"
|
||||
},
|
||||
"event_id": "$14259997323TLwtb:example.com",
|
||||
"origin_server_ts": 1425999732392,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.join_rules",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 6547561012,
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
|
||||
"displayname": null,
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1426600438280zExKY:example.com",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 1426600438277,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "@alice:example.com",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 7148267200,
|
||||
"content": {
|
||||
"creator": "@alice:example.com"
|
||||
},
|
||||
"event_id": "$14259997320KhbwJ:example.com",
|
||||
"origin_server_ts": 1425999732089,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 1622568720,
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1431525430134MxlLX:example.com",
|
||||
"origin_server_ts": 1431525430569,
|
||||
"replaces_state": "$142652023736BSXcM:example.com",
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "@bob:example.com",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@bob:example.com"
|
||||
},
|
||||
{
|
||||
"age": 7148267004,
|
||||
"content": {
|
||||
"ban": 50,
|
||||
"events": {
|
||||
"m.room.name": 100,
|
||||
"m.room.power_levels": 100
|
||||
},
|
||||
"events_default": 0,
|
||||
"kick": 50,
|
||||
"redact": 50,
|
||||
"state_default": 50,
|
||||
"users": {
|
||||
"@alice:example.com": 100
|
||||
},
|
||||
"users_default": 0
|
||||
},
|
||||
"event_id": "$14259997322mqfaq:example.com",
|
||||
"origin_server_ts": 1425999732285,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "",
|
||||
"type": "m.room.power_levels",
|
||||
"user_id": "@alice:example.com"
|
||||
}
|
||||
],
|
||||
"visibility": "private"
|
||||
}
|
||||
schema:
|
||||
title: RoomInfo
|
||||
type: object
|
||||
properties:
|
||||
room_id:
|
||||
type: string
|
||||
description: "The ID of this room."
|
||||
membership:
|
||||
type: string
|
||||
description: "The user's membership state in this room."
|
||||
enum: ["invite", "join", "leave", "ban"]
|
||||
messages:
|
||||
type: object
|
||||
title: PaginationChunk
|
||||
description: "The pagination chunk for this room."
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the first value in ``chunk``.
|
||||
Used for pagination.
|
||||
end:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the last value in ``chunk``.
|
||||
Used for pagination.
|
||||
chunk:
|
||||
type: array
|
||||
description: |-
|
||||
If the user is a member of the room this will be a
|
||||
list of the most recent messages for this room. If
|
||||
the user has left the room this will be the
|
||||
messages that preceeded them leaving. This array
|
||||
will consist of at most ``limit`` elements.
|
||||
items:
|
||||
type: object
|
||||
title: RoomEvent
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/room_event.json"
|
||||
required: ["start", "end", "chunk"]
|
||||
state:
|
||||
type: array
|
||||
description: |-
|
||||
If the user is a member of the room this will be the
|
||||
current state of the room as a list of events. If the
|
||||
user has left the room this will be the state of the
|
||||
room when they left it.
|
||||
items:
|
||||
title: StateEvent
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/state_event.json"
|
||||
visibility:
|
||||
type: string
|
||||
enum: ["private", "public"]
|
||||
description: |-
|
||||
Whether this room is visible to the ``/publicRooms`` API
|
||||
or not."
|
||||
required: ["room_id", "membership"]
|
||||
403:
|
||||
description: >
|
||||
You aren't a member of the room and weren't previously a
|
||||
member of the room.
|
||||
|
||||
"/rooms/{roomId}/members":
|
||||
get:
|
||||
summary: Get the m.room.member events for the room.
|
||||
description:
|
||||
Get the list of members for this room.
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room to get the member events for.
|
||||
required: true
|
||||
x-example: "!636q39766251:example.com"
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
A list of members of the room. If you are joined to the room then
|
||||
this will be the current members of the room. If you have left te
|
||||
room then this will be the members of the room when you left.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"chunk": [
|
||||
{
|
||||
"age": 6547561012,
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
|
||||
"displayname": null,
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1426600438280zExKY:example.com",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 1426600438277,
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "@alice:example.com",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@alice:example.com"
|
||||
},
|
||||
{
|
||||
"age": 1622568720,
|
||||
"content": {
|
||||
"avatar_url": "mxc://example.com/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1431525430134MxlLX:example.com",
|
||||
"origin_server_ts": 1431525430569,
|
||||
"replaces_state": "$142652023736BSXcM:example.com",
|
||||
"room_id": "!636q39766251:example.com",
|
||||
"state_key": "@bob:example.com",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@bob:example.com"
|
||||
}
|
||||
]
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
chunk:
|
||||
type: array
|
||||
items:
|
||||
title: MemberEvent
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": "v1-event-schema/m.room.member"
|
||||
403:
|
||||
description: >
|
||||
You aren't a member of the room and weren't previously a
|
||||
member of the room.
|
||||
|
@ -0,0 +1,371 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Sync API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/events":
|
||||
get:
|
||||
summary: Listen on the event stream.
|
||||
description: |-
|
||||
This will listen for new events and return them to the caller. This will
|
||||
block until an event is received, or until the ``timeout`` is reached.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: query
|
||||
type: string
|
||||
name: from
|
||||
description: The token to stream from.
|
||||
required: false
|
||||
x-example: "s3456_9_0"
|
||||
- in: query
|
||||
type: integer
|
||||
name: timeout
|
||||
description: The maximum time in milliseconds to wait for an event.
|
||||
required: false
|
||||
x-example: "35000"
|
||||
- in: query
|
||||
type: string
|
||||
name: archived
|
||||
description: |-
|
||||
Whether to include rooms that the user has left. If absent then
|
||||
only rooms that the user has been invited to or has joined are
|
||||
included. If set to "true" then rooms that the user has left are
|
||||
included as well.
|
||||
required: false
|
||||
x-example: "true"
|
||||
responses:
|
||||
200:
|
||||
description: "The events received, which may be none."
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"start": "s3456_9_0",
|
||||
"end": "s3457_9_0",
|
||||
"chunk": [
|
||||
{
|
||||
"age": 32,
|
||||
"content": {
|
||||
"body": "incoming message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14328055551tzaee:localhost",
|
||||
"origin_server_ts": 1432804485886,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@bob:localhost"
|
||||
}
|
||||
]
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the first value in ``chunk``. Used
|
||||
for pagination.
|
||||
end:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the last value in ``chunk``. Used
|
||||
for pagination.
|
||||
chunk:
|
||||
type: array
|
||||
description: "An array of events."
|
||||
items:
|
||||
type: object
|
||||
title: RoomEvent
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/room_event.json"
|
||||
400:
|
||||
description: "Bad pagination ``from`` parameter."
|
||||
"/initialSync":
|
||||
get:
|
||||
summary: Get the user's current state.
|
||||
description: |-
|
||||
This returns the full state for this user, with an optional limit on the
|
||||
number of messages per room to return.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: query
|
||||
type: integer
|
||||
name: limit
|
||||
description: The maximum number of messages to return for each room.
|
||||
required: false
|
||||
x-example: "2"
|
||||
responses:
|
||||
200:
|
||||
description: The user's current state.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"end": "s3456_9_0",
|
||||
"presence": [
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://localhost/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
|
||||
"displayname": "Bob",
|
||||
"last_active_ago": 31053,
|
||||
"presence": "online",
|
||||
"user_id": "@bob:localhost"
|
||||
},
|
||||
"type": "m.presence"
|
||||
}
|
||||
],
|
||||
"rooms": [
|
||||
{
|
||||
"membership": "join",
|
||||
"messages": {
|
||||
"chunk": [
|
||||
{
|
||||
"age": 343513403,
|
||||
"content": {
|
||||
"body": "foo",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14328044851tzTJS:localhost",
|
||||
"origin_server_ts": 1432804485886,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@alice:localhost"
|
||||
},
|
||||
{
|
||||
"age": 343511809,
|
||||
"content": {
|
||||
"body": "bar",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14328044872spjFg:localhost",
|
||||
"origin_server_ts": 1432804487480,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@bob:localhost"
|
||||
}
|
||||
],
|
||||
"end": "s3456_9_0",
|
||||
"start": "t44-3453_9_0"
|
||||
},
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"state": [
|
||||
{
|
||||
"age": 7148266897,
|
||||
"content": {
|
||||
"join_rule": "public"
|
||||
},
|
||||
"event_id": "$14259997323TLwtb:localhost",
|
||||
"origin_server_ts": 1425999732392,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.join_rules",
|
||||
"user_id": "@alice:localhost"
|
||||
},
|
||||
{
|
||||
"age": 6547561012,
|
||||
"content": {
|
||||
"avatar_url": "mxc://localhost/fzysBrHpPEeTGANCVLXWXNMI#auto",
|
||||
"displayname": null,
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1426600438280zExKY:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 1426600438277,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"state_key": "@alice:localhost",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@alice:localhost"
|
||||
},
|
||||
{
|
||||
"age": 7148267200,
|
||||
"content": {
|
||||
"creator": "@alice:localhost"
|
||||
},
|
||||
"event_id": "$14259997320KhbwJ:localhost",
|
||||
"origin_server_ts": 1425999732089,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"user_id": "@alice:localhost"
|
||||
},
|
||||
{
|
||||
"age": 1622568720,
|
||||
"content": {
|
||||
"avatar_url": "mxc://localhost/GCmhgzMPRjqgpODLsNQzVuHZ#auto",
|
||||
"displayname": "Bob",
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$1431525430134MxlLX:localhost",
|
||||
"origin_server_ts": 1431525430569,
|
||||
"replaces_state": "$142652023736BSXcM:localhost",
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"state_key": "@bob:localhost",
|
||||
"type": "m.room.member",
|
||||
"user_id": "@bob:localhost"
|
||||
},
|
||||
{
|
||||
"age": 7148267004,
|
||||
"content": {
|
||||
"ban": 50,
|
||||
"events": {
|
||||
"m.room.name": 100,
|
||||
"m.room.power_levels": 100
|
||||
},
|
||||
"events_default": 0,
|
||||
"kick": 50,
|
||||
"redact": 50,
|
||||
"state_default": 50,
|
||||
"users": {
|
||||
"@alice:localhost": 100
|
||||
},
|
||||
"users_default": 0
|
||||
},
|
||||
"event_id": "$14259997322mqfaq:localhost",
|
||||
"origin_server_ts": 1425999732285,
|
||||
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.power_levels",
|
||||
"user_id": "@alice:localhost"
|
||||
}
|
||||
],
|
||||
"visibility": "private"
|
||||
}
|
||||
]
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
end:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the last value in ``chunk``. This
|
||||
token should be used with the ``/events`` API to listen for new
|
||||
events.
|
||||
presence:
|
||||
type: array
|
||||
description: A list of presence events.
|
||||
items:
|
||||
type: object
|
||||
title: Event
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/event.json"
|
||||
rooms:
|
||||
type: array
|
||||
items:
|
||||
type: object
|
||||
title: RoomInfo
|
||||
properties:
|
||||
room_id:
|
||||
type: string
|
||||
description: "The ID of this room."
|
||||
membership:
|
||||
type: string
|
||||
description: "The user's membership state in this room."
|
||||
enum: ["invite", "join", "leave", "ban"]
|
||||
invite:
|
||||
type: object
|
||||
title: "InviteEvent"
|
||||
description: "The invite event if ``membership`` is ``invite``"
|
||||
allOf:
|
||||
- "$ref": "v1-event-schema/m.room.member"
|
||||
messages:
|
||||
type: object
|
||||
title: PaginationChunk
|
||||
description: "The pagination chunk for this room."
|
||||
properties:
|
||||
start:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the first value in ``chunk``.
|
||||
Used for pagination.
|
||||
end:
|
||||
type: string
|
||||
description: |-
|
||||
A token which correlates to the last value in ``chunk``.
|
||||
Used for pagination.
|
||||
chunk:
|
||||
type: array
|
||||
description: |-
|
||||
If the user is a member of the room this will be a
|
||||
list of the most recent messages for this room. If
|
||||
the user has left the room this will be the
|
||||
messages that preceeded them leaving. This array
|
||||
will consist of at most ``limit`` elements.
|
||||
items:
|
||||
type: object
|
||||
title: RoomEvent
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/room_event.json"
|
||||
required: ["start", "end", "chunk"]
|
||||
state:
|
||||
type: array
|
||||
description: |-
|
||||
If the user is a member of the room this will be the
|
||||
current state of the room as a list of events. If the
|
||||
user has left the room this will be the state of the
|
||||
room when they left it.
|
||||
items:
|
||||
title: StateEvent
|
||||
type: object
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/state_event.json"
|
||||
visibility:
|
||||
type: string
|
||||
enum: ["private", "public"]
|
||||
description: |-
|
||||
Whether this room is visible to the ``/publicRooms`` API
|
||||
or not."
|
||||
required: ["room_id", "membership"]
|
||||
required: ["end", "rooms", "presence"]
|
||||
404:
|
||||
description: There is no avatar URL for this user or this user does not exist.
|
||||
"/events/{eventId}":
|
||||
get:
|
||||
summary: Get a single event by event ID.
|
||||
description: |-
|
||||
Get a single event based on ``event_id``. You must have permission to
|
||||
retrieve this event e.g. by being a member in the room for this event.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: eventId
|
||||
description: The event ID to get.
|
||||
required: true
|
||||
x-example: "$asfDuShaf7Gafaw:matrix.org"
|
||||
responses:
|
||||
200:
|
||||
description: The full event.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"content": {
|
||||
"body": "Hello world!",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"room_id:": "!wfgy43Sg4a:matrix.org",
|
||||
"user_id": "@bob:matrix.org",
|
||||
"event_id": "$asfDuShaf7Gafaw:matrix.org",
|
||||
"type": "m.room.message"
|
||||
}
|
||||
schema:
|
||||
allOf:
|
||||
- "$ref": "core-event-schema/event.json"
|
||||
404:
|
||||
description: The event was not found or you do not have permission to read this event.
|
@ -0,0 +1,77 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Typing API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/rooms/{roomId}/typing/{userId}":
|
||||
put:
|
||||
summary: Informs the server that the user has started or stopped typing.
|
||||
description: |-
|
||||
This tells the server that the user is typing for the next N
|
||||
milliseconds where N is the value specified in the ``timeout`` key.
|
||||
Alternatively, if ``typing`` is ``false``, it tells the server that the
|
||||
user has stopped typing.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
description: The user who has started to type.
|
||||
required: true
|
||||
x-example: "@alice:example.com"
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room in which the user is typing.
|
||||
required: true
|
||||
x-example: "!wefh3sfukhs:example.com"
|
||||
- in: body
|
||||
name: typingState
|
||||
description: The current typing state.
|
||||
required: true
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{
|
||||
"typing": true,
|
||||
"timeout": 30000
|
||||
}
|
||||
properties:
|
||||
typing:
|
||||
type: boolean
|
||||
description: |-
|
||||
Whether the user is typing or not. If ``false``, the ``timeout``
|
||||
key can be omitted.
|
||||
timeout:
|
||||
type: integer
|
||||
description: The length of time in milliseconds to mark this user as typing.
|
||||
required: ["typing"]
|
||||
responses:
|
||||
200:
|
||||
description: The new typing state was set.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
|
@ -0,0 +1 @@
|
||||
../../../event-schemas/schema/v1
|
@ -0,0 +1,68 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v1 Voice over IP API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/api/v1
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/turnServer":
|
||||
get:
|
||||
summary: Obtain TURN server credentials.
|
||||
description: |-
|
||||
This API provides credentials for the client to use when initiating
|
||||
calls.
|
||||
security:
|
||||
- accessToken: []
|
||||
responses:
|
||||
200:
|
||||
description: The TURN server credentials.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"username":"1443779631:@user:example.com",
|
||||
"password":"JlKfBy1QwLrO20385QyAtEyIv0=",
|
||||
"uris":[
|
||||
"turn:turn.example.com:3478?transport=udp",
|
||||
"turn:10.20.30.40:3478?transport=tcp",
|
||||
"turns:10.20.30.40:443?transport=tcp"
|
||||
],
|
||||
"ttl":86400
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
username:
|
||||
type: string
|
||||
description: |-
|
||||
The username to use.
|
||||
password:
|
||||
type: string
|
||||
description: |-
|
||||
The password to use.
|
||||
uris:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
description: A list of TURN URIs
|
||||
ttl:
|
||||
type: integer
|
||||
description: The time-to-live in seconds
|
||||
required: ["username", "password", "uris", "ttl"]
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
||||
|
@ -0,0 +1 @@
|
||||
../../../event-schemas/schema/v1/core-event-schema
|
@ -0,0 +1 @@
|
||||
.
|
@ -0,0 +1,10 @@
|
||||
type: object
|
||||
description: A Matrix-level Error
|
||||
properties:
|
||||
errcode:
|
||||
type: string
|
||||
description: An error code.
|
||||
error:
|
||||
type: string
|
||||
description: A human-readable error message.
|
||||
required: ["errcode"]
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"events": {
|
||||
"type": "array",
|
||||
"description": "List of events",
|
||||
"items": {
|
||||
"type": "object"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"limit": {
|
||||
"type": "integer",
|
||||
"description":
|
||||
"The maximum number of events to return."
|
||||
},
|
||||
"types": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"A list of event types to include. If this list is absent then all event types are included. A '*' can be used as a wildcard to match any sequence of characters.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"not_types": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"A list of event types to exclude. If this list is absent then no event types are excluded. A matching type will be excluded even if it is listed in the 'types' filter. A '*' can be used as a wildcard to match any sequence of characters.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"senders": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"A list of senders IDs to include. If this list is absent then all senders are included. A '*' can be used as a wildcard to match any sequence of characters.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"not_senders": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"A list of sender IDs to exclude. If this list is absent then no senders are excluded. A matching sender will be excluded even if it is listed in the 'senders' filter. A '*' can be used as a wildcard to match any sequence of characters.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"events": {
|
||||
"type": "array",
|
||||
"description": "List of event ids",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
{
|
||||
"type": "object",
|
||||
"allOf": [{"$ref": "definitions/event_filter.json"}],
|
||||
"properties": {
|
||||
"rooms": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"A list of room IDs to include. If this list is absent then all rooms are included. A '*' can be used as a wildcard to match any sequence of characters.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"not_rooms": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"A list of room IDs to exclude. If this list is absent then no rooms are excluded. A matching room will be excluded even if it is listed in the 'rooms' filter. A '*' can be used as a wildcard to match any sequence of characters.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"room": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"state": {
|
||||
"description":
|
||||
"The state events to include for rooms.",
|
||||
"allOf": [{"$ref": "definitions/room_event_filter.json"}]
|
||||
},
|
||||
"timeline": {
|
||||
"description":
|
||||
"The message and state update events to include for rooms.",
|
||||
"allOf": [{"$ref": "definitions/room_event_filter.json"}]
|
||||
},
|
||||
"ephemeral": {
|
||||
"description":
|
||||
"The events that aren't recorded in the room history, e.g. typing and receipts, to include for rooms.",
|
||||
"allOf": [{"$ref": "definitions/room_event_filter.json"}]
|
||||
}
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"description":
|
||||
"The presence updates to include.",
|
||||
"allOf": [{"$ref": "definitions/event_filter.json"}]
|
||||
},
|
||||
"event_format": {
|
||||
"description":
|
||||
"The format to use for events. 'client' will return the events in a format suitable for clients. 'federation' will return the raw event as receieved over federation. The default is 'client'.",
|
||||
"type": "string",
|
||||
"enum": ["client", "federation"]
|
||||
},
|
||||
"event_fields": {
|
||||
"type": "array",
|
||||
"description":
|
||||
"List of event fields to include. If this list is absent then all fields are included. The entries may include '.' charaters to indicate sub-fields. So ['content.body'] will include the 'body' field of the 'content' object. A literal '.' character in a field name may be escaped using a '\\'. A server may include more fields than were requested.",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
{
|
||||
"type": "object",
|
||||
"allOf": [{"$ref":"definitions/room_event_batch.json"}],
|
||||
"properties": {
|
||||
"limited": {
|
||||
"type": "boolean",
|
||||
"description": "Whether there are more events on the server"
|
||||
},
|
||||
"prev_batch": {
|
||||
"type": "string",
|
||||
"description": "If the batch was limited then this is a token that can be supplied to the server to retrieve more events"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v2 filter API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
basePath: /_matrix/client/v2_alpha
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/user/{userId}/filter":
|
||||
post:
|
||||
summary: Upload a new filter.
|
||||
description: |-
|
||||
Uploads a new filter definition to the homeserver.
|
||||
Returns a filter ID that may be used in /sync requests to
|
||||
retrict which events are returned to the client.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: userId
|
||||
required: true
|
||||
description:
|
||||
The id of the user uploading the filter. The access token must be
|
||||
authorized to make requests for this user id.
|
||||
x-example: "@alice:example.com"
|
||||
- in: body
|
||||
name: filter
|
||||
required: true
|
||||
description: The filter to upload.
|
||||
schema:
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: "definitions/sync_filter.json"
|
||||
example: |-
|
||||
{
|
||||
"room": {
|
||||
"state": {
|
||||
"types": ["m.room.*"],
|
||||
"not_rooms": ["!726s6s6q:example.com"]
|
||||
},
|
||||
"timeline": {
|
||||
"limit": 10,
|
||||
"types": ["m.room.message"],
|
||||
"not_rooms": ["!726s6s6q:example.com"],
|
||||
"not_senders": ["@spam:example.com"]
|
||||
},
|
||||
"emphemeral": {
|
||||
"types": ["m.receipt", "m.typing"],
|
||||
"not_rooms": ["!726s6s6q:example.com"],
|
||||
"not_senders": ["@spam:example.com"]
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"types": ["m.presence"],
|
||||
"not_senders": ["@alice:example.com"]
|
||||
},
|
||||
"event_format": "client",
|
||||
"event_fields": ["type", "content", "sender"]
|
||||
}
|
||||
responses:
|
||||
200:
|
||||
description: The filter was created.
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"filter_id": "66696p746572"
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
filter_id:
|
||||
type: string
|
||||
description: |-
|
||||
The ID of the filter that was created.
|
||||
"/user/{userId}/filter/{filterId}":
|
||||
get:
|
||||
summary: Download a filter
|
||||
parameters:
|
||||
- in: path
|
||||
name: userId
|
||||
type: string
|
||||
description: |-
|
||||
The user ID to download a filter for.
|
||||
x-example: "@alice:example.com"
|
||||
required: true
|
||||
- in: path
|
||||
name: filterId
|
||||
type: string
|
||||
description: |-
|
||||
The filter ID to download.
|
||||
x-example: "66696p746572"
|
||||
required: true
|
||||
responses:
|
||||
200:
|
||||
description: |-
|
||||
"The filter defintion"
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"room": {
|
||||
"state": {
|
||||
"types": ["m.room.*"],
|
||||
"not_rooms": ["!726s6s6q:example.com"]
|
||||
},
|
||||
"timeline": {
|
||||
"limit": 10,
|
||||
"types": ["m.room.message"],
|
||||
"not_rooms": ["!726s6s6q:example.com"],
|
||||
"not_senders": ["@spam:example.com"]
|
||||
},
|
||||
"emphemeral": {
|
||||
"types": ["m.receipt", "m.typing"],
|
||||
"not_rooms": ["!726s6s6q:example.com"],
|
||||
"not_senders": ["@spam:example.com"]
|
||||
}
|
||||
},
|
||||
"presence": {
|
||||
"types": ["m.presence"],
|
||||
"not_senders": ["@alice:example.com"]
|
||||
},
|
||||
"event_format": "client",
|
||||
"event_fields": ["type", "content", "sender"]
|
||||
}
|
||||
schema:
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: "definitions/sync_filter.json"
|
@ -0,0 +1,69 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v2 Receipts API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
- http
|
||||
basePath: /_matrix/client/v2_alpha
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/rooms/{roomId}/receipt/{receiptType}/{eventId}":
|
||||
post:
|
||||
summary: Send a receipt for the given event ID.
|
||||
description: |-
|
||||
This API updates the marker for the given receipt type to the event ID
|
||||
specified.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: path
|
||||
type: string
|
||||
name: roomId
|
||||
description: The room in which to send the event.
|
||||
required: true
|
||||
x-example: "!wefuh21ffskfuh345:example.com"
|
||||
- in: path
|
||||
type: string
|
||||
name: receiptType
|
||||
description: The type of receipt to send.
|
||||
required: true
|
||||
x-example: "m.read"
|
||||
enum: ["m.read"]
|
||||
- in: path
|
||||
type: string
|
||||
name: eventId
|
||||
description: The event ID to acknowledge up to.
|
||||
required: true
|
||||
x-example: "$1924376522eioj:example.com"
|
||||
- in: body
|
||||
name: receipt
|
||||
description: |-
|
||||
Extra receipt information to attach to ``content`` if any. The
|
||||
server will automatically set the ``ts`` field.
|
||||
schema:
|
||||
type: object
|
||||
example: |-
|
||||
{}
|
||||
responses:
|
||||
200:
|
||||
description: The receipt was sent.
|
||||
examples:
|
||||
application/json: |-
|
||||
{}
|
||||
schema:
|
||||
type: object # empty json object
|
||||
429:
|
||||
description: This request was rate-limited.
|
||||
schema:
|
||||
"$ref": "definitions/error.yaml"
|
@ -0,0 +1,286 @@
|
||||
swagger: '2.0'
|
||||
info:
|
||||
title: "Matrix Client-Server v2 sync API"
|
||||
version: "1.0.0"
|
||||
host: localhost:8008
|
||||
schemes:
|
||||
- https
|
||||
basePath: /_matrix/client/v2_alpha
|
||||
consumes:
|
||||
- application/json
|
||||
produces:
|
||||
- application/json
|
||||
securityDefinitions:
|
||||
accessToken:
|
||||
type: apiKey
|
||||
description: The user_id or application service access_token
|
||||
name: access_token
|
||||
in: query
|
||||
paths:
|
||||
"/sync":
|
||||
get:
|
||||
summary: Synchronise the client's state and receive new messages.
|
||||
description: |-
|
||||
Synchronise the client's state with the latest state on the server.
|
||||
Client's use this API when they first log in to get an initial snapshot
|
||||
of the state on the server, and then continue to call this API to get
|
||||
incremental deltas to the state, and to receive new messages.
|
||||
security:
|
||||
- accessToken: []
|
||||
parameters:
|
||||
- in: query
|
||||
name: filter
|
||||
type: string
|
||||
description: |-
|
||||
The ID of a filter created using the filter API.
|
||||
x-example: "66696p746572"
|
||||
- in: query
|
||||
name: since
|
||||
type: string
|
||||
description: |-
|
||||
A point in time to continue a sync from.
|
||||
x-example: "s72594_4483_1934"
|
||||
- in: query
|
||||
name: set_presence
|
||||
type: string
|
||||
enum: ["offline"]
|
||||
description: |-
|
||||
Controls whether the client is automatically marked as online by
|
||||
polling this API. If this parameter is omitted then the client is
|
||||
automatically marked as online when it uses this API. Otherwise if
|
||||
the parameter is set to "offline" then the client is not marked as
|
||||
being online when it uses this API.
|
||||
x-example: "offline"
|
||||
- in: query
|
||||
name: timeout
|
||||
type: integer
|
||||
description: |-
|
||||
The maximum time to poll in milliseconds before returning this
|
||||
request.
|
||||
x-example: 30000
|
||||
responses:
|
||||
200:
|
||||
description:
|
||||
The initial snapshot or delta for the client to use to update their
|
||||
state.
|
||||
schema:
|
||||
type: object
|
||||
properties:
|
||||
next_batch:
|
||||
type: string
|
||||
description: |-
|
||||
The batch token to supply in the ``since`` param of the next
|
||||
``/sync`` request.
|
||||
rooms:
|
||||
title: Rooms
|
||||
type: object
|
||||
description: |-
|
||||
Updates to rooms.
|
||||
properties:
|
||||
joined:
|
||||
title: Joined
|
||||
type: object
|
||||
additionalProperties:
|
||||
title: Joined Room
|
||||
type: object
|
||||
properties:
|
||||
event_map:
|
||||
title: EventMap
|
||||
type: object
|
||||
description: |-
|
||||
A map from event ID to events for this room. The
|
||||
events are referenced from the ``timeline`` and
|
||||
``state`` keys for this room.
|
||||
additionalProperties:
|
||||
title: Event
|
||||
description: An event object.
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: "core-event-schema/event.json"
|
||||
state:
|
||||
title: State
|
||||
type: object
|
||||
description: |-
|
||||
The state updates for the room.
|
||||
allOf:
|
||||
- $ref: "definitions/room_event_batch.json"
|
||||
timeline:
|
||||
title: Timeline
|
||||
type: object
|
||||
description: |-
|
||||
The timeline of messages and state changes in the
|
||||
room.
|
||||
allOf:
|
||||
- $ref: "definitions/timeline_batch.json"
|
||||
ephemeral:
|
||||
title: Ephemeral
|
||||
type: object
|
||||
description: |-
|
||||
The ephemeral events in the room that aren't
|
||||
recorded in the timeline or state of the room.
|
||||
e.g. typing.
|
||||
allOf:
|
||||
- $ref: "definitions/event_batch.json"
|
||||
invited:
|
||||
title: Invited
|
||||
type: object
|
||||
description: |-
|
||||
The rooms that the user has been invited to.
|
||||
additionalProperties:
|
||||
title: Invited Room
|
||||
type: object
|
||||
properties:
|
||||
invite_state:
|
||||
title: InviteState
|
||||
type: object
|
||||
description: |-
|
||||
The state of a room that the user has been invited
|
||||
to. These state events may only have the `sender``,
|
||||
``type``, ``state_key`` and ``content`` keys
|
||||
present. These events do not replace any state that
|
||||
the client already has for the room, for example if
|
||||
the client has archived the room. Instead the
|
||||
client should keep two separate copies of the
|
||||
state: the one from the ``invite_state`` and one
|
||||
from the archived ``state``. If the client joins
|
||||
the room then the current state will be given as a
|
||||
delta against the archived ``state`` not the
|
||||
``invite_state``.
|
||||
allOf:
|
||||
- $ref: "definitions/event_batch.json"
|
||||
archived:
|
||||
title: Archived
|
||||
type: object
|
||||
description: |-
|
||||
The rooms that the user has left or been banned from. The
|
||||
entries in the room_map will lack an ``ephemeral`` key.
|
||||
additionalProperties:
|
||||
title: Archived Room
|
||||
type: object
|
||||
properties:
|
||||
event_map:
|
||||
title: EventMap
|
||||
type: object
|
||||
description: |-
|
||||
A map from event ID to events for this room. The
|
||||
events are referenced from the ``timeline`` and
|
||||
``state`` keys for this room.
|
||||
additionalProperties:
|
||||
title: Event
|
||||
description: An event object.
|
||||
type: object
|
||||
allOf:
|
||||
- $ref: "core-event-schema/event.json"
|
||||
state:
|
||||
title: State
|
||||
type: object
|
||||
description: |-
|
||||
The state updates for the room up to the point when
|
||||
the user left.
|
||||
allOf:
|
||||
- $ref: "definitions/room_event_batch.json"
|
||||
timeline:
|
||||
title: Timeline
|
||||
type: object
|
||||
description: |-
|
||||
The timeline of messages and state changes in the
|
||||
room up to the point when the user left.
|
||||
allOf:
|
||||
- $ref: "definitions/timeline_batch.json"
|
||||
presence:
|
||||
title: Presence
|
||||
type: object
|
||||
description: |-
|
||||
The updates to the presence status of other users.
|
||||
allOf:
|
||||
- $ref: "definitions/event_batch.json"
|
||||
examples:
|
||||
application/json: |-
|
||||
{
|
||||
"next_batch": "s72595_4483_1934",
|
||||
"presence": {
|
||||
"events": [
|
||||
{
|
||||
"sender": "@alice:example.com",
|
||||
"type": "m.presence",
|
||||
"content": {"presence": "online"}
|
||||
}
|
||||
]
|
||||
},
|
||||
"rooms": {
|
||||
"joined": {
|
||||
"!726s6s6q:example.com": {
|
||||
"event_map": {
|
||||
"$66697273743031:example.com": {
|
||||
"sender": "@alice:example.com",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@alice:example.com",
|
||||
"content": {"membership": "join"},
|
||||
"origin_server_ts": 1417731086795
|
||||
},
|
||||
"$7365636s6r6432:example.com": {
|
||||
"sender": "@bob:example.com",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@bob:example.com",
|
||||
"content": {"membership": "join"},
|
||||
"origin_server_ts": 1417731086795
|
||||
},
|
||||
"$74686972643033:example.com": {
|
||||
"sender": "@alice:example.com",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {"age": "124524", "txn_id": "1234"},
|
||||
"content": {
|
||||
"body": "I am a fish",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1417731086797
|
||||
}
|
||||
},
|
||||
"state": {
|
||||
"events": [
|
||||
"$66697273743031:example.com",
|
||||
"$7365636s6r6432:example.com"
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
"$7365636s6r6432:example.com",
|
||||
"$74686972643033:example.com"
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t34-23535_0_0"
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": [
|
||||
{
|
||||
"room_id": "!726s6s6q:example.com",
|
||||
"type": "m.typing",
|
||||
"content": {"user_ids": ["@alice:example.com"]}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"invited": {
|
||||
"!696r7674:example.com": {
|
||||
"invite_state": {
|
||||
"events": [
|
||||
{
|
||||
"sender": "@alice:example.com",
|
||||
"type": "m.room.name",
|
||||
"state_key": "",
|
||||
"content": {"name": "My Room Name"}
|
||||
},
|
||||
{
|
||||
"sender": "@alice:example.com",
|
||||
"type": "m.room.member",
|
||||
"state_key": "@bob:example.com",
|
||||
"content": {"membership": "invite"}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"archived": {}
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "swagger-cli-validator",
|
||||
"version": "0.0.1",
|
||||
"description": "",
|
||||
"main": "validator.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"nopt": "^3.0.2",
|
||||
"swagger-parser": "^3.2.1"
|
||||
}
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
"use strict";
|
||||
var fs = require("fs");
|
||||
var nopt = require("nopt");
|
||||
var parser = require("swagger-parser");
|
||||
var path = require("path");
|
||||
|
||||
var opts = nopt({
|
||||
"help": Boolean,
|
||||
"schema": path
|
||||
}, {
|
||||
"h": "--help",
|
||||
"s": "--schema"
|
||||
});
|
||||
|
||||
if (opts.help) {
|
||||
console.log(
|
||||
"Use swagger-parser to validate against Swagger 2.0\n"+
|
||||
"Usage:\n"+
|
||||
" node validator.js -s <schema_file_or_folder>"
|
||||
);
|
||||
process.exit(0);
|
||||
}
|
||||
if (!opts.schema) {
|
||||
console.error("No [s]chema specified.");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
var errFn = function(err, api) {
|
||||
if (!err) {
|
||||
return;
|
||||
}
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
};
|
||||
|
||||
var isDir = fs.lstatSync(opts.schema).isDirectory()
|
||||
if (isDir) {
|
||||
console.log("Checking directory %s for .yaml files...", opts.schema);
|
||||
fs.readdir(opts.schema, function(err, files) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
}
|
||||
files.forEach(function(f) {
|
||||
var suffix = ".yaml";
|
||||
if (f.indexOf(suffix, f.length - suffix.length) > 0) {
|
||||
parser.validate(path.join(opts.schema, f), function(err, api, metadata) {
|
||||
if (!err) {
|
||||
console.log("%s is valid.", f);
|
||||
}
|
||||
else {
|
||||
console.error("%s is not valid.", f);
|
||||
errFn(err, api, metadata);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
else{
|
||||
parser.validate(opts.schema, function(err, api) {
|
||||
if (!err) {
|
||||
console.log("%s is valid", opts.schema);
|
||||
}
|
||||
else {
|
||||
errFn(err, api);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -0,0 +1,343 @@
|
||||
Registration and Login
|
||||
----------------------
|
||||
|
||||
Clients must register with a home server in order to use Matrix. After
|
||||
registering, the client will be given an access token which must be used in ALL
|
||||
requests to that home server as a query parameter 'access_token'.
|
||||
|
||||
If the client has already registered, they need to be able to login to their
|
||||
account. The home server may provide many different ways of logging in, such as
|
||||
user/password auth, login via a social network (OAuth2), login by confirming a
|
||||
token sent to their email address, etc. This specification does not define how
|
||||
home servers should authorise their users who want to login to their existing
|
||||
accounts, but instead defines the standard interface which implementations
|
||||
should follow so that ANY client can login to ANY home server. Clients login
|
||||
using the |login|_ API. Clients register using the |register|_ API.
|
||||
Registration follows the same general procedure as login, but the path requests
|
||||
are sent to and the details contained in them are different.
|
||||
|
||||
In both registration and login cases, the process takes the form of one or more
|
||||
stages, where at each stage the client submits a set of data for a given stage
|
||||
type and awaits a response from the server, which will either be a final
|
||||
success or a request to perform an additional stage. This exchange continues
|
||||
until the final success.
|
||||
|
||||
In order to determine up-front what the server's requirements are, the client
|
||||
can request from the server a complete description of all of its acceptable
|
||||
flows of the registration or login process. It can then inspect the list of
|
||||
returned flows looking for one for which it believes it can complete all of the
|
||||
required stages, and perform it. As each home server may have different ways of
|
||||
logging in, the client needs to know how they should login. All distinct login
|
||||
stages MUST have a corresponding ``type``. A ``type`` is a namespaced string
|
||||
which details the mechanism for logging in.
|
||||
|
||||
A client may be able to login via multiple valid login flows, and should choose
|
||||
a single flow when logging in. A flow is a series of login stages. The home
|
||||
server MUST respond with all the valid login flows when requested by a simple
|
||||
``GET`` request directly to the ``/login`` or ``/register`` paths::
|
||||
|
||||
{
|
||||
"flows": [
|
||||
{
|
||||
"type": "<login type1a>",
|
||||
"stages": [ "<login type 1a>", "<login type 1b>" ]
|
||||
},
|
||||
{
|
||||
"type": "<login type2a>",
|
||||
"stages": [ "<login type 2a>", "<login type 2b>" ]
|
||||
},
|
||||
{
|
||||
"type": "<login type3>"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
The client can now select which flow it wishes to use, and begin making
|
||||
``POST`` requests to the ``/login`` or ``/register`` paths with JSON body
|
||||
content containing the name of the stage as the ``type`` key, along with
|
||||
whatever additional parameters are required for that login or registration type
|
||||
(see below). After the flow is completed, the client's fully-qualified user
|
||||
ID and a new access token MUST be returned::
|
||||
|
||||
{
|
||||
"user_id": "@user:matrix.org",
|
||||
"access_token": "abcdef0123456789"
|
||||
}
|
||||
|
||||
The ``user_id`` key is particularly useful if the home server wishes to support
|
||||
localpart entry of usernames (e.g. "user" rather than "@user:matrix.org"), as
|
||||
the client may not be able to determine its ``user_id`` in this case.
|
||||
|
||||
If the flow has multiple stages to it, the home server may wish to create a
|
||||
session to store context between requests. If a home server responds with a
|
||||
``session`` key to a request, clients MUST submit it in subsequent requests
|
||||
until the flow is completed::
|
||||
|
||||
{
|
||||
"session": "<session id>"
|
||||
}
|
||||
|
||||
This specification defines the following login types:
|
||||
- ``m.login.password``
|
||||
- ``m.login.oauth2``
|
||||
- ``m.login.email.code``
|
||||
- ``m.login.email.url``
|
||||
- ``m.login.email.identity``
|
||||
|
||||
Password-based
|
||||
~~~~~~~~~~~~~~
|
||||
:Type:
|
||||
``m.login.password``
|
||||
:Description:
|
||||
Login is supported via a username and password.
|
||||
|
||||
To respond to this type, reply with::
|
||||
|
||||
{
|
||||
"type": "m.login.password",
|
||||
"user": "<user_id or user localpart>",
|
||||
"password": "<password>"
|
||||
}
|
||||
|
||||
The home server MUST respond with either new credentials, the next stage of the
|
||||
login process, or a standard error response.
|
||||
|
||||
Captcha-based
|
||||
~~~~~~~~~~~~~
|
||||
:Type:
|
||||
``m.login.recaptcha``
|
||||
:Description:
|
||||
Login is supported by responding to a captcha (in the case of the Synapse
|
||||
implementation, Google's Recaptcha library is used).
|
||||
|
||||
To respond to this type, reply with::
|
||||
|
||||
{
|
||||
"type": "m.login.recaptcha",
|
||||
"challenge": "<challenge token>",
|
||||
"response": "<user-entered text>"
|
||||
}
|
||||
|
||||
.. NOTE::
|
||||
In Synapse, the Recaptcha parameters can be obtained in Javascript by calling:
|
||||
Recaptcha.get_challenge();
|
||||
Recaptcha.get_response();
|
||||
|
||||
The home server MUST respond with either new credentials, the next stage of the
|
||||
login process, or a standard error response.
|
||||
|
||||
OAuth2-based
|
||||
~~~~~~~~~~~~
|
||||
:Type:
|
||||
``m.login.oauth2``
|
||||
:Description:
|
||||
Login is supported via OAuth2 URLs. This login consists of multiple requests.
|
||||
|
||||
To respond to this type, reply with::
|
||||
|
||||
{
|
||||
"type": "m.login.oauth2",
|
||||
"user": "<user_id or user localpart>"
|
||||
}
|
||||
|
||||
The server MUST respond with::
|
||||
|
||||
{
|
||||
"uri": <Authorization Request URI OR service selection URI>
|
||||
}
|
||||
|
||||
The home server acts as a 'confidential' client for the purposes of OAuth2. If
|
||||
the uri is a ``sevice selection URI``, it MUST point to a webpage which prompts
|
||||
the user to choose which service to authorize with. On selection of a service,
|
||||
this MUST link through to an ``Authorization Request URI``. If there is only 1
|
||||
service which the home server accepts when logging in, this indirection can be
|
||||
skipped and the "uri" key can be the ``Authorization Request URI``.
|
||||
|
||||
The client then visits the ``Authorization Request URI``, which then shows the
|
||||
OAuth2 Allow/Deny prompt. Hitting 'Allow' returns the ``redirect URI`` with the
|
||||
auth code. Home servers can choose any path for the ``redirect URI``. The
|
||||
client should visit the ``redirect URI``, which will then finish the OAuth2
|
||||
login process, granting the home server an access token for the chosen service.
|
||||
When the home server gets this access token, it verifies that the cilent has
|
||||
authorised with the 3rd party, and can now complete the login. The OAuth2
|
||||
``redirect URI`` (with auth code) MUST respond with either new credentials, the
|
||||
next stage of the login process, or a standard error response.
|
||||
|
||||
For example, if a home server accepts OAuth2 from Google, it would return the
|
||||
Authorization Request URI for Google::
|
||||
|
||||
{
|
||||
"uri": "https://accounts.google.com/o/oauth2/auth?response_type=code&
|
||||
client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos"
|
||||
}
|
||||
|
||||
The client then visits this URI and authorizes the home server. The client then
|
||||
visits the REDIRECT_URI with the auth code= query parameter which returns::
|
||||
|
||||
{
|
||||
"user_id": "@user:matrix.org",
|
||||
"access_token": "0123456789abcdef"
|
||||
}
|
||||
|
||||
Email-based (code)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
:Type:
|
||||
``m.login.email.code``
|
||||
:Description:
|
||||
Login is supported by typing in a code which is sent in an email. This login
|
||||
consists of multiple requests.
|
||||
|
||||
To respond to this type, reply with::
|
||||
|
||||
{
|
||||
"type": "m.login.email.code",
|
||||
"user": "<user_id or user localpart>",
|
||||
"email": "<email address>"
|
||||
}
|
||||
|
||||
After validating the email address, the home server MUST send an email
|
||||
containing an authentication code and return::
|
||||
|
||||
{
|
||||
"type": "m.login.email.code",
|
||||
"session": "<session id>"
|
||||
}
|
||||
|
||||
The second request in this login stage involves sending this authentication
|
||||
code::
|
||||
|
||||
{
|
||||
"type": "m.login.email.code",
|
||||
"session": "<session id>",
|
||||
"code": "<code in email sent>"
|
||||
}
|
||||
|
||||
The home server MUST respond to this with either new credentials, the next
|
||||
stage of the login process, or a standard error response.
|
||||
|
||||
Email-based (url)
|
||||
~~~~~~~~~~~~~~~~~
|
||||
:Type:
|
||||
``m.login.email.url``
|
||||
:Description:
|
||||
Login is supported by clicking on a URL in an email. This login consists of
|
||||
multiple requests.
|
||||
|
||||
To respond to this type, reply with::
|
||||
|
||||
{
|
||||
"type": "m.login.email.url",
|
||||
"user": "<user_id or user localpart>",
|
||||
"email": "<email address>"
|
||||
}
|
||||
|
||||
After validating the email address, the home server MUST send an email
|
||||
containing an authentication URL and return::
|
||||
|
||||
{
|
||||
"type": "m.login.email.url",
|
||||
"session": "<session id>"
|
||||
}
|
||||
|
||||
The email contains a URL which must be clicked. After it has been clicked, the
|
||||
client should perform another request::
|
||||
|
||||
{
|
||||
"type": "m.login.email.url",
|
||||
"session": "<session id>"
|
||||
}
|
||||
|
||||
The home server MUST respond to this with either new credentials, the next
|
||||
stage of the login process, or a standard error response.
|
||||
|
||||
A common client implementation will be to periodically poll until the link is
|
||||
clicked. If the link has not been visited yet, a standard error response with
|
||||
an errcode of ``M_LOGIN_EMAIL_URL_NOT_YET`` should be returned.
|
||||
|
||||
|
||||
Email-based (identity server)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
:Type:
|
||||
``m.login.email.identity``
|
||||
:Description:
|
||||
Login is supported by authorising an email address with an identity server.
|
||||
|
||||
Prior to submitting this, the client should authenticate with an identity
|
||||
server. After authenticating, the session information should be submitted to
|
||||
the home server.
|
||||
|
||||
To respond to this type, reply with::
|
||||
|
||||
{
|
||||
"type": "m.login.email.identity",
|
||||
"threepidCreds": [
|
||||
{
|
||||
"sid": "<identity server session id>",
|
||||
"clientSecret": "<identity server client secret>",
|
||||
"idServer": "<url of identity server authed with, e.g. 'matrix.org:8090'>"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
N-Factor Authentication
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
Multiple login stages can be combined to create N-factor authentication during
|
||||
login.
|
||||
|
||||
This can be achieved by responding with the ``next`` login type on completion
|
||||
of a previous login stage::
|
||||
|
||||
{
|
||||
"next": "<next login type>"
|
||||
}
|
||||
|
||||
If a home server implements N-factor authentication, it MUST respond with all
|
||||
``stages`` when initially queried for their login requirements::
|
||||
|
||||
{
|
||||
"type": "<1st login type>",
|
||||
"stages": [ <1st login type>, <2nd login type>, ... , <Nth login type> ]
|
||||
}
|
||||
|
||||
This can be represented conceptually as::
|
||||
|
||||
_______________________
|
||||
| Login Stage 1 |
|
||||
| type: "<login type1>" |
|
||||
| ___________________ |
|
||||
| |_Request_1_________| | <-- Returns "session" key which is used throughout.
|
||||
| ___________________ |
|
||||
| |_Request_2_________| | <-- Returns a "next" value of "login type2"
|
||||
|_______________________|
|
||||
|
|
||||
|
|
||||
_________V_____________
|
||||
| Login Stage 2 |
|
||||
| type: "<login type2>" |
|
||||
| ___________________ |
|
||||
| |_Request_1_________| |
|
||||
| ___________________ |
|
||||
| |_Request_2_________| |
|
||||
| ___________________ |
|
||||
| |_Request_3_________| | <-- Returns a "next" value of "login type3"
|
||||
|_______________________|
|
||||
|
|
||||
|
|
||||
_________V_____________
|
||||
| Login Stage 3 |
|
||||
| type: "<login type3>" |
|
||||
| ___________________ |
|
||||
| |_Request_1_________| | <-- Returns user credentials
|
||||
|_______________________|
|
||||
|
||||
Fallback
|
||||
~~~~~~~~
|
||||
Clients cannot be expected to be able to know how to process every single login
|
||||
type. If a client determines it does not know how to handle a given login type,
|
||||
it should request a login fallback page::
|
||||
|
||||
GET matrix/client/api/v1/login/fallback
|
||||
|
||||
This MUST return an HTML page which can perform the entire login process.
|
@ -0,0 +1,14 @@
|
||||
Address book repository
|
||||
=======================
|
||||
|
||||
.. NOTE::
|
||||
This section is a work in progress.
|
||||
|
||||
.. TODO-spec
|
||||
Do we even need it? Clients can use out-of-band addressbook servers for now;
|
||||
this should definitely not be core.
|
||||
- format: POST(?) wodges of json, some possible processing, then return wodges of json on GET.
|
||||
- processing may remove dupes, merge contacts, pepper with extra info (e.g. matrix-ability of
|
||||
contacts), etc.
|
||||
- Standard json format for contacts? Piggy back off vcards?
|
||||
|
@ -0,0 +1,244 @@
|
||||
.. TODO
|
||||
Sometimes application services need to create rooms (e.g. when lazy loading
|
||||
from room aliases). Created rooms need to have a user that created them, so
|
||||
federation works (as it relies on an entry existing in m.room.member). We should
|
||||
be able to add metadata to m.room.member to state that this user is an application
|
||||
service, a virtual user, etc.
|
||||
|
||||
Application Services
|
||||
====================
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
Application services provide a way of implementing custom serverside functionality
|
||||
on top of Matrix without the complexity of implementing the full federation API.
|
||||
By acting as a trusted service logically located behind an existing homeserver,
|
||||
Application services are decoupled from:
|
||||
|
||||
* Signing or validating federated traffic or conversation history
|
||||
* Validating authorisation constraints on federated traffic
|
||||
* Managing routing or retry schemes to the rest of the Matrix federation
|
||||
|
||||
As such, developers can focus entirely on implementing application logic rather
|
||||
than being concerned with the details of managing Matrix federation.
|
||||
|
||||
Features available to application services include:
|
||||
|
||||
* Privileged subscription to any events available to the homeserver
|
||||
* Synthesising virtual users
|
||||
* Synthesising virtual rooms
|
||||
* Injecting message history for virtual rooms
|
||||
|
||||
Features not provided by application services include:
|
||||
|
||||
* Intercepting and filtering/modifying message or behaviour within a room
|
||||
(this is a job for a Policy Server, as it requires a single logical focal
|
||||
point for messages in order to consistently apply the custom business logic)
|
||||
|
||||
Example use cases for application services include:
|
||||
|
||||
* Exposing existing communication services in Matrix
|
||||
|
||||
* Gateways to/from standards-based protocols (SIP, XMPP, IRC, RCS (MSRP), SIMPLE, Lync, etc)
|
||||
* Gateways to/from closed services (e.g. WhatsApp)
|
||||
* Gateways could be architected as:
|
||||
|
||||
* Act as a virtual client on the non-Matrix network
|
||||
(e.g. connect as multiple virtual clients to an IRC or XMPP server)
|
||||
* Act as a server on the non-Matrix network
|
||||
(e.g. speak s2s XMPP federation, or IRC link protocol)
|
||||
* Act as an application service on the non-Matrix network
|
||||
(e.g. link up as IRC services, or an XMPP component)
|
||||
* Exposing a non-Matrix client interface listener from the AS
|
||||
(e.g. listen on port 6667 for IRC clients, or port 5222 for XMPP clients)
|
||||
|
||||
|
||||
* Bridging existing APIs into Matrix
|
||||
* e.g. SMS/MMS aggregator APIs
|
||||
* Domain-specific APIs such as SABRE
|
||||
|
||||
* Integrating more exotic content into Matrix
|
||||
* e.g. MIDI<->Matrix gateway/bridge
|
||||
* 3D world <-> Matrix bridge
|
||||
|
||||
* Application services:
|
||||
* Search engines (e.g. elasticsearch search indices)
|
||||
* Notification systems (e.g. send custom pushes for various hooks)
|
||||
* VoIP Conference services
|
||||
* Text-to-speech and Speech-to-text services
|
||||
* Signal processing
|
||||
* IVR
|
||||
* Server-machine translation
|
||||
* Censorship service
|
||||
* Multi-User Gaming (Dungeons etc)
|
||||
* Other "constrained worlds" (e.g. 3D geometry representations)
|
||||
|
||||
* applying physics to a 3D world on the serverside
|
||||
|
||||
* (applying gravity and friction and air resistance... collision detection)
|
||||
* domain-specific merge conflict resolution of events
|
||||
|
||||
* Payment style transactional usecases with transactional guarantees
|
||||
|
||||
Architecture Outline
|
||||
====================
|
||||
|
||||
The application service registers with its host homeserver to offer its services.
|
||||
|
||||
In the registration process, the AS provides:
|
||||
|
||||
* Credentials to identify itself as an approved application service for that HS
|
||||
* Details of the namespaces of users and rooms the AS is acting on behalf of and
|
||||
"subscribing to"
|
||||
* Namespaces are defined as a list of regexps against which to match room aliases,
|
||||
room IDs, and user IDs. Regexps give the flexibility to say, sub-domain MSISDN
|
||||
ranges per AS, whereas a blunt prefix string does not. These namespaces are further
|
||||
configured by setting whether they are ``exclusive`` or not. An exclusive namespace
|
||||
prevents entities other than the aforementioned AS from creating/editing/deleting
|
||||
entries within that namespace. This does not affect the visibility/readability of
|
||||
entries within that namespace (e.g. it doesn't prevent users joining exclusive
|
||||
aliases, or ASes from listening to exclusive aliases, but does prevent both users
|
||||
and ASes from creating/editing/deleting aliases within that namespace).
|
||||
* There is overlap between selecting events via the csv2 Filter API and subscribing
|
||||
to events here - perhaps subscription involves passing a filter token into the
|
||||
registration API.
|
||||
* A URL base for receiving requests from the HS (as the AS is a server,
|
||||
implementers expect to receive data via inbound requests rather than
|
||||
long-poll outbound requests)
|
||||
|
||||
On HS handling events to unknown users:
|
||||
|
||||
* If the HS receives an event for an unknown user who is in the namespace delegated to
|
||||
the AS, then the HS queries the AS for the profile of that user. If the AS
|
||||
confirms the existence of that user (from its perspective), then the HS
|
||||
creates an account to represent the virtual user.
|
||||
* The namespace of virtual user accounts should conform to a structure like
|
||||
``@.irc.freenode.Arathorn:matrix.org``. This lets Matrix users communicate with
|
||||
foreign users who are not yet mapped into Matrix via 3PID mappings or through
|
||||
an existing non-virtual Matrix user by trying to talk to them via a gateway.
|
||||
* The AS can alternatively preprovision virtual users using the existing CS API
|
||||
rather than lazy-loading them in this manner.
|
||||
* The AS may want to link the matrix ID of the sender through to their 3PID in
|
||||
the remote ecosystem. E.g. a message sent from ``@matthew:matrix.org`` may wish
|
||||
to originate from Arathorn on irc.freenode.net in the case of an IRC bridge.
|
||||
It's left as an AS implementation detail as to how the user should authorise
|
||||
the AS to act on its behalf.
|
||||
|
||||
On HS handling events to unknown rooms:
|
||||
|
||||
* If the HS receives an invite to an unknown room which is in the namespace
|
||||
delegated to the AS, then the HS queries the AS for the existence of that room.
|
||||
If the AS confirms its existence (from its perspective), then the HS creates
|
||||
the room.
|
||||
* The initial state of the room may be populated by the AS by querying an
|
||||
initialSync API (probably a subset of the CS initialSync API, to reuse the
|
||||
same pattern for the equivalent function). As messages have to be signed
|
||||
from the point of ``m.room.create``, we will not be able to back-populate
|
||||
arbitrary history for rooms which are lazy-created in this manner, and instead
|
||||
have to chose the amount of history to be synchronised into the AS as a one-off.
|
||||
* If exposing arbitrary history is required, then:
|
||||
|
||||
* either: the room history must be preemptively provisioned in the HS by the AS via
|
||||
the CS API (TODO: meaning the CS API needs to support massaged
|
||||
timestamps), resulting in conversation history being replicated between
|
||||
the HS and the source store.
|
||||
* or: the HS must delegate conversation storage entirely to the
|
||||
AS using a Storage API (not defined here) which allows the existing
|
||||
conversation store to back the HS, complete with all necessary Matrix
|
||||
metadata (e.g. hashes, signatures, federation DAG, etc). This obviously
|
||||
increases the burden of implementing an AS considerably, but is the only
|
||||
option if the implementer wants to avoid duplicating conversation history
|
||||
between the external data source and the HS.
|
||||
|
||||
On HS handling events to existing users and rooms:
|
||||
|
||||
* If the HS receives an event for a user or room that already exists (either
|
||||
provisioned by the AS or by normal client interactions), then the message
|
||||
is handled as normal.
|
||||
* Events in the namespaces of rooms and users that the AS has subscribed to
|
||||
are pushed to the AS using the same pattern as the federation API (without
|
||||
any of the encryption or federation metadata). This serves precisely the
|
||||
same purpose as the CS event stream and has the same data flow semantics
|
||||
(and indeed an AS implementer could chose to use the CS event stream instead)
|
||||
|
||||
* Events are linearised to avoid the AS having to handle the complexity of
|
||||
linearisation, and because if linearisation is good enough for CS, it
|
||||
should be good enough for AS. Should the AS require non-linearised events
|
||||
from Matrix, it should implement the federation API rather than the AS API
|
||||
instead.
|
||||
* HS->AS event pushes are retried for reliability with sequence numbers
|
||||
(or logical timestamping?) to preserve the linearisation order and ensure
|
||||
a reliable event stream.
|
||||
* Clustered HSes must linearise just as they do for the CS API. Clustered
|
||||
ASes must loadbalance the inbound stream across the cluster as required.
|
||||
|
||||
On AS relaying events from unknown-to-HS users:
|
||||
|
||||
* AS injects the event to the HS using the CS API, irrespective of whether the
|
||||
target user or room is known to the HS or not. If the HS doesn't recognise
|
||||
the target it goes through the same lazy-load provisioning as per above.
|
||||
* The reason for not using a subset of the federation API here is because it
|
||||
allows AS developers to reuse existing CS SDKs and benefit from the more
|
||||
meaningful error handling of the CS API. The sending user ID must be
|
||||
explicitly specified, as it cannot be inferred from the access_token, which
|
||||
will be the same for all AS requests.
|
||||
|
||||
* TODO: or do we maintain a separate ``access_token`` mapping? It seems like
|
||||
unnecessary overhead for the AS developer; easier to just use a single
|
||||
privileged ``access_token`` and just track which ``user_id`` is emitting events?
|
||||
* If the AS is spoofing the identity of a real (not virtual) matrix user,
|
||||
we should actually let them log themselves in via OAuth2 to give permission
|
||||
to the AS to act on their behalf.
|
||||
* We can't auth gatewayed virtual users from 3rd party systems who are being
|
||||
relayed into Matrix, as the relaying is happening whether the user likes it
|
||||
or not. Therefore we do need to be able to spoof sender ID for virtual users.
|
||||
|
||||
On AS relaying events in unknown-to-HS rooms:
|
||||
|
||||
* See above.
|
||||
|
||||
On AS publishing aliases for virtual rooms:
|
||||
|
||||
* AS uses the normal alias management API to preemptively create/delete public
|
||||
directory entries for aliases for virtual rooms provided by the AS.
|
||||
* In order to create these aliases, the underlying room ID must also exist, so
|
||||
at least the ``m.room.create`` of that room must also be prepopulated. It seems
|
||||
sensible to prepopulate the required initial state and history of the room to
|
||||
avoid a two-phase prepopulation process.
|
||||
|
||||
On unregistering the AS from the HS:
|
||||
|
||||
* An AS must tell the HS when it is going offline in order to stop receiving
|
||||
requests from the HS. It does this by hitting an API on the HS.
|
||||
|
||||
AS Visibility:
|
||||
|
||||
* If an AS needs to sniff events in a room in order to operate on them (e.g.
|
||||
to act as a search engine) but not inject traffic into the room, it should
|
||||
do so by subscribing to the relevant events without actually joining the room.
|
||||
* If the AS needs to participate in the room as a virtual user (e.g. an IVR
|
||||
service, or a bot, or a gatewayed virtual user), it should join the room
|
||||
normally.
|
||||
* There are rare instances where an AS may wish to participate in a room
|
||||
(including inserting messages), but be hidden from the room list - e.g. a
|
||||
conferencing server focus bot may wish to join many rooms as the focus and
|
||||
both listen to VoIP setups and inject its own VoIP answers, without ever
|
||||
being physically seen in the room. In this scenario, the user should set
|
||||
its presence to 'invisible', a state that HSes should only allow AS-authed
|
||||
users to set.
|
||||
|
||||
E2E Encryption
|
||||
|
||||
* The AS obviously has no visibility to E2E encrypted messages, unless it is
|
||||
explicitly added to an encrypted room and participates in the group chat
|
||||
itself.
|
||||
|
||||
Extensions to CS API
|
||||
====================
|
||||
|
||||
* Ability to assert the identity of the virtual user for all methods.
|
||||
* Ability to massage timestamps when prepopulating historical state and
|
||||
messages of virtual rooms (either by overriding ``origin_server_ts`` (preferred) or
|
||||
adding an ``as_ts`` which we expect clients to honour)
|
||||
* Ability to delete aliases (including from the directory) as well as create them.
|
@ -0,0 +1,574 @@
|
||||
.. TODO
|
||||
Sometimes application services need to create rooms (e.g. when lazy loading
|
||||
from room aliases). Created rooms need to have a user that created them, so
|
||||
federation works (as it relies on an entry existing in m.room.member). We
|
||||
should be able to add metadata to m.room.member to state that this user is an
|
||||
application service, a virtual user, etc.
|
||||
|
||||
|
||||
Application Services HTTP API
|
||||
=============================
|
||||
|
||||
.. contents:: Table of Contents
|
||||
|
||||
.. sectnum::
|
||||
|
||||
Application Service -> Home Server
|
||||
----------------------------------
|
||||
This contains home server APIs which are used by the application service.
|
||||
|
||||
Registration API ``[Draft]``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This API registers the application service with its host homeserver to offer its
|
||||
services.
|
||||
|
||||
Inputs:
|
||||
- Credentials (e.g. some kind of string token)
|
||||
- Namespace[users]
|
||||
- Namespace[room aliases]
|
||||
- URL base to receive inbound comms
|
||||
Output:
|
||||
- The credentials the HS will use to query the AS with in return. (e.g. some
|
||||
kind of string token)
|
||||
Side effects:
|
||||
- The HS will start delivering events to the URL base specified if this 200s.
|
||||
API called when:
|
||||
- The application service wants to register with a brand new home server.
|
||||
Notes:
|
||||
- An application service can state whether they should be the only ones who
|
||||
can manage a specified namespace. This is referred to as an "exclusive"
|
||||
namespace. An exclusive namespace prevents humans and other application
|
||||
services from creating/deleting entities in that namespace. Typically,
|
||||
exclusive namespaces are used when the rooms represent real rooms on
|
||||
another service (e.g. IRC). Non-exclusive namespaces are used when the
|
||||
application service is merely augmenting the room itself (e.g. providing
|
||||
logging or searching facilities).
|
||||
- Namespaces are represented by POSIX extended regular expressions in JSON.
|
||||
They look like::
|
||||
|
||||
users: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "@irc\.freenode\.net/.*"
|
||||
}
|
||||
]
|
||||
|
||||
::
|
||||
|
||||
POST /register
|
||||
|
||||
Request format
|
||||
{
|
||||
url: "https://my.application.service.com/matrix/",
|
||||
as_token: "some_AS_token",
|
||||
namespaces: {
|
||||
users: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "@irc\.freenode\.net/.*"
|
||||
}
|
||||
],
|
||||
aliases: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "#irc\.freenode\.net/.*"
|
||||
}
|
||||
],
|
||||
rooms: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "!irc\.freenode\.net/.*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Returns:
|
||||
200 : Registration accepted.
|
||||
400 : Namespaces do not conform to regex
|
||||
401 : Credentials need to be supplied.
|
||||
403 : AS credentials rejected.
|
||||
|
||||
|
||||
200 OK response format
|
||||
|
||||
{
|
||||
hs_token: "string"
|
||||
}
|
||||
|
||||
Unregister API ``[Draft]``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This API unregisters a previously registered AS from the home server.
|
||||
|
||||
Inputs:
|
||||
- AS token
|
||||
Output:
|
||||
- None.
|
||||
Side effects:
|
||||
- The HS will stop delivering events to the URL base specified for this AS if
|
||||
this 200s.
|
||||
API called when:
|
||||
- The application service wants to stop receiving all events from the HS.
|
||||
|
||||
::
|
||||
|
||||
POST /unregister
|
||||
|
||||
Request format
|
||||
{
|
||||
as_token: "string"
|
||||
}
|
||||
|
||||
|
||||
Home Server -> Application Service
|
||||
----------------------------------
|
||||
This contains application service APIs which are used by the home server.
|
||||
|
||||
User Query ``[Draft]``
|
||||
~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
This API is called by the HS to query the existence of a user on the Application
|
||||
Service's namespace.
|
||||
|
||||
Inputs:
|
||||
- User ID
|
||||
- HS Credentials
|
||||
Output:
|
||||
- Whether the user exists.
|
||||
Side effects:
|
||||
- User is created on the HS by the AS via CS APIs during the processing of this request.
|
||||
API called when:
|
||||
- HS receives an event for an unknown user ID in the AS's namespace, e.g. an
|
||||
invite event to a room.
|
||||
Notes:
|
||||
- When the AS receives this request, if the user exists, it must create the user via
|
||||
the CS API.
|
||||
- It can also set arbitrary information about the user (e.g. display name, join rooms, etc)
|
||||
using the CS API.
|
||||
- When this setup is complete, the AS should respond to the HS request. This means the AS
|
||||
blocks the HS until the user is created.
|
||||
- This is deemed more flexible than alternative methods (e.g. returning a JSON blob with the
|
||||
user's display name and get the HS to provision the user).
|
||||
Retry notes:
|
||||
- The home server cannot respond to the client's request until the response to
|
||||
this API is obtained from the AS.
|
||||
- Recommended that home servers try a few times then time out, returning a
|
||||
408 Request Timeout to the client.
|
||||
|
||||
::
|
||||
|
||||
GET /users/$user_id?access_token=$hs_token
|
||||
|
||||
Returns:
|
||||
200 : User is recognised.
|
||||
404 : User not found.
|
||||
401 : Credentials need to be supplied.
|
||||
403 : HS credentials rejected.
|
||||
|
||||
|
||||
200 OK response format
|
||||
|
||||
{}
|
||||
|
||||
Room Alias Query ``[Draft]``
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
This API is called by the HS to query the existence of a room alias on the
|
||||
Application Service's namespace.
|
||||
|
||||
Inputs:
|
||||
- Room alias
|
||||
- HS Credentials
|
||||
Output:
|
||||
- Whether the room exists.
|
||||
Side effects:
|
||||
- Room is created on the HS by the AS via CS APIs during the processing of
|
||||
this request.
|
||||
API called when:
|
||||
- HS receives an event to join a room alias in the AS's namespace.
|
||||
Notes:
|
||||
- When the AS receives this request, if the room exists, it must create the room via
|
||||
the CS API.
|
||||
- It can also set arbitrary information about the room (e.g. name, topic, etc)
|
||||
using the CS API.
|
||||
- It can send messages as other users in order to populate scrollback.
|
||||
- When this setup is complete, the AS should respond to the HS request. This means the AS
|
||||
blocks the HS until the room is created and configured.
|
||||
- This is deemed more flexible than alternative methods (e.g. returning an initial sync
|
||||
style JSON blob and get the HS to provision the room). It also means that the AS knows
|
||||
the room ID -> alias mapping.
|
||||
Retry notes:
|
||||
- The home server cannot respond to the client's request until the response to
|
||||
this API is obtained from the AS.
|
||||
- Recommended that home servers try a few times then time out, returning a
|
||||
408 Request Timeout to the client.
|
||||
|
||||
::
|
||||
|
||||
GET /rooms/$room_alias?access_token=$hs_token
|
||||
|
||||
Returns:
|
||||
200 : Room is recognised.
|
||||
404 : Room not found.
|
||||
401 : Credentials need to be supplied.
|
||||
403 : HS credentials rejected.
|
||||
|
||||
|
||||
200 OK response format
|
||||
|
||||
{}
|
||||
|
||||
Pushing ``[Draft]``
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
This API is called by the HS when the HS wants to push an event (or batch of
|
||||
events) to the AS.
|
||||
|
||||
Inputs:
|
||||
- HS Credentials
|
||||
- Event(s) to give to the AS
|
||||
- HS-generated transaction ID
|
||||
Output:
|
||||
- None.
|
||||
|
||||
Data flows:
|
||||
|
||||
::
|
||||
|
||||
Typical
|
||||
HS ---> AS : Home server sends events with transaction ID T.
|
||||
<--- : AS sends back 200 OK.
|
||||
|
||||
AS ACK Lost
|
||||
HS ---> AS : Home server sends events with transaction ID T.
|
||||
<-/- : AS 200 OK is lost.
|
||||
HS ---> AS : Home server retries with the same transaction ID of T.
|
||||
<--- : AS sends back 200 OK. If the AS had processed these events
|
||||
already, it can NO-OP this request (and it knows if it is the same
|
||||
events based on the transacton ID).
|
||||
|
||||
|
||||
Retry notes:
|
||||
- If the HS fails to pass on the events to the AS, it must retry the request.
|
||||
- Since ASes by definition cannot alter the traffic being passed to it (unlike
|
||||
say, a Policy Server), these requests can be done in parallel to general HS
|
||||
processing; the HS doesn't need to block whilst doing this.
|
||||
- Home servers should use exponential backoff as their retry algorithm.
|
||||
- Home servers MUST NOT alter (e.g. add more) events they were going to
|
||||
send within that transaction ID on retries, as the AS may have already
|
||||
processed the events.
|
||||
|
||||
Ordering notes:
|
||||
- The events sent to the AS should be linearised, as they are from the event
|
||||
stream.
|
||||
- The home server will need to maintain a queue of transactions to send to
|
||||
the AS.
|
||||
|
||||
::
|
||||
|
||||
PUT /transactions/$transaction_id?access_token=$hs_token
|
||||
|
||||
Request format
|
||||
{
|
||||
events: [
|
||||
...
|
||||
]
|
||||
}
|
||||
|
||||
Client-Server v2 API Extensions
|
||||
-------------------------------
|
||||
|
||||
Identity assertion
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
The client-server API infers the user ID from the ``access_token`` provided in
|
||||
every request. It would be an annoying amount of book-keeping to maintain tokens
|
||||
for every virtual user. It would be preferable if the application service could
|
||||
use the CS API with its own ``as_token`` instead, and specify the virtual user
|
||||
they wish to be acting on behalf of. For real users, this would require
|
||||
additional permissions granting the AS permission to masquerade as a matrix user.
|
||||
|
||||
Inputs:
|
||||
- Application service token (``access_token``)
|
||||
|
||||
Either:
|
||||
- User ID in the AS namespace to act as.
|
||||
Or:
|
||||
- OAuth2 token of real user (which may end up being an access token)
|
||||
Notes:
|
||||
- This will apply on all aspects of the CS API, except for Account Management.
|
||||
- The ``as_token`` is inserted into ``access_token`` which is usually where the
|
||||
client token is. This is done on purpose to allow application services to
|
||||
reuse client SDKs.
|
||||
|
||||
::
|
||||
|
||||
/path?access_token=$token&user_id=$userid
|
||||
|
||||
Query Parameters:
|
||||
access_token: The application service token
|
||||
user_id: The desired user ID to act as.
|
||||
|
||||
/path?access_token=$token&user_token=$token
|
||||
|
||||
Query Parameters:
|
||||
access_token: The application service token
|
||||
user_token: The token granted to the AS by the real user
|
||||
|
||||
Timestamp massaging
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
The application service may want to inject events at a certain time (reflecting
|
||||
the time on the network they are tracking e.g. irc, xmpp). Application services
|
||||
need to be able to adjust the ``origin_server_ts`` value to do this.
|
||||
|
||||
Inputs:
|
||||
- Application service token (``as_token``)
|
||||
- Desired timestamp
|
||||
Notes:
|
||||
- This will only apply when sending events.
|
||||
|
||||
::
|
||||
|
||||
/path?access_token=$token&ts=$timestamp
|
||||
|
||||
Query Parameters added to the send event APIs only:
|
||||
access_token: The application service token
|
||||
ts: The desired timestamp
|
||||
|
||||
Server admin style permissions
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
The home server needs to give the application service *full control* over its
|
||||
namespace, both for users and for room aliases. This means that the AS should
|
||||
be able to create/edit/delete any room alias in its namespace, as well as
|
||||
create/delete any user in its namespace. No additional API changes need to be
|
||||
made in order for control of room aliases to be granted to the AS. Creation of
|
||||
users needs API changes in order to:
|
||||
|
||||
- Work around captchas.
|
||||
- Have a 'passwordless' user.
|
||||
|
||||
This involves bypassing the registration flows entirely. This is achieved by
|
||||
including the AS token on a ``/register`` request, along with a login type of
|
||||
``m.login.application_service`` to set the desired user ID without a password.
|
||||
|
||||
::
|
||||
|
||||
/register?access_token=$as_token
|
||||
|
||||
Content:
|
||||
{
|
||||
type: "m.login.application_service",
|
||||
user: "<desired user localpart in AS namespace>"
|
||||
}
|
||||
|
||||
Application services which attempt to create users or aliases *outside* of
|
||||
their defined namespaces will receive an error code ``M_EXCLUSIVE``. Similarly,
|
||||
normal users who attempt to create users or alises *inside* an application
|
||||
service-defined namespace will receive the same ``M_EXCLUSIVE`` error code.
|
||||
|
||||
ID conventions ``[Draft]``
|
||||
--------------------------
|
||||
.. NOTE::
|
||||
- Giving HSes the freedom to namespace still feels like the Right Thing here.
|
||||
- Exposing a public API provides the consistency which was the main complaint
|
||||
against namespacing.
|
||||
- This may have knock-on effects for the AS registration API. E.g. why don't
|
||||
we let ASes specify the *URI* regex they want?
|
||||
|
||||
This concerns the well-defined conventions for mapping 3P network IDs to matrix
|
||||
IDs, which we expect clients to be able to do by themselves.
|
||||
|
||||
User IDs
|
||||
~~~~~~~~
|
||||
Matrix users may wish to directly contact a virtual user, e.g. to send an email.
|
||||
The URI format is a well-structured way to represent a number of different ID
|
||||
types, including:
|
||||
|
||||
- MSISDNs (``tel``)
|
||||
- Email addresses (``mailto``)
|
||||
- IRC nicks (``irc`` - https://tools.ietf.org/html/draft-butcher-irc-url-04)
|
||||
- XMPP (xep-0032)
|
||||
- SIP URIs (RFC 3261)
|
||||
|
||||
As a result, virtual user IDs SHOULD relate to their URI counterpart. This
|
||||
mapping from URI to user ID can be expressed in a number of ways:
|
||||
|
||||
- Expose a C-S API on the HS which takes URIs and responds with user IDs.
|
||||
- Munge the URI with the user ID.
|
||||
|
||||
Exposing an API would allow HSes to internally map user IDs however they like,
|
||||
at the cost of an extra round trip (of which the response can be cached).
|
||||
Munging the URI would allow clients to apply the mapping locally, but would force
|
||||
user X on service Y to always map to the same munged user ID. Considering the
|
||||
exposed API could just be applying this munging, there is more flexibility if
|
||||
an API is exposed.
|
||||
|
||||
::
|
||||
|
||||
GET /_matrix/app/v1/user?uri=$url_encoded_uri
|
||||
|
||||
Returns 200 OK:
|
||||
{
|
||||
user_id: <complete user ID on local HS>
|
||||
}
|
||||
|
||||
Room Aliases
|
||||
~~~~~~~~~~~~
|
||||
We may want to expose some 3P network rooms so Matrix users can join them directly,
|
||||
e.g. IRC rooms. We don't want to expose every 3P network room though, e.g. mailto,
|
||||
tel. Rooms which are publicly accessible (e.g. IRC rooms) can be exposed as an alias by
|
||||
the application service. Private rooms (e.g. sending an email to someone) should not
|
||||
be exposed in this way, but should instead operate using normal invite/join semantics.
|
||||
Therefore, the ID conventions discussed below are only valid for public rooms which
|
||||
expose room aliases.
|
||||
|
||||
Matrix users may wish to join XMPP rooms (e.g. using XEP-0045) or IRC rooms. In both
|
||||
cases, these rooms can be expressed as URIs. For consistency, these "room" URIs
|
||||
SHOULD be mapped in the same way as "user" URIs.
|
||||
|
||||
::
|
||||
|
||||
GET /_matrix/app/v1/alias?uri=$url_encoded_uri
|
||||
|
||||
Returns 200 OK:
|
||||
{
|
||||
alias: <complete room alias on local HS>
|
||||
}
|
||||
|
||||
Event fields
|
||||
~~~~~~~~~~~~
|
||||
We recommend that any gatewayed events should include an ``external_url`` field
|
||||
in their content to provide a way for Matrix clients to link into the 'native'
|
||||
client from which the event originated. For instance, this could contain the
|
||||
message-ID for emails/nntp posts, or a link to a blog comment when gatewaying
|
||||
blog comment traffic in & out of matrix
|
||||
|
||||
|
||||
Examples
|
||||
--------
|
||||
.. NOTE::
|
||||
- User/Alias namespaces are subject to change depending on ID conventions.
|
||||
|
||||
IRC
|
||||
~~~
|
||||
Pre-conditions:
|
||||
- Server admin stores the AS token "T_a" on the home server.
|
||||
- Home server has a token "T_h".
|
||||
- Home server has the domain "hsdomain.com"
|
||||
|
||||
1. Application service registration
|
||||
|
||||
::
|
||||
|
||||
AS -> HS: Registers itself with the home server
|
||||
POST /register
|
||||
{
|
||||
url: "https://someapp.com/matrix",
|
||||
as_token: "T_a",
|
||||
namespaces: {
|
||||
users: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "@irc\.freenode\.net/.*"
|
||||
}
|
||||
],
|
||||
aliases: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "#irc\.freenode\.net/.*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Returns 200 OK:
|
||||
{
|
||||
hs_token: "T_h"
|
||||
}
|
||||
|
||||
2. IRC user "Bob" says "hello?" on "#matrix" at timestamp 1421416883133:
|
||||
|
||||
::
|
||||
|
||||
- AS stores message as potential scrollback.
|
||||
- Nothing happens as no Matrix users are in the room.
|
||||
|
||||
3. Matrix user "@alice:hsdomain.com" wants to join "#matrix":
|
||||
|
||||
::
|
||||
|
||||
User -> HS: Request to join "#irc.freenode.net/#matrix:hsdomain.com"
|
||||
|
||||
HS -> AS: Room Query "#irc.freenode.net/#matrix:hsdomain.com"
|
||||
GET /rooms/%23irc.freenode.net%2F%23matrix%3Ahsdomain.com?access_token=T_h
|
||||
[Starts blocking]
|
||||
AS -> HS: Creates room. Gets room ID "!aasaasasa:hsdomain.com".
|
||||
AS -> HS: Sets room name to "#matrix".
|
||||
AS -> HS: Sends message as ""@irc.freenode.net/Bob:hsdomain.com"
|
||||
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
|
||||
?access_token=T_a
|
||||
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
|
||||
&ts=1421416883133
|
||||
{
|
||||
body: "hello?"
|
||||
msgtype: "m.text"
|
||||
}
|
||||
HS -> AS: User Query "@irc.freenode.net/Bob:hsdomain.com"
|
||||
GET /users/%40irc.freenode.net%2FBob%3Ahsdomain.com?access_token=T_h
|
||||
[Starts blocking]
|
||||
AS -> HS: Creates user using CS API extension.
|
||||
POST /register?access_token=T_a
|
||||
{
|
||||
type: "m.login.application_service",
|
||||
user: "irc.freenode.net/Bob"
|
||||
}
|
||||
AS -> HS: Set user display name to "Bob".
|
||||
[Finishes blocking]
|
||||
[Finished blocking]
|
||||
|
||||
- HS sends room information back to client.
|
||||
|
||||
4. @alice:hsdomain.com says "hi!" in this room:
|
||||
|
||||
::
|
||||
|
||||
User -> HS: Send message "hi!" in room !aasaasasa:hsdomain.com
|
||||
|
||||
- HS sends message.
|
||||
- HS sees the room ID is in the AS namespace and pushes it to the AS.
|
||||
|
||||
HS -> AS: Push event
|
||||
PUT /transactions/1?access_token=T_h
|
||||
{
|
||||
events: [
|
||||
{
|
||||
content: {
|
||||
body: "hi!",
|
||||
msgtype: "m.text"
|
||||
},
|
||||
origin_server_ts: <generated by hs>,
|
||||
user_id: "@alice:hsdomain.com",
|
||||
room_id: "!aasaasasa:hsdomain.com",
|
||||
type: "m.room.message"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
- AS passes this through to IRC.
|
||||
|
||||
|
||||
5. IRC user "Bob" says "what's up?" on "#matrix" at timestamp 1421418084816:
|
||||
|
||||
::
|
||||
|
||||
IRC -> AS: "what's up?"
|
||||
AS -> HS: Send message via CS API extension
|
||||
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
|
||||
?access_token=T_a
|
||||
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
|
||||
&ts=1421418084816
|
||||
{
|
||||
body: "what's up?"
|
||||
msgtype: "m.text"
|
||||
}
|
||||
|
||||
- HS modifies the user_id and origin_server_ts on the event and sends it.
|
@ -0,0 +1,34 @@
|
||||
Macaroon Caveats
|
||||
================
|
||||
|
||||
`Macaroons`_ are issued by Matrix servers as authorization tokens. Macaroons may be restricted by adding caveats to them.
|
||||
|
||||
.. _Macaroons: http://theory.stanford.edu/~ataly/Papers/macaroons.pdf
|
||||
|
||||
Caveats can only be used for reducing the scope of a token, never for increasing it. Servers are required to reject any macroon with a caveat that they do not understand.
|
||||
|
||||
Some caveats are specified in this specification, and must be understood by all servers. The use of non-standard caveats is allowed.
|
||||
|
||||
All caveats must take the form:
|
||||
|
||||
`key` `operator` `value`
|
||||
where `key` is a non-empty string drawn from the character set [A-Za-z0-9_]
|
||||
`operator` is a non-empty string which does not contain whitespace
|
||||
`value` is a non-empty string
|
||||
And these are joined by single space characters.
|
||||
|
||||
Specified caveats:
|
||||
|
||||
+-------------+--------------------------------------------------+------------------------------------------------------------------------------------------------+
|
||||
| Caveat name | Description | Legal Values |
|
||||
+-------------+--------------------------------------------------+------------------------------------------------------------------------------------------------+
|
||||
| gen | Generation of the macaroon caveat spec. | 1 |
|
||||
| user_id | ID of the user for which this macaroon is valid. | Pure equality check. Operator must be =. |
|
||||
| type | The purpose of this macaroon. | access - used to authorize any action except token refresh |
|
||||
| refresh - only used to authorize a token refresh |
|
||||
| time | Time before/after which this macaroon is valid. | A POSIX timestamp in milliseconds (in UTC). |
|
||||
| Operator < means the macaroon is valid before the timestamp, as interpreted by the server. |
|
||||
| Operator > means the macaroon is valid after the timestamp, as interpreted by the server. |
|
||||
| Operator == means the macaroon is valid at exactly the timestamp, as interpreted by the server.|
|
||||
| Note that exact equality of time is largely meaningless. |
|
||||
+-------------+--------------------------------------------------+------------------------------------------------------------------------------------------------+
|
@ -1,76 +0,0 @@
|
||||
Media Repository
|
||||
================
|
||||
|
||||
File uploading and downloading.
|
||||
|
||||
HTTP API
|
||||
--------
|
||||
|
||||
Uploads are POSTed to a resource which returns a token which is used to GET
|
||||
the download. Uploads are POSTed to the sender's local homeserver, but are
|
||||
downloaded from the recipient's local homeserver, which must thus first transfer
|
||||
the content from the origin homeserver using the same API (unless the origin
|
||||
and destination homeservers are the same). The upload/download API is::
|
||||
|
||||
=> POST /_matrix/media/v1/upload HTTP/1.1
|
||||
Content-Type: <media-type>
|
||||
|
||||
<media>
|
||||
|
||||
<= HTTP/1.1 200 OK
|
||||
Content-Type: application/json
|
||||
|
||||
{ "content-uri": "mxc://<server-name>/<media-id>" }
|
||||
|
||||
=> GET /_matrix/media/v1/download/<server-name>/<media-id> HTTP/1.1
|
||||
|
||||
<= HTTP/1.1 200 OK
|
||||
Content-Type: <media-type>
|
||||
Content-Disposition: attachment;filename=<upload-filename>
|
||||
|
||||
<media>
|
||||
|
||||
Clients can get thumbnails by supplying a desired width and height and
|
||||
thumbnailing method::
|
||||
|
||||
=> GET /_matrix/media/v1/thumbnail/<server_name>
|
||||
/<media-id>?width=<w>&height=<h>&method=<m> HTTP/1.1
|
||||
|
||||
<= HTTP/1.1 200 OK
|
||||
Content-Type: image/jpeg or image/png
|
||||
|
||||
<thumbnail>
|
||||
|
||||
The thumbnail methods are "crop" and "scale". "scale" trys to return an
|
||||
image where either the width or the height is smaller than the requested
|
||||
size. The client should then scale and letterbox the image if it needs to
|
||||
fit within a given rectangle. "crop" trys to return an image where the
|
||||
width and height are close to the requested size and the aspect matches
|
||||
the requested size. The client should scale the image if it needs to fit
|
||||
within a given rectangle.
|
||||
|
||||
Homeservers may generate thumbnails for content uploaded to remote
|
||||
homeservers themselves or may rely on the remote homeserver to thumbnail
|
||||
the content. Homeservers may return thumbnails of a different size to that
|
||||
requested. However homeservers should provide extact matches where reasonable.
|
||||
|
||||
Security
|
||||
--------
|
||||
|
||||
Clients may try to upload very large files. Homeservers should not store files
|
||||
that are too large and should not serve them to clients.
|
||||
|
||||
Clients may try to upload very large images. Homeservers should not attempt to
|
||||
generate thumbnails for images that are too large.
|
||||
|
||||
Remote homeservers may host very large files or images. Homeserver should not
|
||||
proxy or thumbnail large files or images from remote homeservers.
|
||||
|
||||
Clients may try to upload a large number of files. Homeservers should limit the
|
||||
number and total size of media that can be uploaded by clients.
|
||||
|
||||
Clients may try to access a large number of remote files through a homeserver.
|
||||
Homeservers should restrict the number and size of remote files that it caches.
|
||||
|
||||
Clients or remote homeservers may try to upload malicious files targeting
|
||||
vunerabilities in either the homeserver thumbnailing or the client decoders.
|
@ -0,0 +1,48 @@
|
||||
Gatewaying to the PSTN via Matrix Application Services
|
||||
======================================================
|
||||
|
||||
Matrix Application Services (AS) provides a way for PSTN users to interact
|
||||
with Matrix via an AS acting as a gateway. Each PSTN user is represented as a
|
||||
virtual user on a specific homeserver maintained by the AS. Typically the AS
|
||||
is provisioned on a well-known AS-supplier HS (e.g. matrix.openmarket.com) or
|
||||
is a service provisioned on the user's local HS.
|
||||
|
||||
In either scenario, the AS maintains virtual users of form
|
||||
@.tel.e164:homeserver. These are lazily created (as per the AS spec) when
|
||||
matrix users try to contact a user id of form @.tel.*:homeserver, or when the
|
||||
AS needs to inject traffic into the HS on behalf of the PSTN user. The reason
|
||||
for these being a visible virtual user rather than an invisible user or an
|
||||
invisible sniffing AS is because they do represent real physical 3rd party
|
||||
endpoints in the PSTN, and need to be able to send return messages.
|
||||
|
||||
Communication with an actual PSTN user happens in a normal Matrix room, which
|
||||
for 1:1 matrix<->pstn contact will typically store all conversation history
|
||||
with that user. On first contact, the matrix user invites the virtual user
|
||||
into the room (or vice versa). In the event of switching to another AS-enabled
|
||||
HS, the matrix user would kick the old AS and invite the new one. In the event
|
||||
of needing loadbalancing between two SMS gateways (for instance), the user
|
||||
would set visibility flags (TODO: specify per-message ACLs, or use crypto to
|
||||
only sign messages so they're visible to certain other users?) to adjust which
|
||||
virtual AS users could see which messages in the room.
|
||||
|
||||
For group chat, one or more AS virtual users may be invited to a group chat,
|
||||
where-upon they will relay all the traffic in that group chat through to their
|
||||
PSTN counterpart (and vice versa). This behaviour requires no additional
|
||||
functionality beyond that required to support 1:1 chat.
|
||||
|
||||
When contacting a user, Matrix clients should check whether a given E.164
|
||||
number is already mapped to a real Matrix user by querying the identity
|
||||
servers (or subscribing to identity updates for a given E.164 number. TODO: ID
|
||||
server subscriptions). If the E.164 number has a validated mapping in the ID
|
||||
server to a Matrix ID, then this target ID should be used instead of
|
||||
contacting the virtual user.
|
||||
|
||||
It's likely that PSTN gateway ASes will need to charge the end-user for use of
|
||||
the gateway. The AS must therefore track credit per matrix ID it interacts
|
||||
with, and stop gatewaying as desired once credit is exhausted. The task of
|
||||
extracting credit from the end-user and adding it to the AS is not covered by
|
||||
the Matrix specification.
|
||||
|
||||
For SMS routing, options are:
|
||||
* Terminate traffic only (from a shared shortcode originator)
|
||||
* Two-way traffic via a VMN. To save allocating huge numbers of VMNs to Matrix users, the VMN can be allocated from a pool such that each {caller,callee} tuple is unique (but the caller number will only work from that specific callee).
|
@ -1,57 +0,0 @@
|
||||
Typing Notifications
|
||||
====================
|
||||
|
||||
Client APIs
|
||||
-----------
|
||||
|
||||
To set "I am typing for the next N msec"::
|
||||
PUT .../rooms/:room_id/typing/:user_id
|
||||
Content: { "typing": true, "timeout": N }
|
||||
# timeout is in msec; I suggest no more than 20 or 30 seconds
|
||||
|
||||
This should be re-sent by the client to continue informing the server the user
|
||||
is still typing; I suggest a safety margin of 5 seconds before the expected
|
||||
timeout runs out. Just keep declaring a new timeout, it will replace the old
|
||||
one.
|
||||
|
||||
To set "I am no longer typing"::
|
||||
PUT ../rooms/:room_id/typing/:user_id
|
||||
Content: { "typing": false }
|
||||
|
||||
Client Events
|
||||
-------------
|
||||
|
||||
All room members will receive an event on the event stream::
|
||||
|
||||
{
|
||||
"type": "m.typing",
|
||||
"room_id": "!room-id-here:matrix.org",
|
||||
"content": {
|
||||
"user_ids": ["list of", "every user", "who is", "currently typing"]
|
||||
}
|
||||
}
|
||||
|
||||
The client must use this list to *REPLACE* its knowledge of every user who is
|
||||
currently typing. The reason for this is that the server DOES NOT remember
|
||||
users who are not currently typing, as that list gets big quickly. The client
|
||||
should mark as not typing, any user ID who is not in that list.
|
||||
|
||||
Server APIs
|
||||
-----------
|
||||
|
||||
Servers will emit EDUs in the following form::
|
||||
|
||||
{
|
||||
"type": "m.typing",
|
||||
"content": {
|
||||
"room_id": "!room-id-here:matrix.org",
|
||||
"user_id": "@user-id-here:matrix.org",
|
||||
"typing": true/false,
|
||||
}
|
||||
}
|
||||
|
||||
Server EDUs don't (currently) contain timing information; it is up to
|
||||
originating HSes to ensure they eventually send "stop" notifications.
|
||||
|
||||
((This will eventually need addressing, as part of the wider typing/presence
|
||||
timer addition work))
|
@ -0,0 +1,13 @@
|
||||
Testing a schema
|
||||
----------------
|
||||
There are [many](http://json-schema.org/implementations.html) JSON Schema
|
||||
validators you can use to validate incoming events. Not all of them support
|
||||
JSON Schema v4, and some of them have bugs which prevent ``$ref`` from being
|
||||
resolved correctly. For basic CLI testing, we recommend and have verified they
|
||||
work with the Node.js package [z-schema](https://github.com/zaggino/z-schema):
|
||||
```
|
||||
$ npm install -g z-schema
|
||||
$ z-schema schema/v1/m.room.message examples/v1/m.room.message_m.text
|
||||
schema validation passed
|
||||
json #1 validation passed
|
||||
```
|
@ -0,0 +1,16 @@
|
||||
#!/bin/bash -e
|
||||
# Runs z-schema over all of the schema files (looking for matching examples)
|
||||
find schema/v1/m.* | while read line
|
||||
do
|
||||
split_path=(${line///// })
|
||||
event_type=(${split_path[2]})
|
||||
echo "Checking $event_type"
|
||||
echo "--------------------"
|
||||
# match exact name or exact name with a #<something>
|
||||
find examples/v1 -name $event_type -o -name "$event_type#*" | while read exline
|
||||
do
|
||||
echo " against $exline"
|
||||
# run z-schema: because of bash -e if this fails we bail with exit code 1
|
||||
z-schema schema/v1/$event_type $exline
|
||||
done
|
||||
done
|
@ -0,0 +1,76 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import traceback
|
||||
|
||||
|
||||
def import_error(module, package, debian, error):
|
||||
sys.stderr.write((
|
||||
"Error importing %(module)s: %(error)r\n"
|
||||
"To install %(module)s run:\n"
|
||||
" pip install %(package)s\n"
|
||||
"or on Debian run:\n"
|
||||
" sudo apt-get install python-%(debian)s\n"
|
||||
) % locals())
|
||||
if __name__ == '__main__':
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
import jsonschema
|
||||
except ImportError as e:
|
||||
import_error("jsonschema", "jsonschema", "jsonschema", e)
|
||||
raise
|
||||
|
||||
try:
|
||||
import yaml
|
||||
except ImportError as e:
|
||||
import_error("yaml", "PyYAML", "yaml", e)
|
||||
raise
|
||||
|
||||
|
||||
def check_example_file(examplepath, schemapath):
|
||||
with open(examplepath) as f:
|
||||
example = yaml.load(f)
|
||||
|
||||
with open(schemapath) as f:
|
||||
schema = yaml.load(f)
|
||||
|
||||
fileurl = "file://" + os.path.abspath(schemapath)
|
||||
|
||||
print ("Checking schema for: %r %r" % (examplepath, schemapath))
|
||||
# Setting the 'id' tells jsonschema where the file is so that it
|
||||
# can correctly resolve relative $ref references in the schema
|
||||
schema['id'] = fileurl
|
||||
try:
|
||||
jsonschema.validate(example, schema)
|
||||
except Exception as e:
|
||||
raise ValueError("Error validating JSON schema for %r %r" % (
|
||||
examplepath, schemapath
|
||||
), e)
|
||||
|
||||
|
||||
def check_example_dir(exampledir, schemadir):
|
||||
errors = []
|
||||
for root, dirs, files in os.walk(exampledir):
|
||||
for filename in files:
|
||||
if filename.startswith("."):
|
||||
# Skip over any vim .swp files.
|
||||
continue
|
||||
examplepath = os.path.join(root, filename)
|
||||
schemapath = examplepath.replace(exampledir, schemadir)
|
||||
try:
|
||||
check_example_file(examplepath, schemapath)
|
||||
except Exception as e:
|
||||
errors.append(sys.exc_info())
|
||||
for (exc_type, exc_value, exc_trace) in errors:
|
||||
traceback.print_exception(exc_type, exc_value, exc_trace)
|
||||
if errors:
|
||||
raise ValueError("Error validating examples")
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
check_example_dir("examples", "schema")
|
||||
except:
|
||||
sys.exit(1)
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"call_id": "12345",
|
||||
"lifetime": 60000,
|
||||
"answer": {
|
||||
"type" : "answer",
|
||||
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.call.answer",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"call_id": "12345",
|
||||
"candidates": [
|
||||
{
|
||||
"sdpMid": "audio",
|
||||
"sdpMLineIndex": 0,
|
||||
"candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156 43670 typ host generation 0"
|
||||
}
|
||||
]
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.call.candidates",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"call_id": "12345"
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.call.hangup",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,17 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"version" : 0,
|
||||
"call_id": "12345",
|
||||
"lifetime": 60000,
|
||||
"offer": {
|
||||
"type" : "offer",
|
||||
"sdp" : "v=0\r\no=- 6584580628695956864 2 IN IP4 127.0.0.1[...]"
|
||||
}
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.call.invite",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": "mxc://localhost:wefuiwegh8742w",
|
||||
"last_active_ago": 2478593,
|
||||
"presence": "online",
|
||||
"user_id": "@example:localhost"
|
||||
},
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.presence"
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"type": "m.receipt",
|
||||
"room_id": "!KpjVgQyZpzBwvMBsnT:matrix.org",
|
||||
"content": {
|
||||
"$1435641916114394fHBLK:matrix.org": {
|
||||
"m.read": {
|
||||
"@rikj:jki.re": {
|
||||
"ts": 1436451550453
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"aliases": ["#somewhere:localhost", "#another:localhost"]
|
||||
},
|
||||
"state_key": "localhost",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.aliases",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"alias": "#somewhere:localhost"
|
||||
},
|
||||
"state_key": "",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.canonical_alias",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"creator": "@example:localhost"
|
||||
},
|
||||
"state_key": "",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.create",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"history_visibility": "shared"
|
||||
},
|
||||
"state_key": "",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.history_visibility",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"join_rule": "public"
|
||||
},
|
||||
"state_key": "",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.join_rules",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"membership": "join",
|
||||
"avatar_url": "mxc://localhost/SEsfnsuifSDFSSEF#auto",
|
||||
"displayname": "Alice Margatroid",
|
||||
"third_party_invite": {
|
||||
"token": "pc98",
|
||||
"public_key": "abc123",
|
||||
"key_validity_url": "https://magic.forest/verifykey",
|
||||
"signature": "q1w2e3",
|
||||
"sender": "@zun:zun.soft"
|
||||
}
|
||||
},
|
||||
"state_key": "@alice:localhost",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.member",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"age": 146,
|
||||
"content": {
|
||||
"body": "Bee Gees - Stayin' Alive",
|
||||
"url": "mxc://localhost/ffed755USFFxlgbQYZGtryd",
|
||||
"info": {
|
||||
"duration": 2140786,
|
||||
"size": 1563685,
|
||||
"mimetype": "audio/mpeg"
|
||||
},
|
||||
"msgtype": "m.audio"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:localhost",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"body": "thinks this is an example emote",
|
||||
"msgtype": "m.emote"
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.message",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"age": 146,
|
||||
"content": {
|
||||
"body": "something-important.doc",
|
||||
"filename": "something-important.doc",
|
||||
"info": {
|
||||
"mimetype": "application/msword",
|
||||
"size": 46144
|
||||
},
|
||||
"msgtype": "m.file",
|
||||
"url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:localhost",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"body": "filename.jpg",
|
||||
"info": {
|
||||
"h": 398,
|
||||
"w": 394,
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 31037
|
||||
},
|
||||
"url": "mxc://localhost/JWEIFJgwEIhweiWJE",
|
||||
"msgtype": "m.image"
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.message",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
{
|
||||
"age": 146,
|
||||
"content": {
|
||||
"body": "Big Ben, London, UK",
|
||||
"geo_uri": "geo:51.5008,0.1247",
|
||||
"thumbnail_url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe",
|
||||
"thumbnail_info": {
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 46144,
|
||||
"w": 300,
|
||||
"h": 300
|
||||
},
|
||||
"msgtype": "m.location"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:localhost",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"body": "This is an example notice",
|
||||
"msgtype": "m.notice"
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.message",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"body": "This is an example text message",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.message",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
{
|
||||
"age": 146,
|
||||
"content": {
|
||||
"body": "Gangnam Style",
|
||||
"url": "mxc://localhost/a526eYUSFFxlgbQYZmo442",
|
||||
"info": {
|
||||
"thumbnail_url": "mxc://localhost/FHyPlCeYUSFFxlgbQYZmoEoe",
|
||||
"thumbnail_info": {
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 46144,
|
||||
"w": 300,
|
||||
"h": 300
|
||||
},
|
||||
"w": 480,
|
||||
"h": 320,
|
||||
"duration": 2140786,
|
||||
"size": 1563685,
|
||||
"mimetype": "video/mp4"
|
||||
},
|
||||
"msgtype": "m.video"
|
||||
},
|
||||
"event_id": "$143273582443PhrSn:localhost",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"room_id": "!jEsUZKDJdhlrceRyVU:localhost",
|
||||
"type": "m.room.message",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"type": "delivered",
|
||||
"target_event_id": "$WEIGFHFW:localhost"
|
||||
},
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.message.feedback",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue