@microsoft/teams-ai
Version:
SDK focused on building AI based applications for Microsoft Teams.
275 lines • 14.4 kB
JavaScript
;
/**
* @module teams-ai
*/
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.TaskModules = exports.TaskModuleInvokeNames = void 0;
const botbuilder_1 = require("botbuilder");
var TaskModuleInvokeNames;
(function (TaskModuleInvokeNames) {
TaskModuleInvokeNames["CONFIG_FETCH_INVOKE_NAME"] = "config/fetch";
TaskModuleInvokeNames["CONFIG_SUBMIT_INVOKE_NAME"] = "config/submit";
TaskModuleInvokeNames["FETCH_INVOKE_NAME"] = "task/fetch";
TaskModuleInvokeNames["SUBMIT_INVOKE_NAME"] = "task/submit";
TaskModuleInvokeNames["DEFAULT_TASK_DATA_FILTER"] = "verb";
})(TaskModuleInvokeNames || (exports.TaskModuleInvokeNames = TaskModuleInvokeNames = {}));
/**
* TaskModules class to enable fluent style registration of handlers related to Task Modules.
* @template TState Type of the turn state object being persisted.
*/
class TaskModules {
_app;
/**
* Creates a new instance of the TaskModules class.
* @param {Application} app Top level application class to register handlers with.
*/
constructor(app) {
this._app = app;
}
/**
* Registers a handler to process the initial fetch of the task module.
* @remarks
* Handlers should respond with either an initial TaskInfo object or a string containing
* a message to display to the user.
* @template TData Optional. Type of the data object being passed to the handler.
* @param {string | RegExp | RouteSelector | string[] | RegExp[] | RouteSelector[]} verb - Name of the verb(s) to register the handler for.
* @param {(context: TurnContext, state: TState, data: TData) => Promise<TaskModuleTaskInfo | string>} handler - Function to call when the handler is triggered.
* @param {TurnContext} handler.context - Context for the current turn of conversation with the user.
* @param {TState} handler.state - Current state of the turn.
* @param {TData} handler.data - Data object passed to the handler.
* @returns {Application<TState>} The application for chaining purposes.
*/
fetch(verb, handler) {
(Array.isArray(verb) ? verb : [verb]).forEach((v) => {
const { DEFAULT_TASK_DATA_FILTER, FETCH_INVOKE_NAME } = TaskModuleInvokeNames;
const filterField = this._app.options.taskModules?.taskDataFilter ?? DEFAULT_TASK_DATA_FILTER;
const selector = createTaskSelector(v, filterField, FETCH_INVOKE_NAME);
this._app.addRoute(selector, async (context, state) => {
if (context?.activity?.channelId === botbuilder_1.Channels.Msteams) {
// Insure that we're in an invoke as expected
if (context?.activity?.type !== botbuilder_1.ActivityTypes.Invoke ||
context?.activity?.name !== FETCH_INVOKE_NAME) {
throw new Error(`Unexpected TaskModules.fetch() triggered for activity type: ${context?.activity?.type}`);
}
// Call handler and then check to see if an invoke response has already been added
const result = await handler(context, state, context.activity.value?.data ?? {});
if (!context.turnState.get(botbuilder_1.INVOKE_RESPONSE_KEY)) {
// Format invoke response
let response;
if (typeof result == 'string') {
// Return message
response = {
task: {
type: 'message',
value: result
}
};
}
else {
// Return card
response = {
task: {
type: 'continue',
value: result
}
};
}
// Queue up invoke response
await context.sendActivity({
value: { body: response, status: 200 },
type: botbuilder_1.ActivityTypes.InvokeResponse
});
}
}
}, true);
});
return this._app;
}
/**
* Registers a handler to process the submission of a task module.
* @remarks
* Handlers should respond with another TaskInfo object, message string, or `null` to indicate
* the task is completed.
* @template TData Optional. Type of the data object being passed to the handler.
* @param {string | RegExp | RouteSelector | string[] | RegExp[] | RouteSelector[]} verb - Name of the verb(s) to register the handler for.
* @param {(context: TurnContext, state: TState, data: TData) => Promise<TaskModuleTaskInfo | string | null | undefined>} handler - Function to call when the handler is triggered.
* @param {TurnContext} handler.context - Context for the current turn of conversation with the user.
* @param {TState} handler.state - Current state of the turn.
* @param {TData} handler.data - Data object passed to the handler.
* @returns {Application<TState>} The application for chaining purposes.
*/
submit(verb, handler) {
(Array.isArray(verb) ? verb : [verb]).forEach((v) => {
const { DEFAULT_TASK_DATA_FILTER, SUBMIT_INVOKE_NAME } = TaskModuleInvokeNames;
const filterField = this._app.options.taskModules?.taskDataFilter ?? DEFAULT_TASK_DATA_FILTER;
const selector = createTaskSelector(v, filterField, SUBMIT_INVOKE_NAME);
this._app.addRoute(selector, async (context, state) => {
if (context?.activity?.channelId === botbuilder_1.Channels.Msteams) {
// Insure that we're in an invoke as expected
if (context?.activity?.type !== botbuilder_1.ActivityTypes.Invoke ||
context?.activity?.name !== SUBMIT_INVOKE_NAME) {
throw new Error(`Unexpected TaskModules.submit() triggered for activity type: ${context?.activity?.type}`);
}
// Call handler and then check to see if an invoke response has already been added
const result = await handler(context, state, context.activity.value?.data ?? {});
if (!result) {
await context.sendActivity({
value: { status: 200 },
type: botbuilder_1.ActivityTypes.InvokeResponse
});
}
if (!context.turnState.get(botbuilder_1.INVOKE_RESPONSE_KEY)) {
// Format invoke response
let response = undefined;
if (typeof result == 'string') {
// Return message
response = {
task: {
type: 'message',
value: result
}
};
}
else if (typeof result == 'object') {
// Return card
response = {
task: {
type: 'continue',
value: result
}
};
}
// Queue up invoke response
await context.sendActivity({
value: { body: response, status: 200 },
type: botbuilder_1.ActivityTypes.InvokeResponse
});
}
}
}, true);
});
return this._app;
}
/**
* Registers a handler for fetching Teams config data for Auth or Task Modules
* @template TData Optional. Type of the data object being passed to the handler.
* @param {(context: TurnContext, state: TState, data: TData) => Promise<TaskModuleTaskInfo | string | null | undefined>} handler - Function to call when the handler is triggered.
* @param {TurnContext} handler.context - Context for the current turn of conversation with the user.
* @param {TState} handler.state - Current state of the turn.
* @param {TData} handler.data - Data object passed to the handler.
* @returns {Application<TState>} The application for chaining purposes.
*/
configFetch(handler) {
const selector = (context) => {
const { CONFIG_FETCH_INVOKE_NAME } = TaskModuleInvokeNames;
return Promise.resolve(context?.activity?.type === botbuilder_1.ActivityTypes.Invoke && context?.activity?.name === CONFIG_FETCH_INVOKE_NAME);
};
this._app.addRoute(selector, async (context, state) => {
if (context?.activity?.channelId === botbuilder_1.Channels.Msteams) {
// Call handler and then check to see if an invoke response has already been added
const result = await handler(context, state, context.activity.value?.data ?? {});
let response;
if (!context.turnState.get(botbuilder_1.INVOKE_RESPONSE_KEY)) {
// Format invoke response)
response = {
responseType: 'config',
config: result
};
if ('cacheInfo' in result) {
response.cacheInfo = result.cacheInfo;
}
// Queue up invoke response
await context.sendActivity({
value: { body: response, status: 200 },
type: botbuilder_1.ActivityTypes.InvokeResponse
});
}
}
}, true);
return this._app;
}
/**
* Registers a handler for submitting Teams config data for Auth or Task Modules
* @template TData Optional. Type of the data object being passed to the handler.
* @param {(context: TurnContext, state: TState, data: TData) => Promise<TaskModuleTaskInfo | string | null | undefined>} handler - Function to call when the handler is triggered.
* @param {TurnContext} handler.context - Context for the current turn of conversation with the user.
* @param {TState} handler.state - Current state of the turn.
* @param {TData} handler.data - Data object passed to the handler.
* @returns {Application<TState>} The application for chaining purposes.
*/
configSubmit(handler) {
const selector = (context) => {
const { CONFIG_SUBMIT_INVOKE_NAME } = TaskModuleInvokeNames;
return Promise.resolve(context?.activity?.type === botbuilder_1.ActivityTypes.Invoke &&
context?.activity?.name === CONFIG_SUBMIT_INVOKE_NAME);
};
this._app.addRoute(selector, async (context, state) => {
if (context?.activity?.channelId === botbuilder_1.Channels.Msteams) {
// Call handler and then check to see if an invoke response has already been added
const result = await handler(context, state, context.activity.value?.data ?? {});
let response;
if (!context.turnState.get(botbuilder_1.INVOKE_RESPONSE_KEY)) {
// Format invoke response)
response = {
responseType: 'config',
config: result
};
if ('cacheInfo' in result) {
response.cacheInfo = result.cacheInfo;
}
// Queue up invoke response
await context.sendActivity({
value: { body: response, status: 200 },
type: botbuilder_1.ActivityTypes.InvokeResponse
});
}
}
}, true);
return this._app;
}
}
exports.TaskModules = TaskModules;
/**
* Creates a route selector function for a given verb, filter field, and invoke name.
* @param {string | RegExp | RouteSelector} verb - The verb to match.
* @param {string} filterField - The field to use for filtering.
* @param {string} invokeName - The name of the invoke action.
* @returns {RouteSelector} The route selector function.
* @private
* @remarks
* This function is used to create a route selector function for a given verb, filter field, and invoke name.
* The route selector function is used to match incoming requests to the appropriate handler function.
*/
function createTaskSelector(verb, filterField, invokeName) {
if (typeof verb == 'function') {
// Return the passed in selector function
return verb;
}
else if (verb instanceof RegExp) {
// Return a function that matches the verb using a RegExp
return (context) => {
const isTeams = context.activity.channelId == botbuilder_1.Channels.Msteams;
const isInvoke = context?.activity?.type == botbuilder_1.ActivityTypes.Invoke && context?.activity?.name == invokeName;
const data = context?.activity?.value?.data;
if (isInvoke && isTeams && typeof data == 'object' && typeof data[filterField] == 'string') {
return Promise.resolve(verb.test(data[filterField]));
}
else {
return Promise.resolve(false);
}
};
}
else {
// Return a function that attempts to match verb
return (context) => {
const isInvoke = context?.activity?.type == botbuilder_1.ActivityTypes.Invoke && context?.activity?.name == invokeName;
const data = context?.activity?.value?.data;
return Promise.resolve(isInvoke && typeof data == 'object' && data[filterField] == verb);
};
}
}
//# sourceMappingURL=TaskModules.js.map