Compare commits

..

No commits in common. "main" and "r0.0.1" have entirely different histories.
main ... r0.0.1

920 changed files with 31659 additions and 72177 deletions

1
.github/CODEOWNERS vendored
View file

@ -1 +0,0 @@
* @matrix-org/spec-core-team

2
.github/FUNDING.yml vendored
View file

@ -1,2 +0,0 @@
patreon: matrixdotorg
liberapay: matrixdotorg

View file

@ -1,13 +0,0 @@
---
name: Clarity problem
about: Report an area of the spec that is unclear.
title: ''
labels: 'clarification'
assignees: ''
---
**Link to problem area**:
**Issue**
What is wrong? How can we improve?

View file

@ -1,8 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Matrix Spec Discussion
url: "https://matrix.to/#/#matrix-spec:matrix.org"
about: Questions about the spec and proposal process can be asked here.
- name: Matrix Security Policy
url: https://www.matrix.org/security-disclosure-policy/
about: Learn more about our security disclosure policy.

View file

@ -1,13 +0,0 @@
---
name: Cosmetic issue
about: Report an issue with how the spec looks.
title: ''
labels: 'aesthetic'
assignees: ''
---
**Link to problem area**:
**Issue**
What is wrong? What can we do to improve?

View file

@ -1,12 +0,0 @@
---
name: Spec idea
about: Suggest a future MSC idea.
title: ''
labels: 'improvement'
assignees: ''
---
**Suggestion**
What would you like to see in Matrix? If your idea is vaguely complete enough, we
recommend submitting [an MSC](https://matrix.org/docs/spec/proposals) instead.

View file

@ -1,37 +0,0 @@
---
name: '[SCT] Release checklist'
about: 'Used by the Spec Core Team to create a new release.'
title: 'Matrix 1.X'
labels: 'release-blocker'
assignees: ''
---
<!-- ------------------------------------------------------------------------ -->
<!-- Please asssign the release coordinator (probably yourself) to this issue -->
<!-- ------------------------------------------------------------------------ -->
Date: **Thursday, May 25, 2023** <!-- CHANGE ME -->
Previous release: <!-- LINK TO LAST RELEASE'S CHECKLIST -->
Preflight checklist ([release steps](https://github.com/matrix-org/matrix-spec/blob/main/meta/releasing.md)):
* [ ] Pin this issue to the repo.
* [ ] Ensure the social media account holders are available for the release day.
* [ ] Blog post written.
* [ ] Check for release blockers that may have been missed.
* [ ] Review/fix the changelog.
Release checklist ([release steps](https://github.com/matrix-org/matrix-spec/blob/main/meta/releasing.md)):
* [ ] Branch stuffs.
* [ ] Github release artifact.
* [ ] Published to spec.matrix.org.
* [ ] All links work.
* [ ] Publish blog post.
* [ ] Announce in #matrix-spec, client developers, HS developers, SCT office, and other rooms as warranted.
* [ ] Post to Twitter/Mastodon.
* [ ] Close this issue.
* [ ] Unpin this issue from the repo.
Known release blockers:
* [ ] <!-- Issue/PR link -->

View file

@ -1,16 +0,0 @@
---
name: Documentation error
about: Report an issue with the spec itself (incorrect text).
title: ''
labels: 'spec-bug'
assignees: ''
---
**Link to problem area**:
**Issue**
What is wrong?
**Expected behaviour**
How can the issue be fixed? Links to implementations/documents which prove the spec is wrong are appreciated.

13
.github/_typos.toml vendored
View file

@ -1,13 +0,0 @@
[files]
extend-exclude = ["/themes", "/attic", "/data-definitions", "*.css", "syntax.scss", "package-lock.json"]
[default]
check-filename = true
[default.extend-identifiers]
au1ba7o = "au1ba7o"
[default.extend-words]
Appy = "Appy"
fo = "fo"
Iy = "Iy"

View file

@ -1,8 +0,0 @@
### Pull Request Checklist
<!-- Please read CONTRIBUTING.rst before submitting your pull request -->
* [ ] Pull request includes a [changelog file](https://github.com/matrix-org/matrix-spec/blob/master/CONTRIBUTING.rst#adding-to-the-changelog)
* [ ] Pull request includes a [sign off](https://github.com/matrix-org/matrix-spec/blob/master/CONTRIBUTING.rst#sign-off)
* [ ] Pull request is classified as ['other changes'](https://github.com/matrix-org/matrix-spec/blob/master/CONTRIBUTING.rst#other-changes)

View file

@ -1,18 +0,0 @@
# workflow steps that ought to pass on a PR, but shouldn't block a preview.
name: "Checks"
on:
pull_request:
jobs:
check-newsfragments:
name: "🔎 Check that new newsfragments are valid"
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: scripts/check-newsfragments
env:
PULL_REQUEST_NUMBER: ${{ github.event.number }}

View file

@ -1,312 +0,0 @@
name: "Spec"
env:
HUGO_VERSION: 0.148.1
PYTHON_VERSION: 3.13
on:
push:
branches:
- main
tags:
- v*
pull_request:
workflow_dispatch:
schedule:
# Run this workflow every day at 2am. This helps keep the page of
# current spec proposals up-to-date.
- cron: '0 2 * * *'
jobs:
validate-openapi:
name: "🔎 Validate OpenAPI specifications"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Node"
uses: actions/setup-node@v4
with:
node-version: '20'
- name: "🔎 Run validator"
run: |
npx @redocly/cli@latest lint data/api/*/*.yaml
check-event-examples:
name: "🔎 Check Event schema examples"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "🔎 Run validator"
run: |
python scripts/check-event-schema-examples.py
check-openapi-examples:
name: "🔎 Check OpenAPI definitions examples"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "🔎 Run validator"
run: |
python scripts/check-openapi-sources.py
check-schemas-examples:
name: "🔎 Check JSON Schemas inline examples"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "🔎 Run validator"
run: |
python scripts/check-json-schemas.py
calculate-baseurl:
name: "⚙️ Calculate baseURL for later jobs"
runs-on: ubuntu-latest
outputs:
baseURL: "${{ steps.set-baseurl.outputs.baseURL }}"
steps:
# For PRs, set the baseURL to `/`.
# For releases, set the baseURL to `/$tag` (eg: `/v1.2`).
# Otherwise, set it to `/unstable`.
- name: "⚙️ Calculate baseURL"
id: set-baseurl
# Double brackets on the elif to avoid auto-escaping refs/tags/* because we need
# the asterisk matching behaviour, not the literal string.
run: |
if [ "${GITHUB_EVENT_NAME}" == "pull_request" ]; then
echo "baseURL=/" >> "$GITHUB_OUTPUT"
elif [[ "${GITHUB_REF}" == refs/tags/* ]]; then
echo "baseURL=/${GITHUB_REF/refs\/tags\//}" >> "$GITHUB_OUTPUT"
else
echo "baseURL=/unstable" >> "$GITHUB_OUTPUT"
fi
build-openapi:
name: "🐍 Build OpenAPI definitions"
runs-on: ubuntu-latest
needs: [calculate-baseurl]
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
cache: 'pip'
cache-dependency-path: scripts/requirements.txt
- name: " Install dependencies"
run: |
pip install -r scripts/requirements.txt
- name: "📦 Asset creation"
run: |
if [[ "${GITHUB_REF}" == refs/tags/* ]]; then
export RELEASE="${GITHUB_REF/refs\/tags\//}"
else
export RELEASE="unstable"
fi
# The output path matches the final deployment path at spec.matrix.org
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api application-service \
-r "$RELEASE" \
-o spec/application-service-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api client-server \
-r "$RELEASE" \
-o spec/client-server-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api push-gateway \
-r "$RELEASE" \
-o spec/push-gateway-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api server-server \
-r "$RELEASE" \
-o spec/server-server-api/api.json
scripts/dump-openapi.py \
--base-url "https://spec.matrix.org${{ needs.calculate-baseurl.outputs.baseURL }}" \
--api identity \
-r "$RELEASE" \
-o spec/identity-service-api/api.json
tar -czf openapi.tar.gz spec
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: openapi-artifact
path: openapi.tar.gz
generate-changelog:
name: "📢 Run towncrier for changelog"
# skip for builds of git tags
if: "!startsWith(github.ref, 'refs/tags/')"
runs-on: ubuntu-latest
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: " Setup Python"
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: " Install towncrier"
run: "pip install 'towncrier'"
- name: "Generate changelog"
run: ./scripts/generate-changelog.sh vUNSTABLE
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: changelog-artifact
path: content/changelog/unstable.md
build-spec:
name: "📖 Build the spec"
runs-on: ubuntu-latest
needs: [calculate-baseurl, build-openapi, generate-changelog]
# run even if generate-changelog was skipped
if: ${{ always() }}
steps:
- name: " Setup Node"
uses: actions/setup-node@v4
with:
node-version: '20'
- name: " Setup Hugo"
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: "⚙️ npm"
run: |
npm i
npm run get-proposals
- name: "📥 Download generated changelog"
if: "needs.generate-changelog.result == 'success'"
uses: actions/download-artifact@v4
with:
name: changelog-artifact
path: content/changelog
- name: "⚙️ hugo"
run: hugo --baseURL "${{ needs.calculate-baseurl.outputs.baseURL }}" -d "spec"
# We manually unpack the spec OpenAPI definition JSON to the website tree
# to make it available to the world in a canonical place:
# https://spec.matrix.org/latest/client-server-api/api.json
# Works for /unstable/ and /v1.1/ as well.
- name: "📥 Spec definition download"
uses: actions/download-artifact@v4
with:
name: openapi-artifact
- name: "📝 Unpack the OpenAPI definitions in the right location"
run: |
tar -xzf openapi.tar.gz
- name: "📦 Tarball creation"
run: tar -czf spec.tar.gz spec
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: spec-artifact
path: spec.tar.gz
htmlcheck:
name: "🔎 Validate generated HTML"
runs-on: ubuntu-latest
needs: [calculate-baseurl, build-spec]
steps:
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: "📥 Fetch built spec"
uses: actions/download-artifact@v4
with:
name: spec-artifact
- name: "📝 Unpack the spec"
# we have to unpack it into the right path given the baseurl, so that the
# links are correct.
# eg if baseurl is `/unstable`, we want to put the site in `spec/unstable`.
run: |
mkdir -p "spec${baseURL}"
tar -C "spec${baseURL}" --strip-components=1 -xvzf spec.tar.gz
env:
baseURL: "${{ needs.calculate-baseurl.outputs.baseURL }}"
- name: "Run htmltest"
uses: wjdp/htmltest-action@master
with:
config: .htmltest.yml
build-historical-spec:
name: "📖 Build the historical backup spec"
runs-on: ubuntu-latest
needs: [build-openapi]
if: ${{ startsWith(github.ref, 'refs/tags/') }}
steps:
- name: " Setup Node"
uses: actions/setup-node@v4
with:
node-version: '20'
- name: " Setup Hugo"
uses: peaceiris/actions-hugo@75d2e84710de30f6ff7268e08f310b60ef14033f # v3.0.0
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true
- name: "📥 Source checkout"
uses: actions/checkout@v4
- name: "⚙️ npm"
run: |
npm i
npm run get-proposals
- name: "⚙️ hugo"
env:
HUGO_PARAMS_VERSION_STATUS: "historical"
# Create a baseURL like `/v1.2` out of the `v1.2` tag
run: |
hugo --baseURL "/${GITHUB_REF/refs\/tags\//}" -d "spec"
- name: "📥 Spec definition download"
uses: actions/download-artifact@v4
with:
name: openapi-artifact
- name: "📝 Unpack the OpenAPI definitions in the right location"
run: |
tar -xzf openapi.tar.gz
- name: "📦 Tarball creation"
run: tar -czf spec-historical.tar.gz spec
- name: "📤 Artifact upload"
uses: actions/upload-artifact@v4
with:
name: spec-historical-artifact
path: spec-historical.tar.gz

View file

@ -1,72 +0,0 @@
# GHA workflow which publishes previews of spec PRs to netlify.
#
# We keep this in a separate workflow to the main spec build, because it
# requires access to the Netlify secret. By having it run on `workflow_run`, we
# will only use the workflow definition file on the default branch, so we can
# ensure that the secret can't be exfiltrated.
#
name: Upload Preview Build to Netlify
on:
workflow_run:
workflows: [Spec]
types: [completed]
jobs:
build:
runs-on: ubuntu-latest
if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.event == 'pull_request'
steps:
- name: "dump context data"
run: |
jq . < $GITHUB_EVENT_PATH
- name: "🔍 Read PR number"
id: readctx
# we need to find the PR number that corresponds to the branch, which we do by
# searching the GH API
env:
OWNER_LOGIN: ${{ github.event.workflow_run.head_repository.owner.login }}
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
run: |
head_branch="${OWNER_LOGIN}:${HEAD_BRANCH}"
echo "head branch: $head_branch"
pulls_uri="https://api.github.com/repos/${{ github.repository }}/pulls?head=$(jq -Rr '@uri' <<<$head_branch)"
pr_number=$(curl -H 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' "$pulls_uri" |
jq -r '.[] | .number')
echo "PR number: $pr_number"
echo "prnumber=$pr_number" >> "$GITHUB_OUTPUT"
- name: '📥 Download artifact'
uses: dawidd6/action-download-artifact@09f2f74827fd3a8607589e5ad7f9398816f540fe # v3.1.4
with:
workflow: main.yaml
run_id: ${{ github.event.workflow_run.id }}
name: spec-artifact
- name: "📦 Extract Artifacts"
run: tar -xzvf spec.tar.gz && rm spec.tar.gz
- name: "📤 Deploy to Netlify"
id: netlify
uses: nwtgck/actions-netlify@4cbaf4c08f1a7bfa537d6113472ef4424e4eb654 # v3.0.0
with:
publish-dir: spec
deploy-message: "Deploy from GitHub Actions"
enable-pull-request-comment: false
enable-commit-comment: false
alias: pr${{ steps.readctx.outputs.prnumber }}
env:
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
timeout-minutes: 1
- name: "📝 Edit PR Description"
# v1.0.1
uses: Beakyn/gha-comment-pull-request@2167a7aee24f9e61ce76a23039f322e49a990409
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
pull-request-number: ${{ steps.readctx.outputs.prnumber }}
description-message: |
Preview: ${{ steps.netlify.outputs.deploy-url }}

View file

@ -1,43 +0,0 @@
name: Release packages
on:
release:
types: [published]
concurrency: ${{ github.workflow }}-${{ github.ref }}
jobs:
# Releases to npm after bumping the package.json version from 0.0.0 to $TAG.0 as the tags only contain MAJOR.MINOR
npm:
name: Publish to npm
runs-on: ubuntu-latest
if: github.event.release.prerelease == false
defaults:
run:
working-directory: packages/npm
permissions:
contents: read
id-token: write
steps:
- name: 🧮 Checkout code
uses: actions/checkout@v4
- name: 🔧 Yarn cache
uses: actions/setup-node@v4
with:
cache: "yarn"
cache-dependency-path: packages/npm/yarn.lock
registry-url: "https://registry.npmjs.org"
# Ensure npm 11.5.1 or later is installed
- name: Update npm
run: npm install -g npm@latest
- name: 🔨 Install dependencies
run: "yarn install --frozen-lockfile"
# We bump the package.json version to git, we just need it for publish to do the right thing
- name: 🎖 Bump package.json version
run: "yarn version --new-version ${VERSION#v} --no-git-tag-version"
env:
VERSION: ${{ github.event.release.tag_name }}.0
- name: 🚀 Publish to npm
run: npm publish --provenance --access public --tag latest

View file

@ -1,19 +0,0 @@
name: Spell Check
on:
push:
branches:
- main
pull_request:
jobs:
run:
name: Spell Check with Typos
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
uses: actions/checkout@v4
- name: Check spelling of proposals
uses: crate-ci/typos@f2c1f08a7b3c1b96050cb786baaa2a94797bdb7d # v1.20.10
with:
config: ${{github.workspace}}/.github/_typos.toml

23
.gitignore vendored
View file

@ -1,17 +1,8 @@
node_modules
/data/msc
/env*
/resources
/scripts/openapi
/scripts/tmp
/hugo-config.toml
/public
scripts/gen
scripts/continuserv/continuserv
scripts/speculator/speculator
templating/out
*.pyc
*.swp
_rendered.rst
/.vscode/
/.idea/
/spec/
changelogs/rendered.*
.hugo_build.lock
hugo_stats.json
supporting-docs/_site
supporting-docs/.sass-cache
api/node_modules

View file

@ -1,7 +0,0 @@
# config file for htmltest. This is used by one of the checks in Github
# Actions.
IgnoreDirectoryMissingTrailingSlash: true
DirectoryPath: spec
CheckExternal: false
IgnoreInternalEmptyHash: true

View file

@ -1,170 +0,0 @@
Contributing to matrix-spec
===========================
Everyone is welcome to contribute to the Matrix specification!
Please ensure that you sign off your contributions. See `Sign off`_ below.
Code style
----------
The documentation style is described at
https://github.com/matrix-org/matrix-spec/blob/main/meta/documentation_style.rst.
Matrix-spec workflows
---------------------
Specification changes
~~~~~~~~~~~~~~~~~~~~~
The Matrix specification documents the APIs which Matrix clients and servers use.
For this to be effective, the APIs need to be present and working correctly in a
server before they can be documented in the specification. This process can take
some time to complete.
Changes to the protocol (new endpoints, ideas, etc) need to go through the
`proposals process <https://matrix.org/docs/spec/proposals>`_.
Other changes
~~~~~~~~~~~~~
The above process is unnecessary for smaller changes, and those which do not
put new requirements on servers. This category of changes includes the
following:
* Changes to the scripts used to generate the specification.
* Addition of features which have been in use in practice for some time, but
have never made it into the spec (including anything with the `spec-omission
<https://github.com/matrix-org/matrix-spec/labels/spec-omission>`_ label).
* Likewise, corrections to the specification, to fix situations where, in
practice, servers and clients behave differently to the specification,
including anything with the `spec-bug
<https://github.com/matrix-org/matrix-spec/labels/spec-bug>`_ label.
(If there is any doubt about whether it is the spec or the implementations
that need fixing, please discuss it with us first in `#matrix-spec:matrix.org`_.)
* Clarifications to the specification which do not change the behaviour of
Matrix servers or clients in a way which might introduce compatibility
problems for existing deployments. This includes anything with the
`clarification <https://github.com/matrix-org/matrix-spec/labels/clarification>`_
label.
For example, areas where the specification is unclear do not require a proposal
to fix. On the other hand, introducing new behaviour is best represented by a
proposal.
* Design or aesthetic changes, such as improving accessibility, colour schemes,
etc. Please check in with us at `#matrix-docs:matrix.org`_ with your proposed
design change before opening a PR so we can work with you on it.
For such changes, please do just open a `pull request`_. If you're not sure if
your change is covered by the above, please visit `#matrix-spec:matrix.org` and
ask.
.. _`pull request`: https://help.github.com/articles/about-pull-requests
.. _`#matrix-spec:matrix.org`: https://matrix.to/#/#matrix-spec:matrix.org
.. _`#matrix-docs:matrix.org`: https://matrix.to/#/#matrix-docs:matrix.org
Adding to the changelog
~~~~~~~~~~~~~~~~~~~~~~~
All changes to the contents of this repository require a changelog entry. Adding to the changelog can only
be done after you've opened your pull request, so be sure to do that first.
The changelog is managed by `Towncrier <https://github.com/twisted/towncrier>`_ in the
form of "news fragments". Depending on which API you changed, an entry should be added to
each relevant API's ``newsfragments`` directory. A directory exists for each API under
``changelogs/``. For instance, news fragments for the client-server API are stored
under ``changelogs/client_server/newsfragments``. Any changes to the repository that do
not affect the spec content itself, such as changes to the build script, formatting, CSS,
etc. should be documented under ``changelogs/internal/newsfragments``.
To create a changelog entry, create a file named in the format ``prNumber.type`` in
the ``newsfragments`` directory. The ``type`` can be one of the following:
* ``new`` - Used when adding new endpoints. Please have the file contents be the
method and route being added, surrounded in markdown code tags. For example: \`POST
/accounts/whoami\`.
* ``feature`` - Used when adding backwards-compatible changes to the API.
* ``clarification`` - Used when an area of the spec is being improved upon and does
not change or introduce any functionality.
* ``breaking`` - Used when the change is not backwards compatible.
* ``deprecation`` - Used when deprecating something.
* ``removal`` - Used when removing something that was unused or previously deprecated.
All news fragments must have a brief summary explaining the change in the
contents of the file. The summary must end in a full stop to be in line with
the style guide and formatting must be done using Markdown.
Sign off
--------
We ask that everybody who contributes to this project signs off their
contributions, as explained below.
We follow a simple 'inbound=outbound' model for contributions: the act of
submitting an 'inbound' contribution means that the contributor agrees to
license their contribution under the same terms as the project's overall 'outbound'
license - in our case, this is Apache Software License v2 (see LICENSE).
In order to have a concrete record that your contribution is intentional
and you agree to license it under the same terms as the project's license, we've adopted the
same lightweight approach used by the `Linux Kernel <https://www.kernel.org/doc/html/latest/process/submitting-patches.html>`_,
`Docker <https://github.com/docker/docker/blob/master/CONTRIBUTING.md>`_, and many other
projects: the `Developer Certificate of Origin <http://developercertificate.org/>`_
(DCO). This is a simple declaration that you wrote
the contribution or otherwise have the right to contribute it to Matrix::
Developer Certificate of Origin
Version 1.1
Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
660 York Street, Suite 102,
San Francisco, CA 94110 USA
Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that:
(a) The contribution was created in whole or in part by me and I
have the right to submit it under the open source license
indicated in the file; or
(b) The contribution is based upon previous work that, to the best
of my knowledge, is covered under an appropriate open source
license and I have the right under that license to submit that
work with modifications, whether created in whole or in part
by me, under the same open source license (unless I am
permitted to submit under a different license), as indicated
in the file; or
(c) The contribution was provided directly to me by some other
person who certified (a), (b) or (c) and I have not modified
it.
(d) I understand and agree that this project and the contribution
are public and that a record of the contribution (including all
personal information I submit with it, including my sign-off) is
maintained indefinitely and may be redistributed consistent with
this project or the open source license(s) involved.
If you agree to this for your contribution, then all that's needed is to
include the line in your commit or pull request comment::
Signed-off-by: Your Name <your@email.example.org>
Git allows you to add this signoff automatically when using the ``-s``
flag to ``git commit``, which uses the name and email set in your
``user.name`` and ``user.email`` git configs.

177
LICENSE
View file

@ -1,177 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

101
README.md
View file

@ -1,101 +0,0 @@
# Matrix Specification
This repository contains the Matrix Specification. The current release version is rendered at https://spec.matrix.org, while the latest available build of the `main` branch is at https://spec.matrix.org/unstable.
Developers looking to use Matrix should join [#matrix-dev:matrix.org](https://matrix.to/#/#matrix-dev:matrix.org)
on Matrix for help.
Spec authors and proposal writers are welcome to join [#matrix-spec:matrix.org](https://matrix.to/#/#matrix-spec:matrix.org).
We welcome contributions! See [CONTRIBUTING.rst](./CONTRIBUTING.rst) for details.
## Structure
The Matrix spec is compiled with [Hugo](https://gohugo.io/) (a static site generator) with the following structure:
* `/assets`: assets that need postprocessing using [Hugo Pipes](https://gohugo.io/hugo-pipes/introduction/).
For example, Sass files would go here.
* `/content`: files that will become pages in the site go here. Typically these are Markdown files with some YAML front
matter indicating, [among other things](https://gohugo.io/content-management/front-matter/), what layout should be
applied to this page. The organization of files under `/content` determines the organization of pages in the built
site.
* `/data`: this can contain TOML, YAML, or JSON files. Files kept here are directly available to template code as
[data objects](https://gohugo.io/templates/data-templates/), so templates don't need to load them from a file and
parse them. This is also where our OpenAPI definitions and schemas are.
* `/layouts`: this contains [Hugo templates](https://gohugo.io/templates/). Some templates define the overall layout of
a page: for example, whether it has header, footer, sidebar, and so on.
* `/layouts/partials`: these templates can be called from other templates, so they can be used to factor out
template code that's used in more than one template. An obvious example here is something like a sidebar, where
several different page layouts might all include the sidebar. But also, partial templates can return values: this
means they can be used like functions, that can be called by multiple templates to do some common processing.
* `/layouts/shortcodes`: these templates can be called directly from files in `/content`.
* `/static`: static files which don't need preprocessing. JS or CSS files could live here.
* `/themes`: you can use just Hugo or use it with a theme. Themes primarily provide additional templates, which are
supplied in a `/themes/$theme_name/layouts` directory. You can use a theme but customise it by providing your own
versions of any of the theme layouts in the base `/layouts` directory. That is, if a theme provides
`/themes/$theme_name/layouts/sidebar.html` and you provide `/layouts/sidebar.html`, then your version of the
template will be used.
It also has the following top-level file:
* `config.toml`: site-wide configuration settings. Some of these are built-in and you can add your own. Config settings
defined here are available in templates. All these directories above are configurable via `config.toml` settings.
Additionally, the following directories may be of interest:
* `/attic`: Here contains historical sections of specification and legacy drafts for the specification.
* `/changelogs`: Various bits of changelog for the specification areas.
* `/data-definitions`: Bits of structured data consumable by Matrix implementations.
* `/meta`: Documentation relating to the spec's processes that are otherwise untracked (release instructions, etc).
* `/scripts`: Various scripts for generating the spec and validating its contents.
* `/packages`: Various packages for shipping spec files like OpenAPI bindings and data definitions.
## Authoring changes to the spec
Please read [CONTRIBUTING.rst](./CONTRIBUTING.rst) before authoring a change to the spec. Note that spec authoring takes
place after an MSC has been accepted, not as part of a proposal itself.
1. Install the extended version (often the OS default) of Hugo:
<https://gohugo.io/getting-started/installing>. Note that at least Hugo
v0.146.0 is required.
Alternatively, use the Docker image at
https://hub.docker.com/r/klakegg/hugo/. (The "extended edition" is required
to process the SCSS.)
2. Run `npm i` to install the dependencies. Note that this will require NodeJS to be installed.
3. Run `npm run get-proposals` to seed proposal data. This is merely for populating the content of the "Spec Change Proposals"
page and is not required.
4. Run `hugo serve` (or `docker run --rm -it -v $(pwd):/src -p 1313:1313
klakegg/hugo:ext serve`) to run a local webserver which builds whenever a file
change is detected. If watching doesn't appear to be working for you, try
adding `--disableFastRender` to the commandline.
5. Edit the specification 🙂
We use a highly customized [Docsy](https://www.docsy.dev/) theme for our generated site, which uses Bootstrap and Font
Awesome. If you're looking at making design-related changes to the spec site, please coordinate with us in
[#matrix-docs:matrix.org](https://matrix.to/#/#matrix-docs:matrix.org) before opening a PR.
## Building the specification
If for some reason you're not a CI/CD system and want to render a static version of the spec for yourself, follow the above
steps for authoring changes to the specification and instead of `hugo serve` run `hugo -d "spec"` - this will generate the
spec to `/spec`. If you'd like to serve the spec off a path instead of a domain root (eg: `/unstable`), add `--baseURL "/unstable"`
to the `hugo -d "spec"` command.
For building the OpenAPI definitions, create a python3 virtualenv and activate it. Then run `pip install -r ./scripts/requirements.txt`
and finally `python ./scripts/dump-openapi.py` to generate it to `./scripts/openapi/api-docs.json`. To make use of the generated file,
there are a number of options:
* You can open `./scripts/openapi-preview.html` in your browser, and then open the file by clicking on `Local JSON File`.
* You can run a local HTTP server by running `./scripts/openapi-http-server.py`, and then view the documentation by
opening `./scripts/openapi-preview.html` in your browser.
## Issue tracking
Specification issues are tracked on github at <https://github.com/matrix-org/matrix-spec/issues>.
See [meta/github-labels.rst](./meta/github-labels.rst) for information on what the labels mean.

30
README.rst Normal file
View file

@ -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/

4
api/README Normal file
View file

@ -0,0 +1,4 @@
To get this running:
python -m SimpleHTTPServer
Go to http://localhost:8000/swagger.html

View file

@ -0,0 +1,200 @@
swagger: '2.0'
info:
title: "Matrix Application Service API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: "/"
consumes:
- application/json
produces:
- application/json
paths:
"/transactions/{txnId}":
put:
summary: Send some events to the application service.
description: |-
This API is called by the HS when the HS wants to push an event (or
batch of events) to the AS.
parameters:
- in: path
name: txnId
type: string
description: |-
The transaction ID for this set of events. Homeservers generate
these IDs and they are used to ensure idempotency of requests.
required: true
x-example: "35"
- in: body
name: body
description: A list of events
schema:
type: object
example: |-
{
"events": [
{
"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"
},
{
"age": 1984,
"content": {
"body": "another incoming message",
"msgtype": "m.text"
},
"event_id": "$1228055551ffsef:localhost",
"origin_server_ts": 1432804485886,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"type": "m.room.message",
"user_id": "@bob:localhost"
}
]
}
description: "Transaction informations"
properties:
events:
type: array
description: A list of events
items:
type: object
title: Event
required: ["events"]
responses:
200:
description: The transaction was processed successfully.
examples:
application/json: |-
{}
schema:
type: object
"/rooms/{roomAlias}":
get:
summary: Query if a room alias should exist on the application service.
description: |-
This endpoint is invoked by the homeserver on an application service to query
the existence of a given room alias. The homeserver will only query room
aliases inside the application service's ``aliases`` namespace. The
homeserver will send this request when it receives a request to join a
room alias within the application service's namespace.
parameters:
- in: path
name: roomAlias
type: string
description: The room alias being queried.
required: true
x-example: "#magicforest:example.com"
responses:
200:
description: |-
The application service indicates that this room alias exists. The
application service MUST have created a room and associated it with
the queried room alias using the client-server API. Additional
information about the room such as its name and topic can be set
before responding.
examples:
application/json: |-
{}
schema:
type: object
401:
description: |-
The homeserver has not supplied credentials to the application service.
Optional error information can be included in the body of this response.
examples:
application/json: |-
{
"errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
}
schema:
type: object
403:
description: |-
The credentials supplied by the homeserver were rejected.
examples:
application/json: |-
{
"errcode": "M_FORBIDDEN"
}
schema:
type: object
404:
description: |-
The application service indicates that this room alias does not exist.
Optional error information can be included in the body of this response.
examples:
application/json: |-
{
"errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
}
schema:
type: object
"/users/{userId}":
get:
summary: Query if a user should exist on the application service.
description: |-
This endpoint is invoked by the homeserver on an application service to query
the existence of a given user ID. The homeserver will only query user IDs
inside the application service's ``users`` namespace. The homeserver will
send this request when it receives an event for an unknown user ID in
the application service's namespace.
parameters:
- in: path
name: userId
type: string
description: The user ID being queried.
required: true
x-example: "@alice:example.com"
responses:
200:
description: |-
The application service indicates that this user exists. The application
service MUST create the user using the client-server API.
examples:
application/json: |-
{}
schema:
type: object
401:
description: |-
The homeserver has not supplied credentials to the application service.
Optional error information can be included in the body of this response.
examples:
application/json: |-
{
"errcode": "COM.EXAMPLE.MYAPPSERVICE_UNAUTHORIZED"
}
schema:
type: object
403:
description: |-
The credentials supplied by the homeserver were rejected.
examples:
application/json: |-
{
"errcode": "M_FORBIDDEN"
}
schema:
type: object
404:
description: |-
The application service indicates that this user does not exist.
Optional error information can be included in the body of this response.
examples:
application/json: |-
{
"errcode": "COM.EXAMPLE.MYAPPSERVICE_NOT_FOUND"
}
schema:
type: object

128
api/check_examples.py Executable file
View file

@ -0,0 +1,128 @@
#! /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
resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml})
jsonschema.validate(example, schema, resolver=resolver)
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
resolver = jsonschema.RefResolver(filepath, schema, handlers={"file": load_yaml})
jsonschema.validate(example, schema, resolver=resolver)
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)
def load_yaml(path):
if not path.startswith("file:///"):
raise Exception("Bad ref: %s" % (path,))
path = path[len("file://"):]
with open(path, "r") as f:
return yaml.load(f)
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)

View file

@ -0,0 +1,109 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Client Config API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/account_data/{type}":
put:
summary: Set some account_data for the user.
description: |-
Set some account_data for the client. This config is only visible to the user
that set the account_data. The config will be synced to clients in the
top-level ``account_data``.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The id of the user to set account_data for. The access token must be
authorized to make requests for this user id.
x-example: "@alice:example.com"
- in: path
type: string
name: type
required: true
description: |-
The event type of the account_data to set. Custom types should be
namespaced to avoid clashes.
x-example: "org.example.custom.config"
- in: body
name: content
required: true
description: |-
The content of the account_data
schema:
type: object
example: |-
{"custom_account_data_key": "custom_config_value"}
responses:
200:
description:
The account_data was successfully added.
tags:
- User data
"/user/{userId}/rooms/{roomId}/account_data/{type}":
put:
summary: Set some account_data for the user.
description: |-
Set some account_data for the client on a given room. This config is only
visible to the user that set the account_data. The config will be synced to
clients in the per-room ``account_data``.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The id of the user to set account_data for. The access token must be
authorized to make requests for this user id.
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
required: true
description: |-
The id of the room to set account_data on.
x-example: "!726s6s6q:example.com"
- in: path
type: string
name: type
required: true
description: |-
The event type of the account_data to set. Custom types should be
namespaced to avoid clashes.
x-example: "org.example.custom.room.config"
- in: body
name: content
required: true
description: |-
The content of the account_data
schema:
type: object
example: |-
{"custom_account_data_key": "custom_account_data_value"}
responses:
200:
description:
The account_data was successfully added.
tags:
- User data

View file

@ -0,0 +1,105 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Administration API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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:
"/admin/whois/{userId}":
get:
summary: Gets information about a particular user.
description: |-
Gets information about a particular user.
This API may be restricted to only be called by the user being looked
up, or by a server admin. Server-local administrator privileges are not
specified in this document.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
description: The user to look up.
required: true
x-example: "@peter:rabbit.rocks"
responses:
200:
description: The lookup was successful.
examples:
application/json: |-
{
"user_id": "@peter:rabbit.rocks",
"devices": {
"teapot": {
"sessions": [
{
"connections": [
{
"ip": "127.0.0.1",
"last_seen": 1411996332123,
"user_agent": "curl/7.31.0-DEV"
},
{
"ip": "10.0.0.2",
"last_seen": 1411996332123,
"user_agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36"
}
]
}
]
}
}
}
schema:
type: object
properties:
user_id:
type: string
description: The Matrix user ID of the user.
devices:
type: object
description: |-
Each key is an identitfier for one of the user's devices.
additionalProperties:
type: object
title: DeviceInfo
properties:
sessions:
type: array
description: A user's sessions (i.e. what they did with an access token from one login).
items:
type: object
title: SessionInfo
properties:
connections:
type: array
description: Information particular connections in the session.
items:
type: object
title: ConnectionInfo
properties:
ip:
type: string
description: Most recently seen IP address of the session.
last_seen:
type: number
description: Unix timestamp that the session was last active.
user_agent:
type: string
description: User agent string last seen in the session.
tags:
- Server administration

View file

@ -0,0 +1,163 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Account Administrative Contact API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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:
"/account/password":
post:
summary: Changes a user's password.
description: |-
This API endpoint uses the User-Interactive Authentication API.
An access token should be submitted to this endpoint if the client has
an active session.
The homeserver may change the flows available depending on whether a
valid access token is provided.
security:
- accessToken: []
parameters:
- in: body
name: body
schema:
type: object
example: |-
{
"new_password": "ihatebananas"
}
properties:
new_password:
type: string
description: The new password for the account.
required: ["new_password"]
responses:
200:
description: The password has been changed.
examples:
application/json: "{}"
schema:
type: object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
tags:
- User data
"/account/3pid":
get:
summary: Gets a list of a user's third party identifiers.
description: |-
Gets a list of the third party identifiers that the homeserver has
associated with the user's account.
This is *not* the same as the list of third party identifiers bound to
the user's Matrix ID in Identity Servers.
Identifiers in this list may be used by the homeserver as, for example,
identifiers that it will accept to reset the user's account password.
security:
- accessToken: []
responses:
200:
description: The lookup was successful.
examples:
application/json: |-
{
"threepids": [
{
"medium": "email",
"address": "monkey@banana.island"
}
]
}
schema:
type: object
properties:
threepids:
type: array
items:
type: object
title: Third party identifier
properties:
medium:
type: string
description: The medium of the third party identifier.
enum: ["email"]
address:
type: string
description: The third party identifier address.
tags:
- User data
post:
summary: Adds contact information to the user's account.
description: Adds contact information to the user's account.
security:
- accessToken: []
parameters:
- in: body
name: body
schema:
type: object
properties:
threePidCreds:
title: "ThreePidCredentials"
type: object
description: The third party credentials to associate with the account.
properties:
client_secret:
type: string
description: The client secret used in the session with the Identity Server.
id_server:
type: string
description: The Identity Server to use.
sid:
type: string
description: The session identifier given by the Identity Server.
required: ["client_secret", "id_server", "sid"]
bind:
type: boolean
description: |-
Whether the homeserver should also bind this third party
identifier to the account's Matrix ID with the passed identity
server. Default: ``false``.
x-example: true
required: ["threePidCreds"]
example: |-
{
"threePidCreds": {
"id_server": "matrix.org",
"sid": "abc123987",
"client_secret": "d0n'tT3ll"
},
"bind": false
}
responses:
200:
description: The addition was successful.
examples:
application/json: "{}"
schema:
type: object
403:
description: The credentials could not be verified with the identity server.
examples:
application/json: |-
{
"errcode": "M_THREEPID_AUTH_FAILED",
"error": "The third party credentials could not be verified by the identity server."
}
tags:
- User data

View file

@ -0,0 +1,78 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Banning API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/ban":
post:
summary: Ban a user in the room.
description: |-
Ban a user in the room. If the user is currently in the room, also kick them.
When a user is banned from a room, they may not join it until they are unbanned.
The caller must have the required power level in order to perform this operation.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier (not alias) from which the user should be banned.
required: true
x-example: "!e42d8c:matrix.org"
- in: body
name: body
required: true
schema:
type: object
example: |-
{
"reason": "Telling unfunny jokes",
"user_id": "@cheeky_monkey:matrix.org"
}
properties:
user_id:
type: string
description: The fully qualified user ID of the user being banned.
reason:
type: string
description: The reason the user has been banned.
required: ["user_id"]
responses:
200:
description: The user has been kicked and banned from the room.
examples:
application/json: |-
{}
schema:
type: object
403:
description: |-
You do not have permission to ban the user from the room. A meaningful ``errcode`` and description error text will be returned. Example reasons for rejections are:
- The banner is not currently in the room.
- The banner's power level is insufficient to ban users from the room.
examples:
application/json: |-
{
"errcode": "M_FORBIDDEN",
"error": "You do not have a high enough power level to ban from this room."
}
tags:
- Room membership

View file

@ -0,0 +1,131 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Content Repository API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
basePath: /_matrix/media/%CLIENT_MAJOR_VERSION%
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"
}
tags:
- Media
"/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
tags:
- Media
"/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
tags:
- Media

View file

@ -0,0 +1,169 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Creation API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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:
"/createRoom":
post:
summary: Create a new room
description: |-
Create a new room with various configuration options.
security:
- accessToken: []
parameters:
- in: body
name: body
description: The desired room configuration.
schema:
type: object
example: |-
{
"preset": "public_chat",
"room_alias_name": "thepub",
"name": "The Grand Duke Pub",
"topic": "All about happy hour",
"creation_content": {
"m.federate": false
}
}
properties:
visibility:
type: string
enum: ["public", "private"]
description: |-
A ``public`` visibility indicates that the room will be shown
in the published room list. A ``private`` visibility will hide
the room from the published room list. Rooms default to
``private`` visibility if this key is not included. NB: This
should not be confused with ``join_rules`` which also uses the
word ``public``.
room_alias_name:
type: string
description: |-
The desired room alias **local part**. If this is included, a
room alias will be created and mapped to the newly created
room. The alias will belong on the *same* homeserver which
created the room. For example, if this was set to "foo" and
sent to the homeserver "example.com" the complete room alias
would be ``#foo:example.com``.
name:
type: string
description: |-
If this is included, an ``m.room.name`` event will be sent
into the room to indicate the name of the room. See Room
Events for more information on ``m.room.name``.
topic:
type: string
description: |-
If this is included, an ``m.room.topic`` event will be sent
into the room to indicate the topic for the room. See Room
Events for more information on ``m.room.topic``.
invite:
type: array
description: |-
A list of user IDs to invite to the room. This will tell the
server to invite everyone in the list to the newly created room.
items:
type: string
invite_3pid:
type: array
description: |-
A list of objects representing third party IDs to invite into
the room.
items:
type: object
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.
required: ["id_server", "medium", "address"]
creation_content:
title: CreationContent
type: object
description: |-
Extra keys to be added to the content of the ``m.room.create``.
The server will clober the following keys: ``creator``. Future
versions of the specification may allow the server to clobber
other keys.
initial_state:
type: array
description: |-
A list of state events to set in the new room. This allows
the user to override the default state events set in the new
room. The expected format of the state events are an object
with type, state_key and content keys set.
Takes precedence over events set by ``presets``, but gets
overriden by ``name`` and ``topic`` keys.
items:
type: object
title: StateEvent
properties:
type:
type: string
state_key:
type: string
content:
type: string
preset:
type: string
enum: ["private_chat", "public_chat", "trusted_private_chat"]
description: |-
Convenience parameter for setting various default state events
based on a preset. Must be either:
``private_chat`` =>
``join_rules`` is set to ``invite``.
``history_visibility`` is set to ``shared``.
``trusted_private_chat`` =>
``join_rules`` is set to ``invite``.
``history_visibility`` is set to ``shared``.
All invitees are given the same power level as the room creator.
``public_chat``: =>
``join_rules`` is set to ``public``.
``history_visibility`` is set to ``shared``.
responses:
200:
description: Information about the newly created room.
schema:
type: object
description: Information about the newly created room.
properties:
room_id:
type: string
description: |-
The created room's ID.
examples:
application/json: |-
{
"room_id": "!sefiuhWgwghwWgh:example.com"
}
400:
description: >
The request body is malformed or the room alias specified is already taken.
tags:
- Room creation

View file

@ -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"]

View file

@ -0,0 +1,45 @@
properties:
content:
description: The content of this event. The fields in this object will vary depending
on the type of event.
title: EventContent
type: object
origin_server_ts:
description: Timestamp in milliseconds on originating homeserver when this event
was sent.
format: int64
type: integer
sender:
description: The MXID of the user who sent this event.
type: string
state_key:
description: Optional. This key will only be present for state events. A unique
key which defines the overwriting semantics for this piece of room state.
type: string
type:
description: The type of event.
type: string
unsigned:
description: Information about this event which was not sent by the originating
homeserver
properties:
age:
description: Time in milliseconds since the event was sent.
format: int64
type: integer
prev_content:
description: Optional. The previous ``content`` for this state. This will
be present only for state events appearing in the ``timeline``. If this
is not a state event, or there is no previous content, this key will be
missing.
title: EventContent
type: object
transaction_id:
description: Optional. The transaction ID set when this message was sent.
This key will only be present for message events sent by the device calling
this API.
type: string
title: Unsigned
type: object
title: Event
type: object

View file

@ -0,0 +1,9 @@
properties:
events:
description: List of events
items:
allOf:
- $ref: event.yaml
type: object
type: array
type: object

View file

@ -0,0 +1,34 @@
title: Filter
properties:
limit:
description: The maximum number of events to return.
type: integer
not_senders:
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.
items:
type: string
type: array
not_types:
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
type: array
senders:
description: A list of senders IDs to include. If this list is absent then all
senders are included.
items:
type: string
type: array
types:
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
type: array
type: object

View file

@ -0,0 +1,9 @@
properties:
kind:
enum:
- event_match
- profile_tag
- contains_display_name
- room_member_count
type: string
type: object

View file

@ -0,0 +1,15 @@
properties:
actions:
items:
type:
- object
- string
type: array
default:
type: boolean
enabled:
type: boolean
rule_id:
type: string
title: PushRule
type: object

View file

@ -0,0 +1,37 @@
properties:
content:
items:
allOf:
- $ref: push_rule.yaml
title: PushRule
type: object
type: array
override:
items:
allOf:
- $ref: push_rule.yaml
title: PushRule
type: object
type: array
room:
items:
allOf:
- $ref: push_rule.yaml
title: PushRule
type: object
type: array
sender:
items:
allOf:
- $ref: push_rule.yaml
title: PushRule
type: object
type: array
underride:
items:
allOf:
- $ref: push_rule.yaml
title: PushRule
type: object
type: array
type: object

View file

@ -0,0 +1,18 @@
allOf:
- $ref: event_filter.yaml
title: RoomEventFilter
properties:
not_rooms:
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.
items:
type: string
type: array
rooms:
description: A list of room IDs to include. If this list is absent then all rooms
are included.
items:
type: string
type: array
type: object

View file

@ -1,30 +1,16 @@
# Copyright 2016 OpenMarket Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
type: object
title: Filter
properties:
event_fields:
description: List of event fields to include. If this list is absent then all
fields are included. The entries are [dot-separated paths for each property](/appendices#dot-separated-property-paths)
to include. So ['content.body'] will include the 'body' field of the 'content' object.
A server may include more fields than were requested.
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
type: array
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 received
a format suitable for clients. 'federation' will return the raw event as receieved
over federation. The default is 'client'.
enum:
- client
@ -41,34 +27,31 @@ properties:
room:
title: RoomFilter
description: Filters to be applied to room data.
type: object
properties:
not_rooms:
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. This filter is applied before the filters in `ephemeral`,
`state`, `timeline` or `account_data`
are excluded. A matching room will be excluded even if it is listed in the ``'rooms'``
filter. This filter is applied before the filters in ``ephemeral``,
``state``, ``timeline`` or ``account_data``
items:
type: string
type: array
rooms:
description: A list of room IDs to include. If this list is absent then all rooms
are included. This filter is applied before the filters in `ephemeral`,
`state`, `timeline` or `account_data`
are included. This filter is applied before the filters in ``ephemeral``,
``state``, ``timeline`` or ``account_data``
items:
type: string
type: array
ephemeral:
allOf:
- $ref: room_event_filter.yaml
description: The ephemeral events to include for rooms. These are the
events that appear in the `ephemeral` property in the `/sync`
response.
description: The events that aren't recorded in the room history, e.g. typing
and receipts, to include for rooms.
include_leave:
description: Include rooms that the user has left in the sync, default false
type: boolean
state:
type: object
allOf:
- $ref: room_event_filter.yaml
description: The state events to include for rooms.
@ -80,3 +63,5 @@ properties:
allOf:
- $ref: room_event_filter.yaml
description: The per user account data to include for rooms.
type: object
type: object

View file

@ -0,0 +1,12 @@
allOf:
- $ref: event_batch.yaml
properties:
limited:
description: True if the number of events returned was limited by the ``limit``
on the filter
type: boolean
prev_batch:
description: If the batch was limited then this is a token that can be supplied
to the server to retrieve earlier events
type: string
type: object

View file

@ -0,0 +1,133 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Directory API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%/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
x-example: "#monkeys:matrix.org"
- 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.
example: |-
{
"room_id": "!abnjk1jdasj98:capuchins.com"
}
responses:
200:
description: The mapping was created.
examples:
application/json: |-
{}
schema:
type: object
tags:
- Room directory
get:
summary: Get the room ID corresponding to this room alias.
parameters:
- in: path
type: string
name: roomAlias
description: The room alias.
required: true
x-example: "#monkeys:matrix.org"
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.
examples:
application/json: |-
{
"room_id": "!abnjk1jdasj98:capuchins.com",
"servers": [
"capuchins.com",
"matrix.org",
"another.com"
]
}
404:
description: There is no mapped room ID for this room alias.
examples:
application/json: |-
{
"errcode": "M_NOT_FOUND",
"error": "Room ID !abnjk1jdasj98:capuchins.com not found."
}
409:
description: A room alias with that name already exists.
examples:
application/json: |-
{
"errcode": "M_UNKNOWN",
"error": "Room alias #monkeys:matrix.org already exists."
}
tags:
- Room directory
delete:
summary: Remove a mapping of room alias to room ID.
description: |-
Remove a mapping of room alias to room ID.
Servers may choose to implement additional access control checks here, for instance that room aliases can only be deleted by their creator or a server administrator.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomAlias
description: The room alias to remove.
required: true
x-example: "#monkeys:matrix.org"
responses:
200:
description: The mapping was deleted.
examples:
application/json: |-
{}
schema:
type: object
tags:
- Room directory

View file

@ -0,0 +1,151 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Event Context API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/context/{eventId}":
get:
summary: Get events and state around the specified event.
description: |-
This API returns a number of events that happened just before and
after the specified event. This allows clients to get the context
surrounding an event.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to get events from.
required: true
x-example: "!636q39766251:example.com"
- in: path
type: string
name: eventId
description: The event to get context around.
required: true
x-example: "$f3h4d129462ha:example.com"
- in: query
type: integer
name: limit
description: |-
The maximum number of events to return. Default: 10.
x-example: 3
responses:
200:
description: The events and state surrounding the requested event.
schema:
type: object
description: The events and state surrounding the requested event.
properties:
start:
type: string
description: |-
A token that can be used to paginate backwards with.
end:
type: string
description: |-
A token that can be used to paginate forwards with.
events_before:
type: array
description: |-
A list of room events that happened just before the
requested event.
items:
type: object
title: RoomEvent
events_after:
type: array
description: |-
A list of room events that happened just after the
requested event.
items:
type: object
title: RoomEvent
state:
type: array
description: |-
The state of the room at the last event returned.
items:
type: object
title: RoomEvent
examples:
application/json: |-
{
"end": "t29-57_2_0_2",
"events_after": [
{
"age": 91911336,
"content": {
"body": "7",
"msgtype": "m.text"
},
"event_id": "$14460306086CiUaL:localhost:8480",
"origin_server_ts": 1446030608551,
"room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480",
"type": "m.room.message",
"user_id": "@test:localhost:8480"
}
],
"events_before": [
{
"age": 91911903,
"content": {
"body": "5",
"msgtype": "m.text"
},
"event_id": "$14460306074UYTlh:localhost:8480",
"origin_server_ts": 1446030607984,
"room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480",
"type": "m.room.message",
"user_id": "@test:localhost:8480"
}
],
"start": "t27-54_2_0_2",
"state": [
{
"age": 3123715284,
"content": {
"creator": "@test:localhost:8480"
},
"event_id": "$14429988040dgQAE:localhost:8480",
"origin_server_ts": 1442998804603,
"room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480",
"state_key": "",
"type": "m.room.create",
"user_id": "@test:localhost:8480"
},
{
"age": 2067105053,
"content": {
"avatar_url": "mxc://localhost:8480/tVWZTAIIfqtXMZZtmGCkVjTD#auto",
"displayname": "Bob2",
"membership": "join"
},
"event_id": "$14440554144URDbf:localhost:8480",
"origin_server_ts": 1444055414834,
"replaces_state": "$14440552472PgiGk:localhost:8480",
"room_id": "!sCDvXTtzjpiPxaqkkt:localhost:8480",
"state_key": "@test:localhost:8480",
"type": "m.room.member",
"user_id": "@test:localhost:8480"
}
]
}
tags:
- Room participation

View file

@ -0,0 +1,143 @@
swagger: '2.0'
info:
title: "Matrix Client-Server filter API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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 future requests to
restrict 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.yaml"
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"]
},
"ephemeral": {
"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.
tags:
- Room participation
"/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"]
},
"ephemeral": {
"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.yaml"
tags:
- Room participation

View file

@ -0,0 +1,108 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Sync Guest API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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 related to a particular room and return
them to the caller. This will block until an event is received, or until
the ``timeout`` is reached.
This API is the same as the non-guest /events endpoint, but can be
called by guest users.
Note that the non-guest ``/events`` endpoint has been deprecated. This
API will also be deprecated at some point, but its replacement is not
yet known.
security:
- accessToken: []
parameters:
- in: query
type: string
name: from
description: |-
The token to stream from. This token is either from a previous
request to this API or from the initial sync API.
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: array
items:
type: string
name: room_id
description: |-
The room IDs for which events should be returned.
x-example:
- "!somewhere:over"
- "!the:rainbow"
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",
"sender": "@bob:localhost"
}
]
}
schema:
type: object
properties:
start:
type: string
description: |-
A token which correlates to the first value in ``chunk``. This
is usually the same token supplied to ``from=``.
end:
type: string
description: |-
A token which correlates to the last value in ``chunk``. This
token should be used in the next request to ``/events``.
chunk:
type: array
description: "An array of events."
items:
type: object
title: Event
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
400:
description: "Bad pagination ``from`` parameter."
# No tags to exclude this from the swagger UI - use the non-guest version instead.

View file

@ -0,0 +1,92 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Joining API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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:
# 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: |-
.. _invite-by-user-id-endpoint:
*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. The other is documented in the*
`third party invites section`_.
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 homeserver will append a
``m.room.member`` event to the room.
.. _third party invites section: `invite-by-third-party-id-endpoint`_
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"
tags:
- Room membership

View file

@ -0,0 +1,70 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Inviting API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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-%CLIENT_MAJOR_VERSION%-rooms-roomid-join"
aliases:
- /_matrix/client/%CLIENT_MAJOR_VERSION%/join/{roomId}
tags:
- Room membership

View file

@ -0,0 +1,96 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Leaving API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/leave":
post:
summary: Stop the requesting user participating in a particular room.
description: |-
This API stops a user participating in a particular room.
If the user was already in the room, they will no longer be able to see
new events in the room. If the room requires an invite to join, they
will need to be re-invited before they can re-join.
If the user was invited to the room, but had not joined, this call
serves to reject the invite.
The user will still be allowed to retrieve history from the room which
they were previously allowed to see.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier to leave.
required: true
x-example: "!nkl290a:matrix.org"
responses:
200:
description: |-
The room has been left.
examples:
application/json: |-
{}
schema:
type: object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
tags:
- Room membership
"/rooms/{roomId}/forget":
post:
summary: Stop the requesting user remembering about a particular room.
description: |-
This API stops a user remembering about a particular room.
In general, history is a first class citizen in Matrix. After this API
is called, however, a user will no longer be able to retrieve history
for this room. If all users on a homeserver forget a room, the room is
eligible for deletion from that homeserver.
If the user is currently joined to the room, they will implicitly leave
the room as part of this API call.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room identifier to forget.
required: true
x-example: "!au1ba7o:matrix.org"
responses:
200:
description: |-
The room has been forgotten.
examples:
application/json: |-
{}
schema:
type: object
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
tags:
- Room membership

View file

@ -0,0 +1,103 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Creation API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
consumes:
- application/json
produces:
- application/json
paths:
"/publicRooms":
get:
summary: Lists the public rooms on the server.
description: |-
Lists the public rooms on the server.
This API returns paginated responses.
responses:
200:
description: A list of the rooms on the server.
schema:
type: object
description: A list of the rooms on the server.
properties:
chunk:
title: "PublicRoomsChunks"
type: array
description: |-
A paginated chunk of public rooms.
items:
type: object
title: "PublicRoomsChunk"
properties:
aliases:
type: array
description: |-
Aliases of the room. May be empty.
items:
type: string
name:
type: string
description: |-
The name of the room, if any. May be null.
num_joined_members:
type: number
description: |-
The number of members joined to the room.
room_id:
type: string
description: |-
The ID of the room.
topic:
type: string
description: |-
The topic of the room, if any. May be null.
world_readable:
type: boolean
description: |-
Whether the room may be viewed by guest users without joining.
guest_can_join:
type: boolean
description: |-
Whether guest users may join the room and participate in it.
If they can, they will be subject to ordinary power level
rules like any other user.
avatar_url:
type: string
description: The URL for the room's avatar, if one is set.
start:
type: string
description: |-
A pagination token for the response.
end:
type: string
description: |-
A pagination token for the response.
examples:
application/json: |-
{
"chunk": [
{
"aliases": ["#murrays:cheese.bar"],
"avatar_url": "mxc://bleeker.street/CHEDDARandBRIE",
"guest_can_join": false,
"name": "CHEESE",
"num_joined_members": 37,
"room_id": "!ol19s:bleecker.street",
"topic": "Tasty tasty cheese",
"world_readable": true
}
],
"start": "p190q",
"end": "p1902"
}
400:
description: >
The request body is malformed or the room alias specified is already taken.
tags:
- Room discovery

View file

@ -0,0 +1,164 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Registration and Login API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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: |-
{
"type": "m.login.password",
"user": "cheeky_monkey",
"password": "ilovebananas"
}
properties:
type:
type: string
description: The login type being used. Currently only "m.login.password" is supported.
user:
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: ["type", "user", "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 homeserver on which the account has been registered.
400:
description: |-
Part of the request was invalid. For example, the login type may not be recognised.
examples:
application/json: |-
{
"errcode": "M_UNKNOWN",
"error": "Bad login type."
}
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"
tags:
- Session management
"/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"
tags:
- Session management

View file

@ -0,0 +1,133 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Rooms API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/messages":
get:
summary: Get a list of events for this room
description: |-
This API returns a list of message and state events for a room. It uses
pagination query parameters to paginate history in the room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to get events from.
required: true
x-example: "!636q39766251:example.com"
- in: query
type: string
name: from
description: |-
The token to start returning events from. This token can be obtained
from the initial sync API.
required: true
x-example: "s345_678_333"
- in: query
type: string
enum: ["b", "f"]
name: dir
description: |-
The direction to return events from.
required: true
x-example: "b"
- in: query
type: integer
name: limit
description: |-
The maximum number of events to return. Default: 10.
x-example: "3"
responses:
200:
description: A list of messages with a new token to request more.
schema:
type: object
description: A list of messages with a new token to request more.
properties:
start:
type: string
description: |-
The token to start paginating from. If ``dir=b`` this will be
the token supplied in ``from``.
end:
type: string
description: |-
The token the pagination ends at. If ``dir=b`` this token should
be used again to request even earlier events.
chunk:
type: array
description: |-
A list of room events.
items:
type: object
title: RoomEvent
examples:
application/json: |-
{
"start": "t47429-4392820_219380_26003_2265",
"end": "t47409-4357353_219380_26003_2265",
"chunk": [
{
"origin_server_ts": 1444812213737,
"user_id": "@alice:example.com",
"event_id": "$1444812213350496Caaaa:example.com",
"content": {
"body": "hello world",
"msgtype":"m.text"
},
"room_id":"!Xq3620DUiqCaoxq:example.com",
"type":"m.room.message",
"age": 1042
},
{
"origin_server_ts": 1444812194656 ,
"user_id": "@bob:example.com",
"event_id": "$1444812213350496Cbbbb:example.com",
"content": {
"body": "the world is big",
"msgtype":"m.text"
},
"room_id":"!Xq3620DUiqCaoxq:example.com",
"type":"m.room.message",
"age": 20123
},
{
"origin_server_ts": 1444812163990,
"user_id": "@bob:example.com",
"event_id": "$1444812213350496Ccccc:example.com",
"content": {
"name": "New room name"
},
"prev_content": {
"name": "Old room name"
},
"state_key": "",
"room_id":"!Xq3620DUiqCaoxq:example.com",
"type":"m.room.name",
"age": 50789
}
]
}
403:
description: >
You aren't a member of the room.
tags:
- Room participation

View file

@ -0,0 +1,411 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Sync API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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. This token is either from a previous
request to this API or from the initial sync API.
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"
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",
"sender": "@bob:localhost"
}
]
}
schema:
type: object
properties:
start:
type: string
description: |-
A token which correlates to the first value in ``chunk``. This
is usually the same token supplied to ``from=``.
end:
type: string
description: |-
A token which correlates to the last value in ``chunk``. This
token should be used in the next request to ``/events``.
chunk:
type: array
description: "An array of events."
items:
type: object
title: Event
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
400:
description: "Bad pagination ``from`` parameter."
tags:
- Room participation
deprecated: true
"/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"
- in: query
type: boolean
name: archived
description: |-
Whether to include rooms that the user has left. If ``false`` 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. By default this is ``false``.
required: false
x-example: "true"
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"
}
],
"account_data": [
{
"type": "org.example.custom.config",
"content": {
"custom_config_key": "custom_config_value"
}
}
],
"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",
"sender": "@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",
"sender": "@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",
"sender": "@alice:localhost"
},
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://localhost/fzysBrHpPEeTGANCVLXWXNMI#auto",
"membership": "join"
},
"event_id": "$1426600438280zExKY:localhost",
"membership": "join",
"origin_server_ts": 1426600438277,
"room_id": "!TmaZBKYIFrIPVGoUYp:localhost",
"state_key": "@alice:localhost",
"type": "m.room.member",
"sender": "@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",
"sender": "@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",
"sender": "@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",
"sender": "@alice:localhost"
}
],
"visibility": "private",
"account_data": [
{
"type": "m.tag",
"content": {"tags": {"work": {"order": 1}}}
},
{
"type": "org.example.custom.room.config",
"content": {
"custom_config_key": "custom_config_value"
}
}
]
}
]
}
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": "../../event-schemas/schema/core-event-schema/event.yaml"
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": "../../event-schemas/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": "../../event-schemas/schema/core-event-schema/room_event.yaml"
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": "../../event-schemas/schema/core-event-schema/state_event.yaml"
visibility:
type: string
enum: ["private", "public"]
description: |-
Whether this room is visible to the ``/publicRooms`` API
or not."
account_data:
type: array
description: |-
The private data that this user has attached to
this room.
items:
title: Event
type: object
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/event.yaml"
required: ["room_id", "membership"]
required: ["end", "rooms", "presence"]
404:
description: There is no avatar URL for this user or this user does not exist.
tags:
- Room participation
deprecated: true
"/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",
"sender": "@bob:matrix.org",
"event_id": "$asfDuShaf7Gafaw:matrix.org",
"type": "m.room.message"
}
schema:
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/event.yaml"
404:
description: The event was not found or you do not have permission to read this event.
tags:
- Room participation
deprecated: true

View file

@ -0,0 +1,215 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Presence API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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"
tags:
- Presence
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
}
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.
tags:
- Presence
"/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"
tags:
- Presence
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": "../../event-schemas/schema/core-event-schema/event.yaml"
tags:
- Presence

View file

@ -0,0 +1,205 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Profile API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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"
tags:
- User data
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.
tags:
- User data
"/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"
tags:
- User data
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.
tags:
- User data
"/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.
tags:
- User data

View file

@ -0,0 +1,144 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Push API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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
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 homeserver 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"
tags:
- Push notifications

View file

@ -0,0 +1,505 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Push Rules API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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.yaml"
]
global:
type: object
description: The global ruleset.
title: Ruleset
allOf: [
"$ref": "definitions/push_ruleset.yaml"
]
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"
}
]
}
}
tags:
- Push notifications
"/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.yaml"
]
tags:
- Push notifications
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
tags:
- Push notifications
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.yaml" ]
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"
tags:
- Push notifications
"/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: object
properties:
enabled:
type: boolean
description: Whether the push rule is enabled or not.
required: ["enabled"]
example: |-
{
"enabled": true
}
responses:
200:
description: The push rule was enabled or disabled.
examples:
application/json: |-
{}
schema:
type: object
tags:
- Push notifications

View file

@ -0,0 +1,71 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Receipts API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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"
tags:
- Room participation

View file

@ -0,0 +1,84 @@
swagger: '2.0'
info:
title: "Matrix Client-Server message redaction API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/api/%CLIENT_MAJOR_VERSION%
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}/redact/{eventId}/{txnId}":
put:
summary: Strips all non-integrity-critical information out of an event.
description: |-
Strips all information out of an event which isn't critical to the
integrity of the server-side representation of the room.
This cannot be undone.
Users may redact their own events, and any user with a power level
greater than or equal to the `redact` power level of the room may
redact events there.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room from which to redact the event.
required: true
x-example: "!637q39766251:example.com"
- in: path
type: string
name: eventId
description: The ID of the event to redact
required: true
x-example: "bai2b1i9:matrix.org"
- in: path
name: txnId
type: string
description: |-
The transaction ID for this event. Clients should generate a
unique ID; it will be used by the server to ensure idempotency of requests.
required: true
x-example: "37"
- in: body
name: body
schema:
type: object
example: |-
{
"reason": "Indecent material"
}
properties:
reason:
type: string
description: The reason for the event being redacted.
responses:
200:
description: "An ID for the redaction event."
examples:
application/json: |-
{
"event_id": "YUwQidLecu"
}
schema:
type: object
properties:
event_id:
type: string
description: |-
A unique identifier for the event.
tags:
- Room participation

View file

@ -0,0 +1,125 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Registration API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
consumes:
- application/json
produces:
- application/json
paths:
"/register":
post:
summary: Register for an account on this homeserver.
description: |-
Register for an account on this homeserver.
There are two kinds of user account:
- `user` accounts. These accounts may use the full API described in this specification.
- `guest` accounts. These accounts may have limited permissions and may not be supported by all servers.
parameters:
- in: query
name: kind
type: string
x-example: guest
required: false
default: user
enum:
- guest
- user
description: The kind of account to register. Defaults to `user`.
- in: body
name: body
schema:
type: object
example: |-
{
"username": "cheeky_monkey",
"password": "ilovebananas",
"bind_email": false
}
properties:
bind_email:
type: boolean
description: |-
If true, the server binds the email used for authentication to
the Matrix ID with the ID Server.
username:
type: string
description: |-
The local part of the desired Matrix ID. If omitted,
the homeserver MUST generate a Matrix ID local part.
password:
type: string
description: The desired password for the account.
required: ["password"]
responses:
200:
description: The account has been registered.
examples:
application/json: |-
{
"user_id": "@cheeky_monkey:matrix.org",
"access_token": "abc123",
"home_server": "matrix.org",
"refresh_token": "def456"
}
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 homeserver on which the account has been registered.
400:
description: |-
Part of the request was invalid. This may include one of the following error codes:
* ``M_USER_IN_USE`` : The desired user ID is already taken.
* ``M_EXCLUSIVE`` : The desired user ID is in the exclusive namespace
claimed by an application service.
These errors may be returned at any stage of the registration process,
including after authentication if the requested user ID was registered
whilst the client was performing authentication.
Homeservers MUST perform the relevant checks and return these codes before
performing `User-Interactive Authentication`_, although they may also return
them after authentication is completed if, for example, the requested user ID
was registered whilst the client was performing authentication.
examples:
application/json: |-
{
"errcode": "M_USER_IN_USE",
"error": "Desired user ID is already taken."
}
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
tags:
- User data

View file

@ -0,0 +1,81 @@
swagger: '2.0'
info:
title: "Matrix Client-Server message event send API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/send/{eventType}/{txnId}":
put:
summary: Send a message event to the given room.
description: |-
This endpoint is used to send a message event to a room. Message events
allow access to historical events and pagination, making them suited
for "once-off" activity in a room.
The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See
`Room Events`_ for the m. event specification.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to send the event to.
required: true
x-example: "!636q39766251:example.com"
- in: path
type: string
name: eventType
description: The type of event to send.
required: true
x-example: "m.room.message"
- in: path
name: txnId
type: string
description: |-
The transaction ID for this event. Clients should generate an
ID unique across requests with the same access token; it will be
used by the server to ensure idempotency of requests.
required: true
x-example: "35"
- in: body
name: body
schema:
type: object
example: |-
{
"msgtype": "m.text",
"body": "hello"
}
responses:
200:
description: "An ID for the sent event."
examples:
application/json: |-
{
"event_id": "YUwRidLecu"
}
schema:
type: object
properties:
event_id:
type: string
description: |-
A unique identifier for the event.
tags:
- Room participation

View file

@ -0,0 +1,137 @@
swagger: '2.0'
info:
title: "Matrix Client-Server state event send API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}":
put:
summary: Send a state event to the given room.
description: |
State events can be sent using this endpoint. This endpoint is
equivalent to calling `/rooms/{roomId}/state/{eventType}/{stateKey}`
with an empty `stateKey`. Previous state events with matching
`<roomId>` and `<eventType>`, and empty `<stateKey>`, will be overwritten.
Requests to this endpoint **cannot use transaction IDs**
like other ``PUT`` paths because they cannot be differentiated from the
``state_key``. Furthermore, ``POST`` is unsupported on state paths.
The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See
`Room Events`_ for the ``m.`` event specification.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to set the state in
required: true
x-example: "!636q39766251:example.com"
- in: path
type: string
name: eventType
description: The type of event to send.
required: true
x-example: "m.room.name"
- in: path
type: string
name: stateKey
description: The state_key for the state to send. Defaults to the empty string.
required: true
x-example: ""
- in: body
name: body
schema:
type: object
example: |-
{
"name": "New name for the room"
}
responses:
200:
description: "An ID for the sent event."
examples:
application/json: |-
{
"event_id": "YUwRidLecu"
}
schema:
type: object
properties:
event_id:
type: string
description: |-
A unique identifier for the event.
tags:
- Room participation
"/rooms/{roomId}/state/{eventType}":
put:
summary: Send a state event to the given room.
description: |
State events can be sent using this endpoint. These events will be
overwritten if ``<room id>``, ``<event type>`` and ``<state key>`` all
match. This endpoint forces the state key to be the empty string.
Requests to this endpoint **cannot use transaction IDs**
like other ``PUT`` paths because they cannot be differentiated from the
``state_key``. Furthermore, ``POST`` is unsupported on state paths.
The body of the request should be the content object of the event; the
fields in this object will vary depending on the type of event. See
`Room Events`_ for the ``m.`` event specification.
security:
- accessToken: []
parameters:
- in: path
type: string
name: roomId
description: The room to set the state in
required: true
x-example: "!636q39766251:example.com"
- in: path
type: string
name: eventType
description: The type of event to send.
required: true
x-example: "m.room.name"
- in: body
name: body
schema:
type: object
example: |-
{
"name": "New name for the room"
}
responses:
200:
description: "An ID for the sent event."
examples:
application/json: |-
{
"event_id": "YUwRidLecu"
}
schema:
type: object
properties:
event_id:
type: string
description: |-
A unique identifier for the event.
tags:
- Room participation

View file

@ -0,0 +1,504 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Rooms API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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.
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.
tags:
- Room participation
"/rooms/{roomId}/state/{eventType}":
get:
summary: Get the state identified by the type, with the empty state 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.
This looks up the state event with the empty state key.
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"
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.
tags:
- Room participation
"/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",
"sender": "@alice:example.com"
},
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
"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",
"sender": "@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",
"sender": "@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",
"sender": "@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",
"sender": "@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": "../../event-schemas/schema/core-event-schema/state_event.yaml"
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.
tags:
- Room participation
"/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",
"sender": "@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",
"sender": "@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",
"sender": "@alice:example.com"
},
{
"age": 6547561012,
"content": {
"avatar_url": "mxc://example.com/fzysBrHpPEeTGANCVLXWXNMI#auto",
"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",
"sender": "@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",
"sender": "@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",
"sender": "@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",
"sender": "@alice:example.com"
}
],
"visibility": "private",
"account_data": [{
"type": "m.tag",
"content": {"tags": {"work": {"order": "1"}}}
}]
}
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": "../../event-schemas/schema/core-event-schema/room_event.yaml"
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": "../../event-schemas/schema/core-event-schema/state_event.yaml"
visibility:
type: string
enum: ["private", "public"]
description: |-
Whether this room is visible to the ``/publicRooms`` API
or not."
account_data:
type: array
description: |-
The private data that this user has attached to this room.
items:
title: Event
type: object
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/event.yaml"
required: ["room_id"]
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.
tags:
- Room participation
deprecated: true
"/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",
"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",
"sender": "@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",
"sender": "@bob:example.com"
}
]
}
schema:
type: object
properties:
chunk:
type: array
items:
title: MemberEvent
type: object
allOf:
- "$ref": "../../event-schemas/schema/m.room.member"
403:
description: >
You aren't a member of the room and weren't previously a
member of the room.
tags:
- Room participation

View file

@ -0,0 +1,341 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Search API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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:
"/search":
post:
summary: Perform a server-side search.
description: |-
Performs a full text search across different categories.
security:
- accessToken: []
parameters:
- in: query
name: next_batch
type: string
description: |-
The point to return events from. If given, this should be a
`next_batch` result from a previous call to this endpoint.
x-example: "YWxsCgpOb25lLDM1ODcwOA"
- in: body
name: body
schema:
type: object
example: |-
{
"search_categories": {
"room_events": {
"keys": [
"content.body"
],
"search_term": "martians and men"
}
},
"order_by": "recent",
"groupings": {
"group_by": [
{
"key": "room_id"
}
]
}
}
properties:
search_categories:
type: object
title: "Categories"
description: Describes which categories to search in and
their criteria.
properties:
room_events:
type: object
title: "Room Events"
description: Mapping of category name to search criteria.
properties:
search_term:
type: string
description: The string to search events for
keys:
type: array
items:
type: string
enum: ["content.body", "content.name", "content.topic"]
description: The keys to search. Defaults to all.
filter:
type: object
title: Filter
description: |-
This takes a `filter <https://matrix.org/docs/spec/%CLIENT_RELEASE_LABEL%/client_server.html#filtering>`_.
order_by:
title: "Ordering"
type: string
enum: ["recent", "rank"]
description: "The order in which to search for results."
event_context:
title: "Event Context"
type: object
description: |-
Configures whether any context for the events
returned are included in the response.
properties:
before_limit:
type: integer
title: "Before limit"
description: |-
How many events before the result are
returned.
after_limit:
type: integer
title: "After limit"
description: |-
How many events after the result are
returned.
include_profile:
type: boolean
title: "Return profile information"
description: |-
Requests that the server returns the
historic profile information for the users
that sent the events that were returned.
include_state:
type: boolean
title: Include current state
description: |-
Requests the server return the current state for
each room returned.
groupings:
type: object
title: Groupings
description: |-
Requests that the server partitions the result set
based on the provided list of keys.
properties:
group_by:
type: array
title: Groups
description: List of groups to request.
items:
type: object
title: Group
description: Configuration for group.
properties:
key:
type: string
title: Group Key
description: |-
Key that defines the group.
enum: ["room_id", "sender"]
required: ["search_term"]
required: ["search_categories"]
responses:
200:
description: Results of the search.
schema:
type: object
title: Results
required: ["search_categories"]
properties:
search_categories:
type: object
title: Categories
description: Describes which categories to search in and
their criteria.
properties:
room_events:
type: object
title: Room Event Results
description: Mapping of category name to search criteria.
properties:
count:
type: number
description: An approximate count of the total number of results found.
results:
type: array
title: Results
description: List of results in the requested order.
items:
type: object
title: Result
description: The result object.
properties:
rank:
type: number
description: A number that describes how closely
this result matches the search. Higher is
closer.
result:
type: object
title: Event
description: The event that matched.
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
context:
type: object
title: Event Context
description: Context for result, if requested.
properties:
start:
type: string
title: Start Token
description: |-
Pagination token for the start of the chunk
end:
type: string
title: End Token
description: |-
Pagination token for the end of the chunk
profile_info:
type: object
title: Profile Information
description: |-
The historic profile information of the
users that sent the events returned.
additionalProperties:
type: object
title: User Profile
properties:
displayname:
type: string
title: Display name
avatar_url:
type: string
title: Avatar Url
events_before:
type: array
title: Events Before
description: Events just before the result.
items:
type: object
title: Event
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
events_after:
type: array
title: Events After
description: Events just after the result.
items:
type: object
title: Event
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
state:
type: object
title: Current state
description: |-
The current state for every room in the results.
This is included if the request had the
``include_state`` key set with a value of ``true``.
additionalProperties:
type: array
title: Room State
items:
type: object
title: Event
allOf:
- "$ref": "../../event-schemas/schema/core-event-schema/room_event.yaml"
groups:
type: object
title: Groups
description: Any groups that were requested.
additionalProperties:
type: object
title: Group Key
description: The results for a given group.
additionalProperties:
type: object
title: Group Value
description: |-
The results for a particular group value.
properties:
next_batch:
type: string
title: Next Batch in Group
description: |-
Token that can be used to get the next batch
of results in the group, by passing as the
`next_batch` parameter to the next call. If
this field is absent, there are no more
results in this group.
order:
type: integer
title: Group Order
description: |-
Key that can be used to order different
groups.
results:
type: array
description: |-
Which results are in this group.
items:
type: string
title: Result Event ID
next_batch:
type: string
title: Next Batch
description: |-
Token that can be used to get the next batch of
results, by passing as the `next_batch` parameter to
the next call. If this field is absent, there are no
more results.
examples:
application/json: |-
{
"search_categories": {
"room_events": {
"groups": {
"room_id": {
"!qPewotXpIctQySfjSy:localhost": {
"order": 1,
"next_batch": "BdgFsdfHSf-dsFD",
"results": [
"$144429830826TWwbB:localhost"
]
}
}
},
"next_batch": "5FdgFsd234dfgsdfFD",
"count": 1224,
"results": [
{
"rank": 0.00424866,
"result": {
"age": 526228296,
"content": {
"body": "Test content martians and men",
"msgtype": "m.text"
},
"event_id": "$144429830826TWwbB:localhost",
"origin_server_ts": 1444298308034,
"room_id": "!qPewotXpIctQySfjSy:localhost",
"type": "m.room.message",
"sender": "@test:localhost"
}
}
]
}
}
}
400:
description: Part of the request was invalid.
429:
description: This request was rate-limited.
schema:
"$ref": "definitions/error.yaml"
tags:
- Search

320
api/client-server/sync.yaml Normal file
View file

@ -0,0 +1,320 @@
swagger: '2.0'
info:
title: "Matrix Client-Server sync API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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.
Clients 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 or a filter JSON
object enocoded as a string. The server will detect whether it is
an ID or a JSON object by whether the first character is a ``"{"``
open brace. Passing the JSON inline is best suited to one off
requests. Creating a filter using the filter API is recommended for
clients that reuse the same filter multiple times, for example in
long poll requests.
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: full_state
type: boolean
description: |-
Controls whether to include the full state for all rooms the user
is a member of.
If this is set to ``true``, then all state events will be returned,
even if ``since`` is non-empty. The timeline will still be limited
by the ``since`` parameter. In this case, the ``timeout`` parameter
will be ignored and the query will return immediately, possibly with
an empty timeline.
If ``false``, and ``since`` is non-empty, only state which has
changed since the point indicated by ``since`` will be returned.
By default, this is ``false``.
x-example: "false"
- 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:
join:
title: Joined Rooms
type: object
description: |-
The rooms that the user has joined.
additionalProperties:
title: Joined Room
type: object
properties:
state:
title: State
type: object
description: |-
Updates to the state, between the time indicated by
the ``since`` parameter, and the start of the
``timeline`` (or all state up to the start of the
``timeline``, if ``since`` is not given, or
``full_state`` is true).
allOf:
- $ref: "definitions/event_batch.yaml"
timeline:
title: Timeline
type: object
description: |-
The timeline of messages and state changes in the
room.
allOf:
- $ref: "definitions/timeline_batch.yaml"
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.yaml"
account_data:
title: Account Data
type: object
description: |-
The private data that this user has attached to
this room.
allOf:
- $ref: "definitions/event_batch.yaml"
invite:
title: Invited Rooms
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.yaml"
leave:
title: Left rooms
type: object
description: |-
The rooms that the user has left or been banned from.
additionalProperties:
title: Left Room
type: object
properties:
state:
title: State
type: object
description: |-
The state updates for the room up to the start of the timeline.
allOf:
- $ref: "definitions/event_batch.yaml"
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.yaml"
presence:
title: Presence
type: object
description: |-
The updates to the presence status of other users.
allOf:
- $ref: "definitions/event_batch.yaml"
examples:
application/json: |-
{
"next_batch": "s72595_4483_1934",
"presence": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.presence",
"content": {"presence": "online"}
}
]
},
"account_data": {
"events": [
{
"type": "org.example.custom.config",
"content": {
"custom_config_key": "custom_config_value"
}
}
]
},
"rooms": {
"join": {
"!726s6s6q:example.com": {
"state": {
"events": [
{
"sender": "@alice:example.com",
"type": "m.room.member",
"state_key": "@alice:example.com",
"content": {"membership": "join"},
"origin_server_ts": 1417731086795,
"event_id": "$66697273743031:example.com"
}
]
},
"timeline": {
"events": [
{
"sender": "@bob:example.com",
"type": "m.room.member",
"state_key": "@bob:example.com",
"content": {"membership": "join"},
"prev_content": {"membership": "invite"},
"origin_server_ts": 1417731086795,
"event_id": "$7365636s6r6432:example.com"
},
{
"sender": "@alice:example.com",
"type": "m.room.message",
"age": 124524,
"txn_id": "1234",
"content": {
"body": "I am a fish",
"msgtype": "m.text"
},
"origin_server_ts": 1417731086797,
"event_id": "$74686972643033:example.com"
}
],
"limited": true,
"prev_batch": "t34-23535_0_0"
},
"ephemeral": {
"events": [
{
"type": "m.typing",
"content": {"user_ids": ["@alice:example.com"]}
}
]
},
"account_data": {
"events": [
{
"type": "m.tag",
"content": {"tags": {"work": {"order": 1}}}
},
{
"type": "org.example.custom.room.config",
"content": {
"custom_config_key": "custom_config_value"
}
}
]
}
}
},
"invite": {
"!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"}
}
]
}
}
},
"leave": {}
}
}
tags:
- Room participation

153
api/client-server/tags.yaml Normal file
View file

@ -0,0 +1,153 @@
swagger: '2.0'
info:
title: "Matrix Client-Server tag API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/rooms/{roomId}/tags":
get:
summary: List the tags for a room.
description: |-
List the tags set by a user on a room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The id of the user to get tags for. The access token must be
authorized to make requests for this user id.
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
required: true
description: |-
The id of the room to get tags for.
x-example: "!726s6s6q:example.com"
responses:
200:
description:
The list of tags for the user for the room.
schema:
type: object
properties:
tags:
title: Tags
type: object
examples:
application/json: |-
{
"tags": {
"work": {"order": "1"},
"pinned": {}
}
}
tags:
- User data
"/user/{userId}/rooms/{roomId}/tags/{tag}":
put:
summary: Add a tag to a room.
description: |-
Add a tag to the room.
security:
- accessToken: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The id of the user to add a tag for. The access token must be
authorized to make requests for this user id.
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
required: true
description: |-
The id of the room to add a tag to.
x-example: "!726s6s6q:example.com"
- in: path
type: string
name: tag
required: true
description: |-
The tag to add.
x-example: "work"
- in: body
name: body
required: true
description: |-
Extra data for the tag, e.g. ordering.
schema:
type: object
example: |-
{"order": "1"}
responses:
200:
description:
The tag was successfully added.
schema:
type: object
examples:
application/json: |-
{}
tags:
- User data
delete:
summary: Remove a tag from the room.
description: |-
Remove a tag from the room.
security:
- access_token: []
parameters:
- in: path
type: string
name: userId
required: true
description: |-
The id of the user to remove a tag for. The access token must be
authorized to make requests for this user id.
x-example: "@alice:example.com"
- in: path
type: string
name: roomId
required: true
description: |-
The id of the room to remove a tag from.
x-example: "!726s6s6q:example.com"
- in: path
type: string
name: tag
required: true
description: |-
The tag to remove.
x-example: "work"
responses:
200:
description:
The tag was successfully removed
schema:
type: object
examples:
application/json: |-
{}
tags:
- User data

View file

@ -0,0 +1,125 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Room Membership API for third party identifiers"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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}/invite":
post:
summary: Invite a user to participate in a particular room.
description: |-
.. _invite-by-third-party-id-endpoint:
*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. The other is documented in the*
`joining rooms section`_.
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 homeserver 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 homeserver will
append a ``m.room.third_party_invite`` event to the room.
.. _joining rooms section: `invite-by-user-id-endpoint`_
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"
}
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.
required: ["id_server", "medium", "address"]
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"
tags:
- Room membership

View file

@ -0,0 +1,78 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Typing API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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"
tags:
- Room participation

View file

@ -0,0 +1,41 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Versions API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client
produces:
- application/json
paths:
"/versions":
get:
summary: Gets the versions of the specification supported by the server.
description: |-
Gets the versions of the specification supported by the server.
Values will take the form ``rX.Y.Z``.
Only the latest ``Z`` value will be reported for each supported ``X.Y`` value.
i.e. if the server implements ``r0.0.0``, ``r0.0.1``, and ``r1.2.0``, it will report ``r0.0.1`` and ``r1.2.0``.
responses:
200:
description: The versions supported by the server.
examples:
application/json: |-
{
"versions": ["r0.0.1"]
}
schema:
type: object
properties:
versions:
type: array
description: The supported versions.
items:
type: string
description: The supported versions
tags:
- Server administration

View file

@ -0,0 +1,69 @@
swagger: '2.0'
info:
title: "Matrix Client-Server Voice over IP API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/client/%CLIENT_MAJOR_VERSION%
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:
"/voip/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"
tags:
- VOIP

38
api/files/backbone-min.js vendored Normal file
View file

@ -0,0 +1,38 @@
// Backbone.js 0.9.2
// (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Backbone may be freely distributed under the MIT license.
// For all details and documentation:
// http://backbonejs.org
(function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks=
{});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g=
z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent=
{};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null==
b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent:
b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)};
a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error,
h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t();
return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending=
{};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length||
!this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator);
this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c<d;c++){if(!(e=a[c]=this._prepareModel(a[c],b)))throw Error("Can't add an invalid model to a collection");g=e.cid;i=e.id;j[g]||this._byCid[g]||null!=i&&(k[i]||this._byId[i])?
l.push(c):j[g]=k[i]=e}for(c=l.length;c--;)a.splice(l[c],1);c=0;for(d=a.length;c<d;c++)(e=a[c]).on("all",this._onModelEvent,this),this._byCid[e.cid]=e,null!=e.id&&(this._byId[e.id]=e);this.length+=d;A.apply(this.models,[null!=b.at?b.at:this.models.length,0].concat(a));this.comparator&&this.sort({silent:!0});if(b.silent)return this;c=0;for(d=this.models.length;c<d;c++)if(j[(e=this.models[c]).cid])b.index=c,e.trigger("add",e,this,b);return this},remove:function(a,b){var c,d,e,g;b||(b={});a=f.isArray(a)?
a.slice():[a];c=0;for(d=a.length;c<d;c++)if(g=this.getByCid(a[c])||this.get(a[c]))delete this._byId[g.id],delete this._byCid[g.cid],e=this.indexOf(g),this.models.splice(e,1),this.length--,b.silent||(b.index=e,g.trigger("remove",g,this,b)),this._removeReference(g);return this},push:function(a,b){a=this._prepareModel(a,b);this.add(a,b);return a},pop:function(a){var b=this.at(this.length-1);this.remove(b,a);return b},unshift:function(a,b){a=this._prepareModel(a,b);this.add(a,f.extend({at:0},b));return a},
shift:function(a){var b=this.at(0);this.remove(b,a);return b},get:function(a){return null==a?void 0:this._byId[null!=a.id?a.id:a]},getByCid:function(a){return a&&this._byCid[a.cid||a]},at:function(a){return this.models[a]},where:function(a){return f.isEmpty(a)?[]:this.filter(function(b){for(var c in a)if(a[c]!==b.get(c))return!1;return!0})},sort:function(a){a||(a={});if(!this.comparator)throw Error("Cannot sort a set without a comparator");var b=f.bind(this.comparator,this);1==this.comparator.length?
this.models=this.sortBy(b):this.models.sort(b);a.silent||this.trigger("reset",this,a);return this},pluck:function(a){return f.map(this.models,function(b){return b.get(a)})},reset:function(a,b){a||(a=[]);b||(b={});for(var c=0,d=this.models.length;c<d;c++)this._removeReference(this.models[c]);this._reset();this.add(a,f.extend({silent:!0},b));b.silent||this.trigger("reset",this,b);return this},fetch:function(a){a=a?f.clone(a):{};void 0===a.parse&&(a.parse=!0);var b=this,c=a.success;a.success=function(d,
e,f){b[a.add?"add":"reset"](b.parse(d,f),a);c&&c(b,d)};a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},create:function(a,b){var c=this,b=b?f.clone(b):{},a=this._prepareModel(a,b);if(!a)return!1;b.wait||c.add(a,b);var d=b.success;b.success=function(e,f){b.wait&&c.add(e,b);d?d(e,f):e.trigger("sync",a,f,b)};a.save(null,b);return a},parse:function(a){return a},chain:function(){return f(this.models).chain()},_reset:function(){this.length=0;this.models=[];this._byId=
{};this._byCid={}},_prepareModel:function(a,b){b||(b={});a instanceof o?a.collection||(a.collection=this):(b.collection=this,a=new this.model(a,b),a._validate(a.attributes,b)||(a=!1));return a},_removeReference:function(a){this==a.collection&&delete a.collection;a.off("all",this._onModelEvent,this)},_onModelEvent:function(a,b,c,d){("add"==a||"remove"==a)&&c!=this||("destroy"==a&&this.remove(b,d),b&&a==="change:"+b.idAttribute&&(delete this._byId[b.previous(b.idAttribute)],this._byId[b.id]=b),this.trigger.apply(this,
arguments))}});f.each("forEach,each,map,reduce,reduceRight,find,detect,filter,select,reject,every,all,some,any,include,contains,invoke,max,min,sortBy,sortedIndex,toArray,size,first,initial,rest,last,without,indexOf,shuffle,lastIndexOf,isEmpty,groupBy".split(","),function(a){r.prototype[a]=function(){return f[a].apply(f,[this.models].concat(f.toArray(arguments)))}});var u=g.Router=function(a){a||(a={});a.routes&&(this.routes=a.routes);this._bindRoutes();this.initialize.apply(this,arguments)},B=/:\w+/g,
C=/\*\w+/g,D=/[-[\]{}()+?.,\\^$|#\s]/g;f.extend(u.prototype,k,{initialize:function(){},route:function(a,b,c){g.history||(g.history=new m);f.isRegExp(a)||(a=this._routeToRegExp(a));c||(c=this[b]);g.history.route(a,f.bind(function(d){d=this._extractParameters(a,d);c&&c.apply(this,d);this.trigger.apply(this,["route:"+b].concat(d));g.history.trigger("route",this,b,d)},this));return this},navigate:function(a,b){g.history.navigate(a,b)},_bindRoutes:function(){if(this.routes){var a=[],b;for(b in this.routes)a.unshift([b,
this.routes[b]]);b=0;for(var c=a.length;b<c;b++)this.route(a[b][0],a[b][1],this[a[b][1]])}},_routeToRegExp:function(a){a=a.replace(D,"\\$&").replace(B,"([^/]+)").replace(C,"(.*?)");return RegExp("^"+a+"$")},_extractParameters:function(a,b){return a.exec(b).slice(1)}});var m=g.History=function(){this.handlers=[];f.bindAll(this,"checkUrl")},s=/^[#\/]/,E=/msie [\w.]+/;m.started=!1;f.extend(m.prototype,k,{interval:50,getHash:function(a){return(a=(a?a.location:window.location).href.match(/#(.*)$/))?a[1]:
""},getFragment:function(a,b){if(null==a)if(this._hasPushState||b){var a=window.location.pathname,c=window.location.search;c&&(a+=c)}else a=this.getHash();a.indexOf(this.options.root)||(a=a.substr(this.options.root.length));return a.replace(s,"")},start:function(a){if(m.started)throw Error("Backbone.history has already been started");m.started=!0;this.options=f.extend({},{root:"/"},this.options,a);this._wantsHashChange=!1!==this.options.hashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=
!(!this.options.pushState||!window.history||!window.history.pushState);var a=this.getFragment(),b=document.documentMode;if(b=E.exec(navigator.userAgent.toLowerCase())&&(!b||7>=b))this.iframe=i('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow,this.navigate(a);this._hasPushState?i(window).bind("popstate",this.checkUrl):this._wantsHashChange&&"onhashchange"in window&&!b?i(window).bind("hashchange",this.checkUrl):this._wantsHashChange&&(this._checkUrlInterval=setInterval(this.checkUrl,
this.interval));this.fragment=a;a=window.location;b=a.pathname==this.options.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!b)return this.fragment=this.getFragment(null,!0),window.location.replace(this.options.root+"#"+this.fragment),!0;this._wantsPushState&&this._hasPushState&&b&&a.hash&&(this.fragment=this.getHash().replace(s,""),window.history.replaceState({},document.title,a.protocol+"//"+a.host+this.options.root+this.fragment));if(!this.options.silent)return this.loadUrl()},
stop:function(){i(window).unbind("popstate",this.checkUrl).unbind("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);m.started=!1},route:function(a,b){this.handlers.unshift({route:a,callback:b})},checkUrl:function(){var a=this.getFragment();a==this.fragment&&this.iframe&&(a=this.getFragment(this.getHash(this.iframe)));if(a==this.fragment)return!1;this.iframe&&this.navigate(a);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(a){var b=this.fragment=this.getFragment(a);return f.any(this.handlers,
function(a){if(a.route.test(b))return a.callback(b),!0})},navigate:function(a,b){if(!m.started)return!1;if(!b||!0===b)b={trigger:b};var c=(a||"").replace(s,"");this.fragment!=c&&(this._hasPushState?(0!=c.indexOf(this.options.root)&&(c=this.options.root+c),this.fragment=c,window.history[b.replace?"replaceState":"pushState"]({},document.title,c)):this._wantsHashChange?(this.fragment=c,this._updateHash(window.location,c,b.replace),this.iframe&&c!=this.getFragment(this.getHash(this.iframe))&&(b.replace||
this.iframe.document.open().close(),this._updateHash(this.iframe.location,c,b.replace))):window.location.assign(this.options.root+a),b.trigger&&this.loadUrl(a))},_updateHash:function(a,b,c){c?a.replace(a.toString().replace(/(javascript:|#).*$/,"")+"#"+b):a.hash=b}});var v=g.View=function(a){this.cid=f.uniqueId("view");this._configure(a||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()},F=/^(\S+)\s*(.*)$/,w="model,collection,el,id,attributes,className,tagName".split(",");
f.extend(v.prototype,k,{tagName:"div",$:function(a){return this.$el.find(a)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();return this},make:function(a,b,c){a=document.createElement(a);b&&i(a).attr(b);c&&i(a).html(c);return a},setElement:function(a,b){this.$el&&this.undelegateEvents();this.$el=a instanceof i?a:i(a);this.el=this.$el[0];!1!==b&&this.delegateEvents();return this},delegateEvents:function(a){if(a||(a=n(this,"events"))){this.undelegateEvents();
for(var b in a){var c=a[b];f.isFunction(c)||(c=this[a[b]]);if(!c)throw Error('Method "'+a[b]+'" does not exist');var d=b.match(F),e=d[1],d=d[2],c=f.bind(c,this),e=e+(".delegateEvents"+this.cid);""===d?this.$el.bind(e,c):this.$el.delegate(d,e,c)}}},undelegateEvents:function(){this.$el.unbind(".delegateEvents"+this.cid)},_configure:function(a){this.options&&(a=f.extend({},this.options,a));for(var b=0,c=w.length;b<c;b++){var d=w[b];a[d]&&(this[d]=a[d])}this.options=a},_ensureElement:function(){if(this.el)this.setElement(this.el,
!1);else{var a=n(this,"attributes")||{};this.id&&(a.id=this.id);this.className&&(a["class"]=this.className);this.setElement(this.make(this.tagName,a),!1)}}});o.extend=r.extend=u.extend=v.extend=function(a,b){var c=G(this,a,b);c.extend=this.extend;return c};var H={create:"POST",update:"PUT","delete":"DELETE",read:"GET"};g.sync=function(a,b,c){var d=H[a];c||(c={});var e={type:d,dataType:"json"};c.url||(e.url=n(b,"url")||t());if(!c.data&&b&&("create"==a||"update"==a))e.contentType="application/json",
e.data=JSON.stringify(b.toJSON());g.emulateJSON&&(e.contentType="application/x-www-form-urlencoded",e.data=e.data?{model:e.data}:{});if(g.emulateHTTP&&("PUT"===d||"DELETE"===d))g.emulateJSON&&(e.data._method=d),e.type="POST",e.beforeSend=function(a){a.setRequestHeader("X-HTTP-Method-Override",d)};"GET"!==e.type&&!g.emulateJSON&&(e.processData=!1);return i.ajax(f.extend(e,c))};g.wrapError=function(a,b,c){return function(d,e){e=d===b?e:d;a?a(b,e,c):b.trigger("error",b,e,c)}};var x=function(){},G=function(a,
b,c){var d;d=b&&b.hasOwnProperty("constructor")?b.constructor:function(){a.apply(this,arguments)};f.extend(d,a);x.prototype=a.prototype;d.prototype=new x;b&&f.extend(d.prototype,b);c&&f.extend(d,c);d.prototype.constructor=d;d.__super__=a.prototype;return d},n=function(a,b){return!a||!a[b]?null:f.isFunction(a[b])?a[b]():a[b]},t=function(){throw Error('A "url" property or function must be specified');}}).call(this);

16
api/files/css Normal file
View file

@ -0,0 +1,16 @@
/* latin */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 400;
src: local('Droid Sans'), local('DroidSans'), url(http://fonts.gstatic.com/s/droidsans/v5/s-BiyweUPV0v-yRb-cjciPk_vArhqVIZ0nv9q090hN8.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}
/* latin */
@font-face {
font-family: 'Droid Sans';
font-style: normal;
font-weight: 700;
src: local('Droid Sans Bold'), local('DroidSans-Bold'), url(http://fonts.gstatic.com/s/droidsans/v5/EFpQQyG9GqCrobXxL-KRMYWiMMZ7xLd792ULpGE4W_Y.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2212, U+2215, U+E0FF, U+EFFD, U+F000;
}

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

2
api/files/jquery-1.8.0.min.js vendored Normal file

File diff suppressed because one or more lines are too long

18
api/files/jquery.ba-bbq.min.js vendored Normal file
View file

@ -0,0 +1,18 @@
/*
* jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010
* http://benalman.com/projects/jquery-bbq-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M<N?O[P]||(R[M+1]&&isNaN(R[M+1])?{}:[]):J}}else{if($.isArray(H[P])){H[P].push(J)}else{if(H[P]!==i){H[P]=[H[P],J]}else{H[P]=J}}}}else{if(P){H[P]=F?i:""}}});return H};function z(H,F,G){if(F===i||typeof F==="boolean"){G=F;F=a[H?D:A]()}else{F=E(F)?F.replace(H?w:x,""):F}return l(F,G)}l[A]=B(z,0);l[D]=v=B(z,1);$[y]||($[y]=function(F){return $.extend(C,F)})({a:k,base:k,iframe:t,img:t,input:t,form:"action",link:k,script:t});j=$[y];function s(I,G,H,F){if(!E(H)&&typeof H!=="object"){F=H;H=G;G=i}return this.each(function(){var L=$(this),J=G||j()[(this.nodeName||"").toLowerCase()]||"",K=J&&L.attr(J)||"";L.attr(J,a[I](K,H,F))})}$.fn[A]=B(s,A);$.fn[D]=B(s,D);b.pushState=q=function(I,F){if(E(I)&&/^#/.test(I)&&F===i){F=2}var H=I!==i,G=c(p[g][k],H?I:{},H?F:2);p[g][k]=G+(/#/.test(G)?"":"#")};b.getState=u=function(F,G){return F===i||typeof F==="boolean"?v(F):v(G)[F]};b.removeState=function(F){var G={};if(F!==i){G=u();$.each($.isArray(F)?F:arguments,function(I,H){delete G[H]})}q(G,2)};e[d]=$.extend(e[d],{add:function(F){var H;function G(J){var I=J[D]=c();J.getState=function(K,L){return K===i||typeof K==="boolean"?l(I,K):l(I,L)[K]};H.apply(this,arguments)}if($.isFunction(F)){H=F;return G}else{H=F.handler;F.handler=G}}})})(jQuery,this);
/*
* jQuery hashchange event - v1.2 - 2/11/2010
* http://benalman.com/projects/jquery-hashchange-plugin/
*
* Copyright (c) 2010 "Cowboy" Ben Alman
* Dual licensed under the MIT and GPL licenses.
* http://benalman.com/about/license/
*/
(function($,i,b){var j,k=$.event.special,c="location",d="hashchange",l="href",f=$.browser,g=document.documentMode,h=f.msie&&(g===b||g<8),e="on"+d in i&&!h;function a(m){m=m||i[c][l];return m.replace(/^[^#]*#?(.*)$/,"$1")}$[d+"Delay"]=100;k[d]=$.extend(k[d],{setup:function(){if(e){return false}$(j.start)},teardown:function(){if(e){return false}$(j.stop)}});j=(function(){var m={},r,n,o,q;function p(){o=q=function(s){return s};if(h){n=$('<iframe src="javascript:0"/>').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this);

1
api/files/jquery.slideto.min.js vendored Normal file
View file

@ -0,0 +1 @@
(function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery);

8
api/files/jquery.wiggle.min.js vendored Normal file
View file

@ -0,0 +1,8 @@
/*
jQuery Wiggle
Author: WonderGroup, Jordan Thomas
URL: http://labs.wondergroup.com/demos/mini-ui/index.html
License: MIT (http://en.wikipedia.org/wiki/MIT_License)
*/
jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('<div class="wiggle-wrap"></div>').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);}
if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});};

125
api/files/reset.css Normal file
View file

@ -0,0 +1,125 @@
/* http://meyerweb.com/eric/tools/css/reset/ v2.0 | 20110126 */
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
line-height: 1;
}
ol,
ul {
list-style: none;
}
blockquote,
q {
quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}

1221
api/files/screen.css Normal file

File diff suppressed because it is too large Load diff

2765
api/files/shred.bundle.js Normal file

File diff suppressed because it is too large Load diff

211
api/files/swagger-oauth.js Normal file
View file

@ -0,0 +1,211 @@
var appName;
var popupMask;
var popupDialog;
var clientId;
var realm;
function handleLogin() {
var scopes = [];
if(window.swaggerUi.api.authSchemes
&& window.swaggerUi.api.authSchemes.oauth2
&& window.swaggerUi.api.authSchemes.oauth2.scopes) {
scopes = window.swaggerUi.api.authSchemes.oauth2.scopes;
}
if(window.swaggerUi.api
&& window.swaggerUi.api.info) {
appName = window.swaggerUi.api.info.title;
}
if(popupDialog.length > 0)
popupDialog = popupDialog.last();
else {
popupDialog = $(
[
'<div class="api-popup-dialog">',
'<div class="api-popup-title">Select OAuth2.0 Scopes</div>',
'<div class="api-popup-content">',
'<p>Scopes are used to grant an application different levels of access to data on behalf of the end user. Each API may declare one or more scopes.',
'<a href="#">Learn how to use</a>',
'</p>',
'<p><strong>' + appName + '</strong> API requires the following scopes. Select which ones you want to grant to Swagger UI.</p>',
'<ul class="api-popup-scopes">',
'</ul>',
'<p class="error-msg"></p>',
'<div class="api-popup-actions"><button class="api-popup-authbtn api-button green" type="button">Authorize</button><button class="api-popup-cancel api-button gray" type="button">Cancel</button></div>',
'</div>',
'</div>'].join(''));
$(document.body).append(popupDialog);
popup = popupDialog.find('ul.api-popup-scopes').empty();
for (i = 0; i < scopes.length; i ++) {
scope = scopes[i];
str = '<li><input type="checkbox" id="scope_' + i + '" scope="' + scope.scope + '"/>' + '<label for="scope_' + i + '">' + scope.scope;
if (scope.description) {
str += '<br/><span class="api-scope-desc">' + scope.description + '</span>';
}
str += '</label></li>';
popup.append(str);
}
}
var $win = $(window),
dw = $win.width(),
dh = $win.height(),
st = $win.scrollTop(),
dlgWd = popupDialog.outerWidth(),
dlgHt = popupDialog.outerHeight(),
top = (dh -dlgHt)/2 + st,
left = (dw - dlgWd)/2;
popupDialog.css({
top: (top < 0? 0 : top) + 'px',
left: (left < 0? 0 : left) + 'px'
});
popupDialog.find('button.api-popup-cancel').click(function() {
popupMask.hide();
popupDialog.hide();
});
popupDialog.find('button.api-popup-authbtn').click(function() {
popupMask.hide();
popupDialog.hide();
var authSchemes = window.swaggerUi.api.authSchemes;
var host = window.location;
var redirectUrl = host.protocol + '//' + host.host + "/o2c.html";
var url = null;
var p = window.swaggerUi.api.authSchemes;
for (var key in p) {
if (p.hasOwnProperty(key)) {
var o = p[key].grantTypes;
for(var t in o) {
if(o.hasOwnProperty(t) && t === 'implicit') {
var dets = o[t];
url = dets.loginEndpoint.url + "?response_type=token";
window.swaggerUi.tokenName = dets.tokenName;
}
}
}
}
var scopes = []
var o = $('.api-popup-scopes').find('input:checked');
for(k =0; k < o.length; k++) {
scopes.push($(o[k]).attr("scope"));
}
window.enabledScopes=scopes;
url += '&redirect_uri=' + encodeURIComponent(redirectUrl);
url += '&realm=' + encodeURIComponent(realm);
url += '&client_id=' + encodeURIComponent(clientId);
url += '&scope=' + encodeURIComponent(scopes);
window.open(url);
});
popupMask.show();
popupDialog.show();
return;
}
function handleLogout() {
for(key in window.authorizations.authz){
window.authorizations.remove(key)
}
window.enabledScopes = null;
$('.api-ic.ic-on').addClass('ic-off');
$('.api-ic.ic-on').removeClass('ic-on');
// set the info box
$('.api-ic.ic-warning').addClass('ic-error');
$('.api-ic.ic-warning').removeClass('ic-warning');
}
function initOAuth(opts) {
var o = (opts||{});
var errors = [];
appName = (o.appName||errors.push("missing appName"));
popupMask = (o.popupMask||$('#api-common-mask'));
popupDialog = (o.popupDialog||$('.api-popup-dialog'));
clientId = (o.clientId||errors.push("missing client id"));
realm = (o.realm||errors.push("missing realm"));
if(errors.length > 0){
log("auth unable initialize oauth: " + errors);
return;
}
$('pre code').each(function(i, e) {hljs.highlightBlock(e)});
$('.api-ic').click(function(s) {
if($(s.target).hasClass('ic-off'))
handleLogin();
else {
handleLogout();
}
false;
});
}
function onOAuthComplete(token) {
if(token) {
if(token.error) {
var checkbox = $('input[type=checkbox],.secured')
checkbox.each(function(pos){
checkbox[pos].checked = false;
});
alert(token.error);
}
else {
var b = token[window.swaggerUi.tokenName];
if(b){
// if all roles are satisfied
var o = null;
$.each($('.auth #api_information_panel'), function(k, v) {
var children = v;
if(children && children.childNodes) {
var requiredScopes = [];
$.each((children.childNodes), function (k1, v1){
var inner = v1.innerHTML;
if(inner)
requiredScopes.push(inner);
});
var diff = [];
for(var i=0; i < requiredScopes.length; i++) {
var s = requiredScopes[i];
if(window.enabledScopes && window.enabledScopes.indexOf(s) == -1) {
diff.push(s);
}
}
if(diff.length > 0){
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-on').addClass('ic-off');
$(o.parentNode).find('.api-ic.ic-on').removeClass('ic-on');
// sorry, not all scopes are satisfied
$(o).find('.api-ic').addClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
else {
o = v.parentNode;
$(o.parentNode).find('.api-ic.ic-off').addClass('ic-on');
$(o.parentNode).find('.api-ic.ic-off').removeClass('ic-off');
// all scopes are satisfied
$(o).find('.api-ic').addClass('ic-info');
$(o).find('.api-ic').removeClass('ic-warning');
$(o).find('.api-ic').removeClass('ic-error');
}
}
});
window.authorizations.add("oauth2", new ApiKeyAuthorization("Authorization", "Bearer " + b, "header"));
}
}
}
}

2315
api/files/swagger-ui.js Normal file

File diff suppressed because it is too large Load diff

1604
api/files/swagger.js Normal file

File diff suppressed because it is too large Load diff

32
api/files/underscore-min.js vendored Normal file
View file

@ -0,0 +1,32 @@
// Underscore.js 1.3.3
// (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc.
// Underscore is freely distributable under the MIT license.
// Portions of Underscore are inspired or borrowed from Prototype,
// Oliver Steele's Functional, and John Resig's Micro-Templating.
// For all details and documentation:
// http://documentcloud.github.com/underscore
(function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source==
c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break;
g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a,
c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e<f;e++){if(e in a&&c.call(d,a[e],e,a)===o)break}else for(e in a)if(b.has(a,e)&&c.call(d,a[e],e,a)===o)break};b.map=b.collect=function(a,c,b){var e=[];if(a==null)return e;if(z&&a.map===z)return a.map(c,b);j(a,function(a,g,h){e[e.length]=c.call(b,a,g,h)});if(a.length===+a.length)e.length=a.length;return e};b.reduce=b.foldl=b.inject=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(A&&
a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a,
c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b,
a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck=
function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b<e.computed&&
(e={value:a,computed:b})});return e.value};b.shuffle=function(a){var b=[],d;j(a,function(a,f){d=Math.floor(Math.random()*(f+1));b[f]=b[d];b[d]=a});return b};b.sortBy=function(a,c,d){var e=b.isFunction(c)?c:function(a){return a[c]};return b.pluck(b.map(a,function(a,b,c){return{value:a,criteria:e.call(d,a,b,c)}}).sort(function(a,b){var c=a.criteria,d=b.criteria;return c===void 0?1:d===void 0?-1:c<d?-1:c>d?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]};
j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e<f;){var g=e+f>>1;d(a[g])<d(c)?e=g+1:f=g}return e};b.toArray=function(a){return!a?[]:b.isArray(a)||b.isArguments(a)?i.call(a):a.toArray&&b.isFunction(a.toArray)?a.toArray():b.values(a)};b.size=function(a){return b.isArray(a)?a.length:b.keys(a).length};b.first=b.head=b.take=function(a,b,d){return b!=null&&!d?i.call(a,0,b):a[0]};b.initial=function(a,b,d){return i.call(a,
0,a.length-(b==null||d?1:b))};b.last=function(a,b,d){return b!=null&&!d?i.call(a,Math.max(a.length-b,0)):a[a.length-1]};b.rest=b.tail=function(a,b,d){return i.call(a,b==null||d?1:b)};b.compact=function(a){return b.filter(a,function(a){return!!a})};b.flatten=function(a,c){return b.reduce(a,function(a,e){if(b.isArray(e))return a.concat(c?e:b.flatten(e));a[a.length]=e;return a},[])};b.without=function(a){return b.difference(a,i.call(arguments,1))};b.uniq=b.unique=function(a,c,d){var d=d?b.map(a,d):a,
e=[];a.length<3&&(c=true);b.reduce(d,function(d,g,h){if(c?b.last(d)!==g||!d.length:!b.include(d,g)){d.push(g);e.push(a[h])}return d},[]);return e};b.union=function(){return b.uniq(b.flatten(arguments,true))};b.intersection=b.intersect=function(a){var c=i.call(arguments,1);return b.filter(b.uniq(a),function(a){return b.every(c,function(c){return b.indexOf(c,a)>=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=
i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e<c;e++)d[e]=b.pluck(a,""+e);return d};b.indexOf=function(a,c,d){if(a==null)return-1;var e;if(d){d=b.sortedIndex(a,c);return a[d]===c?d:-1}if(q&&a.indexOf===q)return a.indexOf(c);d=0;for(e=a.length;d<e;d++)if(d in a&&a[d]===c)return d;return-1};b.lastIndexOf=function(a,b){if(a==null)return-1;if(F&&a.lastIndexOf===F)return a.lastIndexOf(b);for(var d=a.length;d--;)if(d in a&&a[d]===b)return d;return-1};b.range=function(a,b,d){if(arguments.length<=
1){b=a||0;a=0}for(var d=arguments[2]||1,e=Math.max(Math.ceil((b-a)/d),0),f=0,g=Array(e);f<e;){g[f++]=a;a=a+d}return g};var H=function(){};b.bind=function(a,c){var d,e;if(a.bind===t&&t)return t.apply(a,i.call(arguments,1));if(!b.isFunction(a))throw new TypeError;e=i.call(arguments,2);return d=function(){if(!(this instanceof d))return a.apply(c,e.concat(i.call(arguments)));H.prototype=a.prototype;var b=new H,g=a.apply(b,e.concat(i.call(arguments)));return Object(g)===g?g:b}};b.bindAll=function(a){var c=
i.call(arguments,1);c.length==0&&(c=b.functions(a));j(c,function(c){a[c]=b.bind(a[c],a)});return a};b.memoize=function(a,c){var d={};c||(c=b.identity);return function(){var e=c.apply(this,arguments);return b.has(d,e)?d[e]:d[e]=a.apply(this,arguments)}};b.delay=function(a,b){var d=i.call(arguments,2);return setTimeout(function(){return a.apply(null,d)},b)};b.defer=function(a){return b.delay.apply(b,[a,1].concat(i.call(arguments,1)))};b.throttle=function(a,c){var d,e,f,g,h,i,j=b.debounce(function(){h=
g=false},c);return function(){d=this;e=arguments;f||(f=setTimeout(function(){f=null;h&&a.apply(d,e);j()},c));g?h=true:i=a.apply(d,e);j();g=true;return i}};b.debounce=function(a,b,d){var e;return function(){var f=this,g=arguments;d&&!e&&a.apply(f,g);clearTimeout(e);e=setTimeout(function(){e=null;d||a.apply(f,g)},b)}};b.once=function(a){var b=false,d;return function(){if(b)return d;b=true;return d=a.apply(this,arguments)}};b.wrap=function(a,b){return function(){var d=[a].concat(i.call(arguments,0));
return b.apply(this,d)}};b.compose=function(){var a=arguments;return function(){for(var b=arguments,d=a.length-1;d>=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&
c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty=
function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"};
b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a,
b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e<a;e++)b.call(d,e)};b.escape=function(a){return(""+a).replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#x27;").replace(/\//g,"&#x2F;")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId=
function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape||
u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c};
b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d,
this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this);

15
api/package.json Normal file
View file

@ -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"
}
}

View file

@ -0,0 +1,191 @@
swagger: '2.0'
info:
title: "Matrix Push Notification API"
version: "1.0.0"
host: localhost:8008
schemes:
- https
- http
basePath: /_matrix/push/%CLIENT_MAJOR_VERSION%
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

76
api/swagger.html Normal file
View file

@ -0,0 +1,76 @@
<!DOCTYPE html>
<html><head><meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Matrix Client-Server API Documentation</title>
<link href="./files/css" rel="stylesheet" type="text/css">
<link href="./files/reset.css" media="screen" rel="stylesheet" type="text/css">
<link href="./files/screen.css" media="screen" rel="stylesheet" type="text/css">
<link href="./files/reset.css" media="print" rel="stylesheet" type="text/css">
<link href="./files/screen.css" media="print" rel="stylesheet" type="text/css">
<script type="text/javascript" src="./files/shred.bundle.js"></script>
<script src="./files/jquery-1.8.0.min.js" type="text/javascript"></script>
<script src="./files/jquery.slideto.min.js" type="text/javascript"></script>
<script src="./files/jquery.wiggle.min.js" type="text/javascript"></script>
<script src="./files/jquery.ba-bbq.min.js" type="text/javascript"></script>
<script src="./files/handlebars-1.0.0.js" type="text/javascript"></script>
<script src="./files/underscore-min.js" type="text/javascript"></script>
<script src="./files/backbone-min.js" type="text/javascript"></script>
<script src="./files/swagger.js" type="text/javascript"></script>
<script src="./files/swagger-ui.js" type="text/javascript"></script>
<script src="./files/highlight.7.3.pack.js" type="text/javascript"></script>
<!-- enabling this will enable oauth2 implicit scope support -->
<script src="./files/swagger-oauth.js" type="text/javascript"></script>
<script type="text/javascript">
$(function () {
window.swaggerUi = new SwaggerUi({
url: "http://localhost:8000/client-server/api-docs",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post', 'put', 'delete'],
onComplete: function(swaggerApi, swaggerUi){
log("Loaded SwaggerUI");
if(typeof initOAuth == "function") {
initOAuth({
clientId: "your-client-id",
realm: "your-realms",
appName: "your-app-name"
});
}
$('pre code').each(function(i, e) {
hljs.highlightBlock(e)
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: "none"
});
$('#input_apiKey').change(function() {
var key = $('#input_apiKey')[0].value;
log("key: " + key);
if(key && key.trim() != "") {
log("added key " + key);
window.authorizations.add("key", new ApiKeyAuthorization("access_token", key, "query"));
}
})
window.swaggerUi.load();
});
</script>
</head>
<body class="swagger-section">
<div id="swagger-header">
<div class="swagger-ui-wrap">
<form id="api_selector">
<div class="input"><input placeholder="access_token" id="input_apiKey" name="apiKey" type="text"></div>
</form>
</div>
</div>
<div id="message-bar" class="swagger-ui-wrap message-fail">Can't read from server. It may not have the appropriate access-control-origin settings.</div>
<div id="swagger-ui-container" class="swagger-ui-wrap"></div>
</body></html>

71
api/validator.js Normal file
View file

@ -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);
}
});
};

View file

@ -1,63 +0,0 @@
/* cyrillic-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-cyrillic-ext-normal.woff2) format('woff2');
unicode-range: U+0460-052F, U+1C80-1C8A, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
}
/* cyrillic */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-cyrillic-normal.woff2) format('woff2');
unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
/* greek-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-greek-ext-normal.woff2) format('woff2');
unicode-range: U+1F00-1FFF;
}
/* greek */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-greek-normal.woff2) format('woff2');
unicode-range: U+0370-0377, U+037A-037F, U+0384-038A, U+038C, U+038E-03A1, U+03A3-03FF;
}
/* vietnamese */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-vietnamese-normal.woff2) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-latin-ext-normal.woff2) format('woff2');
unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100 900;
font-display: swap;
src: local('Inter'), url(../../fonts/Inter-latin-normal.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

View file

@ -1,30 +0,0 @@
# Fonts
## Inter.css
`Inter.css` is a local copy of
https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap, modified to pull
font files (`.woff2`) from local sources. It was created
using `download_google_fonts_css.py`.
## download_google_fonts_css.py
`download_google_fonts_css.py` is a script that takes a google fonts CSS URL, downloads
the file and linked fonts, then saves the fonts locally along with a modified CSS file to
load them. Example call:
```sh
python3 download_google_fonts_css.py \
"https://fonts.googleapis.com/css2?family=Inter:opsz,wght@14..32,100..900&display=swap" \
../../../static/fonts \
../../fonts
```
Which would pop out a `Inter.css` file that should be `@import url("Inter.css")`d
somewhere in the site's SCSS (currently in
[/assets/scss/_variables_project.scss](/assets/scss/_variables_project.scss)).
Re-running the script and committing any new files is only necessary when a desired
font updates (not very often), or we want to change the font we're using. In that case,
remove the existing font files at `/static/fonts/*.woff2` and re-run the script with a
different URL.

View file

@ -1,131 +0,0 @@
#!/usr/bin/env python3
# Copyright 2021 The Matrix.org Foundation C.I.C.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# This script takes a google fonts CSS URL, downloads the referenced fonts locally
# and spits out a modified CSS file which now points to those local fonts.
#
# Purely for the purposes of converting a website to use local font files instead of
# making requests to Google Fonts.
import re
import requests
import subprocess
import sys
# Pull the font filename to process and output directory to point at
if len(sys.argv) < 4:
print(f"""
Error: Not enough arguments.
Usage: {sys.argv[0]} google_fonts_url font_directory css_font_path
* google fonts url: A URL leading to a google font css file (i.e https://fonts.googleapis.com/css?family=Inter)
* font directory: The location that font files will be downloaded to (i.e ../../fonts)
* font path: The directory the resulting CSS will be pointing to, relative to site root (i.e /unstable/css/fonts).
This is where the browser will look for fonts.
""")
sys.exit(1)
google_fonts_url = sys.argv[1]
font_output_dir = sys.argv[2]
css_font_path = sys.argv[3]
# Get font name
if google_fonts_url.count(":") > 1:
# Account for font weights specified in the URL
# (i.e https://fonts.googleapis.com/css?family=Inter:300,300i,400,400i,700,700i)
url_match = re.match(r".*family=(.*):", google_fonts_url)
else:
url_match = re.match(r".*family=(.*)", google_fonts_url)
if not url_match:
print("Unable to extract font name, invalid google fonts URL:", google_fonts_url)
print("URL should look something like: https://fonts.googleapis.com/css?family=Inter...")
sys.exit(1)
font_name = url_match.group(1)
# Ensure font paths end with a trailing slash
if not font_output_dir.endswith("/"):
font_output_dir += "/"
if not css_font_path.endswith("/"):
css_font_path += "/"
# Download the css file and split by newline
resp = requests.get(
google_fonts_url,
# We need to set a believable user-agent, else google fonts won't give us
# all of the font weights we requested for some reason
headers={
"User-Agent": "User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:86.0) Gecko/20100101 Firefox/86.0"
}
)
if resp.status_code != 200:
print("Failed to download:", google_fonts_url, resp.status_code)
sys.exit(1)
original_contents = resp.text.split("\n")
# Download all referenced font files and write out new font file
new_css_file_lines = []
# Store metadata for helping give friendly names to each font file
font_lang = None
font_family = None
font_style = None
for line in original_contents:
# Check if this line contains a font URL
match = re.match(r".*url\((.*)\) format.*", line)
if match:
# Download the font file
font_url = match.group(1)
print("Downloading font:", font_url)
resp = requests.get(font_url)
if resp.status_code == 200:
# Save the font file
filename = "%s-%s-%s.woff2" % (
font_family, font_lang, font_style
)
font_filepath = font_output_dir + filename
with open(font_filepath, "wb") as f:
print("Writing font file:", font_filepath)
f.write(resp.content)
# Replace google URL with local URL and allow the browser to load the
# local font if it exists.
line = re.sub(r"url\(.+?\)", f"local('{font_family}'), url({css_font_path + filename})", line)
else:
print("Warning: failed to download font file:", font_url)
# Check for font metadata. If there is some, we'll note it down and use it to help
# name font files when we write them out.
# Makes for nicer font filenames than fvQtMwCp50KnMw2boKod... etc.
font_lang_match = re.match(r"^/\* (.+) \*/$", line)
if font_lang_match:
font_lang = font_lang_match.group(1)
font_family_match = re.match(r".*font-family: '(.+)';$", line)
if font_family_match:
font_family = font_family_match.group(1)
font_style_match = re.match(r".*font-style: (.+);$", line)
if font_style_match:
font_style = font_style_match.group(1)
# Append the potentially modified line to the new css file
new_css_file_lines.append(line)
# Write out the new font css file
with open(font_name + ".css", "w") as f:
new_css_file_contents = "\n".join(new_css_file_lines)
f.write(new_css_file_contents)

View file

@ -1 +0,0 @@
requests

View file

@ -1,23 +0,0 @@
# Spec diagrams
Non-ascii diagrams for the spec can be placed here for reference in the actual spec.
Please include source material so the diagram can be recreated by a future editor.
https://www.diagrams.net/ is a great ([open source](https://github.com/jgraph/drawio))
tool for these sorts of things - include your `.drawio` file next to your diagram.
Suggested settings for diagrams.net:
* Export as WebP.
* 200% size.
* `20` for a border width.
* Light appearance.
* No shadow, or grid.
To reference a diagram, use the `diagram` shortcode. For example:
```
{{% diagram name="membership" alt="Diagram presenting the possible membership state transitions" %}}
```
Where `name` is the file name without extension, and `alt` is a textual
replacement for the image, useful for accessibility.

View file

@ -1 +0,0 @@
<mxfile host="Electron" modified="2022-02-18T05:31:13.369Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/16.5.1 Chrome/96.0.4664.110 Electron/16.0.7 Safari/537.36" etag="FwSKbslSItXwNPoLLuzM" version="16.5.1" type="device"><diagram id="4a_pTli-mcEMNPq0ciXK" name="Page-1">1VvZkto4FP0aqjIPdNnyBo9peklSnZmeoWayvKQECHC3sSghN5CvHxnLeFMbyeCFh1TQ9ZUX6Zyje6/UPWO02j0SuF5+xTPk9YA22/WMux4Auu4M2H+hZR9ZHE2LDAvizrhTYhi7vxE3xm6BO0ObjCPF2KPuOmucYt9HU5qxQULwNus2x172qWu4QAXDeAq9ovWbO6PLyDoATmL/hNzFMn6ybg+jKysYO/Mv2SzhDG9TJuO+Z4wIxjT6tdqNkBcOXjwuUb+Hd64eX4wgn8p0eNas3x+fbr2x/3O79n9ao2BI+zq/zRv0Av7F7A4v2PX5S9N9PBJoxgaGNzGhS7zAPvTuE+stwYE/Q+HjNNZKfJ4wXjOjzowviNI9n2UYUMxMS7ry+NXiJ/HX2+CATFHZd3BoQLJAtMQPRH7ht6QewAfsEeEVomTPHDh++9qNo5tG1IkgD1L3LQsMyPG1OPblt/tICNynHNZsTOkm9bTn0JA8Cgw5TjhRDDs3nWr+7Ef0Brne8evg+XzDRirbIzUEiekAIxVIFRDl+m8uRQVAZeGyXTKf8RoepnnLxEQSGm+IULSTmUymPZkRAxZvbxNi67E8LVOkjv1Ec50ZPeWhskXs8xBkIAMaJr2Q0g+v7vT1utgIJNloqrFRu9GG9qAKGZvEPwCiSZ3AVhQV7Vz6Pex+Y/HWj9SVux2/86Gxjxs+G4CoE7Di9rFb2Ej6HVpxx/oBYyjLt21Vk+9GEVMAjHABVtLLOfbpA1y5Xjg5Izb+Lgrl5E+0rUdMTbN1MR2IeBf4bTMvxTsWTVhy1NPTvIu71UI948JaLU2zsybbKMy1aJo7xRjLygVsoHXGCFeqSHs0d94Dtsde4XbChsFehL+g5+EtG0+gTfbFq2HHXyTwWLpWuPZhHUw8d9oDoxBDdPpHu4shqLQaOpnVUKuNkqYkJWWTmWYoqRc5ye7zTuzfxGy/N3GnwhixcoNy5W5gti3l4MfqeuSjOx2MlXWVeU9Qll2xQX0rtixgZIPlZuTBLMw0z3O7vGYb+SJL62u2MMhtUWSF+aVWiTL1MQYM69NYx7TPU9kYiPnqlGNlbxF9Je8lKgw2INfaiYBRO0aI+QAwDBi1q4kQs0uAbNKWw3ONSZs8oC+9BsiB17TlwKtcJAe553CSvFskL/gPcrxIFckvVs4R7qm0FdQkUXC1IFihcNgAntULgANg6LUItGXWg3Fdzz3HKN8IsrVS//Y2goBwd6MDsUo1cVdM/zOh6SfkvSHqTuF5LKkrVWQkN0GljRVVcBsxaeIoRysR5CbBanRKtIVhiDRQwdUAVV3OHcs6U84bEL5irvvqY8Embqdz3eN610Su+/W7/up/s/99HgXOuK/dYQT+FpwkCHfDhSN5ZamuZHUox4uKZLRkVw3zFPlMfQA6RD4haIAINOJSU/cTSkUpbwM1p0EzMIYdAk3p11740FLTm/CNnmgSsq+48l3qANNLsFrH/pBMz9Vw53wRl6zvXIaOsgnySToOHKuRSF83c6VzKxPpnyzV5PxbTGOHBUzf7ygiDHe98J0LhUwfH16M/Vuh1QSRYiUzQbauKCJS8bnCmYRcecwQ6AcQ6Idd1/aGIaww85gvKjEXR/xwuQ8nbHhLDyW0GTOCKnpzc5lELi2dPvZRbIre2mmoEq142DPM9WzTaUKqBrnExywvuJlmqX+LJ6+LStV+DaMR6BeDgwYiX/XixdDQzCYADYbZKptuqZWQc/4trr3C8yE8m8tLfdvn5c88a301RTt1IT9zh/yiOPuCbNsb//X58fafh/92v54o+fFZcJw0F3VUDTny19rFZaWkR/H0XHUASu9uqAPQzMhbv8VzcayZ/Mlf5J784aRx/z8=</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

View file

@ -1 +0,0 @@
<mxfile host="app.diagrams.net" modified="2022-09-27T03:26:23.216Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="YZcXq9Sm_7Lqw5o2RvSU" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7ZpdU+IwFIZ/DZc6bdNWuJQCujPquOvOrl452TbQaNowIXztr9+EJv0goLCirYwyo81JGpL3PQ8nU2mBIFlcMDiOr2mESMuxokUL9FqO41i+I/7IyDKL2JbvZZERw5GKFYE7/BfpgSo6xRGaVAZySgnH42owpGmKQl6JQcbovDpsSEn1XcdwhIzAXQiJGf2NIx5n0bZzVsQvER7F+p1tv5P1JFAPVjuZxDCi81II9FsgYJTy7CpZBIhI9bQu2X2DLb35whhK+S43/PIf6YMfPSfXNzfx1WP3ez/2ToDaxwySqdrxz5ghGInYudQaJ4jgFKkt8KXWhdFpGiE5td0C3XmMObobw1D2zkUqiFjME6K6hzTlA5hgIrPgEpEZ4jiEsgMTElBC2WpS0O/Jl4jPEJMjyDnBo1T0cTpW09ypJajty4FosVURO9dZZCiiCeJsKYbo9NTWqOT0dHteOO2c+acqY+Oyz201FKr8GuWzFxaIC+XCPo742x3pHrsjAFQdcS3TEdu3Nvhhv5sfruHHNcTpsTuRq7zUHJhO5G59jBO2YcS5If0rYsPJOCsSQ7yQBq2LHAR9byDW2D2Egp2qgsDeoOAGAcG76dc25EKRKHaqSRmP6YimkPSLaLeay8WYKyrTbhV8QpwvVeWGU06rkqMF5vfi2lLXD/JafKRmrd6i1NVb6kYq9nuvJ5CN0l2yWdy2aun7Kiz9FHhOxHZv0Fz8/kETmObGyn2/bKuQiU5ZiF7QUx1tOGQjxF/LWzNNGCKQ41l1HQc33TGg6TYZGsdvGDQueDM0T9NkrMenNEU1cFRC56GM1RaOCPyDSBeGz6PVTkpeDwL5eqlsHRAw8BkAAwZgwZsBOwRIbn5u1afbjlczSs4XSnWh5O6IklMnSuZJu9cElMBaTXLdumuSewwg7Xm2awhI3o4ggTpB8gyQ+o0AyW5eTbINYb5Q+iCU/B1RcutEyXwaN2gCSu76g7K6axLoGEIlpwzBkGOaHgNje577IjiJ8+XLxi3kHLF0FXEstzkYnr2toqmEPLFObfFTSUpbfbjvTKqa/ZZisYliCB0OJ2Jp6ymaL+L/s9b878dFE+g2C6Xv1lwoQXsD3yjC/BjY3rN+fh62Ozuy7dVZYs3KcdkECF3QvNOqZQhz/LQ1BCX9ILrZj1D0KkswfWsETO6HPdcXzeJLFdlBofhuCuj/Aw==</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1 +0,0 @@
<mxfile host="app.diagrams.net" modified="2022-09-27T03:11:43.523Z" agent="5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36" etag="L_ujIRop4Jndk67DcTE9" version="14.6.7" type="device"><diagram id="_rQ0dgHO1UnHExDn0l7E" name="Page-1">7VpbU+IwFP41PMqkTS/wKDfdGXXcdWdXn5wsDTTaNkwIAvvrN6UJbYlCF1gbXcYZTU5P0ub0u6S1DdiNFxcMTcJrGuCoYYNg0YC9hm3bwLPFnzSyzCIW8NwsMmYkkLE8cEd+Y5UoozMS4GkpkVMacTIpB4c0SfCQl2KIMTovp41oVD7rBI2xFrgbokiP/iQBD7Noy/bz+CUm41Cd2fLa2ZEYqWS5kmmIAjovhGC/AbuMUp614kUXR2n1VF2ycYM3jq4vjOGEVxnww3ukD17wHF/f3IRXj52v/dA9k7O8oGgmF3wur5YvVQnmIeH4boKGaX8u7nMDdkIeR6JniSaaTrLCj8gCi3N15JSYcbx481qtdQUEdjCNMWdLkaIGQFk0BRtH9uf5PVApYaH8KobkXR+vZ84LIxqyNn9Tp5ZWFhwIoMguZTykY5qgqJ9HO4zOkiAtyapOec4VpRMZfMKcLyXq0YzTcmnxgvB70Qay/ZC2m67s9RaFQ72l6iRivfdqgrRTGJV282Grnho3ogkfoJhEaeA7iQXpbHCD5+L3NxqjZH1j03Vvv62iTHTGhnhLPaUscMTGmO/Cpw4ThiPEyUv5Oo5+022NHB0TyGED08jhn8hxTHLAiuSwayWHr7EjbvKQYRQcjIanWTxR+QlN8IEAadpuASNWNYSApu8WQWLtgEiApuF6AWnnFnGOWbKK2MAR0Qj9wlEHDZ/Hq+V2aUTZqkBw0E1/NoF2iaMXzMkQ1QOvWrUXaujqGqG9vmna2z5p7zHJ4VQkB6xVe52Po71F6QXVALIpvWAHQj6O9FZFV63OroOrZ4L0Qsc06XVP0ntMcrgVyeHUKr3ux5He/ba99ifd9laF13YVOgNNB/pSIitDbjXdOWNoWUiYUJLwaeFst2kgP5WjZpSCp577B2/kW7C1LV80sivYGK0uh45GU1GYTYKsS7A/Z3TK9E0wFMeCTdcwS3FOlnJMznsVOe/Wain6e0ZjLWWv3bz9SXfzVdF16G5+L/dwwYa27XAPG7S25Zfd41jO4GnIHxjhDG3THjXgyReOyVy/InO9Wn1BZ0fcFK4w5IQmRjvDf/+KvSq+Dn2LuJ8ztMrOoH04sOkMbbgt/984g/7fpQsTnME17v2/fZASgJMzlOvZrshcv1ZnAK84Aw4If7ddwrvq/puKXcEQpkIeSDL+vloMyAOi54LXbbCA8wp2UgP2an1abWvQuzRBmD3XNGG2tLKchPkAcqgPQneyo10nOyxdmb+YQA///T6oFN38o9ZsO5h/Gwz7fwA=</diagram></mxfile>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
<style>
path { fill: #000000; }
@media (prefers-color-scheme: dark) {
path { fill: #ffffff; }
}
</style>
<path d="M 30,2.0000001 V 30 h -1 -2 v 2 h 5 V -3.3333334e-8 L 27,0 v 2 z"/>
<path d="M 9.9515939,10.594002 V 12.138 h 0.043994 c 0.3845141,-0.563728 0.8932271,-1.031728 1.4869981,-1.368 0.580003,-0.322998 1.244999,-0.485 1.993002,-0.485 0.72,0 1.376999,0.139993 1.971998,0.42 0.595,0.279004 1.047001,0.771001 1.355002,1.477001 0.338003,-0.500001 0.795999,-0.941 1.376999,-1.323001 0.579999,-0.382998 1.265998,-0.574 2.059998,-0.574 0.602003,0 1.160002,0.074 1.674002,0.220006 0.514,0.148006 0.953998,0.382998 1.321999,0.706998 0.36601,0.322999 0.653001,0.746 0.859,1.268002 0.205001,0.521998 0.307994,1.15 0.307994,1.887001 v 7.632997 h -3.127 v -6.463997 c 0,-0.383002 -0.01512,-0.743002 -0.04399,-1.082003 -0.02079,-0.3072 -0.103219,-0.607113 -0.242003,-0.881998 -0.133153,-0.25081 -0.335962,-0.457777 -0.584001,-0.596002 -0.257008,-0.146003 -0.605998,-0.220006 -1.046997,-0.220006 -0.440002,0 -0.796003,0.085 -1.068,0.253002 -0.272013,0.170003 -0.485001,0.390002 -0.639001,0.662003 -0.159119,0.287282 -0.263585,0.601602 -0.307994,0.926997 -0.05197,0.346923 -0.07801,0.697217 -0.07801,1.048002 v 6.353999 h -3.128005 v -6.398 c 0,-0.338003 -0.0072,-0.673001 -0.02116,-1.004001 -0.01134,-0.313663 -0.07487,-0.623229 -0.187994,-0.915999 -0.107943,-0.276623 -0.300435,-0.512126 -0.550001,-0.673001 -0.25799,-0.168 -0.636,-0.253002 -1.134999,-0.253002 -0.198123,0.0083 -0.394383,0.04195 -0.584002,0.100006 -0.258368,0.07446 -0.498455,0.201827 -0.704999,0.373985 -0.227981,0.183987 -0.421999,0.449 -0.583997,0.794003 -0.161008,0.345978 -0.242003,0.797998 -0.242003,1.356998 v 6.618999 H 6.99942 V 10.590001 Z"/>
<path d="M 2,2.0000001 V 30 h 3 v 2 H 0 V 9.2650922e-8 L 5,0 v 2 z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

Some files were not shown because too many files have changed in this diff Show more