0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-30 10:42:47 +01:00
construct/modules/static/charybdis/m.js
2017-08-23 15:15:01 -06:00

2066 lines
40 KiB
JavaScript

/*
* IRCd Charybdis 5/Matrix
*
* Copyright (C) 2017 Charybdis Development Team
* Copyright (C) 2017 Jason Volk (jason@zemos.net)
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice is present in all copies.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
*/
'use strict';
/**
******************************************************************************
*
* MATRIX PROTOCOL
*
*/
mc.m = {}
/**************************************
*
* Matrix utilities
*
*/
/** local part of id
*/
mc.m.local = (mxid) =>
typeif(mxid, "string", () => mxid.split(':')[0]);
/** domain part of id - Chops off the domain and other non-name characters from an mxid
*/
mc.m.domid = (mxid) =>
typeif(mxid, "string", () => mxid.split(':')[1]);
/** short id - Chops off the domain and other non-name characters from an mxid
*/
mc.m.sid = (mxid) =>
typeif(mxid, "string", () => mc.m.local(mxid).replace(/^[\@\!\$]/, ''));
/** pretty_errcode
*/
mc.m.pretty_errcode = (errcode) =>
typeif(errcode, "string", () => errcode.replace(/^M_/, "").replace(/_/g, " "));
/** Randomness
*
* This is a library to generate various elements of the matrix protocol
* when they need to have random content.
*/
mc.m.random = {}
/** Generate a random mxid */
mc.m.random.mxid = (prefix = "$", domain = "localhost") =>
prefix + (new Date).getTime() + mc.random.string(5) + "@" + domain;
/** Sigil specification
*/
mc.m.sigil =
{
'@': "user_id",
'!': "room_id",
'$': "event_id",
'#': "room_alias",
};
/** Find sigil reverse lookup
*/
Object.defineProperty(mc.m.sigil, 'find', {
enumerable: false,
valud: function(what)
{
return Object.keys(this).find((key) =>
{
return (this)[key] == what;
});
}});
/** Validators
*
* This is a library of various validator functions for the matrix protocol
*/
mc.m.valid = {};
mc.m.valid["user_id"] = function(mxid)
{
return mc.m.valid.mxid(mxid, "@");
};
mc.m.valid["room_id"] = function(mxid)
{
return mc.m.valid.mxid(mxid, "!");
};
mc.m.valid["event_id"] = function(mxid)
{
return mc.m.valid.mxid(mxid, "!");
};
mc.m.valid["room_alias"] = function(mxid)
{
return mc.m.valid.mxid(mxid, "#");
};
mc.m.valid.mxid = function(mxid, sigil = undefined, opts = {})
{
if(!this.sigil.valid(mxid))
return false;
let [local, domain] = mxid.split(':');
if(local.length <= 1)
return false;
if(sigil !== undefined)
if(local[0] != sigil)
return false;
if(opts.valid_domain === true)
if(!valid_domain(domain))
return false;
return true;
};
mc.m.valid.sigil = function(string)
{
if(typeof(string) != "string")
return false;
if(!this.expr.test(string))
return false;
return true;
};
mc.m.valid.sigil.expr = /^(\@|\!|\$|\@)/;
/** Translates an mxc:// when the registered window protocol handler isn't relevant
*/
mc.m.xc = (url) =>
{
if(typeof(url) != "string")
return url;
if(!url.startsWith("mxc://"))
return url;
let mxc_protocol_path = mc.opts.base_url + "/_matrix/media/r0/download/";
return url.replace("mxc://", mxc_protocol_path);
};
/** ???
*
*/
mc.m.error = async function(path)
{
let ret =
{
error: await path.error,
};
if(!ret.error)
return false;
ret.errcode = await path.errcode;
return ret;
};
/**************************************
*
* Matrix Protocol Specification / IO
*
*/
/**
******************************************************************************
* 2 API Standards
*
*/
/** 2.1 Versions
*
* Gets the versions of the specification supported by the server. Values will take
* the form rX.Y.Z. Only the latest Z value will be reported for each supported X.Y
* value. i.e. if the server implements r0.0.0, r0.0.1, and r1.2.0, it will report
* r0.0.1 and r1.2.0.
*/
mc.m.versions = {};
mc.m.versions.get = function(opts = {})
{
Object.update(opts,
{
method: "GET",
resource: "versions",
prefix: "/_matrix/client/",
version: null,
});
return new mc.io.request(opts, (error, data) =>
{
return callback(arguments, error, data);
});
};
/**
******************************************************************************
* 3 Client Authentication
*
*/
/**
* 3.2 Login
*/
mc.m.login = {};
/** 3.2 Login Flows
*/
mc.m.login.get = function(opts = {})
{
Object.update(opts,
{
method: "GET",
resource: "login",
prefix: "/_matrix/client/",
version: 0,
});
return new mc.io.request(opts, (error, data) =>
{
return callback(arguments, error, data);
});
};
/** 3.2.1 Login
*/
mc.m.login.post = function(opts = {})
{
Object.defaults(opts,
{
content:
{
type: "m.login.dummy"
},
});
Object.update(opts,
{
method: "POST",
resource: "login",
prefix: "/_matrix/client/",
version: 0,
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/** 3.1.2.1 Password-based
*
*/
mc.m.login.password = function(username, password, opts = {})
{
Object.defaults(opts,
{
content:
{
user: username,
password: password,
type: "m.login.password",
},
});
return mc.m.login.post(opts, (error, data) => callback(arguments, error, data));
};
/** 3.1.2.3 Token-based
*
*/
mc.m.login.token = function(username, token, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
content:
{
user: username,
token: token,
type: "m.login.token",
txn_id: mc.local.txnid++,
},
});
return mc.m.login.post(opts, (error, data) => callback(arguments, error, data));
};
/** 3.2.2 tokenrefresh
*
*/
mc.m.tokenrefresh = {};
mc.m.tokenrefresh.post = function(opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
content:
{
refresh_token: mc.local.refresh_token,
},
});
Object.update(opts,
{
method: "POST",
resource: "tokenrefresh",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/** 3.2.3 logout
*
*/
mc.m.logout = {};
mc.m.logout.post = function(opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "POST",
resource: "logout",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/** 3.3 Account registration and management
*
*/
mc.m.register = {};
mc.m.account = {};
/** 3.3.1 register
*
*/
mc.m.register.post = function(opts = {})
{
Object.defaults(opts,
{
query:
{
// The kind of account to register. Defaults to user. One of: ["guest", "user"]
kind: "guest",
},
content:
{
// The local part of the desired Matrix ID. If omitted, the homeserver MUST
// generate a Matrix ID local part.
username: undefined,
// If true, the server binds the email used for authentication to the Matrix ID
// with the ID Server.
bind_email: undefined,
// Required. The desired password for the account.
password: undefined,
// Additional authentication information for the user-interactive authentication API.
auth:
{
// The value of the session key given by the homeserver.
session: undefined,
// Required. The login type that the client is attempting to complete.
type: "m.login.dummy",
}
},
});
Object.update(opts,
{
method: "POST",
resource: "register",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/** 3.4 Account Administrative contact information
*
*/
mc.m.account['3pid'] = {};
/** 3.4.1 POST 3pid
*
*/
/** 3.4.2 GET 3pid
*
*/
mc.m.account['3pid'].get = function(opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "account/3pid",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/**
******************************************************************************
* 5 Filtering
*
* Filters can be created on the server and can be passed as as a parameter to APIs
* which return events. These filters alter the data returned from those APIs. Not
* all APIs accept filters.
*
*/
// Protocol suite
mc.m.filter = {};
/** 5.1 GET Filter
*
* Download a filter
*/
mc.m.filter.get = function(filter_id, opts = {})
{
let user_id = mc.uri(mc.session.user_id);
let resource = "user/" + user_id + "/filter/" + mc.uri(filter_id);
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/** 5.2 POST Filter
*
* Uploads a new filter definition to the homeserver. Returns a filter ID that may be used in
* future requests to restrict which events are returned to the mc.
*/
mc.m.filter.post = function(content = {}, opts = {})
{
if(!mc.instance.authentic)
throw new mc.error("Posting a filter requires authentication");
let filter =
{
// A list of event types to exclude. If this list is absent then no event types are
// excluded. A matching type will be excluded even if it is listed in the 'types'
// filter. A '*' can be used as a wildcard to match any sequence of characters.
not_types: [],
// The maximum number of events to return.
limit: undefined,
// A list of senders IDs to include. If this list is absent then all senders are included.
senders: [],
// A list of event types to include. If this list is absent then all event types are
// included. A '*' can be used as a wildcard to match any sequence of characters.
types: [],
// A list of sender IDs to exclude. If this list is absent then no senders are excluded.
// A matching sender will be excluded even if it is listed in the 'senders' filter.
not_senders: [],
};
let room_event_filter =
{
// A list of event types to exclude. If this list is absent then no event types are
// excluded. A matching type will be excluded even if it is listed in the 'types'
// filter. A '*' can be used as a wildcard to match any sequence of characters.
not_types: [],
// A list of room IDs to exclude. If this list is absent then no rooms are excluded.
// A matching room will be excluded even if it is listed in the 'rooms' filter.
not_rooms: [],
// The maximum number of events to return.
limit: undefined,
// A list of room IDs to include. If this list is absent then all rooms are included.
rooms: [],
// A list of sender IDs to exclude. If this list is absent then no senders are excluded.
// A matching sender will be excluded even if it is listed in the 'senders' filter.
not_senders: [],
// A list of senders IDs to include. If this list is absent then all senders are included.
senders: [],
// A list of event types to include. If this list is absent then all event types are
// included. A '*' can be used as a wildcard to match any sequence of characters.
types: [],
};
let room_filter =
{
// Include rooms that the user has left in the sync, default false
include_leave: undefined,
// The per user account data to include for rooms.
account_data: undefined, //filter,
// The message and state update events to include for rooms.
timeline: undefined, //room_event_filter,
// The events that aren't recorded in the room history, e.g. typing and receipts,
// to include for rooms.
ephemeral: undefined, //room_event_filter,
// The state events to include for rooms.
state: undefined, //room_event_filter,
// A list of room IDs to exclude. If this list is absent then no rooms are
// excluded. A matching room will be excluded even if it is listed in the
// 'rooms' filter. This filter is applied before the filters in ephemeral,
// state, timeline or account_data
not_rooms: undefined, //[],
// A list of room IDs to include. If this list is absent then all rooms are
// included. This filter is applied before the filters in ephemeral, state,
// timeline or account_data
rooms: undefined, //[],
};
Object.defaults(content,
{
// List of event fields to include. If this list is absent then all fields are included.
// The entries may include '.' charaters to indicate sub-fields. So ['content.body'] will
// include the 'body' field of the 'content' object. A literal '.' character in a field
// name may be escaped using a '\'. A server may include more fields than were requested.
event_fields: undefined, //[],
// The format to use for events. 'client' will return the events in a format suitable for
// clients. 'federation' will return the raw event as receieved over federation. The default
// is 'client'. One of: ["client", "federation"]
event_format: undefined,
// The user account data that isn't associated with rooms to include.
account_data: undefined, //filter,
// Filters to be applied to room data.
room: undefined, //room_filter,
// The presence updates to include.
presence: undefined, //filter,
});
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
content: content,
});
Object.update(opts,
{
method: "POST",
resource: "user/" + mc.uri(mc.my.mxid) + "/filter",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/**
******************************************************************************
* 6 Events
*
*/
/** 6.2 Syncing
*
*/
mc.m.sync = {};
/** 6.2.1 get sync
*
*/
mc.m.sync.get = function(opts = {})
{
Object.defaults(opts, mc.m.sync.get.opts);
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
}
});
Object.update(opts,
{
method: "GET",
resource: "sync",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
mc.m.sync.get.opts =
{
// This timeout is our XHR timeout, not the longpoll timeout sent to the server in the
// query string. This only fires if there's a real connection problem. Set this to
// something higher than the longpoll timeout.
timeout: 305 * 1000,
};
mc.m.sync.get.opts.query =
{
// The maximum time to poll in milliseconds before returning this request.
timeout: 300 * 1000,
// The ID of a filter created using the filter API or a filter JSON object
// encoded as a string. The server will detect whether it is an ID or a JSON
// object by whether the first character is a "{" open brace. Passing the JSON
// inline is best suited to one off requests. Creating a filter using the filter
// API is recommended for clients that reuse the same filter multiple times, for
// example in long poll requests.
filter: undefined,
// Controls whether the client is automatically marked as online by polling this API.
// If this parameter is omitted then the client is automatically marked as online
// when it uses this API. Otherwise if the parameter is set to "offline" then the
// client is not marked as being online when it uses this API. One of: ["offline"]
set_presence: undefined, //"online",
// Controls whether to include the full state for all rooms the user is a member of.
// If this is set to true, then all state events will be returned, even if since is
// non-empty. The timeline will still be limited by the since parameter. In this case,
// the timeout parameter will be ignored and the query will return immediately,
// possibly with an empty timeline.
// If false, and since is non-empty, only state which has changed since the point
// indicated by since will be returned. By default, this is false.
full_state: false,
};
/**
* 6.3 Getting events for a room
* 6.4 Sending events to a room
* 6.5 Redactions
*/
mc.m.rooms = {};
mc.m.rooms.state = {};
mc.m.rooms.members = {};
mc.m.rooms.messages = {};
mc.m.rooms.send = {};
mc.m.rooms.redact = {};
// abstract
mc.m.rooms.request = function(room_id, resource, opts = {})
{
Object.defaults(opts,
{
method: "GET",
query:
{
access_token: mc.session.access_token,
},
});
let uri = mc.uri(room_id);
Object.update(opts,
{
resource: "rooms/" + uri + "/" + resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
// abstract
mc.m.rooms.state.request = function(room_id, type = undefined, state_key = undefined, opts = {})
{
let resource = "state";
if(type !== undefined)
{
resource += "/" + type;
if(state_key !== undefined && state_key.length)
resource += "/" + state_key;
}
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
};
/** 6.3 Getting events for a room
*/
/** 6.3.1 GET state
*+ 6.3.2 GET state eventType
*+ 6.3.3 GET state eventType stateKey
*/
mc.m.rooms.state.get = function(room_id, type = undefined, state_key = undefined, opts = {})
{
Object.update(opts,
{
method: "GET",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.state.request(room_id, type, state_key, opts, handler);
};
/** 6.3.4 GET members
*/
mc.m.rooms.members.get = function(room_id, opts = {})
{
let resource = "members";
Object.update(opts,
{
method: "GET",
});
Object.defaults(opts,
{
query:
{
filter: mc.filter("members"),
},
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
};
/** 6.3.5 GET messages
*/
mc.m.rooms.messages.get = function(room_id, opts = {})
{
let resource = "messages";
Object.update(opts,
{
method: "GET",
});
Object.defaults(opts,
{
query:
{
//from: undefined,
//to: undefined,
//dir: "b",
limit: 128,
},
});
let handler = (error, data) =>
{
if(error || !data)
return callback(arguments, error, data);
switch(opts.query.dir)
{
case 'b':
opts.query.from = data.start;
opts.query.to = undefined;
break;
case 'f':
opts.query.from = data.end;
opts.query.to = undefined;
break;
}
return callback(arguments, error, data);
};
return mc.m.rooms.request(room_id, resource, opts, handler);
};
/** 6.4 Sending events to a room
*/
/** 6.4.1 PUT state eventType
*+ 6.4.2 PUT state eventType stateKey
*/
mc.m.rooms.state.put = function(room_id, type, state_key = "", content = {}, opts = {})
{
Object.defaults(opts,
{
content: content,
});
Object.update(opts,
{
method: "PUT",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.state.request(room_id, type, state_key, opts, handler);
}
/** 6.4.3 PUT send eventType txnId
*
* Content of the event passed in opts.content
*/
mc.m.rooms.send.put = function(room_id, type, opts = {})
{
Object.update(opts,
{
method: "PUT",
txnid: mc.local.txnid++,
});
let resource = "send/" + type + "/" + opts.txnid;
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
};
/**
******************************************************************************
* 7 Rooms
*
*/
/** 7.1 Creation
*
*/
mc.m.createRoom = {};
mc.m.createRoom.post = function(opts = {})
{
Object.defaults(opts, mc.m.createRoom.post.opts);
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "POST",
resource: "createRoom",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
mc.m.createRoom.post.opts = {};
mc.m.createRoom.post.opts.content =
{
// A list of user IDs to invite to the room. This will tell the server to invite everyone in the list
// to the newly created room.
invite: undefined,
// If this is included, an m.room.name event will be sent into the room to indicate the name of the
// room. See Room Events for more information on m.room.name.
name: undefined,
// A public visibility indicates that the room will be shown in the published room list. A private
// visibility will hide the room from the published room list. Rooms default to private visibility if this key is not included. NB: This should not be confused with join_rules which also uses the word public.
// One of: ["public", "private"]
visibility: undefined,
// A list of objects representing third party IDs to invite into the room.
invite_3pid: undefined,
// If this is included, an m.room.topic event will be sent into the room to indicate the topic for
// the room. See Room Events for more information on m.room.topic.
topic: undefined,
// Extra keys to be added to the content of the m.room.create. The server will clober the following
// keys: creator. Future versions of the specification may allow the server to clobber other keys.
creation_content: undefined,
// A list of state events to set in the new room. This allows the user to override the default
// state events set in the new room. The expected format of the state events are an object with
// 'type', 'state_key' and 'content' keys set. Takes precedence over events set by presets, but gets
// overriden by name and topic keys.
initial_state: undefined,
// The desired room alias local part. If this is included, a room alias will be created and mapped to
// the newly created room. The alias will belong on the same homeserver which created the room. For
// example, if this was set to "foo" and sent to the homeserver "example.com" the complete room alias
// would be #foo:example.com.
room_alias_name: undefined,
};
/** 7.2 Room aliases
*
*/
mc.m.directory = {};
/** 7.2.1 PUT
*
*/
mc.m.directory.put = function(alias, room_id, opts = {})
{
Object.update(opts,
{
method: "PUT",
content:
{
room_id: room_id,
},
});
return mc.m.directory.request(alias, opts, (error, data) => callback(arguments, error, data));
};
/** 7.2.2 DELETE
*
*/
mc.m.directory.del = function(alias, opts = {})
{
Object.update(opts,
{
method: "DELETE",
});
return mc.m.directory.request(alias, opts, (error, data) => callback(arguments, error, data));
};
/** 7.2.3 GET
*
*/
mc.m.directory.get = function(alias, opts = {})
{
Object.update(opts,
{
method: "GET",
});
return mc.m.directory.request(alias, opts, (error, data) => callback(arguments, error, data));
};
/** Abstract
*/
mc.m.directory.request = function(alias, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
resource: "directory/room/" + mc.uri(alias),
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/** 7.4 Room Membership
*
*/
/** 7.4.1 Joining Rooms
*
*/
/** 7.4.1.1 POST invite
*/
mc.m.rooms.invite = {};
mc.m.rooms.invite.post = function(room_id, user_id, opts = {})
{
let resource = "invite";
Object.defaults(opts,
{
content:
{
user_id: user_id,
},
});
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.4.1.2 POST join
*/
mc.m.join = {};
mc.m.join.post = function(room_id_or_alias, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
content:
{
third_party_signed: undefined,
},
});
Object.update(opts,
{
method: "POST",
resource: "join/" + mc.uri(room_id_or_alias),
prefix: "/_matrix/client/",
});
let handler = (error, data) => callback(arguments, error, data);
return new mc.io.request(opts, handler);
}
/** 7.4.1.3 POST join
*/
mc.m.rooms.join = {};
mc.m.rooms.join.post = function(room_id, opts = {})
{
let resource = "join";
Object.defaults(opts,
{
content:
{
third_party_signed: undefined,
},
});
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.4.2 Leaving rooms
*
*/
/** 7.4.2.1 POST forget
*/
mc.m.rooms.forget = {};
mc.m.rooms.forget.post = function(room_id, opts = {})
{
let resource = "forget";
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.4.2.2 POST leave
*/
mc.m.rooms.leave = {};
mc.m.rooms.leave.post = function(room_id, opts = {})
{
let resource = "leave";
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.4.2.3 POST kick
*/
mc.m.rooms.kick = {};
mc.m.rooms.kick.post = function(room_id, user_id, reason = undefined, opts = {})
{
let resource = "kick";
Object.defaults(opts,
{
content:
{
user_id: user_id,
reason: reason,
},
});
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.4.3 Banning users in a room
*
*/
/** 7.4.3.1 POST unban
*/
mc.m.rooms.unban = {};
mc.m.rooms.unban.post = function(room_id, user_id, opts = {})
{
let resource = "unban";
Object.defaults(opts,
{
content:
{
user_id: user_id,
},
});
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.4.3.2 POST ban
*/
mc.m.rooms.ban = {};
mc.m.rooms.ban.post = function(room_id, user_id, reason = undefined, opts = {})
{
let resource = "ban";
Object.defaults(opts,
{
content:
{
user_id: user_id,
reason: reason,
},
});
Object.update(opts,
{
method: "POST",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
}
/** 7.5 Listing rooms
*
*/
mc.m.publicRooms = {};
/** 7.5.1
*
*/
mc.m.publicRooms.get = function(opts = {})
{
Object.update(opts,
{
method: "GET",
resource: "publicRooms",
prefix: "/_matrix/client/",
});
Object.defaults(opts,
{
query:
{
limit: 64,
access_token: mc.session.access_token,
},
});
return new mc.io.request(opts);
};
/** EXPERIMENTAL
*
*/
mc.m.publicRooms.post = function(opts = {})
{
Object.defaults(opts,
{
query:
{
server: undefined,
access_token: mc.session.access_token,
},
content:
{
// limit param (content?)
limit: 128,
// since param (content?)
since: undefined,
// filter_id (content?)
filter: undefined,
// boolean
include_all_networks: undefined,
third_party_instance_id: undefined,
},
});
Object.update(opts,
{
method: "POST",
resource: "publicRooms",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/**
******************************************************************************
* 8 Profiles
*
*/
mc.m.profile = {};
mc.m.profile.displayname = {};
mc.m.profile.avatar_url = {};
/** 8.1 PUT displayname
*/
mc.m.profile.displayname.put = function(user_id, displayname, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
content:
{
displayname: displayname,
},
});
Object.update(opts,
{
method: "PUT",
resource: "profile/" + mc.uri(user_id) + "/displayname",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 8.2 GET displayname
*/
mc.m.profile.displayname.get = function(user_id, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "profile/" + mc.uri(user_id) + "/displayname",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 8.3 PUT avatar_url
*/
mc.m.profile.avatar_url.put = function(user_id, avatar_url, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
content:
{
avatar_url: avatar_url,
},
});
Object.update(opts,
{
method: "PUT",
resource: "profile/" + mc.uri(user_id) + "/avatar_url",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 8.4 GET avatar_url
*/
mc.m.profile.avatar_url.get = function(user_id, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "profile/" + mc.uri(user_id) + "/avatar_url",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 8.5 GET profile
*
*/
mc.m.profile.get = function(user_id, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "profile/" + mc.uri(user_id),
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/**
******************************************************************************
* 11 Modules
*
*/
/**
**************************************
* 11.4 Typing notifications
*
*/
mc.m.rooms.typing = {};
/** 11.4.2.1 PUT typing
*/
mc.m.rooms.typing.put = function(room_id, user_id, opts = {})
{
Object.defaults(opts,
{
content:
{
timeout: 15 * 1000,
typing: true,
},
});
Object.update(opts,
{
method: "PUT",
resource: "typing" + "/" + mc.uri(user_id),
prefix: "/_matrix/client/",
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, opts.resource, opts, handler);
}
/**
**************************************
* 11.5 Receipts
*
*/
mc.m.rooms.receipt = {};
/** 11.5.2.1 POST receipt
*/
mc.m.rooms.receipt.post = function(room_id, event_id, type = "m.read", opts = {})
{
Object.update(opts,
{
method: "POST",
resource: "receipt/" + mc.uri(type) + "/" + mc.uri(event_id),
prefix: "/_matrix/client/",
});
return mc.m.rooms.request(room_id, opts.resource, opts);
}
/**
**************************************
* 11.6 Presence
*
*/
mc.m.presence = {};
mc.m.presence.list = {};
mc.m.presence.status = {};
/** 11.6.2
*/
/** 11.6.2.1 PUT status
*/
mc.m.presence.status.put = function(user_id, presence, status_msg = undefined, opts = {})
{
if(user_id === undefined)
user_id = mc.session.user_id;
if(presence === undefined)
presence = mc.instance.presence;
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
content:
{
// The status message to attach to this state.
status_msg: status_msg,
// Required. The new presence state. One of: ["online", "offline", "unavailable"]
presence: presence,
},
});
let resource = "presence";
resource += "/" + mc.uri(user_id);
resource += "/" + "status";
Object.update(opts,
{
method: "PUT",
resource: resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 11.6.2.2 GET status
*/
mc.m.presence.status.get = function(user_id, opts = {})
{
if(user_id === undefined)
user_id = mc.session.user_id;
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "presence/" + mc.uri(user_id) + "/status",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 11.6.2.3 POST list
*
*/
mc.m.presence.list.post = function(user_id, delta = { drop: [], invite: [] }, opts = {})
{
if(user_id === undefined)
user_id = mc.session.user_id;
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
content:
{
drop: delta.drop,
invite: delta.invite,
},
});
Object.update(opts,
{
method: "POST",
resource: "presence/list/" + mc.uri(user_id),
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 11.6.2.4 GET list
*/
mc.m.presence.list.get = function(user_id, opts = {})
{
if(user_id === undefined)
user_id = mc.session.user_id;
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "presence/list/" + mc.uri(user_id),
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/**
**************************************
* 11.7 Content Repository
*
*/
mc.m.media = {};
/**
* Register the mxc:// protocol with the browser for href's.
*/
//let mxc_protocol_path = mc.opts.base_url + "/_matrix/media/r0/download/%s";
//window.navigator.registerProtocolHandler("web+mxc", mxc_protocol_path, "Matrix Content");
/**
* 11.7.1 Client Behavior
*
*/
mc.m.media.download = {};
mc.m.media.upload = {};
/** 11.7.1.1 GET media
*
*/
mc.m.media.download.get = function(server_name, media_id, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
// This IO request is a corner-case. Setting these is required
// to override the mc.io.request()'s defaulting to JSON.
responseType: "arraybuffer",
});
//XXX: have to restore prefix clobber
let their_prefix = opts.prefix;
Object.update(opts,
{
method: "GET",
prefix: "/_matrix/media/",
resource: "download/" + server_name + "/" + media_id,
});
let handler = (error, data, xhr) =>
{
opts.prefix = their_prefix;
if(error)
return callback(arguments, error, data, undefined);
let type = xhr.getResponseHeader("content-type");
return callback(arguments, error, data, type);
};
return new mc.io.request(opts, handler);
}
/** 11.7.1.2 POST media
*
* All arguments in opts.
* opts.contentType
* opts.content -> xhr.send()
*
*/
mc.m.media.upload.post = function(opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
//XXX: have to restore prefix clobber
let their_prefix = opts.prefix;
Object.update(opts,
{
method: "POST",
resource: "upload",
prefix: "/_matrix/media/",
});
return new mc.io.request(opts, (error, data) =>
{
opts.prefix = their_prefix;
callback(arguments, error, data);
});
}
/**
**************************************
* 11.10 Push Notifications
*
*/
/** 11.10.1 pushers
*
*/
mc.m.pushers = {};
/** 11.10.1.2 GET pushers
*
*/
mc.m.pushers.get = function(opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "pushers",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts, (error, data) => callback(arguments, error, data));
};
/**
**************************************
* 11.12 Server Side Search
*
*/
mc.m.search = {};
/** 11.12.1.1 search
*
*/
mc.m.search.post = function(opts = {})
{
Object.defaults(opts, mc.m.search.post.opts);
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "POST",
resource: "search",
prefix: "/_matrix/client/",
});
let remain = opts.query.limit;
opts.query.limit = Math.min(remain, opts.chunk_size);
///TODO: XXX
// We continue to paginate automatically until finished or the user
// callback returns false.
let handler = (error, data) =>
{
if(callback(arguments, error, data) === false)
return;
if(maybe(() => data.search_categories.room_events.next_batch) === undefined)
return;
//remain -= data.search_categories.room_events.
//opts.query.limit = Math.min(remain, opts.chunk_size);
opts.query.since = data.search_categories.room_events.next_batch;
return new mc.io.request(opts, handler);
};
return new mc.io.request(opts, handler);
};
mc.m.search.post.opts =
{
chunk_size: 10,
query:
{
limit: 10,
},
};
mc.m.search.post.opts.content =
{
search_categories:
{
room_events:
{
// Required. The string to search events for.
search_term: "",
// Takes a section 5 filter
filter: undefined,
// The keys to search. Defaults to all.
// One of: ["content.body", "content.name", "content.topic"]
keys: undefined,
// Requests that the server partitions the result set based on the
// provided list of keys.
groupings:
{
// List of groups to request.
group_by: undefined,
},
// The order in which to search for results. One of: ["recent", "rank"]
order_by: undefined,
// Requests the server return the current state for each room returned.
include_state: undefined,
// Configures whether any context for the events returned are included
// in the response.
event_context:
{
// How many events before the result are returned.
before_limit: undefined, // integer
// How many events after the result are returned.
after_limit: undefined, // integer
// Requests that the server returns the historic profile
// information for the users that sent the events that were
// returned.
include_profile: undefined, // boolean
},
},
},
};
/**
**************************************
* 11.14 Room Previews
*
*/
/** 11.14.1
*
*/
/** 11.14.1.1 GET events
*
*/
mc.m.events = {};
mc.m.events.get = function(room_id, opts = {})
{
Object.defaults(opts,
{
query:
{
room_id: room_id,
from: undefined,
timeout: 60 * 1000,
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "events",
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/**
**************************************
* 11.16 Client Config
*
*/
/** 11.16.2
*
*/
mc.m.user = {};
mc.m.user.account_data = {};
mc.m.user.rooms = {};
mc.m.user.rooms.account_data = {};
/** 11.16.2.1 PUT per room account data
*
*/
mc.m.user.rooms.account_data.put = function(user_id, room_id, type, content, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
let resource;
resource = "user/" + mc.uri(user_id);
resource +="/rooms/" + mc.uri(room_id);
resource += "/account_data/" + mc.uri(type);
Object.update(opts,
{
method: "PUT",
resource: resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
mc.m.user.rooms.account_data.del = function(user_id, room_id, type, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
let resource;
resource = "user/" + mc.uri(user_id);
resource +="/rooms/" + mc.uri(room_id);
resource += "/account_data/" + mc.uri(type);
Object.update(opts,
{
method: "DELETE",
resource: resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/** 11.16.2.2 PUT per room account data
*
*/
mc.m.user.account_data.put = function(user_id, type, content, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
let resource;
resource = "user/" + mc.uri(user_id);
resource += "/account_data/" + mc.uri(type);
Object.update(opts,
{
method: "PUT",
resource: resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
mc.m.user.account_data.del = function(user_id, type, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
let resource;
resource = "user/" + mc.uri(user_id);
resource += "/account_data/" + mc.uri(type);
Object.update(opts,
{
method: "DELETE",
resource: resource,
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/**
**************************************
* 11.17 Server Administration
*
*/
mc.m.admin = {};
mc.m.admin.whois = {};
/** 11.17.1
*
*/
/** 11.17.1.1 GET whois
*
*/
mc.m.admin.whois.get = function(user_id, opts = {})
{
Object.defaults(opts,
{
query:
{
access_token: mc.session.access_token,
},
});
Object.update(opts,
{
method: "GET",
resource: "admin/whois/" + mc.uri(user_id),
prefix: "/_matrix/client/",
});
return new mc.io.request(opts);
};
/**
**************************************
* 11.18 Event Context
*
*/
/** 11.18.1 GET context
*
*/
mc.m.rooms.context = {};
mc.m.rooms.context.get = function(room_id, event_id, opts = {})
{
let resource = "context/" + event_id;
Object.defaults(opts,
{
query:
{
//from: undefined,
//to: undefined,
limit: 0,
},
});
let handler = (error, data) => callback(arguments, error, data);
return mc.m.rooms.request(room_id, resource, opts, handler);
};