@novo-learning/novo-sdk
Version:
SDK for the Novolanguage Speech Analysis API
113 lines (99 loc) • 3.6 kB
text/typescript
import { AsrMessageType } from '../entities/asr-message-type';
import { IntermediateResult, Result } from '../entities/index';
import { EventBus } from '../event-bus';
import { PermissionDeniedEvent } from '../events';
import { AsrMessageReceivedEvent } from '../events/asr-message-received.event';
import { RecognitionProgressEvent } from '../events/recognition-progress.event';
import { Deferred } from '../utils/deferred.promise';
class Message<T> {
timestamp = new Date();
defer = new Deferred<T>();
constructor(readonly request: { event: string; data: { id: number; params?: unknown } }) {}
}
export class MessageQueue {
private messageId = 0;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
private requests: { [key: number]: Message<any> } = {};
constructor(private readonly eventBus: EventBus, private readonly websocket: WebSocket) {
this.websocket.onmessage = (event): void => {
try {
const result: { event: AsrMessageType; data: IntermediateResult | Result; error?: string } = JSON.parse(
event.data,
);
if (result.error) {
if (result.error.indexOf('Unauthorized') >= 0) {
this.eventBus.dispatch<PermissionDeniedEvent>(PermissionDeniedEvent.type);
return;
}
console.error(result.error);
return;
}
if (!result?.data) {
return;
}
if (this.isIntermediateResult(result)) {
this.parseIntermediateResult(result.data);
} else {
this.parseResult(result.data as Result);
}
} catch (err: unknown) {
if (typeof event.data === 'string') {
if (event.data.indexOf('Unauthorized') >= 0) {
this.eventBus.dispatch<PermissionDeniedEvent>(PermissionDeniedEvent.type);
return;
}
}
// Message was not JSON
console.error(err);
}
};
}
async request<P, T>(method: string, params?: P): Promise<T | undefined> {
if (this.websocket?.readyState === WebSocket.OPEN) {
const id = this.getID();
const request = {
event: method,
data: { id, params },
};
this.requests[id] = new Message<T>(request);
this.websocket.send(JSON.stringify(request));
return this.requests[id].defer.promise;
}
return undefined;
}
parseIntermediateResult(result: IntermediateResult): void {
this.eventBus.dispatch<RecognitionProgressEvent>(RecognitionProgressEvent.type, result);
}
parseResult(result: Result): void {
if (result.result !== AsrMessageType.PONG) {
this.eventBus.dispatch<AsrMessageReceivedEvent>(AsrMessageReceivedEvent.type, { message: result });
}
if (!(result.id in this.requests)) {
console.error('Could not interpret result', result);
return;
}
const request = this.requests[result.id];
if (request != null) {
delete this.requests[result.id];
}
if (result.result) {
request.defer.resolve(result.result);
} else {
request.defer.reject(result.error);
}
}
isIntermediateResult(result: {
event: AsrMessageType;
data: Result | IntermediateResult;
}): result is { event: AsrMessageType; data: IntermediateResult } {
return result.event === AsrMessageType.INTERMEDIATE_RESULT;
}
clearMessageQueue(): void {
Object.keys(this.requests).forEach((requestId) =>
this.requests[Number(requestId)].defer.reject('Connection to speech recognizer was lost'),
);
}
private getID(): number {
return ++this.messageId;
}
}