private async receiveConversationMessageOverride()

in src/common.speech/Transcription/ConversationServiceAdapter.ts [181:520]


    private async receiveConversationMessageOverride(): Promise<void> {
        if (this.isDisposed() || this.terminateMessageLoop) {
            return Promise.resolve();
        }
        // we won't rely on the cascading promises of the connection since we want to continually be available to receive messages
        const communicationCustodian: Deferred<void> = new Deferred<void>();

        try {
            const connection: IConnection = await this.fetchConnection();
            const message: ConversationConnectionMessage = await connection.read() as ConversationConnectionMessage;
            if (this.isDisposed() || this.terminateMessageLoop) {
                // We're done.
                communicationCustodian.resolve();
                return Promise.resolve();
            }

            if (!message) {
                return this.receiveConversationMessageOverride();
            }

            const sessionId: string = this.privConversationRequestSession.sessionId;
            let sendFinal: boolean = false;

            try {
                switch (message.conversationMessageType.toLowerCase()) {
                    case "info":
                    case "participant_command":
                    case "command":
                        const commandPayload: CommandResponsePayload = CommandResponsePayload.fromJSON(message.textBody);
                        switch (commandPayload.command.toLowerCase()) {

                            /**
                             * 'ParticpantList' is the first message sent to the user after the websocket connection has opened.
                             * The consuming client must wait for this message to arrive
                             * before starting to send their own data.
                             */
                            case "participantlist":

                                const participantsPayload: IParticipantsListPayloadResponse = ParticipantsListPayloadResponse.fromJSON(message.textBody);

                                const participantsResult: IInternalParticipant[] = participantsPayload.participants.map((p: IParticipantPayloadResponse) => {
                                    const participant: IInternalParticipant = {
                                        avatar: p.avatar,
                                        displayName: p.nickname,
                                        id: p.participantId,
                                        isHost: p.ishost,
                                        isMuted: p.ismuted,
                                        isUsingTts: p.usetts,
                                        preferredLanguage: p.locale
                                    };
                                    return participant;
                                });

                                if (!!this.privConversationServiceConnector.participantsListReceived) {
                                    this.privConversationServiceConnector.participantsListReceived(this.privConversationServiceConnector,
                                        new ParticipantsListEventArgs(participantsPayload.roomid, participantsPayload.token,
                                            participantsPayload.translateTo, participantsPayload.profanityFilter,
                                            participantsPayload.roomProfanityFilter, participantsPayload.roomLocked,
                                            participantsPayload.muteAll, participantsResult, sessionId));
                                }
                                break;

                            /**
                             * 'SetTranslateToLanguages' represents the list of languages being used in the Conversation by all users(?).
                             * This is sent at the start of the Conversation
                             */
                            case "settranslatetolanguages":

                                if (!!this.privConversationServiceConnector.participantUpdateCommandReceived) {
                                    this.privConversationServiceConnector.participantUpdateCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantAttributeEventArgs(commandPayload.participantId,
                                            ConversationTranslatorCommandTypes.setTranslateToLanguages,
                                            commandPayload.value, sessionId));
                                }

                                break;

                            /**
                             * 'SetProfanityFiltering' lets the client set the level of profanity filtering.
                             * If sent by the participant the setting will effect only their own profanity level.
                             * If sent by the host, the setting will effect all participants including the host.
                             * Note: the profanity filters differ from Speech Service (?): 'marked', 'raw', 'removed', 'tagged'
                             */
                            case "setprofanityfiltering":

                                if (!!this.privConversationServiceConnector.participantUpdateCommandReceived) {
                                    this.privConversationServiceConnector.participantUpdateCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantAttributeEventArgs(commandPayload.participantId,
                                            ConversationTranslatorCommandTypes.setProfanityFiltering,
                                            commandPayload.value, sessionId));
                                }

                                break;

                            /**
                             * 'SetMute' is sent if the participant has been muted by the host.
                             * Check the 'participantId' to determine if the current user has been muted.
                             */
                            case "setmute":

                                if (!!this.privConversationServiceConnector.participantUpdateCommandReceived) {
                                    this.privConversationServiceConnector.participantUpdateCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantAttributeEventArgs(commandPayload.participantId,
                                            ConversationTranslatorCommandTypes.setMute,
                                            commandPayload.value, sessionId));
                                }

                                break;

                            /**
                             * 'SetMuteAll' is sent if the Conversation has been muted by the host.
                             */
                            case "setmuteall":

                                if (!!this.privConversationServiceConnector.muteAllCommandReceived) {
                                    this.privConversationServiceConnector.muteAllCommandReceived(this.privConversationServiceConnector,
                                        new MuteAllEventArgs(commandPayload.value as boolean, sessionId));
                                }

                                break;

                            /**
                             * 'RoomExpirationWarning' is sent towards the end of the Conversation session to give a timeout warning.
                             */
                            case "roomexpirationwarning":

                                if (!!this.privConversationServiceConnector.conversationExpiration) {
                                    this.privConversationServiceConnector.conversationExpiration(this.privConversationServiceConnector,
                                        new ConversationExpirationEventArgs(commandPayload.value as number, this.privConversationRequestSession.sessionId));
                                }

                                break;

                            /**
                             * 'SetUseTts' is sent as a confirmation if the user requests TTS to be turned on or off.
                             */
                            case "setusetts":

                                if (!!this.privConversationServiceConnector.participantUpdateCommandReceived) {
                                    this.privConversationServiceConnector.participantUpdateCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantAttributeEventArgs(commandPayload.participantId,
                                            ConversationTranslatorCommandTypes.setUseTTS,
                                            commandPayload.value, sessionId));
                                }

                                break;

                            /**
                             * 'SetLockState' is set if the host has locked or unlocked the Conversation.
                             */
                            case "setlockstate":

                                if (!!this.privConversationServiceConnector.lockRoomCommandReceived) {
                                    this.privConversationServiceConnector.lockRoomCommandReceived(this.privConversationServiceConnector,
                                        new LockRoomEventArgs(commandPayload.value as boolean, sessionId));
                                }

                                break;

                            /**
                             * 'ChangeNickname' is received if a user changes their display name.
                             * Any cached particpiants list should be updated to reflect the display name.
                             */
                            case "changenickname":

                                if (!!this.privConversationServiceConnector.participantUpdateCommandReceived) {
                                    this.privConversationServiceConnector.participantUpdateCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantAttributeEventArgs(commandPayload.participantId,
                                            ConversationTranslatorCommandTypes.changeNickname,
                                            commandPayload.nickname, sessionId));
                                }

                                break;

                            /**
                             * 'JoinSession' is sent when a user joins the Conversation.
                             */
                            case "joinsession":

                                const joinParticipantPayload: ParticipantPayloadResponse = ParticipantPayloadResponse.fromJSON(message.textBody);

                                const joiningParticipant: IInternalParticipant = {
                                    avatar: joinParticipantPayload.avatar,
                                    displayName: joinParticipantPayload.nickname,
                                    id: joinParticipantPayload.participantId,
                                    isHost: joinParticipantPayload.ishost,
                                    isMuted: joinParticipantPayload.ismuted,
                                    isUsingTts: joinParticipantPayload.usetts,
                                    preferredLanguage: joinParticipantPayload.locale,
                                };

                                if (!!this.privConversationServiceConnector.participantJoinCommandReceived) {
                                    this.privConversationServiceConnector.participantJoinCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantEventArgs(
                                            joiningParticipant,
                                            sessionId));
                                }

                                break;

                            /**
                             * 'LeaveSession' is sent when a user leaves the Conversation'.
                             */
                            case "leavesession":

                                const leavingParticipant: IInternalParticipant = {
                                    id: commandPayload.participantId
                                };

                                if (!!this.privConversationServiceConnector.participantLeaveCommandReceived) {
                                    this.privConversationServiceConnector.participantLeaveCommandReceived(this.privConversationServiceConnector,
                                        new ParticipantEventArgs(leavingParticipant, sessionId));
                                }

                                break;

                            /**
                             * 'DisconnectSession' is sent when a user is disconnected from the session (e.g. network problem).
                             * Check the 'ParticipantId' to check whether the message is for the current user.
                             */
                            case "disconnectsession":

                                const disconnectParticipant: IInternalParticipant = {
                                    id: commandPayload.participantId
                                };

                                break;

                            case "token":
                                const token = new CognitiveTokenAuthentication(
                                    (authFetchEventId: string): Promise<string> => {
                                        const authorizationToken = commandPayload.token;
                                        return Promise.resolve(authorizationToken);
                                    },
                                    (authFetchEventId: string): Promise<string> => {
                                        const authorizationToken = commandPayload.token;
                                        return Promise.resolve(authorizationToken);
                                    });
                                this.authentication = token;

                                break;

                            /**
                             * Message not recognized.
                             */
                            default:
                                break;
                        }
                        break;

                    /**
                     * 'partial' (or 'hypothesis') represents a unfinalized speech message.
                     */
                    case "partial":

                    /**
                     * 'final' (or 'phrase') represents a finalized speech message.
                     */
                    case "final":

                        const speechPayload: SpeechResponsePayload = SpeechResponsePayload.fromJSON(message.textBody);

                        const speechResult: ConversationTranslationResult = new ConversationTranslationResult(speechPayload.participantId,
                            this.getTranslations(speechPayload.translations),
                            speechPayload.language,
                            undefined,
                            undefined,
                            speechPayload.recognition,
                            undefined,
                            undefined,
                            message.textBody,
                            undefined);

                        if (speechPayload.isFinal) {
                            // check the length, sometimes empty finals are returned
                            if (speechResult.text !== undefined && speechResult.text.length > 0) {
                                sendFinal = true;
                            } else if (speechPayload.id === this.privLastPartialUtteranceId) {
                                // send final as normal. We had a non-empty partial for this same utterance
                                // so sending the empty final is important
                                sendFinal = true;
                            } else {
                                // suppress unneeded final
                            }

                            if (sendFinal) {
                                if (!!this.privConversationServiceConnector.translationReceived) {
                                    this.privConversationServiceConnector.translationReceived(this.privConversationServiceConnector,
                                        new ConversationReceivedTranslationEventArgs(ConversationTranslatorMessageTypes.final, speechResult, sessionId));
                                }
                            }
                        } else if (speechResult.text !== undefined) {
                            this.privLastPartialUtteranceId = speechPayload.id;
                            if (!!this.privConversationServiceConnector.translationReceived) {
                                this.privConversationServiceConnector.translationReceived(this.privConversationServiceConnector,
                                    new ConversationReceivedTranslationEventArgs(ConversationTranslatorMessageTypes.partial, speechResult, sessionId));
                            }
                        }

                        break;

                    /**
                     * "translated_message" is a text message or instant message (IM).
                     */
                    case "translated_message":

                        const textPayload: TextResponsePayload = TextResponsePayload.fromJSON(message.textBody);

                        const textResult: ConversationTranslationResult = new ConversationTranslationResult(textPayload.participantId,
                            this.getTranslations(textPayload.translations),
                            textPayload.language,
                            undefined,
                            undefined,
                            textPayload.originalText,
                            undefined,
                            undefined,
                            undefined,
                            message.textBody,
                            undefined);

                        if (!!this.privConversationServiceConnector.translationReceived) {
                            this.privConversationServiceConnector.translationReceived(this.privConversationServiceConnector,
                                new ConversationReceivedTranslationEventArgs(ConversationTranslatorMessageTypes.instantMessage, textResult, sessionId));
                        }
                        break;

                    default:
                        // ignore any unsupported message types
                        break;
                }
            } catch (e) {
                // continue
            }
            return this.receiveConversationMessageOverride();
        } catch (e) {
            this.terminateMessageLoop = true;
        }

        return communicationCustodian.promise;
    }