1 | Matrix Javascript SDK
|
2 | =====================
|
3 |
|
4 | This is the [Matrix](https://matrix.org) Client-Server r0 SDK for
|
5 | JavaScript. This SDK can be run in a browser or in Node.js.
|
6 |
|
7 | Quickstart
|
8 | ==========
|
9 |
|
10 | In a browser
|
11 | ------------
|
12 | Download the browser version from
|
13 | https://github.com/matrix-org/matrix-js-sdk/releases/latest and add that as a
|
14 | ``<script>`` to your page. There will be a global variable ``matrixcs``
|
15 | attached to ``window`` through which you can access the SDK. See below for how to
|
16 | include libolm to enable end-to-end-encryption.
|
17 |
|
18 | The browser bundle supports recent versions of browsers. Typically this is ES2015
|
19 | or `> 0.5%, last 2 versions, Firefox ESR, not dead` if using
|
20 | [browserlists](https://github.com/browserslist/browserslist).
|
21 |
|
22 | Please check [the working browser example](examples/browser) for more information.
|
23 |
|
24 | In Node.js
|
25 | ----------
|
26 |
|
27 | Ensure you have the latest LTS version of Node.js installed.
|
28 |
|
29 | This SDK targets Node 10 for compatibility, which translates to ES6. If you're using
|
30 | a bundler like webpack you'll likely have to transpile dependencies, including this
|
31 | SDK, to match your target browsers.
|
32 |
|
33 | Using `yarn` instead of `npm` is recommended. Please see the Yarn [install guide](https://yarnpkg.com/docs/install/)
|
34 | if you do not have it already.
|
35 |
|
36 | ``yarn add matrix-js-sdk``
|
37 |
|
38 | ```javascript
|
39 | import * as sdk from "matrix-js-sdk";
|
40 | const client = sdk.createClient("https://matrix.org");
|
41 | client.publicRooms(function(err, data) {
|
42 | console.log("Public Rooms: %s", JSON.stringify(data));
|
43 | });
|
44 | ```
|
45 |
|
46 | See below for how to include libolm to enable end-to-end-encryption. Please check
|
47 | [the Node.js terminal app](examples/node) for a more complex example.
|
48 |
|
49 | To start the client:
|
50 |
|
51 | ```javascript
|
52 | await client.startClient({initialSyncLimit: 10});
|
53 | ```
|
54 |
|
55 | You can perform a call to `/sync` to get the current state of the client:
|
56 |
|
57 | ```javascript
|
58 | client.once('sync', function(state, prevState, res) {
|
59 | if(state === 'PREPARED') {
|
60 | console.log("prepared");
|
61 | } else {
|
62 | console.log(state);
|
63 | process.exit(1);
|
64 | }
|
65 | });
|
66 | ```
|
67 |
|
68 | To send a message:
|
69 |
|
70 | ```javascript
|
71 | const content = {
|
72 | "body": "message text",
|
73 | "msgtype": "m.text"
|
74 | };
|
75 | client.sendEvent("roomId", "m.room.message", content, "", (err, res) => {
|
76 | console.log(err);
|
77 | });
|
78 | ```
|
79 |
|
80 | To listen for message events:
|
81 |
|
82 | ```javascript
|
83 | client.on("Room.timeline", function(event, room, toStartOfTimeline) {
|
84 | if (event.getType() !== "m.room.message") {
|
85 | return; // only use messages
|
86 | }
|
87 | console.log(event.event.content.body);
|
88 | });
|
89 | ```
|
90 |
|
91 | By default, the `matrix-js-sdk` client uses the `MemoryStore` to store events as they are received. For example to iterate through the currently stored timeline for a room:
|
92 |
|
93 | ```javascript
|
94 | Object.keys(client.store.rooms).forEach((roomId) => {
|
95 | client.getRoom(roomId).timeline.forEach(t => {
|
96 | console.log(t.event);
|
97 | });
|
98 | });
|
99 | ```
|
100 |
|
101 | What does this SDK do?
|
102 | ----------------------
|
103 |
|
104 | This SDK provides a full object model around the Matrix Client-Server API and emits
|
105 | events for incoming data and state changes. Aside from wrapping the HTTP API, it:
|
106 | - Handles syncing (via `/initialSync` and `/events`)
|
107 | - Handles the generation of "friendly" room and member names.
|
108 | - Handles historical `RoomMember` information (e.g. display names).
|
109 | - Manages room member state across multiple events (e.g. it handles typing, power
|
110 | levels and membership changes).
|
111 | - Exposes high-level objects like `Rooms`, `RoomState`, `RoomMembers` and `Users`
|
112 | which can be listened to for things like name changes, new messages, membership
|
113 | changes, presence changes, and more.
|
114 | - Handle "local echo" of messages sent using the SDK. This means that messages
|
115 | that have just been sent will appear in the timeline as 'sending', until it
|
116 | completes. This is beneficial because it prevents there being a gap between
|
117 | hitting the send button and having the "remote echo" arrive.
|
118 | - Mark messages which failed to send as not sent.
|
119 | - Automatically retry requests to send messages due to network errors.
|
120 | - Automatically retry requests to send messages due to rate limiting errors.
|
121 | - Handle queueing of messages.
|
122 | - Handles pagination.
|
123 | - Handle assigning push actions for events.
|
124 | - Handles room initial sync on accepting invites.
|
125 | - Handles WebRTC calling.
|
126 |
|
127 | Later versions of the SDK will:
|
128 | - Expose a `RoomSummary` which would be suitable for a recents page.
|
129 | - Provide different pluggable storage layers (e.g. local storage, database-backed)
|
130 |
|
131 | Usage
|
132 | =====
|
133 |
|
134 |
|
135 | Conventions
|
136 | -----------
|
137 |
|
138 | ### Emitted events
|
139 |
|
140 | The SDK will emit events using an ``EventEmitter``. It also
|
141 | emits object models (e.g. ``Rooms``, ``RoomMembers``) when they
|
142 | are updated.
|
143 |
|
144 | ```javascript
|
145 | // Listen for low-level MatrixEvents
|
146 | client.on("event", function(event) {
|
147 | console.log(event.getType());
|
148 | });
|
149 |
|
150 | // Listen for typing changes
|
151 | client.on("RoomMember.typing", function(event, member) {
|
152 | if (member.typing) {
|
153 | console.log(member.name + " is typing...");
|
154 | }
|
155 | else {
|
156 | console.log(member.name + " stopped typing.");
|
157 | }
|
158 | });
|
159 |
|
160 | // start the client to setup the connection to the server
|
161 | client.startClient();
|
162 | ```
|
163 |
|
164 | ### Promises and Callbacks
|
165 |
|
166 | Most of the methods in the SDK are asynchronous: they do not directly return a
|
167 | result, but instead return a [Promise](http://documentup.com/kriskowal/q/)
|
168 | which will be fulfilled in the future.
|
169 |
|
170 | The typical usage is something like:
|
171 |
|
172 | ```javascript
|
173 | matrixClient.someMethod(arg1, arg2).then(function(result) {
|
174 | ...
|
175 | });
|
176 | ```
|
177 |
|
178 | Alternatively, if you have a Node.js-style ``callback(err, result)`` function,
|
179 | you can pass the result of the promise into it with something like:
|
180 |
|
181 | ```javascript
|
182 | matrixClient.someMethod(arg1, arg2).nodeify(callback);
|
183 | ```
|
184 |
|
185 | The main thing to note is that it is an error to discard the result of a
|
186 | promise-returning function, as that will cause exceptions to go unobserved. If
|
187 | you have nothing better to do with the result, just call ``.done()`` on it. See
|
188 | http://documentup.com/kriskowal/q/#the-end for more information.
|
189 |
|
190 | Methods which return a promise show this in their documentation.
|
191 |
|
192 | Many methods in the SDK support *both* Node.js-style callbacks *and* Promises,
|
193 | via an optional ``callback`` argument. The callback support is now deprecated:
|
194 | new methods do not include a ``callback`` argument, and in the future it may be
|
195 | removed from existing methods.
|
196 |
|
197 | Examples
|
198 | --------
|
199 | This section provides some useful code snippets which demonstrate the
|
200 | core functionality of the SDK. These examples assume the SDK is setup like this:
|
201 |
|
202 | ```javascript
|
203 | import * as sdk from "matrix-js-sdk";
|
204 | const myUserId = "@example:localhost";
|
205 | const myAccessToken = "QGV4YW1wbGU6bG9jYWxob3N0.qPEvLuYfNBjxikiCjP";
|
206 | const matrixClient = sdk.createClient({
|
207 | baseUrl: "http://localhost:8008",
|
208 | accessToken: myAccessToken,
|
209 | userId: myUserId
|
210 | });
|
211 | ```
|
212 |
|
213 | ### Automatically join rooms when invited
|
214 |
|
215 | ```javascript
|
216 | matrixClient.on("RoomMember.membership", function(event, member) {
|
217 | if (member.membership === "invite" && member.userId === myUserId) {
|
218 | matrixClient.joinRoom(member.roomId).then(function() {
|
219 | console.log("Auto-joined %s", member.roomId);
|
220 | });
|
221 | }
|
222 | });
|
223 |
|
224 | matrixClient.startClient();
|
225 | ```
|
226 |
|
227 | ### Print out messages for all rooms
|
228 |
|
229 | ```javascript
|
230 | matrixClient.on("Room.timeline", function(event, room, toStartOfTimeline) {
|
231 | if (toStartOfTimeline) {
|
232 | return; // don't print paginated results
|
233 | }
|
234 | if (event.getType() !== "m.room.message") {
|
235 | return; // only print messages
|
236 | }
|
237 | console.log(
|
238 | // the room name will update with m.room.name events automatically
|
239 | "(%s) %s :: %s", room.name, event.getSender(), event.getContent().body
|
240 | );
|
241 | });
|
242 |
|
243 | matrixClient.startClient();
|
244 | ```
|
245 |
|
246 | Output:
|
247 | ```
|
248 | (My Room) @megan:localhost :: Hello world
|
249 | (My Room) @megan:localhost :: how are you?
|
250 | (My Room) @example:localhost :: I am good
|
251 | (My Room) @example:localhost :: change the room name
|
252 | (My New Room) @megan:localhost :: done
|
253 | ```
|
254 |
|
255 | ### Print out membership lists whenever they are changed
|
256 |
|
257 | ```javascript
|
258 | matrixClient.on("RoomState.members", function(event, state, member) {
|
259 | const room = matrixClient.getRoom(state.roomId);
|
260 | if (!room) {
|
261 | return;
|
262 | }
|
263 | const memberList = state.getMembers();
|
264 | console.log(room.name);
|
265 | console.log(Array(room.name.length + 1).join("=")); // underline
|
266 | for (var i = 0; i < memberList.length; i++) {
|
267 | console.log(
|
268 | "(%s) %s",
|
269 | memberList[i].membership,
|
270 | memberList[i].name
|
271 | );
|
272 | }
|
273 | });
|
274 |
|
275 | matrixClient.startClient();
|
276 | ```
|
277 |
|
278 | Output:
|
279 | ```
|
280 | My Room
|
281 | =======
|
282 | (join) @example:localhost
|
283 | (leave) @alice:localhost
|
284 | (join) Bob
|
285 | (invite) @charlie:localhost
|
286 | ```
|
287 |
|
288 | API Reference
|
289 | =============
|
290 |
|
291 | A hosted reference can be found at
|
292 | http://matrix-org.github.io/matrix-js-sdk/index.html
|
293 |
|
294 | This SDK uses JSDoc3 style comments. You can manually build and
|
295 | host the API reference from the source files like this:
|
296 |
|
297 | ```
|
298 | $ yarn gendoc
|
299 | $ cd .jsdoc
|
300 | $ python -m SimpleHTTPServer 8005
|
301 | ```
|
302 |
|
303 | Then visit ``http://localhost:8005`` to see the API docs.
|
304 |
|
305 | End-to-end encryption support
|
306 | =============================
|
307 |
|
308 | The SDK supports end-to-end encryption via the Olm and Megolm protocols, using
|
309 | [libolm](https://gitlab.matrix.org/matrix-org/olm). It is left up to the
|
310 | application to make libolm available, via the ``Olm`` global.
|
311 |
|
312 | It is also necessry to call ``matrixClient.initCrypto()`` after creating a new
|
313 | ``MatrixClient`` (but **before** calling ``matrixClient.startClient()``) to
|
314 | initialise the crypto layer.
|
315 |
|
316 | If the ``Olm`` global is not available, the SDK will show a warning, as shown
|
317 | below; ``initCrypto()`` will also fail.
|
318 |
|
319 | ```
|
320 | Unable to load crypto module: crypto will be disabled: Error: global.Olm is not defined
|
321 | ```
|
322 |
|
323 | If the crypto layer is not (successfully) initialised, the SDK will continue to
|
324 | work for unencrypted rooms, but it will not support the E2E parts of the Matrix
|
325 | specification.
|
326 |
|
327 | To provide the Olm library in a browser application:
|
328 |
|
329 | * download the transpiled libolm (from https://packages.matrix.org/npm/olm/).
|
330 | * load ``olm.js`` as a ``<script>`` *before* ``browser-matrix.js``.
|
331 |
|
332 | To provide the Olm library in a node.js application:
|
333 |
|
334 | * ``yarn add https://packages.matrix.org/npm/olm/olm-3.1.4.tgz``
|
335 | (replace the URL with the latest version you want to use from
|
336 | https://packages.matrix.org/npm/olm/)
|
337 | * ``global.Olm = require('olm');`` *before* loading ``matrix-js-sdk``.
|
338 |
|
339 | If you want to package Olm as dependency for your node.js application, you can
|
340 | use ``yarn add https://packages.matrix.org/npm/olm/olm-3.1.4.tgz``. If your
|
341 | application also works without e2e crypto enabled, add ``--optional`` to mark it
|
342 | as an optional dependency.
|
343 |
|
344 |
|
345 | Contributing
|
346 | ============
|
347 | *This section is for people who want to modify the SDK. If you just
|
348 | want to use this SDK, skip this section.*
|
349 |
|
350 | First, you need to pull in the right build tools:
|
351 | ```
|
352 | $ yarn install
|
353 | ```
|
354 |
|
355 | Building
|
356 | --------
|
357 |
|
358 | To build a browser version from scratch when developing::
|
359 | ```
|
360 | $ yarn build
|
361 | ```
|
362 |
|
363 | To run tests (Jasmine)::
|
364 | ```
|
365 | $ yarn test
|
366 | ```
|
367 |
|
368 | To run linting:
|
369 | ```
|
370 | $ yarn lint
|
371 | ```
|