@microsoft/agents-hosting-teams
Version:
Microsoft 365 Agents SDK for JavaScript
444 lines • 22.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TeamsApplication = void 0;
/**
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License.
*/
const agents_activity_1 = require("@microsoft/agents-activity");
const agents_hosting_1 = require("@microsoft/agents-hosting");
const teamsInfo_1 = require("../teamsInfo");
const parsers_1 = require("../parsers");
const adaptive_cards_actions_1 = require("./adaptive-cards-actions");
const messages_1 = require("./messages");
const messaging_extension_1 = require("./messaging-extension");
const meeting_1 = require("./meeting");
const task_1 = require("./task");
const logger = (0, agents_hosting_1.debug)('agents:teams-application');
/**
* Represents a Teams application that extends the AgentApplication class.
* Provides various functionalities for handling Teams-specific events, messages, and interactions.
* @template TState - The type of the turn state.
*/
class TeamsApplication extends agents_hosting_1.AgentApplication {
/**
* Initializes a new instance of the TeamsApplication class.
* @param options - Partial options for configuring the Teams application.
*/
constructor(options) {
super();
/**
* Routes for handling invoke activities.
*/
this._invokeRoutes = [];
this._teamsOptions = {
...super.options,
removeRecipientMention: (options === null || options === void 0 ? void 0 : options.removeRecipientMention) !== undefined ? options.removeRecipientMention : true,
taskModules: options === null || options === void 0 ? void 0 : options.taskModules
};
if ((options === null || options === void 0 ? void 0 : options.storage) && (options === null || options === void 0 ? void 0 : options.authorization)) {
this._teamsAuthManager = new agents_hosting_1.Authorization(this.options.storage, this.options.authorization);
}
this._adaptiveCards = new adaptive_cards_actions_1.AdaptiveCardsActions(this);
this._messages = new messages_1.Messages(this);
this._messageExtensions = new messaging_extension_1.MessageExtensions(this);
this._meetings = new meeting_1.Meetings(this);
this._taskModules = new task_1.TaskModules(this);
}
/**
* Gets the Teams application options.
*/
get teamsOptions() {
return this._teamsOptions;
}
/**
* Gets the task modules handler.
*/
get taskModules() {
return this._taskModules;
}
/**
* Gets the adaptive cards actions handler.
*/
get adaptiveCards() {
return this._adaptiveCards;
}
/**
* Gets the messages handler.
*/
get messages() {
return this._messages;
}
/**
* Gets the messaging extensions handler.
*/
get messageExtensions() {
return this._messageExtensions;
}
/**
* Gets the meetings handler.
*/
get meetings() {
return this._meetings;
}
/**
* Gets the Teams OAuth flow manager.
* @throws Error if no authentication options were configured.
*/
get teamsAuthManager() {
if (!this._teamsAuthManager) {
throw new Error('The Application.authentication property is unavailable because no authentication options were configured.');
}
return this._teamsAuthManager;
}
/**
* Adds a route to the application.
* @param selector - The route selector.
* @param handler - The route handler.
* @param isInvokeRoute - Whether the route is for invoke activities.
*/
addRoute(selector, handler, isInvokeRoute = false) {
if (isInvokeRoute) {
this._invokeRoutes.push({ selector, handler });
}
else {
this._routes.push({ selector, handler });
}
return this;
}
/**
* Runs the application for the given turn context.
* @param turnContext - The turn context.
*/
async run(turnContext) {
await this.runInternalTeams(turnContext);
}
async runInternalTeams(turnContext) {
return await this.startLongRunningCall(turnContext, async (context) => {
var _a;
this.startTypingTimer(context);
try {
if (this._teamsOptions.removeRecipientMention && context.activity.type === agents_activity_1.ActivityTypes.Message) {
context.activity.text = context.activity.removeRecipientMention();
}
const { storage, turnStateFactory } = this._teamsOptions;
const state = turnStateFactory();
await state.load(context, storage);
if (!(await this.callEventHandlers(context, state, this._beforeTurn))) {
await state.save(context, storage);
return false;
}
if (Array.isArray(this._teamsOptions.fileDownloaders) && this._teamsOptions.fileDownloaders.length > 0) {
const inputFiles = (_a = state.temp.inputFiles) !== null && _a !== void 0 ? _a : [];
for (let i = 0; i < this._teamsOptions.fileDownloaders.length; i++) {
const files = await this._teamsOptions.fileDownloaders[i].downloadFiles(context, state);
inputFiles.push(...files);
}
state.temp.inputFiles = inputFiles;
}
if (context.activity.type === agents_activity_1.ActivityTypes.Invoke) {
for (let i = 0; i < this._invokeRoutes.length; i++) {
const route = this._invokeRoutes[i];
if (await route.selector(context)) {
await route.handler(context, state);
if (await this.callEventHandlers(context, state, this._afterTurn)) {
await state.save(context, storage);
}
return true;
}
}
}
for (let i = 0; i < this._routes.length; i++) {
const route = this._routes[i];
if (await route.selector(context)) {
await route.handler(context, state);
if (await this.callEventHandlers(context, state, this._afterTurn)) {
await state.save(context, storage);
}
return true;
}
}
if (await this.callEventHandlers(context, state, this._afterTurn)) {
await state.save(context, storage);
}
return false;
}
catch (err) {
logger.error(err);
throw err;
}
finally {
this.stopTypingTimer();
}
});
}
/**
* Handles conversation update events.
* @param event - The conversation update event.
* @param handler - The handler for the event.
*/
onConversationUpdate(event, handler) {
if (typeof handler !== 'function') {
throw new Error(`ConversationUpdate 'handler' for ${event} is ${typeof handler}. Type of 'handler' must be a function.`);
}
const selector = this.createTeamsConversationUpdateSelector(event);
this.addRoute(selector, handler);
return this;
}
/**
* Handles message event updates.
* @param event - The message event.
* @param handler - The handler for the event.
*/
onMessageEventUpdate(event, handler) {
if (typeof handler !== 'function') {
throw new Error(`MessageUpdate 'handler' for ${event} is ${typeof handler}. Type of 'handler' must be a function.`);
}
const selector = this.createMessageEventUpdateSelector(event);
this.addRoute(selector, handler);
return this;
}
/**
* Handles message reactions.
* @param event - The message reaction event.
* @param handler - The handler for the event.
*/
onMessageReactions(event, handler) {
const selector = this.createMessageReactionSelector(event);
this.addRoute(selector, handler);
return this;
}
/**
* Handles file consent accept actions.
* @param handler - The handler for the file consent accept action.
*/
fileConsentAccept(handler) {
const selector = (context) => {
const valueAction = (0, parsers_1.parseValueAction)(context.activity.value);
return Promise.resolve(context.activity.type === agents_activity_1.ActivityTypes.Invoke &&
context.activity.name === 'fileConsent/invoke' &&
valueAction === 'accept');
};
const handlerWrapper = async (context, state) => {
await handler(context, state, context.activity.value);
await context.sendActivity({
type: agents_activity_1.ActivityTypes.InvokeResponse,
value: { status: 200 }
});
};
this.addRoute(selector, handlerWrapper, true);
return this;
}
/**
* Handles file consent decline actions.
* @param handler - The handler for the file consent decline action.
*/
fileConsentDecline(handler) {
const selector = (context) => {
const valueAction = (0, parsers_1.parseValueAction)(context.activity.value);
return Promise.resolve(context.activity.type === agents_activity_1.ActivityTypes.Invoke &&
context.activity.name === 'fileConsent/invoke' &&
valueAction === 'decline');
};
const handlerWrapper = async (context, state) => {
await handler(context, state, context.activity.value);
await context.sendActivity({
type: agents_activity_1.ActivityTypes.InvokeResponse,
value: { status: 200 }
});
};
this.addRoute(selector, handlerWrapper, true);
return this;
}
/**
* Handles handoff actions.
* @param handler - The handler for the handoff action.
*/
onHandoff(handler) {
const selector = (context) => {
return Promise.resolve(context.activity.type === agents_activity_1.ActivityTypes.Invoke && context.activity.name === 'handoff/action');
};
const handlerWrapper = async (context, state) => {
const valueContinuation = (0, parsers_1.parseValueContinuation)(context.activity.value);
await handler(context, state, valueContinuation);
await context.sendActivity({
type: agents_activity_1.ActivityTypes.InvokeResponse,
value: { status: 200 }
});
};
this.addRoute(selector, handlerWrapper, true);
return this;
}
/**
* Gets the channels of a team.
* @param context - The turn context, conversation reference, or activity.
*/
async getTeamChannels(context) {
var _a;
let teamsChannels = [];
const reference = this.getConversationReference(context);
if (((_a = reference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType) === 'channel') {
await this.continueConversationAsync(reference, async (ctx) => {
var _a, _b, _c, _d, _e, _f, _g, _h;
const teamId = (_d = (_c = (_b = (_a = ctx.activity) === null || _a === void 0 ? void 0 : _a.channelData) === null || _b === void 0 ? void 0 : _b.team) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : (((_f = (_e = ctx.activity) === null || _e === void 0 ? void 0 : _e.conversation) === null || _f === void 0 ? void 0 : _f.name) === undefined ? (_h = (_g = ctx.activity) === null || _g === void 0 ? void 0 : _g.conversation) === null || _h === void 0 ? void 0 : _h.id : undefined);
if (teamId) {
teamsChannels = await teamsInfo_1.TeamsInfo.getTeamChannels(ctx, teamId);
}
});
}
return teamsChannels;
}
/**
* Gets the details of a team.
* @param context - The turn context, conversation reference, or activity.
*/
async getTeamDetails(context) {
var _a;
let teamDetails;
const reference = this.getConversationReference(context);
if (((_a = reference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType) === 'channel') {
await this.continueConversationAsync(reference, async (ctx) => {
var _a, _b, _c, _d, _e, _f, _g, _h;
const teamId = (_d = (_c = (_b = (_a = ctx.activity) === null || _a === void 0 ? void 0 : _a.channelData) === null || _b === void 0 ? void 0 : _b.team) === null || _c === void 0 ? void 0 : _c.id) !== null && _d !== void 0 ? _d : (((_f = (_e = ctx.activity) === null || _e === void 0 ? void 0 : _e.conversation) === null || _f === void 0 ? void 0 : _f.name) === undefined ? (_h = (_g = ctx.activity) === null || _g === void 0 ? void 0 : _g.conversation) === null || _h === void 0 ? void 0 : _h.id : undefined);
if (teamId) {
teamDetails = await teamsInfo_1.TeamsInfo.getTeamDetails(ctx, teamId);
}
});
}
return teamDetails;
}
/**
* Gets the paged members of a team.
* @param context - The turn context or conversation reference.
* @param pageSize - The number of members per page.
* @param continuationToken - The continuation token for pagination.
*/
async getPagedMembers(context, pageSize, continuationToken) {
let pagedMembers = { members: [], continuationToken: '' };
await this.continueConversationAsync(context, async (ctx) => {
pagedMembers = await teamsInfo_1.TeamsInfo.getPagedMembers(ctx, pageSize, continuationToken);
});
return pagedMembers;
}
/**
* Handles Teams read receipt events.
* @param handler - The handler for the read receipt event.
*/
onTeamsReadReceipt(handler) {
const selector = (context) => {
return Promise.resolve(context.activity.type === agents_activity_1.ActivityTypes.Event &&
context.activity.channelId === 'msteams' &&
context.activity.name === 'application/vnd.microsoft/readReceipt');
};
const handlerWrapper = (context, state) => {
const readReceiptInfo = context.activity.value;
return handler(context, state, readReceiptInfo);
};
this.addRoute(selector, handlerWrapper);
return this;
}
createMessageEventUpdateSelector(event) {
switch (event) {
case 'editMessage':
return (context) => {
var _a, _b, _c;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.MessageUpdate &&
((_c = (_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.eventType) === event);
};
case 'softDeleteMessage':
return (context) => {
var _a, _b, _c;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.MessageDelete &&
((_c = (_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.eventType) === event);
};
case 'undeleteMessage':
return (context) => {
var _a, _b, _c;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.MessageUpdate &&
((_c = (_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.eventType) === event);
};
default:
throw new Error(`Invalid TeamsMessageEvent type: ${event}`);
}
}
createMessageReactionSelector(event) {
switch (event) {
case 'reactionsAdded':
return (context) => {
var _a, _b;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.MessageReaction &&
Array.isArray((_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.reactionsAdded) &&
context.activity.reactionsAdded.length > 0);
};
case 'reactionsRemoved':
return (context) => {
var _a, _b;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.MessageReaction &&
Array.isArray((_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.reactionsRemoved) &&
context.activity.reactionsRemoved.length > 0);
};
}
}
getConversationReference(context) {
let reference;
if (typeof context.activity === 'object') {
reference = context.activity.getConversationReference();
}
else if (typeof context.type === 'string') {
reference = context.getConversationReference();
}
else {
reference = context;
}
return reference;
}
createTeamsConversationUpdateSelector(event) {
switch (event) {
case 'channelCreated':
case 'channelDeleted':
case 'channelRenamed':
case 'channelRestored':
return (context) => {
var _a, _b, _c, _d, _e, _f;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.ConversationUpdate &&
((_c = (_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.eventType) === event &&
((_e = (_d = context === null || context === void 0 ? void 0 : context.activity) === null || _d === void 0 ? void 0 : _d.channelData) === null || _e === void 0 ? void 0 : _e.channel) &&
((_f = context.activity.channelData) === null || _f === void 0 ? void 0 : _f.team));
};
case 'membersAdded':
return (context) => {
var _a, _b;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.ConversationUpdate &&
Array.isArray((_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.membersAdded) &&
context.activity.membersAdded.length > 0);
};
case 'membersRemoved':
return (context) => {
var _a, _b;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.ConversationUpdate &&
Array.isArray((_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.membersRemoved) &&
context.activity.membersRemoved.length > 0);
};
case 'teamRenamed':
case 'teamDeleted':
case 'teamHardDeleted':
case 'teamArchived':
case 'teamUnarchived':
case 'teamRestored':
return (context) => {
var _a, _b, _c, _d, _e;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.ConversationUpdate &&
((_c = (_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.eventType) === event &&
((_e = (_d = context === null || context === void 0 ? void 0 : context.activity) === null || _d === void 0 ? void 0 : _d.channelData) === null || _e === void 0 ? void 0 : _e.team));
};
default:
return (context) => {
var _a, _b, _c;
return Promise.resolve(((_a = context === null || context === void 0 ? void 0 : context.activity) === null || _a === void 0 ? void 0 : _a.type) === agents_activity_1.ActivityTypes.ConversationUpdate &&
((_c = (_b = context === null || context === void 0 ? void 0 : context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.eventType) === event);
};
}
}
}
exports.TeamsApplication = TeamsApplication;
//# sourceMappingURL=teamsApplication.js.map