Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion libraries/botbuilder-lg/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
"antlr4ts": "0.5.0-alpha.1",
"botframework-expressions": "4.1.6",
"lodash": "^4.17.11",
"uuid": "^3.3.3"
"uuid": "^3.3.3",
"botbuilder-core": "4.1.6"
},
"devDependencies": {
"@types/mocha": "^5.2.5",
Expand Down
357 changes: 357 additions & 0 deletions libraries/botbuilder-lg/src/activityFactory.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,357 @@
/**
* @module botbuilder-lg
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/

import { Activity, SuggestedActions, Attachment, ActivityTypes, ActionTypes, CardAction } from 'botframework-schema';
import { MessageFactory, CardFactory } from 'botbuilder-core';

/**
* The ActivityFactory
* to generate text and then uses simple markdown semantics like chatdown to create Activity.
*/
export class ActivityFactory {
private static genericCardTypeMapping: Map<string, string> = new Map<string, string>
([
[ 'herocard', CardFactory.contentTypes.heroCard ],
[ 'thumbnailcard', CardFactory.contentTypes.thumbnailCard ],
[ 'audiocard', CardFactory.contentTypes.audioCard ],
[ 'videocard', CardFactory.contentTypes.videoCard ],
[ 'animationcard', CardFactory.contentTypes.animationCard ],
[ 'signincard', CardFactory.contentTypes.signinCard ],
[ 'oauthcard', CardFactory.contentTypes.oauthCard ],
]);

/**
* Generate the activity.
* @param lgResult string result from languageGenerator.
*/
public static CreateActivity(lgResult: any): Partial<Activity> {
if (typeof lgResult === 'string') {
return this.buildActivityFromText(lgResult.trim());
}

return this.buildActivityFromLGStructuredResult(lgResult);
}

/**
* Given a lg result, create a text activity. This method will create a MessageActivity from text.
* @param text lg text output.
*/
private static buildActivityFromText(text: string): Partial<Activity> {
return MessageFactory.text(text, text);
}

/**
* Given a structured lg result, create an activity. This method will create an MessageActivity from object
* @param lgValue lg output.
*/
private static buildActivityFromLGStructuredResult(lgValue: any): Partial<Activity> {
let activity: Partial<Activity> = {};

const type: string = this.getStructureType(lgValue);
if (ActivityFactory.genericCardTypeMapping.has(type)) {
const attachment: Attachment = this.getAttachment(lgValue);
if (attachment !== undefined) {
activity = MessageFactory.attachment(attachment);
} else {
throw new Error(`'${ lgValue }' is not an attachment format.`);
}
} else if (type === 'activity') {
activity = this.buildActivityFromObject(lgValue);
} else {
throw new Error(`type ${ type } is not support currently.`);
}

return activity;
}

private static buildActivityFromObject(activityValue: any): Partial<Activity> {
let activity: Partial<Activity> = {};

if ('type' in activityValue && activityValue.type === 'event') {
activity = this.buildEventActivity(activityValue);
} else {
activity = this.buildMessageActivity(activityValue);
}

return activity;
}

private static buildEventActivity(eventValue: any): Partial<Activity> {
let activity: Partial<Activity> = { type: ActivityTypes.Event };
for (const item of Object.keys(eventValue)) {
const property: string = item.trim();
const value: any = eventValue[item];
switch (property.toLowerCase()) {
case 'name':
activity.name = value.toString();
break;
case 'value':
activity.value = value.toString();
break;
default:
activity[property] = value;
break;
}
}

return activity;
}

private static buildMessageActivity(messageValue: any): Partial<Activity> {
let activity: Partial<Activity> = { type: ActivityTypes.Message };
for (const key of Object.keys(messageValue)) {
const property: string = key.trim();
const value: any = messageValue[key];

switch (property.toLowerCase()) {
case 'text':
activity.text = value.toString();
break;
case 'speak':
activity.speak = value.toString();
break;
case 'inputhint':
activity.inputHint = value.toString();
break;
case 'attachments':
activity.attachments = this.getAttachments(value);
break;
case 'suggestedactions':
activity.suggestedActions = this.getSuggestions(value);
break;
case 'attachmentlayout':
activity.attachmentLayout = value.toString();
default:
activity[property] = value;
break;
}
}

return activity;
}

private static getSuggestions(suggestionsValue: any): SuggestedActions {
let actions: any[] = this.normalizedToList(suggestionsValue);

let suggestedActions: SuggestedActions = {
actions : this.getCardActions(actions),
to: []
};

return suggestedActions;
}

private static getButtons(buttonsValue: any): CardAction[] {
let actions: any[] = this.normalizedToList(buttonsValue);
return this.getCardActions(actions);
}

private static getCardActions(actions: any[]): CardAction[] {
let cardActions: (string|CardAction)[] = [];
for (const action of actions) {
if (typeof action === 'string') {
cardActions.push(action.trim());
} else {
const cardAction: CardAction = this.getCardAction(action);
if (cardAction !== undefined) {
cardActions.push(cardAction);
}
}
}

return CardFactory.actions(cardActions);
}

private static getCardAction(cardActionValue: any): CardAction {
const type: string = this.getStructureType(cardActionValue);
let cardAction: CardAction = {
type: ActionTypes.ImBack,
title: '',
value: ''
};

if(type !== 'cardaction') {
return undefined;
}

for (const key of Object.keys(cardActionValue)) {
const property: string = key.trim();
const value: any = cardActionValue[key];

switch (property.toLowerCase()) {
case 'type':
cardAction.type = value.toString();
break;
case 'title':
cardAction.title = value.toString();
break;
case 'value':
cardAction.value = value.toString();
break;
case 'displaytext':
cardAction.displayText = value.toString();
break;
case 'text':
cardAction.text = value.toString();
break;
case 'image':
cardAction.image = value.toString();
break;
default:
cardAction[property] = value;
break;
}
}

return cardAction;
}

private static getStructureType(input: any): string {
let result = '';

if (input !== undefined) {
if ('$type' in input) {
result = input['$type'].toString();
} else if ('type' in input) {
result = input['type'].toString();
}
}

return result.trim().toLowerCase();
}

private static getAttachments(input: any): Attachment[] {
let attachments: Attachment[] = [];
let attachmentsJsonList: any[] = this.normalizedToList(input);

for (const attachmentsJson of attachmentsJsonList) {
if (typeof attachmentsJson === 'object') {
const attachment = this.getAttachment(attachmentsJson);
if (attachment !== undefined) {
attachments.push(attachment);
} else {
throw new Error(`'${ attachmentsJson }' is not an attachment format.`);
}
} else {
throw new Error(`'${ attachmentsJson }' is not an attachment format.`);
}
}

return attachments;
}

private static getAttachment(input: any): Attachment {
let attachment: Attachment = {
contentType: ''
};
const type: string = this.getStructureType(input);
if (ActivityFactory.genericCardTypeMapping.has(type)) {
attachment = this.getCardAttachment(ActivityFactory.genericCardTypeMapping.get(type), input);
} else if(type === 'adaptivecard') {
attachment = CardFactory.adaptiveCard(input);
} else {
attachment = undefined;
}

return attachment;
}

private static getCardAttachment(type: string, input: any): Attachment {
let card: any = {};

for (const key in input) {
const property: string = key.trim().toLowerCase();
const value: any = input[key];

switch (property) {
case 'title':
case 'subtitle':
case 'text':
case 'aspect':
case 'value':
card[property] = value;
break;
case 'connectionname':
card['connectionName'] = value;
break;

case 'image':
case 'images':
if (type === CardFactory.contentTypes.heroCard || type === CardFactory.contentTypes.thumbnailCard) {
if (!('images' in card)) {
card['images'] = [];
}

let imageList: string[] = this.normalizedToList(value).map((u): string => u.toString());
imageList.forEach( (u): any => card['images'].push({url : u}));
} else {
card['image'] = {url: value.toString()};
}
break;
case 'media':
if (!('media' in card)) {
card['media'] = [];
}

let mediaList: string[] = this.normalizedToList(value).map((u): string => u.toString());
mediaList.forEach( (u): any => card['media'].push({url : u}));
break;
case 'buttons':
if (!('buttons' in card)) {
card['buttons'] = [];
}

let buttons: any[] = this.getButtons(value);
buttons.forEach( (u): any => card[property].push(u));
break;
case 'autostart':
case 'shareable':
case 'autoloop':
const boolValue: boolean = this.getValidBooleanValue(value.toString());
if (boolValue !== undefined) {
card[property] = boolValue;
}
break;
default:
card[property] = value;
break;
}
}

const attachment: Attachment = {
contentType: type,
content: card
};

return attachment;
}

private static getValidBooleanValue(boolValue: string): boolean{
if (boolValue.toLowerCase() == 'true')
{
return true;
}
else if (boolValue.toLowerCase() == 'false')
{
return false;
}

return undefined;
}

private static normalizedToList(item: any): any[] {
if (item === undefined) {
return [];
} else if (Array.isArray(item)){
return item;
} else {
return [item];
}
}

}
1 change: 1 addition & 0 deletions libraries/botbuilder-lg/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,4 @@ export * from './lgImport';
export * from './range';
export * from './position';
export * from './evaluationTarget';
export * from './activityFactory';
Loading