2018-04-13 00:29:24 +00:00
|
|
|
/*
|
|
|
|
Copyright 2015, 2016 OpenMarket Ltd
|
2019-12-20 00:57:50 +00:00
|
|
|
Copyright 2019 The Matrix.org Foundation C.I.C.
|
2018-04-13 00:29:24 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2019-12-20 01:02:36 +00:00
|
|
|
import {createNewMatrixCall as jsCreateNewMatrixCall, Room} from "matrix-js-sdk";
|
2019-09-18 08:27:43 +00:00
|
|
|
import CallHandler from './CallHandler';
|
2019-12-20 21:13:46 +00:00
|
|
|
import {MatrixClientPeg} from "./MatrixClientPeg";
|
2018-04-13 00:29:24 +00:00
|
|
|
|
|
|
|
// FIXME: this is Riot (Vector) specific code, but will be removed shortly when
|
|
|
|
// we switch over to jitsi entirely for video conferencing.
|
|
|
|
|
|
|
|
// FIXME: This currently forces Vector to try to hit the matrix.org AS for conferencing.
|
|
|
|
// This is bad because it prevents people running their own ASes from being used.
|
|
|
|
// This isn't permanent and will be customisable in the future: see the proposal
|
|
|
|
// at docs/conferencing.md for more info.
|
2018-10-12 02:54:10 +00:00
|
|
|
const USER_PREFIX = "fs_";
|
|
|
|
const DOMAIN = "matrix.org";
|
2018-04-13 00:29:24 +00:00
|
|
|
|
2019-12-20 00:57:50 +00:00
|
|
|
export function ConferenceCall(matrixClient, groupChatRoomId) {
|
2018-04-13 00:29:24 +00:00
|
|
|
this.client = matrixClient;
|
|
|
|
this.groupRoomId = groupChatRoomId;
|
2019-12-20 00:57:50 +00:00
|
|
|
this.confUserId = getConferenceUserIdForRoom(this.groupRoomId);
|
2018-04-13 00:29:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ConferenceCall.prototype.setup = function() {
|
2018-10-12 02:54:10 +00:00
|
|
|
const self = this;
|
2018-04-13 00:29:24 +00:00
|
|
|
return this._joinConferenceUser().then(function() {
|
|
|
|
return self._getConferenceUserRoom();
|
|
|
|
}).then(function(room) {
|
|
|
|
// return a call for *this* room to be placed. We also tack on
|
|
|
|
// confUserId to speed up lookups (else we'd need to loop every room
|
|
|
|
// looking for a 1:1 room with this conf user ID!)
|
2019-12-20 01:02:36 +00:00
|
|
|
const call = jsCreateNewMatrixCall(self.client, room.roomId);
|
2018-04-13 00:29:24 +00:00
|
|
|
call.confUserId = self.confUserId;
|
|
|
|
call.groupRoomId = self.groupRoomId;
|
|
|
|
return call;
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
ConferenceCall.prototype._joinConferenceUser = function() {
|
|
|
|
// Make sure the conference user is in the group chat room
|
2018-10-12 02:54:10 +00:00
|
|
|
const groupRoom = this.client.getRoom(this.groupRoomId);
|
2018-04-13 00:29:24 +00:00
|
|
|
if (!groupRoom) {
|
|
|
|
return Promise.reject("Bad group room ID");
|
|
|
|
}
|
2018-10-12 02:54:10 +00:00
|
|
|
const member = groupRoom.getMember(this.confUserId);
|
2018-04-13 00:29:24 +00:00
|
|
|
if (member && member.membership === "join") {
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
return this.client.invite(this.groupRoomId, this.confUserId);
|
|
|
|
};
|
|
|
|
|
|
|
|
ConferenceCall.prototype._getConferenceUserRoom = function() {
|
|
|
|
// Use an existing 1:1 with the conference user; else make one
|
2018-10-12 02:54:10 +00:00
|
|
|
const rooms = this.client.getRooms();
|
|
|
|
let confRoom = null;
|
|
|
|
for (let i = 0; i < rooms.length; i++) {
|
|
|
|
const confUser = rooms[i].getMember(this.confUserId);
|
2018-04-13 00:29:24 +00:00
|
|
|
if (confUser && confUser.membership === "join" &&
|
2018-08-22 15:35:58 +00:00
|
|
|
rooms[i].getJoinedMemberCount() === 2) {
|
2018-04-13 00:29:24 +00:00
|
|
|
confRoom = rooms[i];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (confRoom) {
|
|
|
|
return Promise.resolve(confRoom);
|
|
|
|
}
|
|
|
|
return this.client.createRoom({
|
|
|
|
preset: "private_chat",
|
2018-10-12 02:54:10 +00:00
|
|
|
invite: [this.confUserId],
|
2018-04-13 00:29:24 +00:00
|
|
|
}).then(function(res) {
|
2019-09-18 08:27:43 +00:00
|
|
|
return new Room(res.room_id, null, MatrixClientPeg.get().getUserId());
|
2018-04-13 00:29:24 +00:00
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if this user ID is in fact a conference bot.
|
|
|
|
* @param {string} userId The user ID to check.
|
|
|
|
* @return {boolean} True if it is a conference bot.
|
|
|
|
*/
|
2019-12-20 00:57:50 +00:00
|
|
|
export function isConferenceUser(userId) {
|
2018-04-13 00:29:24 +00:00
|
|
|
if (userId.indexOf("@" + USER_PREFIX) !== 0) {
|
|
|
|
return false;
|
|
|
|
}
|
2018-10-12 02:54:10 +00:00
|
|
|
const base64part = userId.split(":")[0].substring(1 + USER_PREFIX.length);
|
2018-04-13 00:29:24 +00:00
|
|
|
if (base64part) {
|
2018-10-12 02:54:10 +00:00
|
|
|
const decoded = new Buffer(base64part, "base64").toString();
|
2018-04-13 00:29:24 +00:00
|
|
|
// ! $STUFF : $STUFF
|
|
|
|
return /^!.+:.+/.test(decoded);
|
|
|
|
}
|
|
|
|
return false;
|
2019-12-20 00:57:50 +00:00
|
|
|
}
|
2018-04-13 00:29:24 +00:00
|
|
|
|
2019-12-20 00:57:50 +00:00
|
|
|
export function getConferenceUserIdForRoom(roomId) {
|
2018-04-13 00:29:24 +00:00
|
|
|
// abuse browserify's core node Buffer support (strip padding ='s)
|
2018-10-12 02:54:10 +00:00
|
|
|
const base64RoomId = new Buffer(roomId).toString("base64").replace(/=/g, "");
|
2018-04-13 00:29:24 +00:00
|
|
|
return "@" + USER_PREFIX + base64RoomId + ":" + DOMAIN;
|
2019-12-20 00:57:50 +00:00
|
|
|
}
|
2018-04-13 00:29:24 +00:00
|
|
|
|
2019-12-20 00:57:50 +00:00
|
|
|
export function createNewMatrixCall(client, roomId) {
|
2018-10-12 02:54:10 +00:00
|
|
|
const confCall = new ConferenceCall(
|
|
|
|
client, roomId,
|
2018-04-13 00:29:24 +00:00
|
|
|
);
|
|
|
|
return confCall.setup();
|
2019-12-20 00:57:50 +00:00
|
|
|
}
|
2018-04-13 00:29:24 +00:00
|
|
|
|
2019-12-20 00:57:50 +00:00
|
|
|
export function getConferenceCallForRoom(roomId) {
|
2018-04-13 00:29:24 +00:00
|
|
|
// search for a conference 1:1 call for this group chat room ID
|
2018-10-12 02:54:10 +00:00
|
|
|
const activeCall = CallHandler.getAnyActiveCall();
|
2018-04-13 00:29:24 +00:00
|
|
|
if (activeCall && activeCall.confUserId) {
|
2019-12-20 00:57:50 +00:00
|
|
|
const thisRoomConfUserId = getConferenceUserIdForRoom(
|
2018-10-12 02:54:10 +00:00
|
|
|
roomId,
|
2018-04-13 00:29:24 +00:00
|
|
|
);
|
|
|
|
if (thisRoomConfUserId === activeCall.confUserId) {
|
|
|
|
return activeCall;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return null;
|
2019-12-20 00:57:50 +00:00
|
|
|
}
|
2018-04-13 00:29:24 +00:00
|
|
|
|
2019-12-20 00:57:50 +00:00
|
|
|
// TODO: Document this.
|
|
|
|
export const slot = 'conference';
|