Merge remote-tracking branch 'remotes/origin/master'
commit
66268260c7
@ -1,39 +1,194 @@
|
||||
This repository contains the documentation for Matrix.
|
||||
This repository contains the Matrix specification.
|
||||
|
||||
Structure
|
||||
=========
|
||||
If you want to ask more about the specification, join us on
|
||||
`#matrix-dev:matrix.org <http://matrix.to/#/#matrix-dev:matrix.org>`_.
|
||||
|
||||
We welcome contributions to the spec! See the notes below on `Building the
|
||||
specification`_, and `<CONTRIBUTING.rst>`_ to get started making contributions.
|
||||
|
||||
Note that the Matrix Project lists, which were previously kept in this
|
||||
repository, are now in https://github.com/matrix-org/matrix.org.
|
||||
|
||||
- ``api`` : Contains the HTTP API specification.
|
||||
- ``attic``: Contains historical sections of specification for reference
|
||||
Structure of this repository
|
||||
============================
|
||||
|
||||
- ``api`` : `OpenAPI`_ (swagger) specifications for the the HTTP APIs.
|
||||
- ``attic``: historical sections of specification for reference
|
||||
purposes.
|
||||
- ``changelogs``: Contains change logs for the various parts of the
|
||||
- ``changelogs``: change logs for the various parts of the
|
||||
specification.
|
||||
- ``drafts``: Previously, contained documents which were under discussion for
|
||||
future incusion into the specification and/or supporting documentation. This
|
||||
is now historical, as we use separate discussion documents (see
|
||||
`<CONTRIBUTING.rst>`_).
|
||||
- ``event-schemas``: Contains the `JSON Schema`_ for all Matrix events
|
||||
- ``event-schemas``: 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
|
||||
- ``meta``: documents outlining the processes involved when writing
|
||||
documents, e.g. documentation style, guidelines.
|
||||
- ``scripts``: Contains scripts to generate formatted versions of the
|
||||
- ``scripts``: 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.
|
||||
- ``specification``: the specification split up into sections.
|
||||
|
||||
Contributing
|
||||
============
|
||||
.. _OpenAPI: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/2.0.md
|
||||
.. _JSON Schema: http://json-schema.org/
|
||||
|
||||
Known issues with the specification are represented as JIRA issues at
|
||||
`<https://matrix.org/jira/browse/SPEC>`_.
|
||||
Building the specification
|
||||
==========================
|
||||
|
||||
If you want to ask more about the specification, join us on
|
||||
`#matrix-dev:matrix.org <http://matrix.to/#/#matrix-dev:matrix.org>`_.
|
||||
The Matrix Spec is generated by a set of scripts, from the RST documents, API
|
||||
specs and event schemas in this repository.
|
||||
|
||||
If you would like to contribute to the specification or supporting
|
||||
documentation, see `<CONTRIBUTING.rst>`_.
|
||||
Preparation
|
||||
-----------
|
||||
|
||||
.. _JSON Schema: http://json-schema.org/
|
||||
To use the scripts, it is best to create a Python virtualenv as follows::
|
||||
|
||||
virtualenv env
|
||||
env/bin/pip install -r scripts/requirements.txt
|
||||
|
||||
(Benjamin Synders has contributed a script for `Nix`_ users, which can be
|
||||
invoked with ``nix-shell scripts/contrib/shell.nix``.)
|
||||
|
||||
.. TODO: Possibly we need some libs installed; should record what they are.
|
||||
|
||||
.. _`Nix`: https://nixos.org/nix/
|
||||
|
||||
Generating the specification
|
||||
----------------------------
|
||||
|
||||
To rebuild the specification, use ``scripts/gendoc.py``::
|
||||
|
||||
source env/bin/activate
|
||||
./scripts/gendoc.py
|
||||
|
||||
The above will write the rendered version of the specification to
|
||||
``scripts/gen``. To view it, point your browser at ``scripts/gen/index.html``.
|
||||
|
||||
Generating the OpenAPI (Swagger) specs
|
||||
--------------------------------------
|
||||
|
||||
`Swagger`_ is a framework for representing RESTful APIs. We use it to generate
|
||||
interactive documentation for our APIs.
|
||||
|
||||
Before the Swagger docs can be used in the Swagger UI (or other tool expecting
|
||||
a Swagger specs, they must be combined into a single json file. This can be
|
||||
done as follows::
|
||||
|
||||
source env/bin/activate
|
||||
./dump-swagger.py
|
||||
|
||||
By default, ``dump-swagger`` will write to ``scripts/swagger/api-docs.json``.
|
||||
|
||||
To make use of the generated file, there are a number of options:
|
||||
|
||||
* It can be uploaded from your filesystem to an online editor/viewer such as
|
||||
http://editor.swagger.io/
|
||||
* You can run a local HTTP server by running
|
||||
``./scripts/swagger-http-server.py``, and then view the documentation via an
|
||||
online viewer; for example, at
|
||||
http://petstore.swagger.io/?url=http://localhost:8000/api-docs.json
|
||||
* You can host the swagger UI yourself. See
|
||||
https://github.com/swagger-api/swagger-ui#how-to-run for advice on how to do
|
||||
so.
|
||||
|
||||
.. _`Swagger`: http://swagger.io/
|
||||
|
||||
Continuserv
|
||||
-----------
|
||||
|
||||
Continuserv is a script which will rebuild the specification every time a file
|
||||
is changed, and will serve it to a browser over HTTP. It is intended for use by
|
||||
specification authors, so that they can quickly see the effects of their
|
||||
changes.
|
||||
|
||||
It is written in Go, so you will need the ``go`` compiler installed on your
|
||||
computer. You will also need to install fsnotify by running::
|
||||
|
||||
go get gopkg.in/fsnotify.v1
|
||||
|
||||
Then, create a virtualenv as described above under `Preparation`_,
|
||||
and::
|
||||
|
||||
source env/bin/activate
|
||||
go run ./scripts/continuserv/main.go
|
||||
|
||||
You will then be able to view the generated spec by visiting
|
||||
http://localhost:8000/index.html.
|
||||
|
||||
Issue tracking
|
||||
==============
|
||||
|
||||
Issues with the Matrix specification are tracked in `GitHub
|
||||
<https://github.com/matrix-org/matrix-doc/issues>`_.
|
||||
|
||||
The following labels are used to help categorize issues:
|
||||
|
||||
`spec-omission <https://github.com/matrix-org/matrix-doc/labels/spec-omission>`_
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Things which have been implemented but not currently specified. These may range
|
||||
from entire API endpoints, to particular options or return parameters.
|
||||
|
||||
Issues with this label will have been implemented in `Synapse
|
||||
<https://github.com/matrix-org/synapse>`_. Normally there will be a design
|
||||
document in Google Docs or similar which describes the feature.
|
||||
|
||||
Examples:
|
||||
|
||||
* `Spec PUT /directory/list <https://github.com/matrix-org/matrix-doc/issues/417>`_
|
||||
* `Unspec'd server_name request param for /join/{roomIdOrAlias}
|
||||
<https://github.com/matrix-org/matrix-doc/issues/904>`_
|
||||
|
||||
`clarification <https://github.com/matrix-org/matrix-doc/labels/clarification>`_
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
An area where the spec could do with being more explicit.
|
||||
|
||||
Examples:
|
||||
|
||||
* `Spec the implicit limit on /syncs
|
||||
<https://github.com/matrix-org/matrix-doc/issues/708>`_
|
||||
|
||||
* `Clarify the meaning of the currently_active flags in presence events
|
||||
<https://github.com/matrix-org/matrix-doc/issues/686>`_
|
||||
|
||||
`spec-bug <https://github.com/matrix-org/matrix-doc/labels/spec-bug>`_
|
||||
----------------------------------------------------------------------
|
||||
|
||||
Something which is in the spec, but is wrong.
|
||||
|
||||
Note: this is *not* for things that are badly designed or don't work well
|
||||
(for which see 'improvement' or 'feature') - it is for places where the
|
||||
spec doesn't match reality.
|
||||
|
||||
Examples:
|
||||
|
||||
* `swagger is wrong for directory PUT
|
||||
<https://github.com/matrix-org/matrix-doc/issues/933>`_
|
||||
|
||||
* `receipts section still refers to initialSync
|
||||
<https://github.com/matrix-org/matrix-doc/issues/695>`_
|
||||
|
||||
`improvement <https://github.com/matrix-org/matrix-doc/labels/improvement>`_
|
||||
----------------------------------------------------------------------------
|
||||
|
||||
A suggestion for a relaatively simple improvement to the protocol.
|
||||
|
||||
Examples:
|
||||
|
||||
* `We need a 'remove 3PID' API so that users can remove mappings
|
||||
<https://github.com/matrix-org/matrix-doc/issues/620>`_
|
||||
* `We should mandate that /publicRooms requires an access_token
|
||||
<https://github.com/matrix-org/matrix-doc/issues/612>`_
|
||||
|
||||
`feature <https://github.com/matrix-org/matrix-doc/labels/feature>`_
|
||||
--------------------------------------------------------------------
|
||||
|
||||
A suggestion for a significant extension to the matrix protocol which
|
||||
needs considerable consideration before implementation.
|
||||
|
||||
Examples:
|
||||
|
||||
* `Peer-to-peer Matrix <https://github.com/matrix-org/matrix-doc/issues/710>`_
|
||||
* `Specify a means for clients to "edit" previous messages
|
||||
<https://github.com/matrix-org/matrix-doc/issues/682>`_
|
||||
|
@ -1,3 +1,2 @@
|
||||
This directory contains swagger-compatible representations of our APIs. See
|
||||
scripts/README.md for details on how to make use of them.
|
||||
|
||||
the main README.rst for details on how to make use of them.
|
||||
|
@ -1,52 +0,0 @@
|
||||
State Resolution
|
||||
================
|
||||
This section describes why we need state resolution and how it works.
|
||||
|
||||
|
||||
Motivation
|
||||
-----------
|
||||
We want to be able to associate some shared state with rooms, e.g. a room name
|
||||
or members list. This is done by having a current state dictionary that maps
|
||||
from the pair event type and state key to an event.
|
||||
|
||||
However, since the servers involved in the room are distributed we need to be
|
||||
able to handle the case when two (or more) servers try and update the state at
|
||||
the same time. This is done via the state resolution algorithm.
|
||||
|
||||
|
||||
State Tree
|
||||
------------
|
||||
State events contain a reference to the state it is trying to replace. These
|
||||
relations form a tree where the current state is one of the leaf nodes.
|
||||
|
||||
Note that state events are events, and so are part of the PDU graph. Thus we
|
||||
can be sure that (modulo the internet being particularly broken) we will see
|
||||
all state events eventually.
|
||||
|
||||
|
||||
Algorithm requirements
|
||||
----------------------
|
||||
We want the algorithm to have the following properties:
|
||||
|
||||
- Since we aren't guaranteed what order we receive state events in, except that
|
||||
we see parents before children, the state resolution algorithm must not depend
|
||||
on the order and must always come to the same result.
|
||||
- If we receive a state event whose parent is the current state, then the
|
||||
algorithm will select it.
|
||||
- The algorithm does not depend on internal state, ensuring all servers should
|
||||
come to the same decision.
|
||||
|
||||
These three properties mean it is enough to keep track of the current state and
|
||||
compare it with any new proposed state, rather than having to keep track of all
|
||||
the leafs of the tree and recomputing across the entire state tree.
|
||||
|
||||
|
||||
Current Implementation
|
||||
----------------------
|
||||
The current implementation works as follows: Upon receipt of a newly proposed
|
||||
state change we first find the common ancestor. Then we take the maximum
|
||||
across each branch of the users' power levels, if one is higher then it is
|
||||
selected as the current state. Otherwise, we check if one chain is longer than
|
||||
the other, if so we choose that one. If that also fails, then we concatenate
|
||||
all the pdu ids and take a SHA1 hash and compare them to select a common
|
||||
ancestor.
|
@ -0,0 +1,12 @@
|
||||
{
|
||||
"age": 242352,
|
||||
"content": {
|
||||
"pinned": ["$someevent:localhost"]
|
||||
},
|
||||
"state_key": "",
|
||||
"origin_server_ts": 1431961217939,
|
||||
"event_id": "$WLGTSEFSEF:localhost",
|
||||
"type": "m.room.pinned_events",
|
||||
"room_id": "!Cuyf34gef24t:localhost",
|
||||
"sender": "@example:localhost"
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
---
|
||||
allOf:
|
||||
- $ref: core-event-schema/state_event.yaml
|
||||
description: This event is used to "pin" particular events in a room for other participants to review later. The order of the pinned events is guaranteed and based upon the order supplied in the event. Clients should be aware that the current user may not be able to see some of the events pinned due to visibility settings in the room. Clients are responsible for determining if a particular event in the pinned list is displayable, and have the option to not display it if it cannot be pinned in the client.
|
||||
properties:
|
||||
content:
|
||||
properties:
|
||||
pinned:
|
||||
description: An ordered list of event IDs to pin.
|
||||
items:
|
||||
type: string
|
||||
type: array
|
||||
required:
|
||||
- pinned
|
||||
type: object
|
||||
state_key:
|
||||
description: A zero-length string.
|
||||
pattern: '^$'
|
||||
type: string
|
||||
type:
|
||||
enum:
|
||||
- m.room.pinned_events
|
||||
type: string
|
||||
title: Pinned events in a room
|
||||
type: object
|
@ -1,45 +0,0 @@
|
||||
Generating the HTML for the specification
|
||||
=========================================
|
||||
|
||||
Requirements:
|
||||
- docutils (for converting RST to HTML)
|
||||
- Jinja2 (for templating)
|
||||
- PyYAML (for reading YAML files)
|
||||
|
||||
Nix[2] users can enter an environment with the appropriate tools and
|
||||
dependencies available by invoking `nix-shell contrib/shell.nix` in this
|
||||
directory.
|
||||
|
||||
To generate the complete specification along with supporting documentation, run:
|
||||
python gendoc.py
|
||||
|
||||
The output of this will be inside the "scripts/gen" folder.
|
||||
|
||||
Matrix.org only ("gen" folder has matrix.org tweaked pages):
|
||||
./matrix-org-gendoc.sh /path/to/matrix.org/includes/nav.html
|
||||
|
||||
|
||||
Generating the Swagger documentation
|
||||
====================================
|
||||
Swagger[1] is a framework for representing RESTful APIs. We use it to generate
|
||||
interactive documentation for our APIs.
|
||||
|
||||
Swagger UI reads a JSON description of the API. To generate this file from the
|
||||
YAML files in the `api` folder, run:
|
||||
./dump-swagger.py
|
||||
|
||||
By default, `dump-swagger` will write to `scripts/swagger/api-docs.json`.
|
||||
|
||||
To make use of the generated file, there are a number of options:
|
||||
* It can be uploaded from your filesystem to an online editor/viewer such as
|
||||
http://editor.swagger.io/
|
||||
* You can run a local HTTP server by running `./swagger-http-server.py`, and
|
||||
then view the documentation via an online viewer; for example, at
|
||||
http://petstore.swagger.io/?url=http://localhost:8000/api-docs.json
|
||||
* You can host the swagger UI yourself:
|
||||
* download the latest release from https://github.com/swagger-api/swagger-ui
|
||||
* copy the contents of the 'dist' directory alongside `api-docs.json`
|
||||
* View the UI via your browser at http://\<hostname>?url=api-docs.json
|
||||
|
||||
[1] http://swagger.io/
|
||||
[2] https://nixos.org/nix/
|
@ -0,0 +1,8 @@
|
||||
# no doubt older versions would be fine for many of these but these were
|
||||
# current at the time of writing
|
||||
|
||||
docutils >= 0.14
|
||||
pygments >= 2.2.0
|
||||
Jinja2 >= 2.9.6
|
||||
jsonschema >= 2.6.0
|
||||
PyYAML >= 3.12
|
@ -0,0 +1,13 @@
|
||||
{% import 'tables.tmpl' as tables -%}
|
||||
|
||||
{{common_event.title}} Fields
|
||||
{{(7 + common_event.title | length) * title_kind}}
|
||||
|
||||
{{common_event.desc}}
|
||||
|
||||
{% for table in common_event.tables %}
|
||||
{{"``"+table.title+"``" if table.title else "" }}
|
||||
|
||||
{{ tables.paramtable(table.rows, ["Key", "Type", "Description"]) }}
|
||||
|
||||
{% endfor %}
|
@ -0,0 +1,225 @@
|
||||
.. Copyright 2016 Openmarket Ltd.
|
||||
.. Copyright 2017 New Vector 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.
|
||||
|
||||
Identifier Grammar
|
||||
------------------
|
||||
|
||||
Server Name
|
||||
~~~~~~~~~~~
|
||||
|
||||
A homeserver is uniquely identified by its server name. This value is used in a
|
||||
number of identifiers, as described below.
|
||||
|
||||
The server name represents the address at which the homeserver in question can
|
||||
be reached by other homeservers. The complete grammar is::
|
||||
|
||||
server_name = dns_name [ ":" port]
|
||||
dns_name = host
|
||||
port = *DIGIT
|
||||
|
||||
where ``host`` is as defined by `RFC3986, section 3.2.2
|
||||
<https://tools.ietf.org/html/rfc3986#section-3.2.2>`_.
|
||||
|
||||
Examples of valid server names are:
|
||||
|
||||
* ``matrix.org``
|
||||
* ``matrix.org:8888``
|
||||
* ``1.2.3.4`` (IPv4 literal)
|
||||
* ``1.2.3.4:1234`` (IPv4 literal with explicit port)
|
||||
* ``[1234:5678::abcd]`` (IPv6 literal)
|
||||
* ``[1234:5678::abcd]:5678`` (IPv6 literal with explicit port)
|
||||
|
||||
|
||||
Common Identifier Format
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The Matrix protocol uses a common format to assign unique identifiers to a
|
||||
number of entities, including users, events and rooms. Each identifier takes
|
||||
the form::
|
||||
|
||||
&localpart:domain
|
||||
|
||||
where ``&`` represents a 'sigil' character; ``domain`` is the `server name`_ of
|
||||
the homeserver which allocated the identifier, and ``localpart`` is an
|
||||
identifier allocated by that homeserver.
|
||||
|
||||
The sigil characters are as follows:
|
||||
|
||||
* ``@``: User ID
|
||||
* ``!``: Room ID
|
||||
* ``$``: Event ID
|
||||
* ``#``: Room alias
|
||||
|
||||
The precise grammar defining the allowable format of an identifier depends on
|
||||
the type of identifier.
|
||||
|
||||
User Identifiers
|
||||
++++++++++++++++
|
||||
|
||||
Users within Matrix are uniquely identified by their Matrix user ID. The user
|
||||
ID is namespaced to the homeserver which allocated the account and has the
|
||||
form::
|
||||
|
||||
@localpart:domain
|
||||
|
||||
The ``localpart`` of a user ID is an opaque identifier for that user. It MUST
|
||||
NOT be empty, and MUST contain only the characters ``a-z``, ``0-9``, ``.``,
|
||||
``_``, ``=``, ``-``, and ``/``.
|
||||
|
||||
The ``domain`` of a user ID is the `server name`_ of the homeserver which
|
||||
allocated the account.
|
||||
|
||||
The length of a user ID, including the ``@`` sigil and the domain, MUST NOT
|
||||
exceed 255 characters.
|
||||
|
||||
The complete grammar for a legal user ID is::
|
||||
|
||||
user_id = "@" user_id_localpart ":" server_name
|
||||
user_id_localpart = 1*user_id_char
|
||||
user_id_char = DIGIT
|
||||
/ %x61-7A ; a-z
|
||||
/ "-" / "." / "=" / "_" / "/"
|
||||
|
||||
.. admonition:: Rationale
|
||||
|
||||
A number of factors were considered when defining the allowable characters
|
||||
for a user ID.
|
||||
|
||||
Firstly, we chose to exclude characters outside the basic US-ASCII character
|
||||
set. User IDs are primarily intended for use as an identifier at the protocol
|
||||
level, and their use as a human-readable handle is of secondary
|
||||
benefit. Furthermore, they are useful as a last-resort differentiator between
|
||||
users with similar display names. Allowing the full unicode character set
|
||||
would make very difficult for a human to distinguish two similar user IDs. The
|
||||
limited character set used has the advantage that even a user unfamiliar with
|
||||
the Latin alphabet should be able to distinguish similar user IDs manually, if
|
||||
somewhat laboriously.
|
||||
|
||||
We chose to disallow upper-case characters because we do not consider it
|
||||
valid to have two user IDs which differ only in case: indeed it should be
|
||||
possible to reach ``@user:matrix.org`` as ``@USER:matrix.org``. However,
|
||||
user IDs are necessarily used in a number of situations which are inherently
|
||||
case-sensitive (notably in the ``state_key`` of ``m.room.member``
|
||||
events). Forbidding upper-case characters (and requiring homeservers to
|
||||
downcase usernames when creating user IDs for new users) is a relatively simple
|
||||
way to ensure that ``@USER:matrix.org`` cannot refer to a different user to
|
||||
``@user:matrix.org``.
|
||||
|
||||
Finally, we decided to restrict the allowable punctuation to a very basic set
|
||||
to reduce the possibility of conflicts with special characters in various
|
||||
situations. For example, "*" is used as a wildcard in some APIs (notably the
|
||||
filter API), so it cannot be a legal user ID character.
|
||||
|
||||
The length restriction is derived from the limit on the length of the
|
||||
``sender`` key on events; since the user ID appears in every event sent by the
|
||||
user, it is limited to ensure that the user ID does not dominate over the actual
|
||||
content of the events.
|
||||
|
||||
Matrix user IDs are sometimes informally referred to as MXIDs.
|
||||
|
||||
Historical User IDs
|
||||
<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
Older versions of this specification were more tolerant of the characters
|
||||
permitted in user ID localparts. There are currently active users whose user
|
||||
IDs do not conform to the permitted character set, and a number of rooms whose
|
||||
history includes events with a ``sender`` which does not conform. In order to
|
||||
handle these rooms successfully, clients and servers MUST accept user IDs with
|
||||
localparts from the expanded character set::
|
||||
|
||||
extended_user_id_char = %x21-7E
|
||||
|
||||
Mapping from other character sets
|
||||
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
|
||||
In certain circumstances it will be desirable to map from a wider character set
|
||||
onto the limited character set allowed in a user ID localpart. Examples include
|
||||
a homeserver creating a user ID for a new user based on the username passed to
|
||||
``/register``, or a bridge mapping user ids from another protocol.
|
||||
|
||||
.. TODO-spec
|
||||
|
||||
We need to better define the mechanism by which homeservers can allow users
|
||||
to have non-Latin login credentials. The general idea is for clients to pass
|
||||
the non-Latin in the ``username`` field to ``/register`` and ``/login``, and
|
||||
the HS then maps it onto the MXID space when turning it into the
|
||||
fully-qualified ``user_id`` which is returned to the client and used in
|
||||
events.
|
||||
|
||||
Implementations are free to do this mapping however they choose. Since the user
|
||||
ID is opaque except to the implementation which created it, the only
|
||||
requirement is that the implemention can perform the mapping
|
||||
consistently. However, we suggest the following algorithm:
|
||||
|
||||
1. Encode character strings as UTF-8.
|
||||
|
||||
2. Convert the bytes ``A-Z`` to lower-case.
|
||||
|
||||
* In the case where a bridge must be able to distinguish two different users
|
||||
with ids which differ only by case, escape upper-case characters by
|
||||
prefixing with ``_`` before downcasing. For example, ``A`` becomes
|
||||
``_a``. Escape a real ``_`` with a second ``_``.
|
||||
|
||||
3. Encode any remaining bytes outside the allowed character set, as well as
|
||||
``=``, as their hexadecimal value, prefixed with ``=``. For example, ``#``
|
||||
becomes ``=23``; ``á`` becomes ``=c3=a1``.
|
||||
|
||||
.. admonition:: Rationale
|
||||
|
||||
The suggested mapping is an attempt to preserve human-readability of simple
|
||||
ASCII identifiers (unlike, for example, base-32), whilst still allowing
|
||||
representation of *any* character (unlike punycode, which provides no way to
|
||||
encode ASCII punctuation).
|
||||
|
||||
|
||||
Room IDs and Event IDs
|
||||
++++++++++++++++++++++
|
||||
|
||||
A room has exactly one room ID. A room ID has the format::
|
||||
|
||||
!opaque_id:domain
|
||||
|
||||
An event has exactly one event ID. An event ID has the format::
|
||||
|
||||
$opaque_id:domain
|
||||
|
||||
The ``domain`` of a room/event ID is the `server name`_ of the homeserver which
|
||||
created the room/event. The domain is used only for namespacing to avoid the
|
||||
risk of clashes of identifiers between different homeservers. There is no
|
||||
implication that the room or event in question is still available at the
|
||||
corresponding homeserver.
|
||||
|
||||
Event IDs and Room IDs are case-sensitive. They are not meant to be human
|
||||
readable.
|
||||
|
||||
.. TODO-spec
|
||||
What is the grammar for the opaque part? https://matrix.org/jira/browse/SPEC-389
|
||||
|
||||
Room Aliases
|
||||
++++++++++++
|
||||
|
||||
A room may have zero or more aliases. A room alias has the format::
|
||||
|
||||
#room_alias:domain
|
||||
|
||||
The ``domain`` of a room alias is the `server name`_ of the homeserver which
|
||||
created the alias. Other servers may contact this homeserver to look up the
|
||||
alias.
|
||||
|
||||
Room aliases MUST NOT exceed 255 bytes (including the ``#`` sigil and the
|
||||
domain).
|
||||
|
||||
.. TODO-spec
|
||||
- Need to specify precise grammar for Room Aliases. https://matrix.org/jira/browse/SPEC-391
|
@ -1,129 +0,0 @@
|
||||
Application Services
|
||||
====================
|
||||
|
||||
This file contains examples of some application service
|
||||
|
||||
IRC Bridge
|
||||
----------
|
||||
Pre-conditions:
|
||||
- Server admin stores the AS token "T_a" on the homeserver.
|
||||
- Homeserver has a token "T_h".
|
||||
- Homeserver has the domain "hsdomain.com"
|
||||
|
||||
1. Application service registration
|
||||
|
||||
::
|
||||
|
||||
AS -> HS: Registers itself with the homeserver
|
||||
POST /register
|
||||
{
|
||||
url: "https://someapp.com/matrix",
|
||||
as_token: "T_a",
|
||||
namespaces: {
|
||||
users: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "@irc\.freenode\.net/.*"
|
||||
}
|
||||
],
|
||||
aliases: [
|
||||
{
|
||||
"exclusive": true,
|
||||
"regex": "#irc\.freenode\.net/.*"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Returns 200 OK:
|
||||
{
|
||||
hs_token: "T_h"
|
||||
}
|
||||
|
||||
2. IRC user "Bob" says "hello?" on "#matrix" at timestamp 1421416883133:
|
||||
|
||||
::
|
||||
|
||||
- AS stores message as potential scrollback.
|
||||
- Nothing happens as no Matrix users are in the room.
|
||||
|
||||
3. Matrix user "@alice:hsdomain.com" wants to join "#matrix":
|
||||
|
||||
::
|
||||
|
||||
User -> HS: Request to join "#irc.freenode.net/#matrix:hsdomain.com"
|
||||
|
||||
HS -> AS: Room Query "#irc.freenode.net/#matrix:hsdomain.com"
|
||||
GET /rooms/%23irc.freenode.net%2F%23matrix%3Ahsdomain.com?access_token=T_h
|
||||
[Starts blocking]
|
||||
AS -> HS: Creates room. Gets room ID "!aasaasasa:hsdomain.com".
|
||||
AS -> HS: Sets room name to "#matrix".
|
||||
AS -> HS: Sends message as ""@irc.freenode.net/Bob:hsdomain.com"
|
||||
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
|
||||
?access_token=T_a
|
||||
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
|
||||
&ts=1421416883133
|
||||
{
|
||||
body: "hello?"
|
||||
msgtype: "m.text"
|
||||
}
|
||||
HS -> AS: User Query "@irc.freenode.net/Bob:hsdomain.com"
|
||||
GET /users/%40irc.freenode.net%2FBob%3Ahsdomain.com?access_token=T_h
|
||||
[Starts blocking]
|
||||
AS -> HS: Creates user using CS API extension.
|
||||
POST /register?access_token=T_a
|
||||
{
|
||||
type: "m.login.application_service",
|
||||
user: "irc.freenode.net/Bob"
|
||||
}
|
||||
AS -> HS: Set user display name to "Bob".
|
||||
[Finishes blocking]
|
||||
[Finished blocking]
|
||||
|
||||
- HS sends room information back to client.
|
||||
|
||||
4. @alice:hsdomain.com says "hi!" in this room:
|
||||
|
||||
::
|
||||
|
||||
User -> HS: Send message "hi!" in room !aasaasasa:hsdomain.com
|
||||
|
||||
- HS sends message.
|
||||
- HS sees the room ID is in the AS namespace and pushes it to the AS.
|
||||
|
||||
HS -> AS: Push event
|
||||
PUT /transactions/1?access_token=T_h
|
||||
{
|
||||
events: [
|
||||
{
|
||||
content: {
|
||||
body: "hi!",
|
||||
msgtype: "m.text"
|
||||
},
|
||||
origin_server_ts: <generated by hs>,
|
||||
user_id: "@alice:hsdomain.com",
|
||||
room_id: "!aasaasasa:hsdomain.com",
|
||||
type: "m.room.message"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
- AS passes this through to IRC.
|
||||
|
||||
|
||||
5. IRC user "Bob" says "what's up?" on "#matrix" at timestamp 1421418084816:
|
||||
|
||||
::
|
||||
|
||||
IRC -> AS: "what's up?"
|
||||
AS -> HS: Send message via CS API extension
|
||||
PUT /rooms/%21aasaasasa%3Ahsdomain.com/send/m.room.message
|
||||
?access_token=T_a
|
||||
&user_id=%40irc.freenode.net%2FBob%3Ahsdomain.com
|
||||
&ts=1421418084816
|
||||
{
|
||||
body: "what's up?"
|
||||
msgtype: "m.text"
|
||||
}
|
||||
|
||||
- HS modifies the user_id and origin_server_ts on the event and sends it.
|
@ -1,141 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: Getting involved
|
||||
categories: guides
|
||||
---
|
||||
|
||||
# How can I get involved?
|
||||
Matrix is an ecosystem consisting of several apps written by lots of people. We at Matrix.org have written one server and a few clients, and people in the community have also written several clients, servers, and Application Services. We are collecting [a list of all known Matrix-apps](https://matrix.org/blog/try-matrix-now/).
|
||||
|
||||
|
|
||||
|
||||
You have a few options when it comes to getting involved: if you just want to use Matrix, you can [register an account on a public server using a public webclient](#reg). If you have a virtual private server (VPS) or similar, you might want to [run a server and/or client yourself](#run). If you want to look under the hood, you can [checkout the code and modify it - or write your own client or server](#checkout). Or you can write an [Application Service](#as), for example a bridge to an existing ecosystem.
|
||||
|
||||
|
|
||||
|
||||
We very much welcome [contributions](https://github.com/matrix-org/synapse/blob/master/CONTRIBUTING.rst) to any of our projects, which you can find in our [github space](https://github.com/matrix-org/).
|
||||
|
||||
|
|
||||
|
||||
<a class="anchor" id="reg"></a>
|
||||
|
||||
## Access Matrix via a public webclient/server
|
||||
|
||||
The easiest thing to do if you want to just have a play, is to use the [Riot.im
|
||||
webclient](https://riot.im). You can use it as a guest, or register for an
|
||||
account. For details of other clients, see
|
||||
[https://matrix.org/blog/try-matrix-now](https://matrix.org/blog/try-matrix-now).
|
||||
|
||||
<a class="anchor" id="run"></a>
|
||||
|
||||
## Run a server and/or client yourself
|
||||
|
||||
You can clone our open source projects and run clients and servers yourself. Here is how:
|
||||
|
||||
### Running your own client:
|
||||
|
||||
You can run your own Matrix client; there are [numerous clients
|
||||
available](https://matrix.org/blog/try-matrix-now/). You can easily [run your
|
||||
own copy](https://github.com/vector-im/vector-web#getting-started) of the
|
||||
Riot.im web client.
|
||||
|
||||
### Running your own homeserver:
|
||||
|
||||
One of the core features of Matrix is that anyone can run a homeserver and join the federated network on equal terms (there is no hierarchy). If you want to set up your own homeserver, please see the relevant docs of the server you want to run. If you want to run Matrix.org's reference homeserver, please consult the [readme of the Synapse project](https://github.com/matrix-org/synapse/blob/master/README.rst).
|
||||
|
||||
|
|
||||
|
||||
Note that Synapse comes with a bundled Matrix.org webclient - but you can tell it to use your [development checkout snapshot instead](https://github.com/matrix-org/matrix-angular-sdk#matrix-angular-sdk) (or to not run a webclient at all via the "web_client: false" config option).
|
||||
|
||||
|
|
||||
|
||||
<a class="anchor" id="checkout"></a>
|
||||
|
||||
## Checkout our code - or write your own
|
||||
|
||||
As described above, you can clone our code and [run a server and/or client yourself](#run). Infact, all the code that we at Matrix.org write is available from [our github](http://github.com/matrix-org) - and other servers and clients may also be open sourced - see [our list of all known Matrix-apps](https://matrix.org/blog/try-matrix-now/).
|
||||
|
||||
|
|
||||
|
||||
You can also implement your own client or server - after all, Matrix is at its core "just" a specification of a protocol.
|
||||
|
||||
|
|
||||
|
||||
### Write your own client:
|
||||
|
||||
The [client-server API
|
||||
spec](http://matrix.org/docs/spec/client_server/latest.html) describes what API
|
||||
calls are available to clients, and there is a [HOWTO
|
||||
guide](http://matrix.org/docs/guides/client-server.html) which provides an
|
||||
introduction to using the API along with some common operations. A quick
|
||||
step-by-step guide would include:
|
||||
|
||||
|
|
||||
|
||||
1. Get a user either by registering your user in an existing client or running the [new-user script](https://github.com/matrix-org/synapse/blob/master/scripts/register_new_matrix_user) if you are running your own Synapse homeserver.
|
||||
|
||||
2. Assuming the homeserver you are using allows logins by password, log in via the login API:
|
||||
|
||||
```
|
||||
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "http://localhost:8008/_matrix/client/api/v1/login"
|
||||
```
|
||||
|
||||
3. If successful, this returns the following, including an `access_token`:
|
||||
|
||||
{
|
||||
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
|
||||
"home_server": "localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
||||
|
||||
4. This ``access_token`` will be used for authentication for the rest of your API calls. Potentially the next step you want is to make a call to the sync API and get the last few events from each room your user is in:
|
||||
|
||||
```
|
||||
curl -XGET "http://localhost:8008/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN"
|
||||
```
|
||||
|
||||
5. The above will return something like this:
|
||||
|
||||
{
|
||||
"next_batch": "s72595_4483_1934",
|
||||
"rooms": {
|
||||
"join": {
|
||||
"!726s6s6q:example.com": {
|
||||
"state": {
|
||||
"events": [
|
||||
...
|
||||
]
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
...
|
||||
]
|
||||
}
|
||||
},
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
You can then use the "next_batch" token to start listening for new events like so:
|
||||
|
||||
```
|
||||
curl -XGET "http://localhost:8008/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN&since=s72595_4483_1934"
|
||||
```
|
||||
|
||||
6. This request will block waiting for an incoming event, timing out after several seconds if there is no event. This ensures that you only get new events. Now you have the initial room states, and a stream of events - a good client should be able to process all these events and present them to the user. And potentially you might want to add functionality to generate events as well (such as messages from the user, for example)!
|
||||
|
||||
### Write your own server:
|
||||
|
||||
We are still working on the server-server spec, so the best thing to do if you are interested in writing a server, is to come talk to us in [#matrix:matrix.org](https://matrix.to/#/#matrix:matrix.org).
|
||||
|
||||
If you are interested in how federation works, please see the [Server-Server API spec](http://matrix.org/docs/spec/server_server/unstable.html).
|
||||
|
||||
|
|
||||
|
||||
<a class="anchor" id="as"></a>
|
||||
|
||||
## Write an Application Service:
|
||||
|
||||
Information about Application services and how they can be used can be found in the [Application services](./application_services.html) document! (This is based on Kegan's excellent blog posts, but now lives here so it can be kept up-to-date!)
|
@ -1,18 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
categories: guides
|
||||
---
|
||||
<div class="home">
|
||||
|
||||
<h1>Guides</h1>
|
||||
|
||||
<p>Here is a collection of guides that might help you get involved with Matrix.</p>
|
||||
<p>First, there is the <a href="./getting_involved.html" title="Getting Involved">Getting Involved</a> guide, which explains various ways of getting started with Matrix, and the <a href="./faq.html" title="FAQ">FAQ</a>, which tries to answer all your questions relating to Matrix.</p>
|
||||
<p>The <a href="/docs/guides/client-server.html" title="Client-Server API">Client-Server API</a> guide explains in detail how to use the CS API, which is useful if you want to write a client (or modify an existing one) - or if you're just interested in how it works "under the hood".</p>
|
||||
<p>If you were using the old v1 CS API, there is also the <a href="/docs/guides/client-server-migrating-from-v1.html">v1 migration guide</a> which justs lists the changes from v1 to r0.</p>
|
||||
<p><a href="./lets-encrypt.html">Let's Encrypt Matrix</a> explains how to use Let's Encrypt's certificates with your Synapse installation. This guide was written by William A Stevens.</p>
|
||||
<p>The <a href="./application_services.html" title="Application services">Application services</a> guide introduces and explains Application services, and what they can be used for.</p>
|
||||
<p><a href="./types-of-bridging.html">Types of Bridging</a> should be read by all bridge developers to ensure everyone has the same mental map of terminology when implementing bridges.</p>
|
||||
<p>The <a href="./e2e_implementation.html">End-to-end Encryption Implementation Guide</a> is intended for client developers who wish to add support for end-to-end encryption.</p>
|
||||
|
||||
</div>
|
@ -1,37 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: Let's Encrypt Matrix
|
||||
categories: guides
|
||||
---
|
||||
|
||||
====================
|
||||
Let's Encrypt Matrix
|
||||
====================
|
||||
|
||||
Let's Encrypt is a free Certificate Authority that makes it easy to secure your server's internet traffic. This makes it really easy to secure your Matrix homeserver, and this guide will explain exactly how you do this. Guide written by William A Stevens - thanks!
|
||||
|
||||
0: Prerequisites
|
||||
================
|
||||
* Install Synapse_.
|
||||
* Install (or Download) certbot_
|
||||
|
||||
1: Get certificates
|
||||
===================
|
||||
When executing the Let's Encrypt client, it will ask for the domain name of your server, and your email address. The domain list can include multiple names and should include any domain you want to access the server from.
|
||||
|
||||
Also, the certificates will be in a folder under /etc/letsencrypt (see below) and owned by root.
|
||||
|
||||
::
|
||||
|
||||
# certbot certonly --standalone
|
||||
|
||||
A note about renewal
|
||||
--------------------
|
||||
These certificates will expire in 3 months. To renew certificates, run ```certbot renew```. It is recommended to create a cronjob, which attempts renewal twice a day. Depending on your distribution, that could be already configured.
|
||||
|
||||
2: Install Certificates
|
||||
=======================
|
||||
At the top of your homeserver.yaml there should be two keys, ```tls_certificate_path``` and ```tls_private_key_path```. These should be changed so that instead of pointing to the default keys, they now point to the Let's Encrypt keys. ```tls_certificate_path``` should point to ```/etc/letsencrypt/live/(your domain name)/fullchain.pem```. ```tls_private_key_path``` should point to ```/etc/letsencrypt/live/(your domain name)/privkey.pem```. ```tls_dh_params_path``` can stay the same as before.
|
||||
|
||||
.. _Synapse: https://github.com/matrix-org/synapse/blob/master/README.rst#synapse-installation
|
||||
.. _certbot: https://certbot.eff.org/
|
@ -1,114 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: Migrating from Client Server API v1
|
||||
categories: guides
|
||||
---
|
||||
|
||||
Migrating from client-server API v1
|
||||
===================================
|
||||
|
||||
This guide assists developers of API clients that target the ``v1`` version of
|
||||
the API to migrate their code to the later ``r0``. It does not aim to introduce
|
||||
new concepts that were added in ``r0`` except where these replace things that
|
||||
were removed since ``v1``.
|
||||
|
||||
Updated Version In Path
|
||||
=======================
|
||||
|
||||
The new version of the API is ``r0``; this should be used in paths where
|
||||
``v1`` used to appear. Additionally, the ``/api`` path component has now been
|
||||
removed. API endpoint paths are now::
|
||||
|
||||
POST /_matrix/client/r0/register
|
||||
GET /_matrix/client/r0/login
|
||||
etc...
|
||||
|
||||
New Registration and Login Endpoints
|
||||
====================================
|
||||
|
||||
The ``r0`` version of the ``/register`` and ``/login`` endpoints is different
|
||||
to the ``v1`` version. See the updated API documentation for details on how the
|
||||
new API works. In brief, the changes are that the new version returns extra
|
||||
information in the form of the ``params`` object, and that a sequence of
|
||||
multiple calls may be statefully chained together by the ``session`` parameter.
|
||||
|
||||
Additionally, whereas in ``v1`` the client performed a ``GET`` request to
|
||||
discover the list of supported flows for ``/register``, in ``r0`` this is done
|
||||
by sending a ``POST`` request with an empty data body. The ``/login`` endpoint
|
||||
continues to use the ``GET`` method as before.
|
||||
|
||||
Deprecated Endpoints
|
||||
====================
|
||||
|
||||
The following endpoints are now deprecated and replaced by the ``/sync`` API::
|
||||
|
||||
/initialSync
|
||||
/events
|
||||
/rooms/:roomId/initialSync
|
||||
|
||||
The new ``/sync`` API takes an optional ``since`` parameter to distinguish the
|
||||
initial sync from subsequent updates for more events.
|
||||
|
||||
The return value takes a different structure to that from the previous
|
||||
``/initialSync`` API. For full details see the API documentation, but the
|
||||
following summary may be useful to compare with ``v1``:
|
||||
|
||||
* ``/initialSync`` returned a ``state`` key containing the most recent state
|
||||
in the room, whereas the new ``/sync`` API's ``state`` corresponds to the
|
||||
room state at the start of the returned timeline. This makes it easier for
|
||||
clients to represent state changes that occur within the region of returned
|
||||
timeline.
|
||||
|
||||
* In ``/events``, if more events occurred since the ``since`` token than the
|
||||
``limit`` parameter allowed, then events from the start of this range were
|
||||
returned and the client had to perform another fetch to incrementally obtain
|
||||
more of them. In the ``/sync`` API the result always contains the most
|
||||
recent events, creating a gap if this would be more events than the
|
||||
requested limit. If this occurs then the client can use the ``prev_batch``
|
||||
token as a reference to obtaining more.
|
||||
|
||||
* The ``state`` contained in the response to a ``/sync`` request that has a
|
||||
``since`` parameter will contain only keys that have changed since the
|
||||
basis given in the ``since`` parameter, rather than containing a full set
|
||||
values.
|
||||
|
||||
The ``/initialSync`` API allowed a parameter called ``limit`` to limit the
|
||||
number of events returned. To apply this limit to the new ``/sync`` API, you
|
||||
can supply an ad-hoc filter::
|
||||
|
||||
GET .../sync?filter={"room":{"timeline":{"limit:$limit}}}
|
||||
|
||||
There is no direct replacement for the per-room ``/rooms/:roomId/initialSync``
|
||||
endpoint, but the behaviour can be recreated by applying an ad-hoc filter using
|
||||
the ``filter`` parameter to ``/sync`` that selects only the required room ID::
|
||||
|
||||
GET .../sync?filter={"room":{"rooms":[$room_id]}}
|
||||
|
||||
However, the way that the new ``/sync`` API works should remove any need to do
|
||||
this kind of query, in the situations where the ``v1`` API needed it.
|
||||
Specifically, on joining a new room the initial information about that room is
|
||||
sent in the next ``/sync`` batch, so it should not be necessary to query that
|
||||
one room specially.
|
||||
|
||||
The following endpoint is deprecated and has no direct replacement::
|
||||
|
||||
/events/:eventId
|
||||
|
||||
However, if the client knows the room ID of the room that the event is in, it
|
||||
can use the ``/rooms/:roomId/context/:eventId`` request to fetch the event
|
||||
itself. By giving the ``limit`` parameter of ``0`` the client can save using
|
||||
extra bandwidth by actually returning additional context events around the
|
||||
requested one.
|
||||
|
||||
Removed POST Endpoint
|
||||
=====================
|
||||
|
||||
The room message sending API endpoint in ``v1`` accepted both ``PUT`` and
|
||||
``POST`` methods, where the client could specify a message ID in the ``PUT``
|
||||
path for de-duplication purposes, or have the server allocate one during
|
||||
``POST``. In ``r0`` this latter form no longer exists. Clients will now have
|
||||
to generate these IDs locally.
|
||||
|
||||
The following URLs have therefore been removed::
|
||||
|
||||
POST .../rooms/:roomId/send/:messageType
|
@ -1,394 +0,0 @@
|
||||
---
|
||||
layout: post
|
||||
title: Client Server API
|
||||
categories: guides
|
||||
---
|
||||
|
||||
|
||||
.. TODO kegan
|
||||
Room config (specifically: message history,
|
||||
public rooms).
|
||||
|
||||
How to use the client-server API
|
||||
================================
|
||||
|
||||
.. NOTE::
|
||||
The git version of this document is ``{% project_version %}``
|
||||
|
||||
This guide focuses on how the client-server APIs *provided by the reference
|
||||
homeserver* can be used. Since this is specific to a homeserver
|
||||
implementation, there may be variations in relation to registering/logging in
|
||||
which are not covered in extensive detail in this guide.
|
||||
|
||||
If you haven't already, get a homeserver up and running on
|
||||
``https://localhost:8448``.
|
||||
|
||||
|
||||
Accounts
|
||||
========
|
||||
Before you can send and receive messages, you must **register** for an account.
|
||||
If you already have an account, you must **login** into it.
|
||||
|
||||
.. NOTE::
|
||||
`Try out the fiddle`__
|
||||
|
||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/register_login
|
||||
|
||||
Registration
|
||||
------------
|
||||
The aim of registration is to get a user ID and access token which you will need
|
||||
when accessing other APIs::
|
||||
|
||||
curl -XPOST -d '{"username":"example", "password":"wordpass", "auth": {"type":"m.login.dummy"}}' "https://localhost:8448/_matrix/client/r0/register"
|
||||
|
||||
{
|
||||
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.AqdSzFmFYrLrTmteXc",
|
||||
"home_server": "localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
||||
|
||||
NB: If a ``user`` is not specified, one will be randomly generated for you.
|
||||
If you do not specify a ``password``, you will be unable to login to the account
|
||||
if you forget the ``access_token``.
|
||||
|
||||
Implementation note: The matrix specification does not enforce how users
|
||||
register with a server. It just specifies the URL path and absolute minimum
|
||||
keys. The reference homeserver uses a username/password to authenticate user,
|
||||
but other homeservers may use different methods. This is why you need to
|
||||
specify the ``type`` of method.
|
||||
|
||||
Login
|
||||
-----
|
||||
The aim when logging in is to get an access token for your existing user ID::
|
||||
|
||||
curl -XGET "https://localhost:8448/_matrix/client/r0/login"
|
||||
|
||||
{
|
||||
"flows": [
|
||||
{
|
||||
"type": "m.login.password"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
curl -XPOST -d '{"type":"m.login.password", "user":"example", "password":"wordpass"}' "https://localhost:8448/_matrix/client/r0/login"
|
||||
|
||||
{
|
||||
"access_token": "QGV4YW1wbGU6bG9jYWxob3N0.vRDLTgxefmKWQEtgGd",
|
||||
"home_server": "localhost",
|
||||
"user_id": "@example:localhost"
|
||||
}
|
||||
|
||||
Implementation note: Different homeservers may implement different methods for
|
||||
logging in to an existing account. In order to check that you know how to login
|
||||
to this homeserver, you must perform a ``GET`` first and make sure you
|
||||
recognise the login type. If you do not know how to login, you can
|
||||
``GET /login/fallback`` which will return a basic webpage which you can use to
|
||||
login. The reference homeserver implementation support username/password login,
|
||||
but other homeservers may support different login methods (e.g. OAuth2).
|
||||
|
||||
|
||||
Communicating
|
||||
=============
|
||||
|
||||
In order to communicate with another user, you must **create a room** with that
|
||||
user and **send a message** to that room.
|
||||
|
||||
.. NOTE::
|
||||
`Try out the fiddle`__
|
||||
|
||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/create_room_send_msg
|
||||
|
||||
Creating a room
|
||||
---------------
|
||||
If you want to send a message to someone, you have to be in a room with them. To
|
||||
create a room::
|
||||
|
||||
curl -XPOST -d '{"room_alias_name":"tutorial"}' "https://localhost:8448/_matrix/client/r0/createRoom?access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
{
|
||||
"room_alias": "#tutorial:localhost",
|
||||
"room_id": "!asfLdzLnOdGRkdPZWu:localhost"
|
||||
}
|
||||
|
||||
The "room alias" is a human-readable string which can be shared with other users
|
||||
so they can join a room, rather than the room ID which is a randomly generated
|
||||
string. You can have multiple room aliases per room.
|
||||
|
||||
.. TODO(kegan)
|
||||
How to add/remove aliases from an existing room.
|
||||
|
||||
|
||||
Sending messages
|
||||
----------------
|
||||
You can now send messages to this room::
|
||||
|
||||
curl -XPOST -d '{"msgtype":"m.text", "body":"hello"}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/send/m.room.message?access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
{
|
||||
"event_id": "YUwRidLecu"
|
||||
}
|
||||
|
||||
The event ID returned is a unique ID which identifies this message.
|
||||
|
||||
NB: There are no limitations to the types of messages which can be exchanged.
|
||||
The only requirement is that ``"msgtype"`` is specified. The Matrix
|
||||
specification outlines the following standard types: ``m.text``, ``m.image``,
|
||||
``m.audio``, ``m.video``, ``m.location``, ``m.emote``. See the specification for
|
||||
more information on these types.
|
||||
|
||||
Users and rooms
|
||||
===============
|
||||
|
||||
Each room can be configured to allow or disallow certain rules. In particular,
|
||||
these rules may specify if you require an **invitation** from someone already in
|
||||
the room in order to **join the room**. In addition, you may also be able to
|
||||
join a room **via a room alias** if one was set up.
|
||||
|
||||
.. NOTE::
|
||||
`Try out the fiddle`__
|
||||
|
||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/room_memberships
|
||||
|
||||
Inviting a user to a room
|
||||
-------------------------
|
||||
You can directly invite a user to a room like so::
|
||||
|
||||
curl -XPOST -d '{"user_id":"@myfriend:localhost"}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/invite?access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
This informs ``@myfriend:localhost`` of the room ID
|
||||
``!CvcvRuDYDzTOzfKKgh:localhost`` and allows them to join the room.
|
||||
|
||||
Joining a room via an invite
|
||||
----------------------------
|
||||
If you receive an invite, you can join the room::
|
||||
|
||||
curl -XPOST -d '{}' "https://localhost:8448/_matrix/client/r0/rooms/%21asfLdzLnOdGRkdPZWu:localhost/join?access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
NB: Only the person invited (``@myfriend:localhost``) can change the membership
|
||||
state to ``"join"``. Repeatedly joining a room does nothing.
|
||||
|
||||
Joining a room via an alias
|
||||
---------------------------
|
||||
Alternatively, if you know the room alias for this room and the room config
|
||||
allows it, you can directly join a room via the alias::
|
||||
|
||||
curl -XPOST -d '{}' "https://localhost:8448/_matrix/client/r0/join/%21asfLdzLnOdGRkdPZWu:localhost?access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
{
|
||||
"room_id": "!CvcvRuDYDzTOzfKKgh:localhost"
|
||||
}
|
||||
|
||||
You will need to use the room ID when sending messages, not the room alias.
|
||||
|
||||
NB: If the room is configured to be an invite-only room, you will still require
|
||||
an invite in order to join the room even though you know the room alias. As a
|
||||
result, it is more common to see a room alias in relation to a public room,
|
||||
which do not require invitations.
|
||||
|
||||
Getting events
|
||||
==============
|
||||
An event is some interesting piece of data that a client may be interested in.
|
||||
It can be a message in a room, a room invite, etc. There are many different ways
|
||||
of getting events, depending on what the client already knows.
|
||||
|
||||
.. NOTE::
|
||||
`Try out the fiddle`__
|
||||
|
||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/event_stream
|
||||
|
||||
Getting all state
|
||||
-----------------
|
||||
If the client doesn't know any information on the rooms the user is
|
||||
invited/joined on, they can get all the user's state for all rooms::
|
||||
|
||||
curl -XGET "https://localhost:8448/_matrix/client/r0/sync?access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
{
|
||||
"account_data": {
|
||||
"events": [
|
||||
{
|
||||
...
|
||||
}
|
||||
]
|
||||
},
|
||||
"next_batch": "s9_3_0_1_1_1",
|
||||
"presence": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"currently_active": true,
|
||||
"last_active_ago": 19,
|
||||
"presence": "online"
|
||||
},
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.presence"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rooms": {
|
||||
"invite": {},
|
||||
"join": {
|
||||
"!asfLdzLnOdGRkdPZWu:localhost": {
|
||||
"account_data": {
|
||||
"events": []
|
||||
},
|
||||
"ephemeral": {
|
||||
"events": []
|
||||
},
|
||||
"state": {
|
||||
"events": []
|
||||
},
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"creator": "@example:localhost"
|
||||
},
|
||||
"event_id": "$14606534990LhqHt:localhost",
|
||||
"origin_server_ts": 1460653499699,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "",
|
||||
"type": "m.room.create",
|
||||
"unsigned": {
|
||||
"age": 239192
|
||||
}
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"avatar_url": null,
|
||||
"displayname": null,
|
||||
"membership": "join"
|
||||
},
|
||||
"event_id": "$14606534991nsZKk:localhost",
|
||||
"membership": "join",
|
||||
"origin_server_ts": 1460653499727,
|
||||
"sender": "@example:localhost",
|
||||
"state_key": "@example:localhost",
|
||||
"type": "m.room.member",
|
||||
"unsigned": {
|
||||
"age": 239164
|
||||
}
|
||||
},
|
||||
...
|
||||
],
|
||||
"limited": false,
|
||||
"prev_batch": "s9_3_0_1_1_1"
|
||||
},
|
||||
"unread_notifications": {}
|
||||
}
|
||||
},
|
||||
"leave": {}
|
||||
}
|
||||
}
|
||||
|
||||
This returns all the room information the user is invited/joined on, as well as
|
||||
all of the presences relevant for these rooms. This can be a LOT of data. You
|
||||
may just want the most recent event for each room. This can be achieved by
|
||||
applying a filter that asks for a limit of 1 timeline event per room::
|
||||
|
||||
curl --globoff -XGET "https://localhost:8448/_matrix/client/r0/sync?filter={'room':{'timeline':{'limit':1}}}&access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
{
|
||||
...
|
||||
"rooms": {
|
||||
"invite": {},
|
||||
"join": {
|
||||
"!asfLdzLnOdGRkdPZWu:localhost": {
|
||||
...
|
||||
"timeline": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"body": "hello",
|
||||
"msgtype": "m.text"
|
||||
},
|
||||
"event_id": "$14606535757KCGXo:localhost",
|
||||
"origin_server_ts": 1460653575105,
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.room.message",
|
||||
"unsigned": {
|
||||
"age": 800348
|
||||
}
|
||||
}
|
||||
],
|
||||
"limited": true,
|
||||
"prev_batch": "t8-8_7_0_1_1_1"
|
||||
},
|
||||
"unread_notifications": {}
|
||||
}
|
||||
},
|
||||
"leave": {}
|
||||
}
|
||||
}
|
||||
|
||||
(additionally we have to ask ``curl`` not to try to interpret any ``{}``
|
||||
characters in the URL, which is what the ``--globoff`` option is for)
|
||||
|
||||
Getting live state
|
||||
------------------
|
||||
In the response to this ``sync`` request the server includes a token that can
|
||||
be used to obtain updates since this point under the object key ``next_batch``.
|
||||
To use this token, specify its value as the ``since`` parameter to another
|
||||
``/sync`` request.::
|
||||
|
||||
curl -XGET "https://localhost:8448/_matrix/client/r0/sync?since=s9_7_0_1_1_1&access_token=YOUR_ACCESS_TOKEN"
|
||||
|
||||
{
|
||||
"account_data": {
|
||||
"events": []
|
||||
},
|
||||
"next_batch": "s9_9_0_1_1_1",
|
||||
"presence": {
|
||||
"events": [
|
||||
{
|
||||
"content": {
|
||||
"currently_active": true,
|
||||
"last_active_ago": 12,
|
||||
"presence": "online"
|
||||
},
|
||||
"sender": "@example:localhost",
|
||||
"type": "m.presence"
|
||||
}
|
||||
]
|
||||
},
|
||||
"rooms": {
|
||||
"invite": {},
|
||||
"join": {},
|
||||
"leave": {}
|
||||
}
|
||||
}
|
||||
|
||||
By default this request will not wait in the server, always returning a value
|
||||
even if nothing interesting happened. However, by applying the ``timeout``
|
||||
query parameter, which gives a duration in miliseconds, we can ask the server
|
||||
to wait for up to that amount of time before it returns. If no interesting
|
||||
events have happened since then, the response will be relatively empty.::
|
||||
|
||||
curl -XGET "https://localhost:8448/_matrix/client/r0/sync?since=s9_13_0_1_1_1&access_token=YOUR_ACCESS_TOKEN"
|
||||
{
|
||||
"account_data": {
|
||||
"events": []
|
||||
},
|
||||
"next_batch": "s9_13_0_1_1_1",
|
||||
"presence": {
|
||||
"events": []
|
||||
},
|
||||
"rooms": {
|
||||
"invite": {},
|
||||
"join": {},
|
||||
"leave": {}
|
||||
}
|
||||
}
|
||||
|
||||
Example application
|
||||
-------------------
|
||||
The following example demonstrates registration and login, live event streaming,
|
||||
creating and joining rooms, sending messages, getting member lists and getting
|
||||
historical messages for a room. This covers most functionality of a messaging
|
||||
application.
|
||||
|
||||
.. NOTE::
|
||||
`Try out the fiddle`__
|
||||
|
||||
.. __: http://jsfiddle.net/gh/get/jquery/1.8.3/matrix-org/matrix-doc/tree/master/supporting-docs/howtos/jsfiddles/example_app
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Try Matrix Now!
|
||||
categories: howtos
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url=../guides/client-server.html" />
|
||||
</head>
|
||||
</html>
|
@ -1,17 +0,0 @@
|
||||
.loggedin {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-spacing:5px;
|
||||
}
|
||||
|
||||
th,td
|
||||
{
|
||||
padding:5px;
|
||||
}
|
@ -1,30 +0,0 @@
|
||||
<div>
|
||||
<p>This room creation / message sending demo requires a homeserver to be running on http://localhost:8008</p>
|
||||
</div>
|
||||
<form class="loginForm">
|
||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
||||
<input type="button" class="login" value="Login"></input>
|
||||
</form>
|
||||
<div class="loggedin">
|
||||
<form class="createRoomForm">
|
||||
<input type="text" id="roomAlias" placeholder="Room alias (optional)"></input>
|
||||
<input type="button" class="createRoom" value="Create Room"></input>
|
||||
</form>
|
||||
<form class="sendMessageForm">
|
||||
<input type="text" id="roomId" placeholder="Room ID"></input>
|
||||
<input type="text" id="messageBody" placeholder="Message body"></input>
|
||||
<input type="button" class="sendMessage" value="Send Message"></input>
|
||||
</form>
|
||||
<table id="rooms">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Room ID</th>
|
||||
<th>My state</th>
|
||||
<th>Room Alias</th>
|
||||
<th>Latest message</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,113 +0,0 @@
|
||||
var accountInfo = {};
|
||||
|
||||
var showLoggedIn = function(data) {
|
||||
accountInfo = data;
|
||||
getCurrentRoomList();
|
||||
$(".loggedin").css({visibility: "visible"});
|
||||
};
|
||||
|
||||
$('.login').live('click', function() {
|
||||
var user = $("#userLogin").val();
|
||||
var password = $("#passwordLogin").val();
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/login",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
showLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
var errMsg = "To try this, you need a homeserver running!";
|
||||
var errJson = $.parseJSON(err.responseText);
|
||||
if (errJson) {
|
||||
errMsg = JSON.stringify(errJson);
|
||||
}
|
||||
alert(errMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var getCurrentRoomList = function() {
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
|
||||
$.getJSON(url, function(data) {
|
||||
var rooms = data.rooms;
|
||||
for (var i=0; i<rooms.length; ++i) {
|
||||
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
|
||||
addRoom(rooms[i]);
|
||||
}
|
||||
}).fail(function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
});
|
||||
};
|
||||
|
||||
$('.createRoom').live('click', function() {
|
||||
var roomAlias = $("#roomAlias").val();
|
||||
var data = {};
|
||||
if (roomAlias.length > 0) {
|
||||
data.room_alias_name = roomAlias;
|
||||
}
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
data.membership = "join"; // you are automatically joined into every room you make.
|
||||
data.latest_message = "";
|
||||
addRoom(data);
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var addRoom = function(data) {
|
||||
row = "<tr>" +
|
||||
"<td>"+data.room_id+"</td>" +
|
||||
"<td>"+data.membership+"</td>" +
|
||||
"<td>"+data.room_alias+"</td>" +
|
||||
"<td>"+data.latest_message+"</td>" +
|
||||
"</tr>";
|
||||
$("#rooms").append(row);
|
||||
};
|
||||
|
||||
$('.sendMessage').live('click', function() {
|
||||
var roomId = $("#roomId").val();
|
||||
var body = $("#messageBody").val();
|
||||
var msgId = $.now();
|
||||
|
||||
if (roomId.length === 0 || body.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$roomid", encodeURIComponent(roomId));
|
||||
|
||||
var data = {
|
||||
msgtype: "m.text",
|
||||
body: body
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
$("#messageBody").val("");
|
||||
// wipe the table and reload it. Using the event stream would be the best
|
||||
// solution but that is out of scope of this fiddle.
|
||||
$("#rooms").find("tr:gt(0)").remove();
|
||||
getCurrentRoomList();
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
.loggedin {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-spacing:5px;
|
||||
}
|
||||
|
||||
th,td
|
||||
{
|
||||
padding:5px;
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
<div>
|
||||
<p>This event stream demo requires a homeserver to be running on http://localhost:8008</p>
|
||||
</div>
|
||||
<form class="loginForm">
|
||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
||||
<input type="button" class="login" value="Login"></input>
|
||||
</form>
|
||||
<div class="loggedin">
|
||||
<form class="sendMessageForm">
|
||||
<input type="button" class="sendMessage" value="Send random message"></input>
|
||||
</form>
|
||||
<p id="streamErrorText"></p>
|
||||
<table id="rooms">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Room ID</th>
|
||||
<th>Latest message</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,145 +0,0 @@
|
||||
var accountInfo = {};
|
||||
|
||||
var eventStreamInfo = {
|
||||
from: "END"
|
||||
};
|
||||
|
||||
var roomInfo = [];
|
||||
|
||||
var longpollEventStream = function() {
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$from", eventStreamInfo.from);
|
||||
|
||||
$.getJSON(url, function(data) {
|
||||
eventStreamInfo.from = data.end;
|
||||
|
||||
var hasNewLatestMessage = false;
|
||||
for (var i=0; i<data.chunk.length; ++i) {
|
||||
if (data.chunk[i].type === "m.room.message") {
|
||||
for (var j=0; j<roomInfo.length; ++j) {
|
||||
if (roomInfo[j].room_id === data.chunk[i].room_id) {
|
||||
roomInfo[j].latest_message = data.chunk[i].content.body;
|
||||
hasNewLatestMessage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNewLatestMessage) {
|
||||
setRooms(roomInfo);
|
||||
}
|
||||
$("#streamErrorText").text("");
|
||||
longpollEventStream();
|
||||
}).fail(function(err) {
|
||||
$("#streamErrorText").text("Event stream error: "+JSON.stringify($.parseJSON(err.responseText)));
|
||||
setTimeout(longpollEventStream, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
var showLoggedIn = function(data) {
|
||||
accountInfo = data;
|
||||
longpollEventStream();
|
||||
getCurrentRoomList();
|
||||
$(".loggedin").css({visibility: "visible"});
|
||||
};
|
||||
|
||||
$('.login').live('click', function() {
|
||||
var user = $("#userLogin").val();
|
||||
var password = $("#passwordLogin").val();
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/login",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
$("#rooms").find("tr:gt(0)").remove();
|
||||
showLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
var errMsg = "To try this, you need a homeserver running!";
|
||||
var errJson = $.parseJSON(err.responseText);
|
||||
if (errJson) {
|
||||
errMsg = JSON.stringify(errJson);
|
||||
}
|
||||
alert(errMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var getCurrentRoomList = function() {
|
||||
$("#roomId").val("");
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
|
||||
$.getJSON(url, function(data) {
|
||||
var rooms = data.rooms;
|
||||
for (var i=0; i<rooms.length; ++i) {
|
||||
if ("messages" in rooms[i]) {
|
||||
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
|
||||
}
|
||||
}
|
||||
roomInfo = rooms;
|
||||
setRooms(roomInfo);
|
||||
}).fail(function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
});
|
||||
};
|
||||
|
||||
$('.sendMessage').live('click', function() {
|
||||
if (roomInfo.length === 0) {
|
||||
alert("There is no room to send a message to!");
|
||||
return;
|
||||
}
|
||||
|
||||
var index = Math.floor(Math.random() * roomInfo.length);
|
||||
|
||||
sendMessage(roomInfo[index].room_id);
|
||||
});
|
||||
|
||||
var sendMessage = function(roomId) {
|
||||
var body = "jsfiddle message @" + $.now();
|
||||
|
||||
if (roomId.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$roomid", encodeURIComponent(roomId));
|
||||
|
||||
var data = {
|
||||
msgtype: "m.text",
|
||||
body: body
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
$("#messageBody").val("");
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var setRooms = function(roomList) {
|
||||
// wipe existing entries
|
||||
$("#rooms").find("tr:gt(0)").remove();
|
||||
|
||||
var rows = "";
|
||||
for (var i=0; i<roomList.length; ++i) {
|
||||
row = "<tr>" +
|
||||
"<td>"+roomList[i].room_id+"</td>" +
|
||||
"<td>"+roomList[i].latest_message+"</td>" +
|
||||
"</tr>";
|
||||
rows += row;
|
||||
}
|
||||
|
||||
$("#rooms").append(rows);
|
||||
};
|
||||
|
@ -1,43 +0,0 @@
|
||||
.roomListDashboard, .roomContents, .sendMessageForm {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.roomList {
|
||||
background-color: #909090;
|
||||
}
|
||||
|
||||
.messageWrapper {
|
||||
background-color: #EEEEEE;
|
||||
height: 400px;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.membersWrapper {
|
||||
background-color: #EEEEEE;
|
||||
height: 200px;
|
||||
width: 50%;
|
||||
overflow: scroll;
|
||||
}
|
||||
|
||||
.textEntry {
|
||||
width: 100%
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-spacing:5px;
|
||||
}
|
||||
|
||||
th,td
|
||||
{
|
||||
padding:5px;
|
||||
}
|
||||
|
||||
.roomList tr:not(:first-child):hover {
|
||||
background-color: orange;
|
||||
cursor: pointer;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
name: Example Matrix Client
|
||||
description: Includes login, live event streaming, creating rooms, sending messages and viewing member lists.
|
||||
authors:
|
||||
- matrix.org
|
||||
resources:
|
||||
- http://matrix.org
|
||||
normalize_css: no
|
@ -1,56 +0,0 @@
|
||||
<div class="signUp">
|
||||
<p>Matrix example application: Requires a local homeserver running at http://localhost:8008</p>
|
||||
<form class="registrationForm">
|
||||
<p>No account? Register:</p>
|
||||
<input type="text" id="userReg" placeholder="Username"></input>
|
||||
<input type="password" id="passwordReg" placeholder="Password"></input>
|
||||
<input type="button" class="register" value="Register"></input>
|
||||
</form>
|
||||
<form class="loginForm">
|
||||
<p>Got an account? Login:</p>
|
||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
||||
<input type="button" class="login" value="Login"></input>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="roomListDashboard">
|
||||
<form class="createRoomForm">
|
||||
<input type="text" id="roomAlias" placeholder="Room alias"></input>
|
||||
<input type="button" class="createRoom" value="Create Room"></input>
|
||||
</form>
|
||||
<table id="rooms" class="roomList">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Room</th>
|
||||
<th>My state</th>
|
||||
<th>Latest message</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="roomContents">
|
||||
<p id="roomName">Select a room</p>
|
||||
<div class="messageWrapper">
|
||||
<table id="messages">
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<form class="sendMessageForm">
|
||||
<input type="text" class="textEntry" id="body" placeholder="Enter text here..." onkeydown="javascript:if (event.keyCode == 13) document.getElementById('sendMsg').focus()"></input>
|
||||
<input type="button" class="sendMessage" id="sendMsg" value="Send"></input>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p>Member list:</p>
|
||||
<div class="membersWrapper">
|
||||
<table id="members">
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,327 +0,0 @@
|
||||
var accountInfo = {};
|
||||
|
||||
var eventStreamInfo = {
|
||||
from: "END"
|
||||
};
|
||||
|
||||
var roomInfo = [];
|
||||
var memberInfo = [];
|
||||
var viewingRoomId;
|
||||
|
||||
// ************** Event Streaming **************
|
||||
var longpollEventStream = function() {
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/events?access_token=$token&from=$from";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$from", eventStreamInfo.from);
|
||||
|
||||
$.getJSON(url, function(data) {
|
||||
eventStreamInfo.from = data.end;
|
||||
|
||||
var hasNewLatestMessage = false;
|
||||
var updatedMemberList = false;
|
||||
var i=0;
|
||||
var j=0;
|
||||
for (i=0; i<data.chunk.length; ++i) {
|
||||
if (data.chunk[i].type === "m.room.message") {
|
||||
console.log("Got new message: " + JSON.stringify(data.chunk[i]));
|
||||
if (viewingRoomId === data.chunk[i].room_id) {
|
||||
addMessage(data.chunk[i]);
|
||||
}
|
||||
|
||||
for (j=0; j<roomInfo.length; ++j) {
|
||||
if (roomInfo[j].room_id === data.chunk[i].room_id) {
|
||||
roomInfo[j].latest_message = data.chunk[i].content.body;
|
||||
hasNewLatestMessage = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.chunk[i].type === "m.room.member") {
|
||||
if (viewingRoomId === data.chunk[i].room_id) {
|
||||
console.log("Got new member: " + JSON.stringify(data.chunk[i]));
|
||||
addMessage(data.chunk[i]);
|
||||
for (j=0; j<memberInfo.length; ++j) {
|
||||
if (memberInfo[j].state_key === data.chunk[i].state_key) {
|
||||
memberInfo[j] = data.chunk[i];
|
||||
updatedMemberList = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!updatedMemberList) {
|
||||
memberInfo.push(data.chunk[i]);
|
||||
updatedMemberList = true;
|
||||
}
|
||||
}
|
||||
if (data.chunk[i].state_key === accountInfo.user_id) {
|
||||
getCurrentRoomList(); // update our join/invite list
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.log("Discarding: " + JSON.stringify(data.chunk[i]));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasNewLatestMessage) {
|
||||
setRooms(roomInfo);
|
||||
}
|
||||
if (updatedMemberList) {
|
||||
$("#members").empty();
|
||||
for (i=0; i<memberInfo.length; ++i) {
|
||||
addMember(memberInfo[i]);
|
||||
}
|
||||
}
|
||||
longpollEventStream();
|
||||
}).fail(function(err) {
|
||||
setTimeout(longpollEventStream, 5000);
|
||||
});
|
||||
};
|
||||
|
||||
// ************** Registration and Login **************
|
||||
var onLoggedIn = function(data) {
|
||||
accountInfo = data;
|
||||
longpollEventStream();
|
||||
getCurrentRoomList();
|
||||
$(".roomListDashboard").css({visibility: "visible"});
|
||||
$(".roomContents").css({visibility: "visible"});
|
||||
$(".signUp").css({display: "none"});
|
||||
};
|
||||
|
||||
$('.login').live('click', function() {
|
||||
var user = $("#userLogin").val();
|
||||
var password = $("#passwordLogin").val();
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/login",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
onLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
alert("Unable to login: is the homeserver running?");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.register').live('click', function() {
|
||||
var user = $("#userReg").val();
|
||||
var password = $("#passwordReg").val();
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/register",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
onLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
var msg = "Is the homeserver running?";
|
||||
var errJson = $.parseJSON(err.responseText);
|
||||
if (errJson !== null) {
|
||||
msg = errJson.error;
|
||||
}
|
||||
alert("Unable to register: "+msg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ************** Creating a room ******************
|
||||
$('.createRoom').live('click', function() {
|
||||
var roomAlias = $("#roomAlias").val();
|
||||
var data = {};
|
||||
if (roomAlias.length > 0) {
|
||||
data.room_alias_name = roomAlias;
|
||||
}
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(response) {
|
||||
$("#roomAlias").val("");
|
||||
response.membership = "join"; // you are automatically joined into every room you make.
|
||||
response.latest_message = "";
|
||||
|
||||
roomInfo.push(response);
|
||||
setRooms(roomInfo);
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// ************** Getting current state **************
|
||||
var getCurrentRoomList = function() {
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
|
||||
$.getJSON(url, function(data) {
|
||||
var rooms = data.rooms;
|
||||
for (var i=0; i<rooms.length; ++i) {
|
||||
if ("messages" in rooms[i]) {
|
||||
rooms[i].latest_message = rooms[i].messages.chunk[0].content.body;
|
||||
}
|
||||
}
|
||||
roomInfo = rooms;
|
||||
setRooms(roomInfo);
|
||||
}).fail(function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
});
|
||||
};
|
||||
|
||||
var loadRoomContent = function(roomId) {
|
||||
console.log("loadRoomContent " + roomId);
|
||||
viewingRoomId = roomId;
|
||||
$("#roomName").text("Room: "+roomId);
|
||||
$(".sendMessageForm").css({visibility: "visible"});
|
||||
getMessages(roomId);
|
||||
getMemberList(roomId);
|
||||
};
|
||||
|
||||
var getMessages = function(roomId) {
|
||||
$("#messages").empty();
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" +
|
||||
encodeURIComponent(roomId) + "/messages?access_token=" + accountInfo.access_token + "&from=END&dir=b&limit=10";
|
||||
$.getJSON(url, function(data) {
|
||||
for (var i=data.chunk.length-1; i>=0; --i) {
|
||||
addMessage(data.chunk[i]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var getMemberList = function(roomId) {
|
||||
$("#members").empty();
|
||||
memberInfo = [];
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/" +
|
||||
encodeURIComponent(roomId) + "/members?access_token=" + accountInfo.access_token;
|
||||
$.getJSON(url, function(data) {
|
||||
for (var i=0; i<data.chunk.length; ++i) {
|
||||
memberInfo.push(data.chunk[i]);
|
||||
addMember(data.chunk[i]);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ************** Sending messages **************
|
||||
$('.sendMessage').live('click', function() {
|
||||
if (viewingRoomId === undefined) {
|
||||
alert("There is no room to send a message to!");
|
||||
return;
|
||||
}
|
||||
var body = $("#body").val();
|
||||
sendMessage(viewingRoomId, body);
|
||||
});
|
||||
|
||||
var sendMessage = function(roomId, body) {
|
||||
var msgId = $.now();
|
||||
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/send/m.room.message?access_token=$token";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$roomid", encodeURIComponent(roomId));
|
||||
|
||||
var data = {
|
||||
msgtype: "m.text",
|
||||
body: body
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
$("#body").val("");
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ************** Navigation and DOM manipulation **************
|
||||
var setRooms = function(roomList) {
|
||||
// wipe existing entries
|
||||
$("#rooms").find("tr:gt(0)").remove();
|
||||
|
||||
var rows = "";
|
||||
for (var i=0; i<roomList.length; ++i) {
|
||||
row = "<tr>" +
|
||||
"<td>"+roomList[i].room_id+"</td>" +
|
||||
"<td>"+roomList[i].membership+"</td>" +
|
||||
"<td>"+roomList[i].latest_message+"</td>" +
|
||||
"</tr>";
|
||||
rows += row;
|
||||
}
|
||||
|
||||
$("#rooms").append(rows);
|
||||
|
||||
$('#rooms').find("tr").click(function(){
|
||||
var roomId = $(this).find('td:eq(0)').text();
|
||||
var membership = $(this).find('td:eq(1)').text();
|
||||
if (membership !== "join") {
|
||||
console.log("Joining room " + roomId);
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/join?access_token=$token";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$roomid", encodeURIComponent(roomId));
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({membership: "join"}),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
loadRoomContent(roomId);
|
||||
getCurrentRoomList();
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
loadRoomContent(roomId);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var addMessage = function(data) {
|
||||
|
||||
var msg = data.content.body;
|
||||
if (data.type === "m.room.member") {
|
||||
if (data.content.membership === undefined) {
|
||||
return;
|
||||
}
|
||||
if (data.content.membership === "invite") {
|
||||
msg = "<em>invited " + data.state_key + " to the room</em>";
|
||||
}
|
||||
else if (data.content.membership === "join") {
|
||||
msg = "<em>joined the room</em>";
|
||||
}
|
||||
else if (data.content.membership === "leave") {
|
||||
msg = "<em>left the room</em>";
|
||||
}
|
||||
else if (data.content.membership === "ban") {
|
||||
msg = "<em>was banned from the room</em>";
|
||||
}
|
||||
}
|
||||
if (msg === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
var row = "<tr>" +
|
||||
"<td>"+data.user_id+"</td>" +
|
||||
"<td>"+msg+"</td>" +
|
||||
"</tr>";
|
||||
$("#messages").append(row);
|
||||
};
|
||||
|
||||
var addMember = function(data) {
|
||||
var row = "<tr>" +
|
||||
"<td>"+data.state_key+"</td>" +
|
||||
"<td>"+data.content.membership+"</td>" +
|
||||
"</tr>";
|
||||
$("#members").append(row);
|
||||
};
|
||||
|
@ -1,7 +0,0 @@
|
||||
.loggedin {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: monospace;
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
<div>
|
||||
<p>This registration/login demo requires a homeserver to be running on http://localhost:8008</p>
|
||||
</div>
|
||||
<form class="registrationForm">
|
||||
<input type="text" id="user" placeholder="Username"></input>
|
||||
<input type="password" id="password" placeholder="Password"></input>
|
||||
<input type="button" class="register" value="Register"></input>
|
||||
</form>
|
||||
<form class="loginForm">
|
||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
||||
<input type="button" class="login" value="Login"></input>
|
||||
</form>
|
||||
<div class="loggedin">
|
||||
<p id="welcomeText"></p>
|
||||
<input type="button" class="testToken" value="Test token"></input>
|
||||
<input type="button" class="logout" value="Logout"></input>
|
||||
<p id="imSyncText"></p>
|
||||
</div>
|
||||
|
@ -1,79 +0,0 @@
|
||||
var accountInfo = {};
|
||||
|
||||
var showLoggedIn = function(data) {
|
||||
accountInfo = data;
|
||||
$(".loggedin").css({visibility: "visible"});
|
||||
$("#welcomeText").text("Welcome " + accountInfo.user_id+". Your access token is: " +
|
||||
accountInfo.access_token);
|
||||
};
|
||||
|
||||
$('.register').live('click', function() {
|
||||
var user = $("#user").val();
|
||||
var password = $("#password").val();
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/register",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
showLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
var errMsg = "To try this, you need a homeserver running!";
|
||||
var errJson = $.parseJSON(err.responseText);
|
||||
if (errJson) {
|
||||
errMsg = JSON.stringify(errJson);
|
||||
}
|
||||
alert(errMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var login = function(user, password) {
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/login",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
showLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
var errMsg = "To try this, you need a homeserver running!";
|
||||
var errJson = $.parseJSON(err.responseText);
|
||||
if (errJson) {
|
||||
errMsg = JSON.stringify(errJson);
|
||||
}
|
||||
alert(errMsg);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('.login').live('click', function() {
|
||||
var user = $("#userLogin").val();
|
||||
var password = $("#passwordLogin").val();
|
||||
$.getJSON("http://localhost:8008/_matrix/client/api/v1/login", function(data) {
|
||||
if (data.flows[0].type !== "m.login.password") {
|
||||
alert("I don't know how to login with this type: " + data.type);
|
||||
return;
|
||||
}
|
||||
login(user, password);
|
||||
});
|
||||
});
|
||||
|
||||
$('.logout').live('click', function() {
|
||||
accountInfo = {};
|
||||
$("#imSyncText").text("");
|
||||
$(".loggedin").css({visibility: "hidden"});
|
||||
});
|
||||
|
||||
$('.testToken').live('click', function() {
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
|
||||
$.getJSON(url, function(data) {
|
||||
$("#imSyncText").text(JSON.stringify(data, undefined, 2));
|
||||
}).fail(function(err) {
|
||||
$("#imSyncText").text(JSON.stringify($.parseJSON(err.responseText)));
|
||||
});
|
||||
});
|
@ -1,17 +0,0 @@
|
||||
.loggedin {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
p {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
table
|
||||
{
|
||||
border-spacing:5px;
|
||||
}
|
||||
|
||||
th,td
|
||||
{
|
||||
padding:5px;
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
<div>
|
||||
<p>This room membership demo requires a homeserver to be running on http://localhost:8008</p>
|
||||
</div>
|
||||
<form class="loginForm">
|
||||
<input type="text" id="userLogin" placeholder="Username"></input>
|
||||
<input type="password" id="passwordLogin" placeholder="Password"></input>
|
||||
<input type="button" class="login" value="Login"></input>
|
||||
</form>
|
||||
<div class="loggedin">
|
||||
<form class="createRoomForm">
|
||||
<input type="button" class="createRoom" value="Create Room"></input>
|
||||
</form>
|
||||
<form class="changeMembershipForm">
|
||||
<input type="text" id="roomId" placeholder="Room ID"></input>
|
||||
<input type="text" id="targetUser" placeholder="Target User ID"></input>
|
||||
<select id="membership">
|
||||
<option value="invite">invite</option>
|
||||
<option value="join">join</option>
|
||||
<option value="leave">leave</option>
|
||||
</select>
|
||||
<input type="button" class="changeMembership" value="Change Membership"></input>
|
||||
</form>
|
||||
<form class="joinAliasForm">
|
||||
<input type="text" id="roomAlias" placeholder="Room Alias (#name:domain)"></input>
|
||||
<input type="button" class="joinAlias" value="Join via Alias"></input>
|
||||
</form>
|
||||
<table id="rooms">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Room ID</th>
|
||||
<th>My state</th>
|
||||
<th>Room Alias</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -1,141 +0,0 @@
|
||||
var accountInfo = {};
|
||||
|
||||
var showLoggedIn = function(data) {
|
||||
accountInfo = data;
|
||||
getCurrentRoomList();
|
||||
$(".loggedin").css({visibility: "visible"});
|
||||
$("#membership").change(function() {
|
||||
if ($("#membership").val() === "invite") {
|
||||
$("#targetUser").css({visibility: "visible"});
|
||||
}
|
||||
else {
|
||||
$("#targetUser").css({visibility: "hidden"});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$('.login').live('click', function() {
|
||||
var user = $("#userLogin").val();
|
||||
var password = $("#passwordLogin").val();
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/login",
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({ user: user, password: password, type: "m.login.password" }),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
$("#rooms").find("tr:gt(0)").remove();
|
||||
showLoggedIn(data);
|
||||
},
|
||||
error: function(err) {
|
||||
var errMsg = "To try this, you need a homeserver running!";
|
||||
var errJson = $.parseJSON(err.responseText);
|
||||
if (errJson) {
|
||||
errMsg = JSON.stringify(errJson);
|
||||
}
|
||||
alert(errMsg);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var getCurrentRoomList = function() {
|
||||
$("#roomId").val("");
|
||||
// wipe the table and reload it. Using the event stream would be the best
|
||||
// solution but that is out of scope of this fiddle.
|
||||
$("#rooms").find("tr:gt(0)").remove();
|
||||
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/initialSync?access_token=" + accountInfo.access_token + "&limit=1";
|
||||
$.getJSON(url, function(data) {
|
||||
var rooms = data.rooms;
|
||||
for (var i=0; i<rooms.length; ++i) {
|
||||
addRoom(rooms[i]);
|
||||
}
|
||||
}).fail(function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
});
|
||||
};
|
||||
|
||||
$('.createRoom').live('click', function() {
|
||||
var data = {};
|
||||
$.ajax({
|
||||
url: "http://localhost:8008/_matrix/client/api/v1/createRoom?access_token="+accountInfo.access_token,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
data.membership = "join"; // you are automatically joined into every room you make.
|
||||
data.latest_message = "";
|
||||
addRoom(data);
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var addRoom = function(data) {
|
||||
row = "<tr>" +
|
||||
"<td>"+data.room_id+"</td>" +
|
||||
"<td>"+data.membership+"</td>" +
|
||||
"<td>"+data.room_alias+"</td>" +
|
||||
"</tr>";
|
||||
$("#rooms").append(row);
|
||||
};
|
||||
|
||||
$('.changeMembership').live('click', function() {
|
||||
var roomId = $("#roomId").val();
|
||||
var member = $("#targetUser").val();
|
||||
var membership = $("#membership").val();
|
||||
|
||||
if (roomId.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/rooms/$roomid/$membership?access_token=$token";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$roomid", encodeURIComponent(roomId));
|
||||
url = url.replace("$membership", membership);
|
||||
|
||||
var data = {};
|
||||
|
||||
if (membership === "invite") {
|
||||
data = {
|
||||
user_id: member
|
||||
};
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify(data),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
getCurrentRoomList();
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('.joinAlias').live('click', function() {
|
||||
var roomAlias = $("#roomAlias").val();
|
||||
var url = "http://localhost:8008/_matrix/client/api/v1/join/$roomalias?access_token=$token";
|
||||
url = url.replace("$token", accountInfo.access_token);
|
||||
url = url.replace("$roomalias", encodeURIComponent(roomAlias));
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
contentType: "application/json; charset=utf-8",
|
||||
data: JSON.stringify({}),
|
||||
dataType: "json",
|
||||
success: function(data) {
|
||||
getCurrentRoomList();
|
||||
},
|
||||
error: function(err) {
|
||||
alert(JSON.stringify($.parseJSON(err.responseText)));
|
||||
}
|
||||
});
|
||||
});
|
@ -1,20 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Riot
|
||||
categories: projects client
|
||||
thumbnail: /docs/projects/images/riot-web-small.png
|
||||
author: Riot.im
|
||||
description: Riot is a glossy web client with an emphasis on performance and usability
|
||||
maturity: Released
|
||||
---
|
||||
|
||||
![screenshot](/docs/projects/images/riot-web-large.png "{{ page.title }}")
|
||||
|
||||
# {{ page.title }}
|
||||
[Riot](https://riot.im) is a glossy Matrix client built on top of [matrix-react-sdk](http://matrix.org/docs/projects/sdk/matrix.org-react-sdk.html) with an emphasis on performance and usability.
|
||||
|
||||
You can use it at [https://riot.im/app](https://riot.im/app), read more at [https://riot.im](https://riot.im) and get the source from [github](https://github.com/vector-im/vector-web)!
|
||||
|
||||
There is also a desktop version, which is available at [riot.im](https://riot.im/desktop.html). Taw has created RPM package builds for Fedora, CentOS, and Red Hat Enterprise Linux which are available via [GitHub](https://github.com/taw00/riot-rpm/) and [FedoraCorp](https://copr.fedorainfracloud.org/coprs/taw/Riot/).
|
||||
|
||||
Josué Tille has contributed a [Yunohost app](https://github.com/Josue-T/riot_ynh).
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Try Matrix Now!
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url=./riot.html" />
|
||||
</head>
|
||||
</html>
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: NEB (N. E. Bot)
|
||||
categories: projects other
|
||||
description: Our dear Matrix Bot
|
||||
author: Kegsay
|
||||
maturity: Late beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Kegsay's general-purpose Python Matrix Bot framework ([github](https://github.com/Kegsay/Matrix-NEB))
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org Android SDK
|
||||
categories: projects sdk
|
||||
author: Matrix.org team
|
||||
maturity: Late beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Matrix.org's Android SDK ([github](https://github.com/matrix-org/matrix-android-sdk))
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org iOS SDK
|
||||
categories: projects sdk
|
||||
author: Matrix.org team
|
||||
maturity: Late beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Matrix.org's iOS SDK ([github](https://github.com/matrix-org/matrix-ios-sdk))
|
@ -1,12 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org AngularJS SDK
|
||||
categories: projects sdk
|
||||
author: Matrix.org team
|
||||
maturity: DEPRECATED
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
AngularJS services from Matrix.org for building communication apps in Angular based on Matrix: [https://github.com/matrix-org/matrix-angular-sdk](https://github.com/matrix-org/matrix-angular-sdk)
|
||||
|
||||
These are no longer being actively maintained.
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Perl Matrix-IRC Bridge
|
||||
categories: projects other
|
||||
description: The first Matrix/IRC bridge
|
||||
author: Tom Molesworth / Paul Evans
|
||||
maturity: Beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
The first Matrix/IRC bridge, originally written by Tom Molesworth using Paul Evans' Net::Async::Matrix Perl client SDK and then later maintained by Paul Evans. Nowadays superceded by the [matrix-appservice-irc Node IRC bridge AS](../as/irc-bridge.html).
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: ivar2 Matrix/IRC Bot
|
||||
categories: projects other
|
||||
description: IRC bot with native Matrix support
|
||||
author: haste / Tor
|
||||
maturity: Beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
ivar2 is a very comprehensive IRC bot written in Lua which has native support for Matrix! [https://github.com/torhve/ivar2](https://github.com/torhve/ivar2)
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Try Matrix Now!
|
||||
categories: projects
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url=./try-matrix-now.html" />
|
||||
</head>
|
||||
</html>
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org Python SDK
|
||||
categories: projects sdk
|
||||
author: Matrix.org team
|
||||
maturity: Alpha
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Matrix.org's Python SDK ([github](https://github.com/matrix-org/matrix-python-sdk))
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org AS Node SDK
|
||||
categories: projects as
|
||||
author: Matrix.org team
|
||||
maturity: Early beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
A framework from Matrix.org for building Application Services in Node.js/Express: [https://github.com/matrix-org/matrix-appservice-node](https://github.com/matrix-org/matrix-appservice-node)
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org JS SDK
|
||||
categories: projects sdk
|
||||
author: Matrix.org team
|
||||
maturity: Early beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Matrix.org's JS SDK ([github](https://github.com/matrix-org/matrix-js-sdk))
|
@ -1,14 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org IRC Bridge
|
||||
categories: projects as
|
||||
description:
|
||||
author: Matrix.org team
|
||||
maturity: Early beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
An application service gateway from Matrix.org for bridging between IRC networks and Matrix, written using [matrix-appservice-node](http://matrix.org/blog/project/matrix-appservice-node/). You can get the [code on github](https://github.com/matrix-org/matrix-appservice-irc).
|
||||
|
||||
Supports dynamically bridging IRC channels on demand; synchronised user-lists, and other goodness.
|
||||
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: redpill IRC bridge
|
||||
categories: projects as
|
||||
author: Tjgillies
|
||||
maturity: Alpha
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Kodo's IRC<->matrix GW written in Ruby ([github](https://github.com/tjgillies/redpill))
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: gomatrix IRC bridge
|
||||
categories: projects as
|
||||
author: Tor
|
||||
maturity: Alpha
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Tor's IRC<->Matrix bridge written in the Go language ([github](https://github.com/torhve/gomirc))
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Matrix.org MatrixKit (iOS)
|
||||
categories: projects sdk
|
||||
author: Matrix.org team
|
||||
maturity: Late beta
|
||||
---
|
||||
|
||||
# {{ page.title }}
|
||||
Matrix.org's reusable UI interfaces for iOS ([github](https://github.com/matrix-org/matrix-ios-kit))
|
@ -1,16 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Riot iOS
|
||||
categories: projects client
|
||||
thumbnail: /docs/projects/images/vector-iOS-small.png
|
||||
description: Riot is a glossy client with an emphasis on performance and usability
|
||||
author: Riot.im
|
||||
maturity: Released
|
||||
---
|
||||
|
||||
![screenshot](/docs/projects/images/vector-iOS-large.png "{{ page.title }}")
|
||||
|
||||
# {{ page.title }}
|
||||
The iOS version of the [Riot](https://matrix.org/docs/projects/client/riot.html) web client. Riot is a glossy client with focus on performance and usability.
|
||||
|
||||
The code is available from [github](https://github.com/vector-im/vector-ios), and the app is available from the Apple [app store](https://itunes.apple.com/gb/app/vector.im/id1083446067?mt=8).
|
@ -1,10 +0,0 @@
|
||||
---
|
||||
layout: project
|
||||
title: Try Matrix Now!
|
||||
---
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0; url=./riot-ios.html" />
|
||||
</head>
|
||||
</html>
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue