Skip to content
This repository has been archived by the owner on Nov 16, 2023. It is now read-only.

Add task module samples #65

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,11 @@
"oauth": "^0.9.15",
"q": "^1.4.1",
"random-number-csprng": "^1.0.2",
"serve-favicon": "^2.4.0"
"serve-favicon": "^2.4.0",
"stjs": "0.0.5"
},
"devDependencies": {
"@microsoft/teams-js": "^1.3.7",
"@types/bluebird": "^3.0.37",
"@types/chai": "^3.4.34",
"@types/mocha": "^2.2.39",
Expand Down
45 changes: 45 additions & 0 deletions src/Bot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import { Strings } from "./locale/locale";
import { loadSessionAsync } from "./utils/DialogUtils";
import * as teams from "botbuilder-teams";
import { ComposeExtensionHandlers } from "./composeExtension/ComposeExtensionHandlers";
import {fetchTemplates, cardTemplates} from "./dialogs/Templates/CardTemplates";
import {renderACAttachment} from "./utils/CardUtils";

// =========================================================
// Bot Setup
Expand Down Expand Up @@ -89,6 +91,7 @@ export class Bot extends builder.UniversalBot {
session.clearDialogStack();

let payload = (event as any).value;
let invokeType = (event as any).name;

// Invokes don't participate in middleware
// If payload has an address, then it is from a button to update a message so we do not what to send typing
Expand All @@ -99,6 +102,48 @@ export class Bot extends builder.UniversalBot {
if (payload && payload.dialog) {
session.beginDialog(payload.dialog, payload);
}

switch (invokeType) {
case "task/fetch":
let taskModule = payload.data.taskModule.toLowerCase();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add comments here (in each "case" block") explaining conceptually what these invokes mean and how we respond to them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also mention which properties are standard from the framework, and which ones you added. For example, "taskModule" is something that you added in the "data" field of the adaptive card action. I'd like this to be clear because it's hard, when reading a sample, to separate what comes from the framework and what the developer added herself.

if (fetchTemplates[taskModule] !== undefined) {
// Return the specified task module response to the bot
callback(null, fetchTemplates[taskModule], 200);
}
else {
callback(new Error(`Error: task module template for ${(payload.taskModule === undefined ? "<undefined>" : payload.taskModule)} not found.`), null, 500);
}
break;
case "task/submit":
if (payload.data !== undefined) {
switch (payload.data.taskResponse) {
case "message":
// Echo the results to the chat stream
session.send("**task/submit results from the Adaptive card:**\n```" + JSON.stringify(payload) + "```");
break;
case "continue":
let fetchResponse = fetchTemplates.submitResponse;
fetchResponse.task.value.card = renderACAttachment(cardTemplates.adaptiveCardSubmitResponse, { results: JSON.stringify(payload.data) });
callback(null, fetchResponse, 200);
break;

case "final":
// do nothing
default:
if (payload.data.levelType !== undefined && payload.data.levelType === "multistep") {
callback(null, fetchTemplates.submitMessageResponse, 200);
}
if (payload.data.levelType === undefined) {
session.send("**task/submit results from Task Module adaptive card:**\n\n```" + JSON.stringify(payload) + "```");
}
else {
session.send("**task/submit results from Task Module Html:**\n\n```" + JSON.stringify(payload) + "```");
}
}
}
break;
}

}
callback(null, "", 200);
};
Expand Down
4 changes: 4 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { AADUserValidation } from "./apis/AADUserValidation";
import { ValidateAADToken } from "./apis/ValidateAADToken";
import { ManifestCreatorStart } from "./pages/ManifestCreatorStart";
import { ManifestCreatorEnd } from "./pages/ManifestCreatorEnd";
import {TaskModuleTab} from "./pages/TaskModuleTab";
import * as builder from "botbuilder";

// Configure instrumentation - tooling with Azure
Expand All @@ -41,6 +42,7 @@ let handlebars = exphbs.create({
extname: ".hbs",
helpers: {
appId: () => { return config.get("app.appId"); },
baseUri: () => {return config.get("app.baseUri"); },
},
});
app.engine("hbs", handlebars.engine);
Expand All @@ -54,6 +56,8 @@ app.get("/vstsAuth", VSTSAuthTab.getRequestHandler());
app.get("/vstsAuthFlowStart", VSTSAuthFlowStartPopUp.getRequestHandler());
app.get("/vstsAuthFlowEnd", VSTSAuthFlowEndPopUp.getRequestHandler());
app.get("/composeExtensionSettings", ComposeExtensionSettingsPopUp.getRequestHandler());
app.get("/TaskModuleTab", TaskModuleTab.getRequestHandler());
app.get("/customform", (req, res) => { res.render("customform", { appId: config.get("app.appId") , query: req.query}); } );

// Tab authentication sample routes
app.get("/tab-auth/simple", (req, res) => { res.render("tab-auth/simple"); });
Expand Down
28 changes: 28 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Task Module Ids
// tslint:disable-next-line:variable-name
export const TaskModuleTitles = {
AdaptiveCardSingleStepTitle: "Post command to bot using single step adaptive card",
AdaptiveCardMultiStepTitle: "Post command to bot using multi step adaptive card",
ActionSubmitResponseTitle: "Action.Submit Response",
SingleStepHtmlCardTitle: "Post Command to bot using single step html card",
MultistepHtmlCardTitle: "Post Command to bot using multistep html card",
};

// Task Module Ids
// tslint:disable-next-line:variable-name
export const TaskModuleIds = {
CustomForm: "customform",
};

// Task Module Sizes
// tslint:disable-next-line:variable-name
export const TaskModuleSizes = {
customform: {
width: 510,
height: 500,
},
adaptivecard: {
width: 700,
height: 255,
},
};
2 changes: 2 additions & 0 deletions src/dialogs/RootDialog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { UpdateTextMsgSetupDialog } from "./examples/teams/UpdateTextMsgSetupDia
import { NotifyDialog } from "./examples/teams/NotifyDialog";
import { PopupSignInDialog } from "./examples/basic/PopupSignInDialog";
import { AdaptiveCardDialog } from "./examples/basic/AdaptiveCardDialog";
import {TaskModuleAdaptiveCardDialog} from "./examples/basic/TaskModuleAdaptiveCard";
// *************************** END OF EXAMPLES *********************************

// Add imports for dialogs
Expand Down Expand Up @@ -105,6 +106,7 @@ export class RootDialog extends builder.IntentDialog {
new NotifyDialog(bot);
new PopupSignInDialog(bot);
new AdaptiveCardDialog(bot);
new TaskModuleAdaptiveCardDialog(bot);
// *************************** END OF EXAMPLES *********************************

// Add child dialogs
Expand Down
150 changes: 150 additions & 0 deletions src/dialogs/Templates/CardTemplates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@

import * as constants from "../../constants";
import { renderACAttachment } from "../../utils/CardUtils";
import * as config from "config";

// Function that works both in Node (where window === undefined) or the browser
export function appRoot(): string {
if (typeof window === "undefined") {
return config.get("app.baseUri");
} else {
return window.location.protocol + "//" + window.location.host;
}
}

export const cardTemplates: any = {
adaptiveCard: {
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"separator": true,
"size": "Large",
"weight": "Bolder",
"text": "Enter Command:",
"isSubtle": true,
"wrap": true,
},
{
"type": "Input.Text",
"id": "commandToBot",
"placeholder": "E.g. timezone",
},
],
"actions": [
{
"type": "Action.Submit",
"id": "postCommand",
"title": "Post Command",
"data": {
"taskResponse": "{{responseType}}",
},
},
{
"type": "Action.Submit",
"id": "cancel",
"title": "Cancel",
},
],
"version": "1.0",
},
adaptiveCardSubmitResponse: {
"type": "AdaptiveCard",
"body": [
{
"type": "TextBlock",
"weight": "Bolder",
"text": "Action.Submit Results",
},
{
"type": "TextBlock",
"separator": true,
"size": "Medium",
"text": "{{results}}",
"wrap": true,
},
],
"actions": [
{
"type": "Action.Submit",
"title": "OK",
"data": {
"taskResponse": "final",
"taskModule": "acResponse",
},
},
],
"version": "1.0",
},
};

export const fetchTemplates: any = {
adaptivecardsinglestep: {
"task": {
"type": "continue",
"value": {
"title": constants.TaskModuleTitles.AdaptiveCardSingleStepTitle,
"height": constants.TaskModuleSizes.adaptivecard.height,
"width": constants.TaskModuleSizes.adaptivecard.width,
// Below wraps it as a builder.Attachment
"card": renderACAttachment(cardTemplates.adaptiveCard, { responseType: "message" }),
},
},
},
adaptivecardmultistep: {
"task": {
"type": "continue",
"value": {
"title": constants.TaskModuleTitles.AdaptiveCardMultiStepTitle,
"height": constants.TaskModuleSizes.adaptivecard.height,
"width": constants.TaskModuleSizes.adaptivecard.width,
"fallbackUrl": null,
// Below wraps it as a builder.Attachment
"card": renderACAttachment(cardTemplates.adaptiveCard, { responseType: "continue" }),
},
},
},

singlestephtmlcard: {
"task": {
"type": "continue",
"value": {
"title": constants.TaskModuleTitles.SingleStepHtmlCardTitle,
"height": constants.TaskModuleSizes.customform.height,
"width": constants.TaskModuleSizes.customform.width,
"fallbackUrl": `${appRoot()}/${constants.TaskModuleIds.CustomForm}?type=singlestep`,
"url": `${appRoot()}/${constants.TaskModuleIds.CustomForm}?type=singlestep`,
},
},
},
multistephtmlcard: {
"task": {
"type": "continue",
"value": {
"title": constants.TaskModuleTitles.MultistepHtmlCardTitle,
"height": constants.TaskModuleSizes.customform.height,
"width": constants.TaskModuleSizes.customform.width,
"fallbackUrl": `${appRoot()}/${constants.TaskModuleIds.CustomForm}?type=multistep`,
"url": `${appRoot()}/${constants.TaskModuleIds.CustomForm}?type=multistep`,
},
},
},
submitMessageResponse: {
"task": {
"type": "message",
"value": "Task completed!",
},
},

submitResponse: {
"task": {
"type": "continue",
"value": {
"title": constants.TaskModuleTitles.ActionSubmitResponseTitle,
"height": "small",
"width": "medium",
"card": {},
},
},
},
};
Loading