node-nlp
Version:
Library for NLU (Natural Language Understanding) done in Node.js
197 lines (166 loc) • 6.96 kB
text/typescript
/**
* @module botbuilder
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
import { Activity } from 'botframework-schema';
import { PagedResult, TranscriptInfo, TranscriptStore } from './transcriptLogger';
/**
* The memory transcript store stores transcripts in volatile memory in a Map.
*
* @remarks
* Because this uses an unbounded volatile dictionary this should only be used for unit tests or
* non-production environments.
*/
export class MemoryTranscriptStore implements TranscriptStore {
private static readonly pageSize: number = 20;
private channels: Map<string, Map<string, Activity[]>> = new Map<string, Map<string, Activity[]>>();
/**
* Log an activity to the transcript.
* @param activity Activity to log.
*/
public logActivity(activity: Activity): void | Promise<void> {
if (!activity) {
throw new Error('activity cannot be null for logActivity()');
}
// get channel
let channel: Map<string, Activity[]>;
if (!this.channels.has(activity.channelId)) {
channel = new Map<string, Activity[]>();
this.channels.set(activity.channelId, channel);
} else {
channel = this.channels.get(activity.channelId);
}
// get conversation transcript
let transcript: Activity[];
if (!channel.has(activity.conversation.id)) {
transcript = [];
channel.set(activity.conversation.id, transcript);
} else {
transcript = channel.get(activity.conversation.id);
}
transcript.push(activity);
return Promise.resolve();
}
/**
* Get activities from the memory transcript store
* @param channelId Channel Id.
* @param conversationId Conversation Id.
* @param continuationToken Continuation token to page through results.
* @param startDate Earliest time to include.
*/
public getTranscriptActivities(
channelId: string,
conversationId: string,
continuationToken?: string,
startDate?: Date
): Promise<PagedResult<Activity>> {
if (!channelId) { throw new Error('Missing channelId'); }
if (!conversationId) { throw new Error('Missing conversationId'); }
const pagedResult: PagedResult<Activity> = new PagedResult<Activity>();
if (this.channels.has(channelId)) {
const channel: Map<string, Activity[]> = this.channels.get(channelId);
if (channel.has(conversationId)) {
const transcript: Activity[] = channel.get(conversationId);
if (continuationToken) {
pagedResult.items = transcript
.sort(timestampSorter)
.filter((a: Activity) => !startDate || a.timestamp >= startDate)
.filter(skipWhileExpression((a: Activity) => a.id !== continuationToken))
.slice(1, MemoryTranscriptStore.pageSize + 1);
} else {
pagedResult.items = transcript
.sort(timestampSorter)
.filter((a: Activity) => !startDate || a.timestamp >= startDate)
.slice(0, MemoryTranscriptStore.pageSize);
}
if (pagedResult.items.length === MemoryTranscriptStore.pageSize) {
pagedResult.continuationToken = pagedResult.items[pagedResult.items.length - 1].id;
}
}
}
return Promise.resolve(pagedResult);
}
/**
* List conversations in the channelId.
* @param channelId Channel Id.
* @param continuationToken Continuation token to page through results.
*/
public listTranscripts(channelId: string, continuationToken?: string): Promise<PagedResult<TranscriptInfo>> {
if (!channelId) { throw new Error('Missing channelId'); }
const pagedResult: PagedResult<TranscriptInfo> = new PagedResult<TranscriptInfo>();
if (this.channels.has(channelId)) {
const channel: Map<string, Activity[]> = this.channels.get(channelId);
if (continuationToken) {
pagedResult.items = Array.from(channel.entries()).map((kv: [string, Activity[]]) => ({
channelId,
id: kv[0],
created: getDate(kv[1])
})).sort(createdSorter)
.filter(skipWhileExpression((a: any) => a.id !== continuationToken))
.slice(1, MemoryTranscriptStore.pageSize + 1);
} else {
pagedResult.items = Array.from(channel.entries()).map((kv: [string, Activity[]]) => ({
channelId,
id: kv[0],
created: getDate(kv[1])
})).sort(createdSorter)
.slice(0, MemoryTranscriptStore.pageSize);
}
if (pagedResult.items.length === MemoryTranscriptStore.pageSize) {
pagedResult.continuationToken = pagedResult.items[pagedResult.items.length - 1].id;
}
}
return Promise.resolve(pagedResult);
}
/**
* Delete a specific conversation and all of it's activities.
* @param channelId Channel Id where conversation took place.
* @param conversationId Id of the conversation to delete.
*/
public deleteTranscript(channelId: string, conversationId: string): Promise<void> {
if (!channelId) { throw new Error('Missing channelId'); }
if (!conversationId) { throw new Error('Missing conversationId'); }
if (this.channels.has(channelId)) {
const channel: Map<string, Activity[]> = this.channels.get(channelId);
if (channel.has(conversationId)) {
channel.delete(conversationId);
}
}
return Promise.resolve();
}
}
/**
* @private
*/
const createdSorter: (a: TranscriptInfo, b: TranscriptInfo) => number = (a: TranscriptInfo, b: TranscriptInfo): number =>
a.created.getTime() - b.created.getTime();
/**
* @private
*/
const timestampSorter: (a: Activity, b: Activity) => number = (a: Activity, b: Activity): number =>
a.timestamp.getTime() - b.timestamp.getTime();
/**
* @private
*/
const skipWhileExpression: (expression: any) => (item: any) => boolean = (expression: any): (item: any) => boolean => {
let skipping: boolean = true;
return (item: any): boolean => {
if (!skipping) { return true; }
if (!expression(item)) {
skipping = false;
}
return !skipping;
};
};
/**
* @private
*/
const getDate: (activities: Activity[]) => Date = (activities: Activity[]): Date => {
if (activities && activities.length > 0) {
return activities[0].timestamp || new Date(0);
}
return new Date(0);
};