'use strict';
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
/*
* Nexmo Client SDK
* Member Object Model
*
* Copyright (c) Nexmo Inc.
*/
const WildEmitter = require('wildemitter');
const nexmoClientError_1 = require("./nexmoClientError");
const nxmEvent_1 = __importDefault(require("./events/nxmEvent"));
const utils_1 = __importDefault(require("./utils"));
/**
* An individual user (i.e. conversation member).
* @class Member
* @param {Conversation} conversation
* @param {object} params
*/
class Member {
constructor(conversation, params) {
this.conversation = conversation;
this.callStatus = null;
this._normalise(params);
WildEmitter.mixin(Member);
}
/**
* Update object instance and align attribute names
*
* Handle params input to keep consistent the member object
* @param {object} params member attributes
* @private
*/
_normalise(params) {
if (params) {
this.user = this.user || {};
this.channel = params.channel || {
type: 'app'
};
let key;
for (key in params) {
switch (key) {
case 'member_id':
this.id = params.member_id;
break;
case 'timestamp':
this.timestamp = params.timestamp;
break;
case 'state':
this.state = params.state;
break;
case 'from':
this.id = params.from; // special case for member events
break;
case 'user_id':
this.user.id = params.user_id;
break;
case 'name':
this.user.name = params.name;
break;
case 'user':
this.user = {
name: params.user.name,
id: params.user.user_id || params.user.id
};
this.display_name = this.display_name || params.user.display_name;
break;
case 'invited_by':
this.invited_by = params.invited_by;
break;
case 'display_name':
this.display_name = this.display_name || params.display_name;
break;
case '_embedded':
if (params._embedded.user) {
this.user = {
id: params._embedded.user.id || this.user.id,
name: params._embedded.user.name || this.user.name
};
this.display_name = this.display_name || params._embedded.user.display_name;
}
case 'conversation':
break;
default:
if (!params.type) {
this[key] = params[key];
}
}
}
// join conversation returns our member with only id,
// compare it for now and use the username we have in the application object
if (this.conversation.application.me && params.user_id === this.conversation.application.me.id) {
this.user.name = this.conversation.application.me.name;
}
// make sure we don't keep a member.user_id, name in any flow
delete this.user_id;
delete this.name;
delete this.user.user_id;
}
}
/**
* Play the given stream only to this member within the conversation
*
* @param {string} [params]
*
* @returns {Promise<NXMEvent>}
* @private
*/
async playStream(params) {
try {
const response = await this.conversation.application.session.sendNetworkRequest({
type: 'POST',
path: `conversations/${this.id}/events`,
data: {
type: 'audio:play',
to: this.id,
body: params
}
});
return new nxmEvent_1.default(this.conversation, response);
}
catch (error) {
throw new nexmoClientError_1.NexmoApiError(error);
}
}
/**
* Speak the given text only to this member within the Conversation.
*
* @param {string} [params]
*
* @returns {Promise<NXMEvent>}
* @private
*/
async sayText(params) {
try {
const response = await this.conversation.application.session.sendNetworkRequest({
type: 'POST',
path: `conversations/${this.id}/events`,
data: {
type: 'audio:say',
cid: this.id,
from: this.conversation.me.id,
to: this.id,
body: {
text: params.text,
voice_name: params.voice_name || 'Amy',
level: params.level || 1,
queue: params.queue || true,
loop: params.loop || 1,
ssml: params.ssml || false
}
}
});
return new nxmEvent_1.default(this.conversation, response);
}
catch (error) {
throw new nexmoClientError_1.NexmoApiError(error);
}
}
/**
* Kick a Member from the Conversation.
*
* @param {object} [reason] the reason for kicking out a member
* @param {string} [reason.reason_code] the code of the reason
* @param {string} [reason.reason_text] the description of the reason
* @example <caption>Remove a member from the Conversation.</caption>
* // Remove a member
* member.kick({reason_code: "Reason Code", reason_text: "Reason Text"})
* .then(() => {
* console.log("Successfully removed member.");
* }).catch((error) => {
* console.error("Error removing member: ", error);
* });
*
* // Remove yourself
* conversation.me.kick({reason_code: "Reason Code", reason_text: "Reason Text"})
* .then(() => {
* console.log("Successfully removed yourself.");
* }).catch((error) => {
* console.error("Error removing yourself: ", error);
* });
*
* @returns {Promise}
*/
async kick(reason) {
var _a, _b;
let path = `conversations/${this.conversation.id}/members/${this.id}`;
let params = new URLSearchParams();
// add member_id of from
if ((_b = (_a = this === null || this === void 0 ? void 0 : this.conversation) === null || _a === void 0 ? void 0 : _a.me) === null || _b === void 0 ? void 0 : _b.id) {
params.append('from', this.conversation.me.id);
}
if (reason) {
Object.keys(reason).forEach((key) => {
params.append(key, reason[key]);
});
}
path += `?${params.toString()}`;
try {
return await this.conversation.application.session.sendNetworkRequest({
type: 'DELETE',
path
});
}
catch (error) {
throw new nexmoClientError_1.NexmoApiError(error);
}
}
/**
* Mute your stream.
*
* @param {boolean} [mute] true for mute, false for unmute
* @param {number} [streamIndex] stream index of the stream
* @example <caption>Mute audio stream of your Member.</caption>
* // Mute yourself
* conversation.me.mute(true);
*
* // Unmute yourself
* conversation.me.mute(false);
*
* @returns {Promise}
*/
mute(mute, streamIndex = null) {
return this.conversation.media.mute(mute, streamIndex);
}
/**
* Earmuff yourself in the Conversation.
*
* @param {boolean} earmuff true or false
* @example <caption>Disables your Member from hearing other Members in the Conversation.</caption>
* // Earmuff yourself
* conversation.me.earmuff(true);
*
* // Unearmuff yourself
* conversation.me.earmuff(false);
*
* @returns {Promise}
*
*/
earmuff(earmuff) {
return this.conversation.media.earmuff(earmuff);
}
/**
* Handle member object events
*
* Handle events that are modifying this member instance
* @param {NXMEvent} event invited, joined, left, media events
* @private
*/
_handleEvent(event) {
switch (event.type) {
case 'member:invited':
this._normalise(event.body); // take care of misaligned objects.
this.state = 'INVITED';
this.timestamp.invited = event.body.timestamp.invited;
if (!event.body.invited_by && event.body.user.media && event.body.user.media.audio_settings
&& event.body.user.media.audio_settings.enabled) {
this._setCallStatusAndEmit('started');
}
break;
case 'member:joined':
this._normalise(event.body); // take care of misaligned objects.
this.state = 'JOINED';
this.timestamp.joined = event.body.timestamp.joined;
if (event.body.channel && event.body.channel.knocking_id) {
this._setCallStatusAndEmit('started');
}
break;
case 'member:left':
this._normalise(event.body); // take care of misaligned objects.
this.state = 'LEFT';
this.timestamp.left = event.body.timestamp.left;
if (event.body.reason && event.body.reason.text) {
this._setCallStatusAndEmit(event.body.reason.text);
}
break;
case 'member:media':
this.media = event.body.media;
break;
case 'leg:status:update':
this.channel.legs = utils_1.default.updateMemberLegs(this.channel.legs, event);
this._setCallStatusAndEmit(event.body.status);
break;
case 'audio:ringing:start':
if (!this.callStatus || this.callStatus === 'started') {
this._setCallStatusAndEmit('ringing');
}
break;
default:
break;
}
}
/**
* Set the member.callStatus and emit a member:call:status event
*
* @param {Member.callStatus} this.callStatus the call status to set
* @private
*/
_setCallStatusAndEmit(callStatus) {
if (this.callStatus !== String(callStatus)) {
this.callStatus = callStatus;
this.conversation.emit('member:call:status', this);
}
}
}
exports.default = Member;
module.exports = Member;