RocketChat/Rocket.Chat

View on GitHub
apps/meteor/client/lib/voip/EEVoipClient.ts

Summary

Maintainability
B
4 hrs
Test Coverage
import type { ICallerInfo, IMediaStreamRenderer, VoIPUserConfiguration } from '@rocket.chat/core-typings';
import { Operation, UserState } from '@rocket.chat/core-typings';
import { Inviter, UserAgent } from 'sip.js';
import type { IncomingResponse } from 'sip.js/lib/core';

import { VoIPUser } from './VoIPUser';

export class EEVoipClient extends VoIPUser {
    constructor(config: VoIPUserConfiguration, mediaRenderer?: IMediaStreamRenderer) {
        super(config, mediaRenderer);
    }

    async makeCallURI(calleeURI: string, mediaRenderer?: IMediaStreamRenderer): Promise<void> {
        if (mediaRenderer) {
            this.mediaStreamRendered = mediaRenderer;
        }
        if (this.session) {
            throw new Error('Session exists');
        }
        if (!this.userAgent) {
            throw new Error('No User Agent.');
        }
        if (this.callState !== 'REGISTERED') {
            throw new Error('Incorrect UA state');
        }
        const target = UserAgent.makeURI(calleeURI);
        if (!target) {
            throw new Error(`Failed to create valid URI ${calleeURI}`);
        }
        // Replace this when device manager code is ready.
        const constraints = {
            audio: true,
            video: false,
        };
        const inviterOptions = {
            sessionDescriptionHandlerOptions: { constraints },
        };
        // Create a new Inviter for the outgoing Session
        const inviter = new Inviter(this.userAgent, target, inviterOptions);
        this.session = inviter;
        this.setupSessionEventHandlers(inviter);
        this._opInProgress = Operation.OP_SEND_INVITE;

        await inviter.invite({
            requestDelegate: {
                onReject: (response: IncomingResponse): void => {
                    if (response.message.reasonPhrase) {
                        this.emit('callfailed', response.message.reasonPhrase || 'unknown');
                    }
                },
            },
        });

        this._callState = 'OFFER_SENT';
        const callerInfo: ICallerInfo = {
            callerId: inviter.remoteIdentity.uri.user ? inviter.remoteIdentity.uri.user : '',
            callerName: inviter.remoteIdentity.displayName,
            host: inviter.remoteIdentity.uri.host,
        };
        this._callerInfo = callerInfo;
        this._userState = UserState.UAC;
        this.emit('stateChanged');
    }

    async makeCall(calleeNumber: string): Promise<void> {
        const hasPlusChar = calleeNumber.includes('+');

        const digits = calleeNumber.replace('+', '');
        this.makeCallURI(`sip:${hasPlusChar ? '*' : ''}${digits}@${this.userConfig.sipRegistrarHostnameOrIP}`);
    }

    static async create(config: VoIPUserConfiguration, mediaRenderer?: IMediaStreamRenderer): Promise<VoIPUser> {
        const voip = new EEVoipClient(config, mediaRenderer);
        await voip.init();
        return voip;
    }
}