UNPKG

botframework-schema

Version:

Activity schema for the Microsoft Bot Framework.

524 lines (475 loc) 20.2 kB
/** * Copyright(c) Microsoft Corporation.All rights reserved. * Licensed under the MIT License. */ import { v4 as uuid } from 'uuid'; import { IEndOfConversationActivity, IEventActivity, IMessageActivity, IContactRelationUpdateActivity, IConversationUpdateActivity, ITypingActivity, IHandoffActivity, IInvokeActivity, ITraceActivity, IInstallationUpdateActivity, IMessageUpdateActivity, IMessageDeleteActivity, IMessageReactionActivity, ISuggestionActivity, } from './activityInterfaces'; import { Activity, ActivityEventNames, ActivityTypes, ChannelAccount, Channels, ConversationAccount, ConversationReference, ICommandActivity, ICommandResultActivity, Mention, } from './index'; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace ActivityEx { /** * Creates an Activity as an IMessageActivity object. * * @returns The new message activity. */ export function createMessageActivity(): Partial<IMessageActivity> { return { type: ActivityTypes.Message }; } /** * Creates an Activity as an IContactRelationUpdateActivity object. * * @returns The new contact relation update activity. */ export function createContactRelationUpdateActivity(): Partial<IContactRelationUpdateActivity> { return { type: ActivityTypes.ContactRelationUpdate }; } /** * Creates an Activity as an IConversationUpdateActivity object. * * @returns The new conversation update activity. */ export function createConversationUpdateActivity(): Partial<IConversationUpdateActivity> { return { type: ActivityTypes.ConversationUpdate }; } /** * Creates an Activity as an ITypingActivity object. * * @returns The new typing activity. */ export function createTypingActivity(): Partial<ITypingActivity> { return { type: ActivityTypes.Typing }; } /** * Creates an Activity as an IHandoffActivity object. * * @returns The new handoff activity. */ export function createHandoffActivity(): Partial<IHandoffActivity> { return { type: ActivityTypes.Handoff }; } /** * Creates an Activity as an IEndOfConversationActivity object. * * @returns The new end of conversation activity. */ export function createEndOfConversationActivity(): Partial<IEndOfConversationActivity> { return { type: ActivityTypes.EndOfConversation }; } /** * Creates an Activity as an IEventActivity object. * * @returns The new event activity. */ export function createEventActivity(): Partial<IEventActivity> { return { type: ActivityTypes.Event }; } /** * Creates an Activity as an IInvokeActivity object. * * @returns The new invoke activity. */ export function createInvokeActivity(): Partial<IInvokeActivity> { return { type: ActivityTypes.Invoke }; } /** * Creates an Activity as an ITraceActivity object. * * @param name The name of the trace operation to create. * @param valueType Optional, identifier for the format of the @param value . Default is the name of type of the @param value . * @param value Optional, the content for this trace operation. * @param label Optional, a descriptive label for this trace operation. * @returns The new trace activity. */ export function createTraceActivity( name: string, valueType?: string, value?: unknown, label?: string, ): Partial<ITraceActivity> { return { type: ActivityTypes.Trace, name: name, label: label, valueType: valueType ? valueType : typeof value, value: value, }; } /** * Creates a new message activity as a response to this activity. * * @param source The activity to respond. * @param text The text of the reply. * @param locale The language code for the @param text . * @returns The new message activity. * @remarks The new activity sets up routing information based on this activity. */ export function createReply(source: Activity, text?: string, locale?: string): Activity { return { type: ActivityTypes.Message, timestamp: new Date(), from: { id: source.recipient ? source.recipient.id : null, name: source.recipient ? source.recipient.name : null, } as ChannelAccount, recipient: { id: source.from ? source.from.id : null, name: source.from ? source.from.name : null, } as ChannelAccount, replyToId: getAppropriateReplyToId(source), serviceUrl: source.serviceUrl, channelId: source.channelId, conversation: { isGroup: source.conversation.isGroup, id: source.conversation.id, name: source.conversation.name, } as ConversationAccount, text: text || '', locale: locale || source.locale, label: source.label, callerId: source.callerId, valueType: source.valueType, localTimezone: source.localTimezone, listenFor: source.listenFor, semanticAction: source.semanticAction, }; } /** * Creates a new trace activity based on the source activity. * * @param source The activity to base the trace. * @param name The name of the trace operation to create. * @param value Optional, the content for this trace operation. * @param valueType Optional, identifier for the format of the @param value . Default is the name of type of the @param value . * @param label Optional, a descriptive label for this trace operation. * @returns The new trace activity. */ export function createTrace( source: Activity, name: string, value?: unknown, valueType?: string, label?: string, ): ITraceActivity { return { type: ActivityTypes.Trace, timestamp: new Date(), from: { id: source.recipient ? source.recipient.id : null, name: source.recipient ? source.recipient.name : null, } as ChannelAccount, recipient: { id: source.from ? source.from.id : null, name: source.from ? source.from.name : null, } as ChannelAccount, replyToId: getAppropriateReplyToId(source), serviceUrl: source.serviceUrl, channelId: source.channelId, conversation: source.conversation, name: name, label: label, valueType: valueType ? valueType : typeof value, value: value, }; } /** * Returns the source activity as an IMessageActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a message activity; or null. */ export function asMessageActivity(source: Partial<Activity>): Partial<IMessageActivity> { return isActivity(source, ActivityTypes.Message) ? source : null; } /** * Returns the source activity as an IContactRelationUpdateActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a contact relation update activity; or null. */ export function asContactRelationUpdateActivity( source: Partial<Activity>, ): Partial<IContactRelationUpdateActivity> { return isActivity(source, ActivityTypes.ContactRelationUpdate) ? source : null; } /** * Returns the source activity as an IInstallationUpdateActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as an installation update activity; or null. */ export function asInstallationUpdateActivity(source: Partial<Activity>): Partial<IInstallationUpdateActivity> { return isActivity(source, ActivityTypes.InstallationUpdate) ? source : null; } /** * Returns the source activity as an IConversationUpdateActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as an conversation update activity; or null. */ export function asConversationUpdateActivity(source: Partial<Activity>): Partial<IConversationUpdateActivity> { return isActivity(source, ActivityTypes.ConversationUpdate) ? source : null; } /** * Returns the source activity as an ITypingActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a typing activity; or null. */ export function asTypingActivity(source: Partial<Activity>): Partial<ITypingActivity> { return isActivity(source, ActivityTypes.Typing) ? source : null; } /** * Returns the source activity as an IEndOfConversationActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as an end of conversation activity; or null. */ export function asEndOfConversationActivity(source: Partial<Activity>): Partial<IEndOfConversationActivity> { return isActivity(source, ActivityTypes.EndOfConversation) ? source : null; } /** * Returns the source activity as an IEventActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as an event activity; or null. */ export function asEventActivity(source: Partial<Activity>): Partial<IEventActivity> { return isActivity(source, ActivityTypes.Event) ? source : null; } /** * Returns the source activity as an IInvokeActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as an invoke activity; or null. */ export function asInvokeActivity(source: Partial<Activity>): Partial<IInvokeActivity> { return isActivity(source, ActivityTypes.Invoke) ? source : null; } /** * Returns the source activity as an IMessageUpdateActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a message update request; or null. */ export function asMessageUpdateActivity(source: Partial<Activity>): Partial<IMessageUpdateActivity> { return isActivity(source, ActivityTypes.MessageUpdate) ? source : null; } /** * Returns the source activity as an IMessageDeleteActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a message delete request; or null. */ export function asMessageDeleteActivity(source: Partial<Activity>): Partial<IMessageDeleteActivity> { return isActivity(source, ActivityTypes.MessageDelete) ? source : null; } /** * Returns the source activity as an IMessageReactionActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a message reaction activity; or null. */ export function asMessageReactionActivity(source: Partial<Activity>): Partial<IMessageReactionActivity> { return isActivity(source, ActivityTypes.MessageReaction) ? source : null; } /** * Returns the source activity as an ISuggestionActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a suggestion activity; or null. */ export function asSuggestionActivity(source: Partial<Activity>): Partial<ISuggestionActivity> { return isActivity(source, ActivityTypes.Suggestion) ? source : null; } /** * Returns the source activity as an ITraceActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a trace activity; or null. */ export function asTraceActivity(source: Partial<Activity>): Partial<ITraceActivity> { return isActivity(source, ActivityTypes.Trace) ? source : null; } /** * Returns the source activity as an IHandoffActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a handoff activity; or null. */ export function asHandoffActivity(source: Partial<Activity>): Partial<IHandoffActivity> { return isActivity(source, ActivityTypes.Handoff) ? source : null; } /** * Returns the source activity as an ICommandActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a command activity; or null. */ export function asCommandActivity<T = unknown>(source: Partial<Activity>): Partial<ICommandActivity<T>> { return isActivity(source, ActivityTypes.Command) ? source : null; } /** * Returns the source activity as an ICommandResultActivity object; or null, if this is not that type of activity. * * @param source The source activity. * @returns This activity as a command result activity; or null. */ export function asCommandResultActivity<T = unknown>( source: Partial<Activity>, ): Partial<ICommandResultActivity<T>> { return isActivity(source, ActivityTypes.CommandResult) ? source : null; } /** * Indicates whether the source activity has content. * * @param source The source activity. * @returns True, if this activity has any content to send; otherwise, false. * @remarks This method is only intended for use with a message activity, where the Activity Type is set to Message. */ export function hasContent(source: Partial<Activity>): boolean { if (source.text !== undefined && source.text.trim().length > 0) { return true; } if (source.summary !== undefined && source.summary.trim().length > 0) { return true; } if (source.attachments !== undefined && source.attachments.length > 0) { return true; } if (source.channelData !== undefined) { return true; } return false; } /** * Resolves the mentions from the entities of the source activity. * * @param source The source activity. * @returns The array of mentions; or an empty array, if none are found. * @remarks This method is only intended for use with a message activity, where the Activity Type is set to Message. * @see cref="entities" . * @see cref="mention" . */ export function getMentions(source: Partial<Activity>): Mention[] { return source.entities.filter((x) => x.type.toLowerCase() === 'mention').map((e) => e as Mention); } /** * Creates a ConversationReference based on the source activity. * * @param source The source activity. * @returns A conversation reference for the conversation that contains the activity. */ export function getConversationReference(source: Partial<Activity>): ConversationReference { return { activityId: getAppropriateReplyToId(source), bot: source.recipient, channelId: source.channelId, conversation: source.conversation, locale: source.locale, serviceUrl: source.serviceUrl, user: source.from, }; } /** * Creates an Activity from conversation reference as it is posted to bot. * * @param reference the conversation reference * @returns the activity */ export function getContinuationActivity(reference: Partial<ConversationReference>): Partial<Activity> { return { type: ActivityTypes.Event, name: ActivityEventNames.ContinueConversation, id: uuid(), channelId: reference.channelId, locale: reference.locale, serviceUrl: reference.serviceUrl, conversation: reference.conversation, recipient: reference.bot, from: reference.user, relatesTo: reference as ConversationReference, }; } /** * Determines if the Activity was sent via an Http/Https connection or Streaming. * This can be determined by looking at the ServiceUrl property: * (1) All channels that send messages via http/https are not streaming * (2) Channels that send messages via streaming have a ServiceUrl that does not begin with http/https. * * @param source The source activity. * @returns True if the Activity was originate from a streaming connection. */ export function isFromStreamingConnection(source: Partial<Activity>): boolean { if (source.serviceUrl !== undefined) { const isHttp: boolean = source.serviceUrl.toLowerCase().startsWith('http'); return !isHttp; } return false; } /** * Indicates whether this activity is of a specified activity type. * * @param source The source activity. * @param activityType The activity type to check for. * @returns True if the activity is of the specified activity type; otherwise, false. */ export function isActivity(source: Partial<Activity>, activityType: string): boolean { /* * NOTE: This code was migrated from .NET implementation applying optimizations to make * it more verbose. The goal is to have zero allocations because it is called by all * of the .asXXXActivity methods to "pseudo-cast" the activity based on its type. */ const type = source.type; // If there's no type set then we can't tell if it's the type they're looking for if (type == undefined || typeof type !== 'string') { return false; } // Check if the full type value starts with the type they're looking for let result: boolean = type.toUpperCase().startsWith(activityType.toUpperCase()); // If the full type value starts with the type they're looking for, then we need to check if it's definitely the right type if (result) { // If the lengths are equal, then it's the exact type they're looking for result = type.length === activityType.length; } if (!result) { // Finally, if the type is longer than the type they're looking for then we need to check if there's a / separator right after the type they're looking for result = type.length > activityType.length && type[activityType.length] === '/'; } return result; } } function getAppropriateReplyToId(source: Partial<Activity>): string | undefined { if ( source.type !== ActivityTypes.ConversationUpdate || (source.channelId !== Channels.Directline && source.channelId !== Channels.Webchat) ) { return source.id; } return undefined; }