You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-07-30 04:23:07 +03:00
Add examples to show it working in node and browser
This commit is contained in:
10
examples/browser/browserTest.js
Normal file
10
examples/browser/browserTest.js
Normal file
@ -0,0 +1,10 @@
|
||||
"use strict";
|
||||
console.log("Loading browser sdk");
|
||||
|
||||
matrixcs.request(request);
|
||||
|
||||
var client = matrixcs.createClient("http://matrix.org");
|
||||
client.publicRooms(function (err, data) {
|
||||
console.log("data %s", JSON.stringify(data));
|
||||
console.error("err %s", JSON.stringify(err));
|
||||
});
|
13
examples/browser/index.html
Normal file
13
examples/browser/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Test</title>
|
||||
<script src="lib/matrix.js"></script>
|
||||
<script src="lib/browser-request.js"></script>
|
||||
<script src="browserTest.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
Sanity Testing (check the console) : This example is here to make sure that
|
||||
the SDK works inside a browser. It simply does a GET /publicRooms on
|
||||
matrix.org
|
||||
</body>
|
||||
</html>
|
498
examples/browser/lib/browser-request.js
Normal file
498
examples/browser/lib/browser-request.js
Normal file
@ -0,0 +1,498 @@
|
||||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.request = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||
// Browser Request
|
||||
//
|
||||
// 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.
|
||||
|
||||
// UMD HEADER START
|
||||
(function (root, factory) {
|
||||
if (typeof define === 'function' && define.amd) {
|
||||
// AMD. Register as an anonymous module.
|
||||
define([], factory);
|
||||
} else if (typeof exports === 'object') {
|
||||
// Node. Does not work with strict CommonJS, but
|
||||
// only CommonJS-like enviroments that support module.exports,
|
||||
// like Node.
|
||||
module.exports = factory();
|
||||
} else {
|
||||
// Browser globals (root is window)
|
||||
root.returnExports = factory();
|
||||
}
|
||||
}(this, function () {
|
||||
// UMD HEADER END
|
||||
|
||||
var XHR = XMLHttpRequest
|
||||
if (!XHR) throw new Error('missing XMLHttpRequest')
|
||||
request.log = {
|
||||
'trace': noop, 'debug': noop, 'info': noop, 'warn': noop, 'error': noop
|
||||
}
|
||||
|
||||
var DEFAULT_TIMEOUT = 3 * 60 * 1000 // 3 minutes
|
||||
|
||||
//
|
||||
// request
|
||||
//
|
||||
|
||||
function request(options, callback) {
|
||||
// The entry-point to the API: prep the options object and pass the real work to run_xhr.
|
||||
if(typeof callback !== 'function')
|
||||
throw new Error('Bad callback given: ' + callback)
|
||||
|
||||
if(!options)
|
||||
throw new Error('No options given')
|
||||
|
||||
var options_onResponse = options.onResponse; // Save this for later.
|
||||
|
||||
if(typeof options === 'string')
|
||||
options = {'uri':options};
|
||||
else
|
||||
options = JSON.parse(JSON.stringify(options)); // Use a duplicate for mutating.
|
||||
|
||||
options.onResponse = options_onResponse // And put it back.
|
||||
|
||||
if (options.verbose) request.log = getLogger();
|
||||
|
||||
if(options.url) {
|
||||
options.uri = options.url;
|
||||
delete options.url;
|
||||
}
|
||||
|
||||
if(!options.uri && options.uri !== "")
|
||||
throw new Error("options.uri is a required argument");
|
||||
|
||||
if(typeof options.uri != "string")
|
||||
throw new Error("options.uri must be a string");
|
||||
|
||||
var unsupported_options = ['proxy', '_redirectsFollowed', 'maxRedirects', 'followRedirect']
|
||||
for (var i = 0; i < unsupported_options.length; i++)
|
||||
if(options[ unsupported_options[i] ])
|
||||
throw new Error("options." + unsupported_options[i] + " is not supported")
|
||||
|
||||
options.callback = callback
|
||||
options.method = options.method || 'GET';
|
||||
options.headers = options.headers || {};
|
||||
options.body = options.body || null
|
||||
options.timeout = options.timeout || request.DEFAULT_TIMEOUT
|
||||
|
||||
if(options.headers.host)
|
||||
throw new Error("Options.headers.host is not supported");
|
||||
|
||||
if(options.json) {
|
||||
options.headers.accept = options.headers.accept || 'application/json'
|
||||
if(options.method !== 'GET')
|
||||
options.headers['content-type'] = 'application/json'
|
||||
|
||||
if(typeof options.json !== 'boolean')
|
||||
options.body = JSON.stringify(options.json)
|
||||
else if(typeof options.body !== 'string')
|
||||
options.body = JSON.stringify(options.body)
|
||||
}
|
||||
|
||||
//BEGIN QS Hack
|
||||
var serialize = function(obj) {
|
||||
var str = [];
|
||||
for(var p in obj)
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
str.push(encodeURIComponent(p) + "=" + encodeURIComponent(obj[p]));
|
||||
}
|
||||
return str.join("&");
|
||||
}
|
||||
|
||||
if(options.qs){
|
||||
var qs = (typeof options.qs == 'string')? options.qs : serialize(options.qs);
|
||||
if(options.uri.indexOf('?') !== -1){ //no get params
|
||||
options.uri = options.uri+'&'+qs;
|
||||
}else{ //existing get params
|
||||
options.uri = options.uri+'?'+qs;
|
||||
}
|
||||
}
|
||||
//END QS Hack
|
||||
|
||||
//BEGIN FORM Hack
|
||||
var multipart = function(obj) {
|
||||
//todo: support file type (useful?)
|
||||
var result = {};
|
||||
result.boundry = '-------------------------------'+Math.floor(Math.random()*1000000000);
|
||||
var lines = [];
|
||||
for(var p in obj){
|
||||
if (obj.hasOwnProperty(p)) {
|
||||
lines.push(
|
||||
'--'+result.boundry+"\n"+
|
||||
'Content-Disposition: form-data; name="'+p+'"'+"\n"+
|
||||
"\n"+
|
||||
obj[p]+"\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
lines.push( '--'+result.boundry+'--' );
|
||||
result.body = lines.join('');
|
||||
result.length = result.body.length;
|
||||
result.type = 'multipart/form-data; boundary='+result.boundry;
|
||||
return result;
|
||||
}
|
||||
|
||||
if(options.form){
|
||||
if(typeof options.form == 'string') throw('form name unsupported');
|
||||
if(options.method === 'POST'){
|
||||
var encoding = (options.encoding || 'application/x-www-form-urlencoded').toLowerCase();
|
||||
options.headers['content-type'] = encoding;
|
||||
switch(encoding){
|
||||
case 'application/x-www-form-urlencoded':
|
||||
options.body = serialize(options.form).replace(/%20/g, "+");
|
||||
break;
|
||||
case 'multipart/form-data':
|
||||
var multi = multipart(options.form);
|
||||
//options.headers['content-length'] = multi.length;
|
||||
options.body = multi.body;
|
||||
options.headers['content-type'] = multi.type;
|
||||
break;
|
||||
default : throw new Error('unsupported encoding:'+encoding);
|
||||
}
|
||||
}
|
||||
}
|
||||
//END FORM Hack
|
||||
|
||||
// If onResponse is boolean true, call back immediately when the response is known,
|
||||
// not when the full request is complete.
|
||||
options.onResponse = options.onResponse || noop
|
||||
if(options.onResponse === true) {
|
||||
options.onResponse = callback
|
||||
options.callback = noop
|
||||
}
|
||||
|
||||
// XXX Browsers do not like this.
|
||||
//if(options.body)
|
||||
// options.headers['content-length'] = options.body.length;
|
||||
|
||||
// HTTP basic authentication
|
||||
if(!options.headers.authorization && options.auth)
|
||||
options.headers.authorization = 'Basic ' + b64_enc(options.auth.username + ':' + options.auth.password);
|
||||
|
||||
return run_xhr(options)
|
||||
}
|
||||
|
||||
var req_seq = 0
|
||||
function run_xhr(options) {
|
||||
var xhr = new XHR
|
||||
, timed_out = false
|
||||
, is_cors = is_crossDomain(options.uri)
|
||||
, supports_cors = ('withCredentials' in xhr)
|
||||
|
||||
req_seq += 1
|
||||
xhr.seq_id = req_seq
|
||||
xhr.id = req_seq + ': ' + options.method + ' ' + options.uri
|
||||
xhr._id = xhr.id // I know I will type "_id" from habit all the time.
|
||||
|
||||
if(is_cors && !supports_cors) {
|
||||
var cors_err = new Error('Browser does not support cross-origin request: ' + options.uri)
|
||||
cors_err.cors = 'unsupported'
|
||||
return options.callback(cors_err, xhr)
|
||||
}
|
||||
|
||||
xhr.timeoutTimer = setTimeout(too_late, options.timeout)
|
||||
function too_late() {
|
||||
timed_out = true
|
||||
var er = new Error('ETIMEDOUT')
|
||||
er.code = 'ETIMEDOUT'
|
||||
er.duration = options.timeout
|
||||
|
||||
request.log.error('Timeout', { 'id':xhr._id, 'milliseconds':options.timeout })
|
||||
return options.callback(er, xhr)
|
||||
}
|
||||
|
||||
// Some states can be skipped over, so remember what is still incomplete.
|
||||
var did = {'response':false, 'loading':false, 'end':false}
|
||||
|
||||
xhr.onreadystatechange = on_state_change
|
||||
xhr.open(options.method, options.uri, true) // asynchronous
|
||||
if(is_cors)
|
||||
xhr.withCredentials = !! options.withCredentials
|
||||
xhr.send(options.body)
|
||||
return xhr
|
||||
|
||||
function on_state_change(event) {
|
||||
if(timed_out)
|
||||
return request.log.debug('Ignoring timed out state change', {'state':xhr.readyState, 'id':xhr.id})
|
||||
|
||||
request.log.debug('State change', {'state':xhr.readyState, 'id':xhr.id, 'timed_out':timed_out})
|
||||
|
||||
if(xhr.readyState === XHR.OPENED) {
|
||||
request.log.debug('Request started', {'id':xhr.id})
|
||||
for (var key in options.headers)
|
||||
xhr.setRequestHeader(key, options.headers[key])
|
||||
}
|
||||
|
||||
else if(xhr.readyState === XHR.HEADERS_RECEIVED)
|
||||
on_response()
|
||||
|
||||
else if(xhr.readyState === XHR.LOADING) {
|
||||
on_response()
|
||||
on_loading()
|
||||
}
|
||||
|
||||
else if(xhr.readyState === XHR.DONE) {
|
||||
on_response()
|
||||
on_loading()
|
||||
on_end()
|
||||
}
|
||||
}
|
||||
|
||||
function on_response() {
|
||||
if(did.response)
|
||||
return
|
||||
|
||||
did.response = true
|
||||
request.log.debug('Got response', {'id':xhr.id, 'status':xhr.status})
|
||||
clearTimeout(xhr.timeoutTimer)
|
||||
xhr.statusCode = xhr.status // Node request compatibility
|
||||
|
||||
// Detect failed CORS requests.
|
||||
if(is_cors && xhr.statusCode == 0) {
|
||||
var cors_err = new Error('CORS request rejected: ' + options.uri)
|
||||
cors_err.cors = 'rejected'
|
||||
|
||||
// Do not process this request further.
|
||||
did.loading = true
|
||||
did.end = true
|
||||
|
||||
return options.callback(cors_err, xhr)
|
||||
}
|
||||
|
||||
options.onResponse(null, xhr)
|
||||
}
|
||||
|
||||
function on_loading() {
|
||||
if(did.loading)
|
||||
return
|
||||
|
||||
did.loading = true
|
||||
request.log.debug('Response body loading', {'id':xhr.id})
|
||||
// TODO: Maybe simulate "data" events by watching xhr.responseText
|
||||
}
|
||||
|
||||
function on_end() {
|
||||
if(did.end)
|
||||
return
|
||||
|
||||
did.end = true
|
||||
request.log.debug('Request done', {'id':xhr.id})
|
||||
|
||||
xhr.body = xhr.responseText
|
||||
if(options.json) {
|
||||
try { xhr.body = JSON.parse(xhr.responseText) }
|
||||
catch (er) { return options.callback(er, xhr) }
|
||||
}
|
||||
|
||||
options.callback(null, xhr, xhr.body)
|
||||
}
|
||||
|
||||
} // request
|
||||
|
||||
request.withCredentials = false;
|
||||
request.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
|
||||
|
||||
//
|
||||
// defaults
|
||||
//
|
||||
|
||||
request.defaults = function(options, requester) {
|
||||
var def = function (method) {
|
||||
var d = function (params, callback) {
|
||||
if(typeof params === 'string')
|
||||
params = {'uri': params};
|
||||
else {
|
||||
params = JSON.parse(JSON.stringify(params));
|
||||
}
|
||||
for (var i in options) {
|
||||
if (params[i] === undefined) params[i] = options[i]
|
||||
}
|
||||
return method(params, callback)
|
||||
}
|
||||
return d
|
||||
}
|
||||
var de = def(request)
|
||||
de.get = def(request.get)
|
||||
de.post = def(request.post)
|
||||
de.put = def(request.put)
|
||||
de.head = def(request.head)
|
||||
return de
|
||||
}
|
||||
|
||||
//
|
||||
// HTTP method shortcuts
|
||||
//
|
||||
|
||||
var shortcuts = [ 'get', 'put', 'post', 'head' ];
|
||||
shortcuts.forEach(function(shortcut) {
|
||||
var method = shortcut.toUpperCase();
|
||||
var func = shortcut.toLowerCase();
|
||||
|
||||
request[func] = function(opts) {
|
||||
if(typeof opts === 'string')
|
||||
opts = {'method':method, 'uri':opts};
|
||||
else {
|
||||
opts = JSON.parse(JSON.stringify(opts));
|
||||
opts.method = method;
|
||||
}
|
||||
|
||||
var args = [opts].concat(Array.prototype.slice.apply(arguments, [1]));
|
||||
return request.apply(this, args);
|
||||
}
|
||||
})
|
||||
|
||||
//
|
||||
// CouchDB shortcut
|
||||
//
|
||||
|
||||
request.couch = function(options, callback) {
|
||||
if(typeof options === 'string')
|
||||
options = {'uri':options}
|
||||
|
||||
// Just use the request API to do JSON.
|
||||
options.json = true
|
||||
if(options.body)
|
||||
options.json = options.body
|
||||
delete options.body
|
||||
|
||||
callback = callback || noop
|
||||
|
||||
var xhr = request(options, couch_handler)
|
||||
return xhr
|
||||
|
||||
function couch_handler(er, resp, body) {
|
||||
if(er)
|
||||
return callback(er, resp, body)
|
||||
|
||||
if((resp.statusCode < 200 || resp.statusCode > 299) && body.error) {
|
||||
// The body is a Couch JSON object indicating the error.
|
||||
er = new Error('CouchDB error: ' + (body.error.reason || body.error.error))
|
||||
for (var key in body)
|
||||
er[key] = body[key]
|
||||
return callback(er, resp, body);
|
||||
}
|
||||
|
||||
return callback(er, resp, body);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Utility
|
||||
//
|
||||
|
||||
function noop() {}
|
||||
|
||||
function getLogger() {
|
||||
var logger = {}
|
||||
, levels = ['trace', 'debug', 'info', 'warn', 'error']
|
||||
, level, i
|
||||
|
||||
for(i = 0; i < levels.length; i++) {
|
||||
level = levels[i]
|
||||
|
||||
logger[level] = noop
|
||||
if(typeof console !== 'undefined' && console && console[level])
|
||||
logger[level] = formatted(console, level)
|
||||
}
|
||||
|
||||
return logger
|
||||
}
|
||||
|
||||
function formatted(obj, method) {
|
||||
return formatted_logger
|
||||
|
||||
function formatted_logger(str, context) {
|
||||
if(typeof context === 'object')
|
||||
str += ' ' + JSON.stringify(context)
|
||||
|
||||
return obj[method].call(obj, str)
|
||||
}
|
||||
}
|
||||
|
||||
// Return whether a URL is a cross-domain request.
|
||||
function is_crossDomain(url) {
|
||||
var rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/
|
||||
|
||||
// jQuery #8138, IE may throw an exception when accessing
|
||||
// a field from window.location if document.domain has been set
|
||||
var ajaxLocation
|
||||
try { ajaxLocation = location.href }
|
||||
catch (e) {
|
||||
// Use the href attribute of an A element since IE will modify it given document.location
|
||||
ajaxLocation = document.createElement( "a" );
|
||||
ajaxLocation.href = "";
|
||||
ajaxLocation = ajaxLocation.href;
|
||||
}
|
||||
|
||||
var ajaxLocParts = rurl.exec(ajaxLocation.toLowerCase()) || []
|
||||
, parts = rurl.exec(url.toLowerCase() )
|
||||
|
||||
var result = !!(
|
||||
parts &&
|
||||
( parts[1] != ajaxLocParts[1]
|
||||
|| parts[2] != ajaxLocParts[2]
|
||||
|| (parts[3] || (parts[1] === "http:" ? 80 : 443)) != (ajaxLocParts[3] || (ajaxLocParts[1] === "http:" ? 80 : 443))
|
||||
)
|
||||
)
|
||||
|
||||
//console.debug('is_crossDomain('+url+') -> ' + result)
|
||||
return result
|
||||
}
|
||||
|
||||
// MIT License from http://phpjs.org/functions/base64_encode:358
|
||||
function b64_enc (data) {
|
||||
// Encodes string using MIME base64 algorithm
|
||||
var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
|
||||
var o1, o2, o3, h1, h2, h3, h4, bits, i = 0, ac = 0, enc="", tmp_arr = [];
|
||||
|
||||
if (!data) {
|
||||
return data;
|
||||
}
|
||||
|
||||
// assume utf8 data
|
||||
// data = this.utf8_encode(data+'');
|
||||
|
||||
do { // pack three octets into four hexets
|
||||
o1 = data.charCodeAt(i++);
|
||||
o2 = data.charCodeAt(i++);
|
||||
o3 = data.charCodeAt(i++);
|
||||
|
||||
bits = o1<<16 | o2<<8 | o3;
|
||||
|
||||
h1 = bits>>18 & 0x3f;
|
||||
h2 = bits>>12 & 0x3f;
|
||||
h3 = bits>>6 & 0x3f;
|
||||
h4 = bits & 0x3f;
|
||||
|
||||
// use hexets to index into b64, and append result to encoded string
|
||||
tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
|
||||
} while (i < data.length);
|
||||
|
||||
enc = tmp_arr.join('');
|
||||
|
||||
switch (data.length % 3) {
|
||||
case 1:
|
||||
enc = enc.slice(0, -2) + '==';
|
||||
break;
|
||||
case 2:
|
||||
enc = enc.slice(0, -1) + '=';
|
||||
break;
|
||||
}
|
||||
|
||||
return enc;
|
||||
}
|
||||
return request;
|
||||
//UMD FOOTER START
|
||||
}));
|
||||
//UMD FOOTER END
|
||||
|
||||
},{}]},{},[1])(1)
|
||||
});
|
605
examples/browser/lib/matrix.js
Normal file
605
examples/browser/lib/matrix.js
Normal file
@ -0,0 +1,605 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- CS: complete register function (doing stages)
|
||||
- Internal: rate limiting
|
||||
- Identity server: linkEmail, authEmail, bindEmail, lookup3pid
|
||||
- uploadContent (?)
|
||||
*/
|
||||
|
||||
// wrap in a closure for browsers
|
||||
var init = function(exports){
|
||||
// expose the underlying request object so different environments can use
|
||||
// different request libs (e.g. request or browser-request)
|
||||
var request;
|
||||
exports.request = function(r) {
|
||||
request = r;
|
||||
};
|
||||
|
||||
// entry point
|
||||
function MatrixClient(credentials) {
|
||||
if (typeof credentials === "string") {
|
||||
credentials = {
|
||||
"baseUrl": credentials
|
||||
};
|
||||
}
|
||||
var requiredKeys = [
|
||||
"baseUrl"
|
||||
];
|
||||
for (var i=0; i<requiredKeys.length; i++) {
|
||||
if (!credentials.hasOwnProperty(requiredKeys[i])) {
|
||||
throw new Error("Missing required key: " + requiredKeys[i]);
|
||||
}
|
||||
}
|
||||
if (credentials.noUserAgent) {
|
||||
HEADERS = undefined;
|
||||
}
|
||||
this.credentials = credentials;
|
||||
};
|
||||
exports.MatrixClient = MatrixClient; // expose the class
|
||||
exports.createClient = function(credentials) {
|
||||
return new MatrixClient(credentials);
|
||||
};
|
||||
|
||||
var CLIENT_PREFIX = "/_matrix/client/api/v1";
|
||||
var HEADERS = {
|
||||
"User-Agent": "matrix-js"
|
||||
};
|
||||
|
||||
MatrixClient.prototype = {
|
||||
isLoggedIn: function() {
|
||||
return this.credentials.accessToken != undefined &&
|
||||
this.credentials.userId != undefined;
|
||||
},
|
||||
|
||||
// Room operations
|
||||
// ===============
|
||||
|
||||
createRoom: function(options, callback) {
|
||||
// valid options include: room_alias_name, visibility, invite
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", "/createRoom", undefined, options
|
||||
);
|
||||
},
|
||||
|
||||
joinRoom: function(roomIdOrAlias, callback) {
|
||||
var path = encodeUri("/join/$roomid", { $roomid: roomIdOrAlias});
|
||||
return this._doAuthedRequest(callback, "POST", path, undefined, {});
|
||||
},
|
||||
|
||||
setRoomName: function(roomId, name, callback) {
|
||||
return this.sendStateEvent(roomId, "m.room.name", {name: name},
|
||||
undefined, callback);
|
||||
},
|
||||
|
||||
setRoomTopic: function(roomId, topic, callback) {
|
||||
return this.sendStateEvent(roomId, "m.room.topic", {topic: topic},
|
||||
undefined, callback);
|
||||
},
|
||||
|
||||
setPowerLevel: function(roomId, userId, powerLevel, event, callback) {
|
||||
var content = {
|
||||
users: {}
|
||||
};
|
||||
if (event && event.type == "m.room.power_levels") {
|
||||
content = event.content;
|
||||
}
|
||||
content.users[userId] = powerLevel;
|
||||
var path = encodeUri("/rooms/$roomId/state/m.room.power_levels", {
|
||||
$roomId: roomId
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
);
|
||||
},
|
||||
|
||||
sendStateEvent: function(roomId, eventType, content, stateKey,
|
||||
callback) {
|
||||
var pathParams = {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$stateKey: stateKey
|
||||
};
|
||||
var path = encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
if (stateKey !== undefined) {
|
||||
path = encodeUri(path + "/$stateKey", pathParams);
|
||||
}
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
);
|
||||
},
|
||||
|
||||
sendEvent: function(roomId, eventType, content, txnId, callback) {
|
||||
if (isFunction(txnId)) { callback = txnId; txnId = undefined; }
|
||||
|
||||
if (!txnId) {
|
||||
txnId = "m" + new Date().getTime();
|
||||
}
|
||||
|
||||
var path = encodeUri("/rooms/$roomId/send/$eventType/$txnId", {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$txnId: txnId
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
)
|
||||
},
|
||||
|
||||
sendMessage: function(roomId, content, txnId, callback) {
|
||||
if (isFunction(txnId)) { callback = txnId; txnId = undefined; }
|
||||
return this.sendEvent(
|
||||
roomId, "m.room.message", content, txnId, callback
|
||||
);
|
||||
},
|
||||
|
||||
sendTextMessage: function(roomId, body, txnId, callback) {
|
||||
var content = {
|
||||
msgtype: "m.text",
|
||||
body: body
|
||||
};
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
},
|
||||
|
||||
sendEmoteMessage: function(roomId, body, txnId, callback) {
|
||||
var content = {
|
||||
msgtype: "m.emote",
|
||||
body: body
|
||||
};
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
},
|
||||
|
||||
sendImageMessage: function(roomId, url, info, text, callback) {
|
||||
if (isFunction(text)) { callback = text; text = undefined; }
|
||||
if (!text) { text = "Image"; }
|
||||
var content = {
|
||||
msgtype: "m.image",
|
||||
url: url,
|
||||
info: info,
|
||||
body: text
|
||||
};
|
||||
return this.sendMessage(roomId, content, callback);
|
||||
},
|
||||
|
||||
sendHtmlMessage: function(roomId, body, htmlBody, callback) {
|
||||
var content = {
|
||||
msgtype: "m.text",
|
||||
format: "org.matrix.custom.html",
|
||||
body: body,
|
||||
formatted_body: htmlBody
|
||||
};
|
||||
return this.sendMessage(roomId, content, callback);
|
||||
},
|
||||
|
||||
sendTyping: function(roomId, isTyping, timeoutMs, callback) {
|
||||
var path = encodeUri("/rooms/$roomId/typing/$userId", {
|
||||
$roomId: roomId,
|
||||
$userId: this.credentials.userId
|
||||
});
|
||||
var data = {
|
||||
typing: isTyping
|
||||
};
|
||||
if (isTyping) {
|
||||
data.timeout = timeoutMs ? timeoutMs : 20000
|
||||
}
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
redactEvent: function(roomId, eventId, callback) {
|
||||
var path = encodeUri("/rooms/$roomId/redact/$eventId", {
|
||||
$roomId: roomId,
|
||||
$eventId: eventId
|
||||
});
|
||||
return this._doAuthedRequest(callback, "POST", path, undefined, {});
|
||||
},
|
||||
|
||||
invite: function(roomId, userId, callback) {
|
||||
return this._membershipChange(roomId, userId, "invite", undefined,
|
||||
callback);
|
||||
},
|
||||
|
||||
leave: function(roomId, callback) {
|
||||
return this._membershipChange(roomId, undefined, "leave", undefined,
|
||||
callback);
|
||||
},
|
||||
|
||||
ban: function(roomId, userId, reason, callback) {
|
||||
return this._membershipChange(roomId, userId, "ban", reason,
|
||||
callback);
|
||||
},
|
||||
|
||||
unban: function(roomId, userId, callback) {
|
||||
// unbanning = set their state to leave
|
||||
return this._setMembershipState(
|
||||
roomId, userId, "leave", undefined, callback
|
||||
);
|
||||
},
|
||||
|
||||
kick: function(roomId, userId, reason, callback) {
|
||||
return this._setMembershipState(
|
||||
roomId, userId, "leave", reason, callback
|
||||
);
|
||||
},
|
||||
|
||||
_setMembershipState: function(roomId, userId, membershipValue, reason,
|
||||
callback) {
|
||||
if (isFunction(reason)) { callback = reason; reason = undefined; }
|
||||
|
||||
var path = encodeUri(
|
||||
"/rooms/$roomId/state/m.room.member/$userId",
|
||||
{ $roomId: roomId, $userId: userId}
|
||||
);
|
||||
|
||||
return this._doAuthedRequest(callback, "PUT", path, undefined, {
|
||||
membership : membershipValue,
|
||||
reason: reason
|
||||
});
|
||||
},
|
||||
|
||||
_membershipChange: function(roomId, userId, membership, reason,
|
||||
callback) {
|
||||
if (isFunction(reason)) { callback = reason; reason = undefined; }
|
||||
|
||||
var path = encodeUri("/rooms/$room_id/$membership", {
|
||||
$room_id: roomId,
|
||||
$membership: membership
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", path, undefined, {
|
||||
user_id: userId, // may be undefined e.g. on leave
|
||||
reason: reason
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Profile operations
|
||||
// ==================
|
||||
|
||||
getProfileInfo: function(userId, info, callback) {
|
||||
if (isFunction(info)) { callback = info; info = undefined; }
|
||||
|
||||
var path = info ?
|
||||
encodeUri("/profile/$userId/$info",
|
||||
{ $userId: userId, $info: info } ) :
|
||||
encodeUri("/profile/$userId",
|
||||
{ $userId: userId } );
|
||||
return this._doAuthedRequest(callback, "GET", path);
|
||||
},
|
||||
|
||||
setProfileInfo: function(info, data, callback) {
|
||||
var path = encodeUri("/profile/$userId/$info", {
|
||||
$userId: this.credentials.userId,
|
||||
$info: info
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
setDisplayName: function(name, callback) {
|
||||
return this.setProfileInfo(
|
||||
"displayname", { displayname: name }, callback
|
||||
);
|
||||
},
|
||||
|
||||
setAvatarUrl: function(url, callback) {
|
||||
return this.setProfileInfo(
|
||||
"avatar_url", { avatar_url: url }, callback
|
||||
);
|
||||
},
|
||||
|
||||
setPresence: function(presence, callback) {
|
||||
var path = encodeUri("/presence/$userId/status", {
|
||||
$userId: this.credentials.userId
|
||||
});
|
||||
var validStates = ["offline", "online", "unavailable"];
|
||||
if (validStates.indexOf(presence) == -1) {
|
||||
throw new Error("Bad presence value: "+presence);
|
||||
}
|
||||
var content = {
|
||||
presence: presence
|
||||
};
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
);
|
||||
},
|
||||
|
||||
// Public (non-authed) operations
|
||||
// ==============================
|
||||
|
||||
publicRooms: function(callback) {
|
||||
return this._doRequest(callback, "GET", "/publicRooms");
|
||||
},
|
||||
|
||||
registerFlows: function(callback) {
|
||||
return this._doRequest(callback, "GET", "/register");
|
||||
},
|
||||
|
||||
loginFlows: function(callback) {
|
||||
return this._doRequest(callback, "GET", "/login");
|
||||
},
|
||||
|
||||
resolveRoomAlias: function(roomAlias, callback) {
|
||||
var path = encodeUri("/directory/room/$alias", {$alias: roomAlias});
|
||||
return this._doRequest(callback, "GET", path);
|
||||
},
|
||||
|
||||
// Syncing operations
|
||||
// ==================
|
||||
|
||||
initialSync: function(limit, callback) {
|
||||
var params = {
|
||||
limit: limit
|
||||
};
|
||||
return this._doAuthedRequest(
|
||||
callback, "GET", "/initialSync", params
|
||||
);
|
||||
},
|
||||
|
||||
roomInitialSync: function(roomId, limit, callback) {
|
||||
if (isFunction(limit)) { callback = limit; limit = undefined; }
|
||||
var path = encodeUri("/rooms/$roomId/initialSync",
|
||||
{$roomId: roomId}
|
||||
);
|
||||
if (!limit) {
|
||||
limit = 30;
|
||||
}
|
||||
return this._doAuthedRequest(
|
||||
callback, "GET", path, { limit: limit }
|
||||
);
|
||||
},
|
||||
|
||||
roomState: function(roomId, callback) {
|
||||
var path = encodeUri("/rooms/$roomId/state", {$roomId: roomId});
|
||||
return this._doAuthedRequest(callback, "GET", path);
|
||||
},
|
||||
|
||||
scrollback: function(roomId, from, limit, callback) {
|
||||
if (isFunction(limit)) { callback = limit; limit = undefined; }
|
||||
var path = encodeUri("/rooms/$roomId/messages", {$roomId: roomId});
|
||||
if (!limit) {
|
||||
limit = 30;
|
||||
}
|
||||
var params = {
|
||||
from: from,
|
||||
limit: limit,
|
||||
dir: 'b'
|
||||
}
|
||||
return this._doAuthedRequest(callback, "GET", path, params);
|
||||
},
|
||||
|
||||
eventStream: function(from, timeout, callback) {
|
||||
if (isFunction(timeout)) { callback = timeout; timeout = undefined;}
|
||||
if (!timeout) {
|
||||
timeout = 30000;
|
||||
}
|
||||
|
||||
var params = {
|
||||
from: from,
|
||||
timeout: timeout
|
||||
};
|
||||
return this._doAuthedRequest(callback, "GET", "/events", params);
|
||||
},
|
||||
|
||||
// Registration/Login operations
|
||||
// =============================
|
||||
|
||||
login: function(loginType, data, callback) {
|
||||
data.type = loginType;
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", "/login", undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
register: function(loginType, data, callback) {
|
||||
data.type = loginType;
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", "/register", undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
loginWithPassword: function(user, password, callback) {
|
||||
return this.login("m.login.password", {
|
||||
user: user,
|
||||
password: password
|
||||
}, callback)
|
||||
},
|
||||
|
||||
// Push operations
|
||||
// ===============
|
||||
|
||||
pushRules: function(callback) {
|
||||
return this._doAuthedRequest(callback, "GET", "/pushrules/");
|
||||
},
|
||||
|
||||
addPushRule: function(scope, kind, ruleId, body, callback) {
|
||||
// NB. Scope not uri encoded because devices need the '/'
|
||||
var path = encodeUri("/pushrules/"+scope+"/$kind/$ruleId", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, body
|
||||
);
|
||||
},
|
||||
|
||||
deletePushRule: function(scope, kind, ruleId, callback) {
|
||||
// NB. Scope not uri encoded because devices need the '/'
|
||||
var path = encodeUri("/pushrules/"+scope+"/$kind/$ruleId", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
});
|
||||
return this._doAuthedRequest(callback, "DELETE", path);
|
||||
},
|
||||
|
||||
// VoIP operations
|
||||
// ===============
|
||||
|
||||
turnServer: function(callback) {
|
||||
return this._doAuthedRequest(callback, "GET", "/voip/turnServer");
|
||||
},
|
||||
|
||||
// URI functions
|
||||
// =============
|
||||
|
||||
getHttpUriForMxc: function(mxc, width, height, resizeMethod) {
|
||||
if (!typeof mxc === "string" || !mxc) {
|
||||
return mxc;
|
||||
}
|
||||
if (mxc.indexOf("mxc://") !== 0) {
|
||||
return mxc;
|
||||
}
|
||||
var serverAndMediaId = mxc.slice(6); // strips mxc://
|
||||
var prefix = "/_matrix/media/v1/download/";
|
||||
var params = {};
|
||||
|
||||
if (width) {
|
||||
params.width = width;
|
||||
}
|
||||
if (height) {
|
||||
params.height = height;
|
||||
}
|
||||
if (resizeMethod) {
|
||||
params.method = resizeMethod;
|
||||
}
|
||||
if (Object.keys(params).length > 0) {
|
||||
// these are thumbnailing params so they probably want the
|
||||
// thumbnailing API...
|
||||
prefix = "/_matrix/media/v1/thumbnail/";
|
||||
}
|
||||
|
||||
var fragmentOffset = serverAndMediaId.indexOf("#"),
|
||||
fragment = "";
|
||||
if (fragmentOffset >= 0) {
|
||||
fragment = serverAndMediaId.substr(fragmentOffset);
|
||||
serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
|
||||
}
|
||||
return this.credentials.baseUrl + prefix + serverAndMediaId +
|
||||
(Object.keys(params).length === 0 ? "" :
|
||||
("?" + encodeParams(params))) + fragment;
|
||||
},
|
||||
|
||||
getIdenticonUri: function(identiconString, width, height) {
|
||||
if (!identiconString) {
|
||||
return;
|
||||
}
|
||||
if (!width) { width = 96; }
|
||||
if (!height) { height = 96; }
|
||||
var params = {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
|
||||
var path = encodeUri("/_matrix/media/v1/identicon/$ident", {
|
||||
$ident: identiconString
|
||||
});
|
||||
return this.credentials.baseUrl + path +
|
||||
(Object.keys(params).length === 0 ? "" :
|
||||
("?" + encodeParams(params)));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the content repository url with query parameters.
|
||||
* @returns An object with a 'base', 'path' and 'params' for base URL,
|
||||
* path and query parameters respectively.
|
||||
*/
|
||||
getContentUri: function() {
|
||||
var params = {
|
||||
access_token: this.credentials.accessToken
|
||||
};
|
||||
return {
|
||||
base: this.credentials.baseUrl,
|
||||
path: "/_matrix/media/v1/upload",
|
||||
params: params
|
||||
};
|
||||
},
|
||||
|
||||
// Internals
|
||||
// =========
|
||||
|
||||
_doAuthedRequest: function(callback, method, path, params, data) {
|
||||
if (!params) { params = {}; }
|
||||
params.access_token = this.credentials.accessToken;
|
||||
return this._doRequest(callback, method, path, params, data);
|
||||
},
|
||||
|
||||
_doRequest: function(callback, method, path, params, data) {
|
||||
var fullUri = this.credentials.baseUrl + CLIENT_PREFIX + path;
|
||||
if (!params) { params = {}; }
|
||||
return this._request(callback, method, fullUri, params, data);
|
||||
},
|
||||
|
||||
_request: function(callback, method, uri, params, data) {
|
||||
console.log(" => %s", uri);
|
||||
console.log(" %s", JSON.stringify(data));
|
||||
if (callback !== undefined && !isFunction(callback)) {
|
||||
throw Error("Expected callback to be a function");
|
||||
}
|
||||
|
||||
return request(
|
||||
{
|
||||
uri: uri,
|
||||
method: method,
|
||||
withCredentials: false,
|
||||
qs: params,
|
||||
body: data,
|
||||
json: true,
|
||||
headers: HEADERS
|
||||
},
|
||||
requestCallback(callback)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var encodeUri = function(pathTemplate, variables) {
|
||||
for (var key in variables) {
|
||||
if (!variables.hasOwnProperty(key)) { continue; }
|
||||
pathTemplate = pathTemplate.replace(
|
||||
key, encodeURIComponent(variables[key])
|
||||
);
|
||||
}
|
||||
return pathTemplate;
|
||||
};
|
||||
|
||||
// avoiding deps on jquery and co
|
||||
var encodeParams = function(params) {
|
||||
var qs = "";
|
||||
for (var key in params) {
|
||||
if (!params.hasOwnProperty(key)) { continue; }
|
||||
qs += "&" + encodeURIComponent(key) + "=" +
|
||||
encodeURIComponent(params[key]);
|
||||
}
|
||||
return qs.substring(1);
|
||||
};
|
||||
|
||||
var requestCallback = function(userDefinedCallback) {
|
||||
if (!userDefinedCallback) {
|
||||
return undefined;
|
||||
}
|
||||
return function(err, response, body) {
|
||||
if (err) {
|
||||
return userDefinedCallback(err);
|
||||
}
|
||||
if (response.statusCode >= 400) {
|
||||
return userDefinedCallback(body);
|
||||
}
|
||||
else {
|
||||
userDefinedCallback(null, body);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var isFunction = function(value) {
|
||||
return Object.prototype.toString.call(value) == "[object Function]";
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof exports === 'undefined') {
|
||||
init(this['matrixcs']={}); // this assigns to "window" on browsers
|
||||
}
|
||||
else {
|
||||
init(exports);
|
||||
}
|
11
examples/node/app.js
Normal file
11
examples/node/app.js
Normal file
@ -0,0 +1,11 @@
|
||||
"use strict";
|
||||
console.log("Loading node sdk");
|
||||
|
||||
var matrix = require("./lib/matrix");
|
||||
matrix.request(require("request"));
|
||||
|
||||
var client = matrix.createClient("http://matrix.org");
|
||||
client.publicRooms(function (err, data) {
|
||||
console.log("data %s", JSON.stringify(data));
|
||||
console.error("err %s", JSON.stringify(err));
|
||||
});
|
605
examples/node/lib/matrix.js
Normal file
605
examples/node/lib/matrix.js
Normal file
@ -0,0 +1,605 @@
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- CS: complete register function (doing stages)
|
||||
- Internal: rate limiting
|
||||
- Identity server: linkEmail, authEmail, bindEmail, lookup3pid
|
||||
- uploadContent (?)
|
||||
*/
|
||||
|
||||
// wrap in a closure for browsers
|
||||
var init = function(exports){
|
||||
// expose the underlying request object so different environments can use
|
||||
// different request libs (e.g. request or browser-request)
|
||||
var request;
|
||||
exports.request = function(r) {
|
||||
request = r;
|
||||
};
|
||||
|
||||
// entry point
|
||||
function MatrixClient(credentials) {
|
||||
if (typeof credentials === "string") {
|
||||
credentials = {
|
||||
"baseUrl": credentials
|
||||
};
|
||||
}
|
||||
var requiredKeys = [
|
||||
"baseUrl"
|
||||
];
|
||||
for (var i=0; i<requiredKeys.length; i++) {
|
||||
if (!credentials.hasOwnProperty(requiredKeys[i])) {
|
||||
throw new Error("Missing required key: " + requiredKeys[i]);
|
||||
}
|
||||
}
|
||||
if (credentials.noUserAgent) {
|
||||
HEADERS = undefined;
|
||||
}
|
||||
this.credentials = credentials;
|
||||
};
|
||||
exports.MatrixClient = MatrixClient; // expose the class
|
||||
exports.createClient = function(credentials) {
|
||||
return new MatrixClient(credentials);
|
||||
};
|
||||
|
||||
var CLIENT_PREFIX = "/_matrix/client/api/v1";
|
||||
var HEADERS = {
|
||||
"User-Agent": "matrix-js"
|
||||
};
|
||||
|
||||
MatrixClient.prototype = {
|
||||
isLoggedIn: function() {
|
||||
return this.credentials.accessToken != undefined &&
|
||||
this.credentials.userId != undefined;
|
||||
},
|
||||
|
||||
// Room operations
|
||||
// ===============
|
||||
|
||||
createRoom: function(options, callback) {
|
||||
// valid options include: room_alias_name, visibility, invite
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", "/createRoom", undefined, options
|
||||
);
|
||||
},
|
||||
|
||||
joinRoom: function(roomIdOrAlias, callback) {
|
||||
var path = encodeUri("/join/$roomid", { $roomid: roomIdOrAlias});
|
||||
return this._doAuthedRequest(callback, "POST", path, undefined, {});
|
||||
},
|
||||
|
||||
setRoomName: function(roomId, name, callback) {
|
||||
return this.sendStateEvent(roomId, "m.room.name", {name: name},
|
||||
undefined, callback);
|
||||
},
|
||||
|
||||
setRoomTopic: function(roomId, topic, callback) {
|
||||
return this.sendStateEvent(roomId, "m.room.topic", {topic: topic},
|
||||
undefined, callback);
|
||||
},
|
||||
|
||||
setPowerLevel: function(roomId, userId, powerLevel, event, callback) {
|
||||
var content = {
|
||||
users: {}
|
||||
};
|
||||
if (event && event.type == "m.room.power_levels") {
|
||||
content = event.content;
|
||||
}
|
||||
content.users[userId] = powerLevel;
|
||||
var path = encodeUri("/rooms/$roomId/state/m.room.power_levels", {
|
||||
$roomId: roomId
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
);
|
||||
},
|
||||
|
||||
sendStateEvent: function(roomId, eventType, content, stateKey,
|
||||
callback) {
|
||||
var pathParams = {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$stateKey: stateKey
|
||||
};
|
||||
var path = encodeUri("/rooms/$roomId/state/$eventType", pathParams);
|
||||
if (stateKey !== undefined) {
|
||||
path = encodeUri(path + "/$stateKey", pathParams);
|
||||
}
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
);
|
||||
},
|
||||
|
||||
sendEvent: function(roomId, eventType, content, txnId, callback) {
|
||||
if (isFunction(txnId)) { callback = txnId; txnId = undefined; }
|
||||
|
||||
if (!txnId) {
|
||||
txnId = "m" + new Date().getTime();
|
||||
}
|
||||
|
||||
var path = encodeUri("/rooms/$roomId/send/$eventType/$txnId", {
|
||||
$roomId: roomId,
|
||||
$eventType: eventType,
|
||||
$txnId: txnId
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
)
|
||||
},
|
||||
|
||||
sendMessage: function(roomId, content, txnId, callback) {
|
||||
if (isFunction(txnId)) { callback = txnId; txnId = undefined; }
|
||||
return this.sendEvent(
|
||||
roomId, "m.room.message", content, txnId, callback
|
||||
);
|
||||
},
|
||||
|
||||
sendTextMessage: function(roomId, body, txnId, callback) {
|
||||
var content = {
|
||||
msgtype: "m.text",
|
||||
body: body
|
||||
};
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
},
|
||||
|
||||
sendEmoteMessage: function(roomId, body, txnId, callback) {
|
||||
var content = {
|
||||
msgtype: "m.emote",
|
||||
body: body
|
||||
};
|
||||
return this.sendMessage(roomId, content, txnId, callback);
|
||||
},
|
||||
|
||||
sendImageMessage: function(roomId, url, info, text, callback) {
|
||||
if (isFunction(text)) { callback = text; text = undefined; }
|
||||
if (!text) { text = "Image"; }
|
||||
var content = {
|
||||
msgtype: "m.image",
|
||||
url: url,
|
||||
info: info,
|
||||
body: text
|
||||
};
|
||||
return this.sendMessage(roomId, content, callback);
|
||||
},
|
||||
|
||||
sendHtmlMessage: function(roomId, body, htmlBody, callback) {
|
||||
var content = {
|
||||
msgtype: "m.text",
|
||||
format: "org.matrix.custom.html",
|
||||
body: body,
|
||||
formatted_body: htmlBody
|
||||
};
|
||||
return this.sendMessage(roomId, content, callback);
|
||||
},
|
||||
|
||||
sendTyping: function(roomId, isTyping, timeoutMs, callback) {
|
||||
var path = encodeUri("/rooms/$roomId/typing/$userId", {
|
||||
$roomId: roomId,
|
||||
$userId: this.credentials.userId
|
||||
});
|
||||
var data = {
|
||||
typing: isTyping
|
||||
};
|
||||
if (isTyping) {
|
||||
data.timeout = timeoutMs ? timeoutMs : 20000
|
||||
}
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
redactEvent: function(roomId, eventId, callback) {
|
||||
var path = encodeUri("/rooms/$roomId/redact/$eventId", {
|
||||
$roomId: roomId,
|
||||
$eventId: eventId
|
||||
});
|
||||
return this._doAuthedRequest(callback, "POST", path, undefined, {});
|
||||
},
|
||||
|
||||
invite: function(roomId, userId, callback) {
|
||||
return this._membershipChange(roomId, userId, "invite", undefined,
|
||||
callback);
|
||||
},
|
||||
|
||||
leave: function(roomId, callback) {
|
||||
return this._membershipChange(roomId, undefined, "leave", undefined,
|
||||
callback);
|
||||
},
|
||||
|
||||
ban: function(roomId, userId, reason, callback) {
|
||||
return this._membershipChange(roomId, userId, "ban", reason,
|
||||
callback);
|
||||
},
|
||||
|
||||
unban: function(roomId, userId, callback) {
|
||||
// unbanning = set their state to leave
|
||||
return this._setMembershipState(
|
||||
roomId, userId, "leave", undefined, callback
|
||||
);
|
||||
},
|
||||
|
||||
kick: function(roomId, userId, reason, callback) {
|
||||
return this._setMembershipState(
|
||||
roomId, userId, "leave", reason, callback
|
||||
);
|
||||
},
|
||||
|
||||
_setMembershipState: function(roomId, userId, membershipValue, reason,
|
||||
callback) {
|
||||
if (isFunction(reason)) { callback = reason; reason = undefined; }
|
||||
|
||||
var path = encodeUri(
|
||||
"/rooms/$roomId/state/m.room.member/$userId",
|
||||
{ $roomId: roomId, $userId: userId}
|
||||
);
|
||||
|
||||
return this._doAuthedRequest(callback, "PUT", path, undefined, {
|
||||
membership : membershipValue,
|
||||
reason: reason
|
||||
});
|
||||
},
|
||||
|
||||
_membershipChange: function(roomId, userId, membership, reason,
|
||||
callback) {
|
||||
if (isFunction(reason)) { callback = reason; reason = undefined; }
|
||||
|
||||
var path = encodeUri("/rooms/$room_id/$membership", {
|
||||
$room_id: roomId,
|
||||
$membership: membership
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", path, undefined, {
|
||||
user_id: userId, // may be undefined e.g. on leave
|
||||
reason: reason
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// Profile operations
|
||||
// ==================
|
||||
|
||||
getProfileInfo: function(userId, info, callback) {
|
||||
if (isFunction(info)) { callback = info; info = undefined; }
|
||||
|
||||
var path = info ?
|
||||
encodeUri("/profile/$userId/$info",
|
||||
{ $userId: userId, $info: info } ) :
|
||||
encodeUri("/profile/$userId",
|
||||
{ $userId: userId } );
|
||||
return this._doAuthedRequest(callback, "GET", path);
|
||||
},
|
||||
|
||||
setProfileInfo: function(info, data, callback) {
|
||||
var path = encodeUri("/profile/$userId/$info", {
|
||||
$userId: this.credentials.userId,
|
||||
$info: info
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
setDisplayName: function(name, callback) {
|
||||
return this.setProfileInfo(
|
||||
"displayname", { displayname: name }, callback
|
||||
);
|
||||
},
|
||||
|
||||
setAvatarUrl: function(url, callback) {
|
||||
return this.setProfileInfo(
|
||||
"avatar_url", { avatar_url: url }, callback
|
||||
);
|
||||
},
|
||||
|
||||
setPresence: function(presence, callback) {
|
||||
var path = encodeUri("/presence/$userId/status", {
|
||||
$userId: this.credentials.userId
|
||||
});
|
||||
var validStates = ["offline", "online", "unavailable"];
|
||||
if (validStates.indexOf(presence) == -1) {
|
||||
throw new Error("Bad presence value: "+presence);
|
||||
}
|
||||
var content = {
|
||||
presence: presence
|
||||
};
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, content
|
||||
);
|
||||
},
|
||||
|
||||
// Public (non-authed) operations
|
||||
// ==============================
|
||||
|
||||
publicRooms: function(callback) {
|
||||
return this._doRequest(callback, "GET", "/publicRooms");
|
||||
},
|
||||
|
||||
registerFlows: function(callback) {
|
||||
return this._doRequest(callback, "GET", "/register");
|
||||
},
|
||||
|
||||
loginFlows: function(callback) {
|
||||
return this._doRequest(callback, "GET", "/login");
|
||||
},
|
||||
|
||||
resolveRoomAlias: function(roomAlias, callback) {
|
||||
var path = encodeUri("/directory/room/$alias", {$alias: roomAlias});
|
||||
return this._doRequest(callback, "GET", path);
|
||||
},
|
||||
|
||||
// Syncing operations
|
||||
// ==================
|
||||
|
||||
initialSync: function(limit, callback) {
|
||||
var params = {
|
||||
limit: limit
|
||||
};
|
||||
return this._doAuthedRequest(
|
||||
callback, "GET", "/initialSync", params
|
||||
);
|
||||
},
|
||||
|
||||
roomInitialSync: function(roomId, limit, callback) {
|
||||
if (isFunction(limit)) { callback = limit; limit = undefined; }
|
||||
var path = encodeUri("/rooms/$roomId/initialSync",
|
||||
{$roomId: roomId}
|
||||
);
|
||||
if (!limit) {
|
||||
limit = 30;
|
||||
}
|
||||
return this._doAuthedRequest(
|
||||
callback, "GET", path, { limit: limit }
|
||||
);
|
||||
},
|
||||
|
||||
roomState: function(roomId, callback) {
|
||||
var path = encodeUri("/rooms/$roomId/state", {$roomId: roomId});
|
||||
return this._doAuthedRequest(callback, "GET", path);
|
||||
},
|
||||
|
||||
scrollback: function(roomId, from, limit, callback) {
|
||||
if (isFunction(limit)) { callback = limit; limit = undefined; }
|
||||
var path = encodeUri("/rooms/$roomId/messages", {$roomId: roomId});
|
||||
if (!limit) {
|
||||
limit = 30;
|
||||
}
|
||||
var params = {
|
||||
from: from,
|
||||
limit: limit,
|
||||
dir: 'b'
|
||||
}
|
||||
return this._doAuthedRequest(callback, "GET", path, params);
|
||||
},
|
||||
|
||||
eventStream: function(from, timeout, callback) {
|
||||
if (isFunction(timeout)) { callback = timeout; timeout = undefined;}
|
||||
if (!timeout) {
|
||||
timeout = 30000;
|
||||
}
|
||||
|
||||
var params = {
|
||||
from: from,
|
||||
timeout: timeout
|
||||
};
|
||||
return this._doAuthedRequest(callback, "GET", "/events", params);
|
||||
},
|
||||
|
||||
// Registration/Login operations
|
||||
// =============================
|
||||
|
||||
login: function(loginType, data, callback) {
|
||||
data.type = loginType;
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", "/login", undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
register: function(loginType, data, callback) {
|
||||
data.type = loginType;
|
||||
return this._doAuthedRequest(
|
||||
callback, "POST", "/register", undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
loginWithPassword: function(user, password, callback) {
|
||||
return this.login("m.login.password", {
|
||||
user: user,
|
||||
password: password
|
||||
}, callback)
|
||||
},
|
||||
|
||||
// Push operations
|
||||
// ===============
|
||||
|
||||
pushRules: function(callback) {
|
||||
return this._doAuthedRequest(callback, "GET", "/pushrules/");
|
||||
},
|
||||
|
||||
addPushRule: function(scope, kind, ruleId, body, callback) {
|
||||
// NB. Scope not uri encoded because devices need the '/'
|
||||
var path = encodeUri("/pushrules/"+scope+"/$kind/$ruleId", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
});
|
||||
return this._doAuthedRequest(
|
||||
callback, "PUT", path, undefined, body
|
||||
);
|
||||
},
|
||||
|
||||
deletePushRule: function(scope, kind, ruleId, callback) {
|
||||
// NB. Scope not uri encoded because devices need the '/'
|
||||
var path = encodeUri("/pushrules/"+scope+"/$kind/$ruleId", {
|
||||
$kind: kind,
|
||||
$ruleId: ruleId
|
||||
});
|
||||
return this._doAuthedRequest(callback, "DELETE", path);
|
||||
},
|
||||
|
||||
// VoIP operations
|
||||
// ===============
|
||||
|
||||
turnServer: function(callback) {
|
||||
return this._doAuthedRequest(callback, "GET", "/voip/turnServer");
|
||||
},
|
||||
|
||||
// URI functions
|
||||
// =============
|
||||
|
||||
getHttpUriForMxc: function(mxc, width, height, resizeMethod) {
|
||||
if (!typeof mxc === "string" || !mxc) {
|
||||
return mxc;
|
||||
}
|
||||
if (mxc.indexOf("mxc://") !== 0) {
|
||||
return mxc;
|
||||
}
|
||||
var serverAndMediaId = mxc.slice(6); // strips mxc://
|
||||
var prefix = "/_matrix/media/v1/download/";
|
||||
var params = {};
|
||||
|
||||
if (width) {
|
||||
params.width = width;
|
||||
}
|
||||
if (height) {
|
||||
params.height = height;
|
||||
}
|
||||
if (resizeMethod) {
|
||||
params.method = resizeMethod;
|
||||
}
|
||||
if (Object.keys(params).length > 0) {
|
||||
// these are thumbnailing params so they probably want the
|
||||
// thumbnailing API...
|
||||
prefix = "/_matrix/media/v1/thumbnail/";
|
||||
}
|
||||
|
||||
var fragmentOffset = serverAndMediaId.indexOf("#"),
|
||||
fragment = "";
|
||||
if (fragmentOffset >= 0) {
|
||||
fragment = serverAndMediaId.substr(fragmentOffset);
|
||||
serverAndMediaId = serverAndMediaId.substr(0, fragmentOffset);
|
||||
}
|
||||
return this.credentials.baseUrl + prefix + serverAndMediaId +
|
||||
(Object.keys(params).length === 0 ? "" :
|
||||
("?" + encodeParams(params))) + fragment;
|
||||
},
|
||||
|
||||
getIdenticonUri: function(identiconString, width, height) {
|
||||
if (!identiconString) {
|
||||
return;
|
||||
}
|
||||
if (!width) { width = 96; }
|
||||
if (!height) { height = 96; }
|
||||
var params = {
|
||||
width: width,
|
||||
height: height
|
||||
};
|
||||
|
||||
var path = encodeUri("/_matrix/media/v1/identicon/$ident", {
|
||||
$ident: identiconString
|
||||
});
|
||||
return this.credentials.baseUrl + path +
|
||||
(Object.keys(params).length === 0 ? "" :
|
||||
("?" + encodeParams(params)));
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the content repository url with query parameters.
|
||||
* @returns An object with a 'base', 'path' and 'params' for base URL,
|
||||
* path and query parameters respectively.
|
||||
*/
|
||||
getContentUri: function() {
|
||||
var params = {
|
||||
access_token: this.credentials.accessToken
|
||||
};
|
||||
return {
|
||||
base: this.credentials.baseUrl,
|
||||
path: "/_matrix/media/v1/upload",
|
||||
params: params
|
||||
};
|
||||
},
|
||||
|
||||
// Internals
|
||||
// =========
|
||||
|
||||
_doAuthedRequest: function(callback, method, path, params, data) {
|
||||
if (!params) { params = {}; }
|
||||
params.access_token = this.credentials.accessToken;
|
||||
return this._doRequest(callback, method, path, params, data);
|
||||
},
|
||||
|
||||
_doRequest: function(callback, method, path, params, data) {
|
||||
var fullUri = this.credentials.baseUrl + CLIENT_PREFIX + path;
|
||||
if (!params) { params = {}; }
|
||||
return this._request(callback, method, fullUri, params, data);
|
||||
},
|
||||
|
||||
_request: function(callback, method, uri, params, data) {
|
||||
console.log(" => %s", uri);
|
||||
console.log(" %s", JSON.stringify(data));
|
||||
if (callback !== undefined && !isFunction(callback)) {
|
||||
throw Error("Expected callback to be a function");
|
||||
}
|
||||
|
||||
return request(
|
||||
{
|
||||
uri: uri,
|
||||
method: method,
|
||||
withCredentials: false,
|
||||
qs: params,
|
||||
body: data,
|
||||
json: true,
|
||||
headers: HEADERS
|
||||
},
|
||||
requestCallback(callback)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
var encodeUri = function(pathTemplate, variables) {
|
||||
for (var key in variables) {
|
||||
if (!variables.hasOwnProperty(key)) { continue; }
|
||||
pathTemplate = pathTemplate.replace(
|
||||
key, encodeURIComponent(variables[key])
|
||||
);
|
||||
}
|
||||
return pathTemplate;
|
||||
};
|
||||
|
||||
// avoiding deps on jquery and co
|
||||
var encodeParams = function(params) {
|
||||
var qs = "";
|
||||
for (var key in params) {
|
||||
if (!params.hasOwnProperty(key)) { continue; }
|
||||
qs += "&" + encodeURIComponent(key) + "=" +
|
||||
encodeURIComponent(params[key]);
|
||||
}
|
||||
return qs.substring(1);
|
||||
};
|
||||
|
||||
var requestCallback = function(userDefinedCallback) {
|
||||
if (!userDefinedCallback) {
|
||||
return undefined;
|
||||
}
|
||||
return function(err, response, body) {
|
||||
if (err) {
|
||||
return userDefinedCallback(err);
|
||||
}
|
||||
if (response.statusCode >= 400) {
|
||||
return userDefinedCallback(body);
|
||||
}
|
||||
else {
|
||||
userDefinedCallback(null, body);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var isFunction = function(value) {
|
||||
return Object.prototype.toString.call(value) == "[object Function]";
|
||||
};
|
||||
};
|
||||
|
||||
if (typeof exports === 'undefined') {
|
||||
init(this['matrixcs']={}); // this assigns to "window" on browsers
|
||||
}
|
||||
else {
|
||||
init(exports);
|
||||
}
|
Reference in New Issue
Block a user