You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Make MatrixHttpApi a generic API interface; move specifics to MatrixClient.
Add a utils file to store common operations like map() and encodeParams(). Modify MatrixClient and MatrixHttpApi to accepts opts as constructors.
This commit is contained in:
564
lib/http-api.js
564
lib/http-api.js
@@ -1,467 +1,50 @@
|
||||
"use strict";
|
||||
|
||||
var utils = require("./utils");
|
||||
|
||||
/*
|
||||
TODO:
|
||||
- CS: complete register function (doing stages)
|
||||
- Identity server: linkEmail, authEmail, bindEmail, lookup3pid
|
||||
- uploadContent (?)
|
||||
*/
|
||||
var CLIENT_PREFIX = "/_matrix/client/api/v1";
|
||||
var CLIENT_V2_PREFIX = "/_matrix/client/v2_alpha";
|
||||
|
||||
/**
|
||||
* A constant representing the URI path for version 1 of the Client-Server HTTP API.
|
||||
*/
|
||||
module.exports.PREFIX_V1 = "/_matrix/client/api/v1";
|
||||
|
||||
/**
|
||||
* A constant representing the URI path for version 2 Alpha of the Client-Server
|
||||
* HTTP API.
|
||||
*/
|
||||
module.exports.PREFIX_V2_ALPHA_PREFIX = "/_matrix/client/v2_alpha";
|
||||
|
||||
var HEADERS = {
|
||||
"User-Agent": "matrix-js"
|
||||
};
|
||||
|
||||
/*
|
||||
/**
|
||||
* Construct a MatrixHttpApi.
|
||||
* @param {Object} credentials The credentials for this client
|
||||
* @param {Object} config The config for this client.
|
||||
* @param {Function} request The request function for doing HTTP requests
|
||||
* @param {Object} opts The options to use for this HTTP API.
|
||||
* @param {string} opts.baseUrl Required. The base client-server URL e.g.
|
||||
* 'http://localhost:8008'.
|
||||
* @param {Function} opts.request Required. The function to call for HTTP
|
||||
* requests. This function must look like function(opts, callback){ ... }.
|
||||
* @param {string} opts.prefix Required. The matrix client prefix to use, e.g.
|
||||
* '/_matrix/client/api/v1'. See PREFIX_V1 and PREFIX_V2_ALPHA for constants.
|
||||
* @param {boolean} opts.setUserAgent True to set a user-agent string on requests.
|
||||
* Default: True, unless there is a 'window' global present in which case the default
|
||||
* is False.
|
||||
* @param {string} opts.accessToken The access_token to send with requests. Can be
|
||||
* null to not send an access token.
|
||||
*/
|
||||
function MatrixHttpApi(credentials, config, request) {
|
||||
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 (config && config.noUserAgent) {
|
||||
HEADERS = undefined;
|
||||
}
|
||||
this.config = config;
|
||||
this.credentials = credentials;
|
||||
this.request = request;
|
||||
function MatrixHttpApi(opts) {
|
||||
utils.checkObjectHasKeys(opts, ["baseUrl", "request", "prefix"]);
|
||||
this.opts = opts;
|
||||
}
|
||||
|
||||
MatrixHttpApi.prototype = {
|
||||
// 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
|
||||
);
|
||||
},
|
||||
|
||||
getStateEvent: function(roomId, eventType, 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, "GET", path
|
||||
);
|
||||
},
|
||||
|
||||
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
|
||||
);
|
||||
},
|
||||
|
||||
getThreePids: function(creds, bind, callback) {
|
||||
var path = "/account/3pid";
|
||||
return this._doAuthedV2Request(
|
||||
callback, "GET", path, undefined, undefined
|
||||
);
|
||||
},
|
||||
|
||||
addThreePid: function(creds, bind, callback) {
|
||||
var path = "/account/3pid";
|
||||
var data = {
|
||||
'threePidCreds': creds,
|
||||
'bind': bind
|
||||
};
|
||||
return this._doAuthedV2Request(
|
||||
callback, "POST", path, undefined, data
|
||||
);
|
||||
},
|
||||
|
||||
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
|
||||
// =============
|
||||
@@ -500,7 +83,7 @@ MatrixHttpApi.prototype = {
|
||||
}
|
||||
return this.credentials.baseUrl + prefix + serverAndMediaId +
|
||||
(Object.keys(params).length === 0 ? "" :
|
||||
("?" + encodeParams(params))) + fragment;
|
||||
("?" + utils.encodeParams(params))) + fragment;
|
||||
},
|
||||
|
||||
getIdenticonUri: function(identiconString, width, height) {
|
||||
@@ -514,12 +97,12 @@ MatrixHttpApi.prototype = {
|
||||
height: height
|
||||
};
|
||||
|
||||
var path = encodeUri("/_matrix/media/v1/identicon/$ident", {
|
||||
var path = utils.encodeUri("/_matrix/media/v1/identicon/$ident", {
|
||||
$ident: identiconString
|
||||
});
|
||||
return this.credentials.baseUrl + path +
|
||||
(Object.keys(params).length === 0 ? "" :
|
||||
("?" + encodeParams(params)));
|
||||
("?" + utils.encodeParams(params)));
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -538,74 +121,59 @@ MatrixHttpApi.prototype = {
|
||||
};
|
||||
},
|
||||
|
||||
// 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);
|
||||
authedRequest: function(callback, method, path, queryParams, data) {
|
||||
if (!queryParams) { queryParams = {}; }
|
||||
queryParams.access_token = this.opts.accessToken;
|
||||
return this.request(callback, method, path, queryParams, data);
|
||||
},
|
||||
|
||||
_doAuthedV2Request: function(callback, method, path, params, data) {
|
||||
if (!params) { params = {}; }
|
||||
params.access_token = this.credentials.accessToken;
|
||||
return this._doV2Request(callback, method, path, params, data);
|
||||
request: function(callback, method, path, queryParams, data) {
|
||||
return this.requestWithPrefix(
|
||||
callback, method, path, queryParams, data, this.opts.prefix
|
||||
);
|
||||
},
|
||||
|
||||
_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);
|
||||
authedRequestWithPrefix: function(callback, method, path, queryParams, data,
|
||||
prefix) {
|
||||
var fullUri = this.opts.baseUrl + prefix + path;
|
||||
if (!queryParams) {
|
||||
queryParams = {};
|
||||
}
|
||||
queryParams.access_token = this.opts.accessToken;
|
||||
return this._request(callback, method, fullUri, queryParams, data);
|
||||
},
|
||||
|
||||
_doV2Request: function(callback, method, path, params, data) {
|
||||
var fullUri = this.credentials.baseUrl + CLIENT_V2_PREFIX + path;
|
||||
if (!params) { params = {}; }
|
||||
return this._request(callback, method, fullUri, params, data);
|
||||
requestWithPrefix: function(callback, method, path, queryParams, data, prefix) {
|
||||
var fullUri = this.opts.baseUrl + prefix + path;
|
||||
if (!queryParams) {
|
||||
queryParams = {};
|
||||
}
|
||||
return this._request(callback, method, fullUri, queryParams, data);
|
||||
},
|
||||
|
||||
_request: function(callback, method, uri, params, data) {
|
||||
if (callback !== undefined && !isFunction(callback)) {
|
||||
throw Error("Expected callback to be a function");
|
||||
_request: function(callback, method, uri, queryParams, data) {
|
||||
if (callback !== undefined && !utils.isFunction(callback)) {
|
||||
throw Error(
|
||||
"Expected callback to be a function but got "+typeof callback
|
||||
);
|
||||
}
|
||||
|
||||
return this.request(
|
||||
return this.opts.request(
|
||||
{
|
||||
uri: uri,
|
||||
method: method,
|
||||
withCredentials: false,
|
||||
qs: params,
|
||||
qs: queryParams,
|
||||
body: data,
|
||||
json: true,
|
||||
headers: HEADERS,
|
||||
_matrix_credentials: this.credentials
|
||||
_matrix_opts: this.opts
|
||||
},
|
||||
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) {
|
||||
@@ -624,11 +192,9 @@ var requestCallback = function(userDefinedCallback) {
|
||||
};
|
||||
};
|
||||
|
||||
var isFunction = function(value) {
|
||||
return Object.prototype.toString.call(value) == "[object Function]";
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* The Matrix HTTP API class.
|
||||
*/
|
||||
module.exports = MatrixHttpApi;
|
||||
module.exports.MatrixHttpApi = MatrixHttpApi;
|
||||
|
||||
Reference in New Issue
Block a user