'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;