@microsoft/teamsfx 1.1.2-alpha.7eddd6cf4.0 → 1.1.2-alpha.8d60b4f8e.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,13 +3,14 @@ import { ConfidentialClientApplication } from '@azure/msal-node';
3
3
  import { createHash } from 'crypto';
4
4
  import { Client } from '@microsoft/microsoft-graph-client';
5
5
  import { ManagedIdentityCredential } from '@azure/identity';
6
- import { ActivityTypes, Channels, TeamsInfo, CardFactory, ActionTypes, MessageFactory, StatusCodes, verifyStateOperationName, tokenExchangeOperationName, TurnContext, BotFrameworkAdapter } from 'botbuilder';
7
- import { Dialog } from 'botbuilder-dialogs';
6
+ import { ActivityTypes, Channels, TeamsInfo, CardFactory, ActionTypes, MessageFactory, StatusCodes, verifyStateOperationName, tokenExchangeOperationName, TurnContext, TeamsActivityHandler, MemoryStorage, UserState, ConversationState, BotFrameworkAdapter } from 'botbuilder';
7
+ import { Dialog, ComponentDialog, WaterfallDialog, DialogSet, DialogTurnStatus } from 'botbuilder-dialogs';
8
8
  import { v4 } from 'uuid';
9
9
  import axios from 'axios';
10
10
  import { Agent } from 'https';
11
11
  import * as path from 'path';
12
12
  import * as fs from 'fs';
13
+ import { __rest } from 'tslib';
13
14
  import { AdaptiveCards } from '@microsoft/adaptivecards-tools';
14
15
 
15
16
  // Copyright (c) Microsoft Corporation.
@@ -39,6 +40,30 @@ var ErrorCode;
39
40
  * Channel is not supported error.
40
41
  */
41
42
  ErrorCode["ChannelNotSupported"] = "ChannelNotSupported";
43
+ /**
44
+ * Failed to retrieve sso token
45
+ */
46
+ ErrorCode["FailedToRetrieveSsoToken"] = "FailedToRetrieveSsoToken";
47
+ /**
48
+ * Failed to process sso handler
49
+ */
50
+ ErrorCode["FailedToProcessSsoHandler"] = "FailedToProcessSsoHandler";
51
+ /**
52
+ * Cannot find command
53
+ */
54
+ ErrorCode["CannotFindCommand"] = "CannotFindCommand";
55
+ /**
56
+ * Failed to run sso step
57
+ */
58
+ ErrorCode["FailedToRunSsoStep"] = "FailedToRunSsoStep";
59
+ /**
60
+ * Failed to run dedup step
61
+ */
62
+ ErrorCode["FailedToRunDedupStep"] = "FailedToRunDedupStep";
63
+ /**
64
+ * Sso activity handler is undefined
65
+ */
66
+ ErrorCode["SsoActivityHandlerIsUndefined"] = "SsoActivityHandlerIsUndefined";
42
67
  /**
43
68
  * Runtime is not supported error.
44
69
  */
@@ -94,6 +119,15 @@ ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
94
119
  ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
95
120
  // ChannelNotSupported Error
96
121
  ErrorMessage.OnlyMSTeamsChannelSupported = "{0} is only supported in MS Teams Channel";
122
+ ErrorMessage.FailedToProcessSsoHandler = "Failed to process sso handler: {0}";
123
+ // FailedToRetrieveSsoToken Error
124
+ ErrorMessage.FailedToRetrieveSsoToken = "Failed to retrieve sso token, user failed to finish the AAD consent flow.";
125
+ // CannotFindCommand Error
126
+ ErrorMessage.CannotFindCommand = "Cannot find command: {0}";
127
+ ErrorMessage.FailedToRunSsoStep = "Failed to run dialog to retrieve sso token: {0}";
128
+ ErrorMessage.FailedToRunDedupStep = "Failed to run dialog to remove duplicated messages: {0}";
129
+ // SsoActivityHandlerIsUndefined Error
130
+ ErrorMessage.SsoActivityHandlerIsNull = "Sso command can only be used or added when sso activity handler is not undefined";
97
131
  // IdentityTypeNotSupported Error
98
132
  ErrorMessage.IdentityTypeNotSupported = "{0} identity is not supported in {1}";
99
133
  // AuthorizationInfoError
@@ -104,6 +138,7 @@ ErrorMessage.EmptyParameter = "Parameter {0} is empty";
104
138
  ErrorMessage.DuplicateHttpsOptionProperty = "Axios HTTPS agent already defined value for property {0}";
105
139
  ErrorMessage.DuplicateApiKeyInHeader = "The request already defined api key in request header with name {0}.";
106
140
  ErrorMessage.DuplicateApiKeyInQueryParam = "The request already defined api key in query parameter with name {0}.";
141
+ ErrorMessage.OnlySupportInQueryActivity = "The handleMessageExtensionQueryWithToken only support in handleTeamsMessagingExtensionQuery with composeExtension/query type.";
107
142
  /**
108
143
  * Error class with code and message thrown by the SDK.
109
144
  */
@@ -2122,41 +2157,80 @@ class CardActionBot {
2122
2157
  * @internal
2123
2158
  */
2124
2159
  class CommandResponseMiddleware {
2125
- constructor(handlers) {
2160
+ constructor(handlers, ssoHandlers, activityHandler) {
2126
2161
  this.commandHandlers = [];
2127
- if (handlers && handlers.length > 0) {
2128
- this.commandHandlers.push(...handlers);
2162
+ this.ssoCommandHandlers = [];
2163
+ handlers = handlers !== null && handlers !== void 0 ? handlers : [];
2164
+ ssoHandlers = ssoHandlers !== null && ssoHandlers !== void 0 ? ssoHandlers : [];
2165
+ this.hasSsoCommand = ssoHandlers.length > 0;
2166
+ this.ssoActivityHandler = activityHandler;
2167
+ if (this.hasSsoCommand && !this.ssoActivityHandler) {
2168
+ internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
2169
+ throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
2129
2170
  }
2171
+ this.commandHandlers.push(...handlers);
2172
+ for (const ssoHandler of ssoHandlers) {
2173
+ this.addSsoCommand(ssoHandler);
2174
+ }
2175
+ }
2176
+ addSsoCommand(ssoHandler) {
2177
+ var _a;
2178
+ (_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.addCommand(async (context, tokenResponse, message) => {
2179
+ const matchResult = this.shouldTrigger(ssoHandler.triggerPatterns, message.text);
2180
+ message.matches = Array.isArray(matchResult) ? matchResult : void 0;
2181
+ const response = await ssoHandler.handleCommandReceived(context, message, tokenResponse);
2182
+ await this.processResponse(context, response);
2183
+ }, ssoHandler.triggerPatterns);
2184
+ this.ssoCommandHandlers.push(ssoHandler);
2185
+ this.commandHandlers.push(ssoHandler);
2186
+ this.hasSsoCommand = true;
2130
2187
  }
2131
2188
  async onTurn(context, next) {
2189
+ var _a, _b;
2132
2190
  if (context.activity.type === ActivityTypes.Message) {
2133
2191
  // Invoke corresponding command handler for the command response
2134
2192
  const commandText = this.getActivityText(context.activity);
2135
- const message = {
2136
- text: commandText,
2137
- };
2138
2193
  for (const handler of this.commandHandlers) {
2139
2194
  const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
2140
2195
  // It is important to note that the command bot will stop processing handlers
2141
2196
  // when the first command handler is matched.
2142
2197
  if (!!matchResult) {
2143
- message.matches = Array.isArray(matchResult) ? matchResult : void 0;
2144
- const response = await handler.handleCommandReceived(context, message);
2145
- if (typeof response === "string") {
2146
- await context.sendActivity(response);
2198
+ if (this.isSsoExecutionHandler(handler)) {
2199
+ await ((_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.run(context));
2147
2200
  }
2148
2201
  else {
2149
- const replyActivity = response;
2150
- if (replyActivity) {
2151
- await context.sendActivity(replyActivity);
2152
- }
2202
+ const message = {
2203
+ text: commandText,
2204
+ };
2205
+ message.matches = Array.isArray(matchResult) ? matchResult : void 0;
2206
+ const response = await handler.handleCommandReceived(context, message);
2207
+ await this.processResponse(context, response);
2153
2208
  }
2154
2209
  break;
2155
2210
  }
2156
2211
  }
2157
2212
  }
2213
+ else {
2214
+ if (this.hasSsoCommand) {
2215
+ await ((_b = this.ssoActivityHandler) === null || _b === void 0 ? void 0 : _b.run(context));
2216
+ }
2217
+ }
2158
2218
  await next();
2159
2219
  }
2220
+ async processResponse(context, response) {
2221
+ if (typeof response === "string") {
2222
+ await context.sendActivity(response);
2223
+ }
2224
+ else {
2225
+ const replyActivity = response;
2226
+ if (replyActivity) {
2227
+ await context.sendActivity(replyActivity);
2228
+ }
2229
+ }
2230
+ }
2231
+ isSsoExecutionHandler(handler) {
2232
+ return this.ssoCommandHandlers.indexOf(handler) >= 0;
2233
+ }
2160
2234
  matchPattern(pattern, text) {
2161
2235
  if (text) {
2162
2236
  if (typeof pattern === "string") {
@@ -2206,14 +2280,15 @@ class CommandBot {
2206
2280
  * @param adapter The bound `BotFrameworkAdapter`.
2207
2281
  * @param options - initialize options
2208
2282
  */
2209
- constructor(adapter, options) {
2210
- this.middleware = new CommandResponseMiddleware(options === null || options === void 0 ? void 0 : options.commands);
2283
+ constructor(adapter, options, ssoCommandActivityHandler, ssoConfig) {
2284
+ this.ssoConfig = ssoConfig;
2285
+ this.middleware = new CommandResponseMiddleware(options === null || options === void 0 ? void 0 : options.commands, options === null || options === void 0 ? void 0 : options.ssoCommands, ssoCommandActivityHandler);
2211
2286
  this.adapter = adapter.use(this.middleware);
2212
2287
  }
2213
2288
  /**
2214
2289
  * Registers a command into the command bot.
2215
2290
  *
2216
- * @param command The command to registered.
2291
+ * @param command The command to register.
2217
2292
  */
2218
2293
  registerCommand(command) {
2219
2294
  if (command) {
@@ -2223,13 +2298,41 @@ class CommandBot {
2223
2298
  /**
2224
2299
  * Registers commands into the command bot.
2225
2300
  *
2226
- * @param commands The command to registered.
2301
+ * @param commands The commands to register.
2227
2302
  */
2228
2303
  registerCommands(commands) {
2229
2304
  if (commands) {
2230
2305
  this.middleware.commandHandlers.push(...commands);
2231
2306
  }
2232
2307
  }
2308
+ /**
2309
+ * Registers a sso command into the command bot.
2310
+ *
2311
+ * @param command The command to register.
2312
+ */
2313
+ registerSsoCommand(ssoCommand) {
2314
+ this.validateSsoActivityHandler();
2315
+ this.middleware.addSsoCommand(ssoCommand);
2316
+ }
2317
+ /**
2318
+ * Registers commands into the command bot.
2319
+ *
2320
+ * @param commands The commands to register.
2321
+ */
2322
+ registerSsoCommands(ssoCommands) {
2323
+ if (ssoCommands.length > 0) {
2324
+ this.validateSsoActivityHandler();
2325
+ for (const ssoCommand of ssoCommands) {
2326
+ this.middleware.addSsoCommand(ssoCommand);
2327
+ }
2328
+ }
2329
+ }
2330
+ validateSsoActivityHandler() {
2331
+ if (!this.middleware.ssoActivityHandler) {
2332
+ internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
2333
+ throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
2334
+ }
2335
+ }
2233
2336
  }
2234
2337
 
2235
2338
  // Copyright (c) Microsoft Corporation.
@@ -2704,6 +2807,10 @@ class TeamsBotInstallation {
2704
2807
  * @returns an array of channels if bot is installed into a team, otherwise returns an empty array.
2705
2808
  */
2706
2809
  async channels() {
2810
+ const channels = [];
2811
+ if (this.type !== NotificationTargetType.Channel) {
2812
+ return channels;
2813
+ }
2707
2814
  let teamsChannels = [];
2708
2815
  await this.adapter.continueConversation(this.conversationReference, async (context) => {
2709
2816
  const teamId = getTeamsBotInstallationId(context);
@@ -2711,7 +2818,6 @@ class TeamsBotInstallation {
2711
2818
  teamsChannels = await TeamsInfo.getTeamChannels(context, teamId);
2712
2819
  }
2713
2820
  });
2714
- const channels = [];
2715
2821
  for (const channel of teamsChannels) {
2716
2822
  channels.push(new Channel(this, channel));
2717
2823
  }
@@ -2736,6 +2842,24 @@ class TeamsBotInstallation {
2736
2842
  });
2737
2843
  return members;
2738
2844
  }
2845
+ /**
2846
+ * Get team details from this bot installation
2847
+ *
2848
+ * @returns the team details if bot is installed into a team, otherwise returns undefined.
2849
+ */
2850
+ async getTeamDetails() {
2851
+ if (this.type !== NotificationTargetType.Channel) {
2852
+ return undefined;
2853
+ }
2854
+ let teamDetails;
2855
+ await this.adapter.continueConversation(this.conversationReference, async (context) => {
2856
+ const teamId = getTeamsBotInstallationId(context);
2857
+ if (teamId !== undefined) {
2858
+ teamDetails = await TeamsInfo.getTeamDetails(context, teamId);
2859
+ }
2860
+ });
2861
+ return teamDetails;
2862
+ }
2739
2863
  }
2740
2864
  /**
2741
2865
  * Provide utilities to send notification to varies targets (e.g., member, group, channel).
@@ -2795,6 +2919,459 @@ class NotificationBot {
2795
2919
  }
2796
2920
  return targets;
2797
2921
  }
2922
+ /**
2923
+ * Returns the first {@link Member} where predicate is true, and undefined otherwise.
2924
+ *
2925
+ * @param predicate find calls predicate once for each member of the installation,
2926
+ * until it finds one where predicate returns true. If such a member is found, find
2927
+ * immediately returns that member. Otherwise, find returns undefined.
2928
+ * @param scope the scope to find members from the installations
2929
+ * (personal chat, group chat, Teams channel).
2930
+ * @returns the first {@link Member} where predicate is true, and undefined otherwise.
2931
+ */
2932
+ async findMember(predicate, scope) {
2933
+ for (const target of await this.installations()) {
2934
+ if (this.matchSearchScope(target, scope)) {
2935
+ for (const member of await target.members()) {
2936
+ if (await predicate(member)) {
2937
+ return member;
2938
+ }
2939
+ }
2940
+ }
2941
+ }
2942
+ return;
2943
+ }
2944
+ /**
2945
+ * Returns the first {@link Channel} where predicate is true, and undefined otherwise.
2946
+ *
2947
+ * @param predicate find calls predicate once for each channel of the installation,
2948
+ * until it finds one where predicate returns true. If such a channel is found, find
2949
+ * immediately returns that channel. Otherwise, find returns undefined.
2950
+ * @returns the first {@link Channel} where predicate is true, and undefined otherwise.
2951
+ */
2952
+ async findChannel(predicate) {
2953
+ for (const target of await this.installations()) {
2954
+ if (target.type === NotificationTargetType.Channel) {
2955
+ const teamDetails = await target.getTeamDetails();
2956
+ for (const channel of await target.channels()) {
2957
+ if (await predicate(channel, teamDetails)) {
2958
+ return channel;
2959
+ }
2960
+ }
2961
+ }
2962
+ }
2963
+ return;
2964
+ }
2965
+ /**
2966
+ * Returns all {@link Member} where predicate is true, and empty array otherwise.
2967
+ *
2968
+ * @param predicate find calls predicate for each member of the installation.
2969
+ * @param scope the scope to find members from the installations
2970
+ * (personal chat, group chat, Teams channel).
2971
+ * @returns an array of {@link Member} where predicate is true, and empty array otherwise.
2972
+ */
2973
+ async findAllMembers(predicate, scope) {
2974
+ const members = [];
2975
+ for (const target of await this.installations()) {
2976
+ if (this.matchSearchScope(target, scope)) {
2977
+ for (const member of await target.members()) {
2978
+ if (await predicate(member)) {
2979
+ members.push(member);
2980
+ }
2981
+ }
2982
+ }
2983
+ }
2984
+ return members;
2985
+ }
2986
+ /**
2987
+ * Returns all {@link Channel} where predicate is true, and empty array otherwise.
2988
+ *
2989
+ * @param predicate find calls predicate for each channel of the installation.
2990
+ * @returns an array of {@link Channel} where predicate is true, and empty array otherwise.
2991
+ */
2992
+ async findAllChannels(predicate) {
2993
+ const channels = [];
2994
+ for (const target of await this.installations()) {
2995
+ if (target.type === NotificationTargetType.Channel) {
2996
+ const teamDetails = await target.getTeamDetails();
2997
+ for (const channel of await target.channels()) {
2998
+ if (await predicate(channel, teamDetails)) {
2999
+ channels.push(channel);
3000
+ }
3001
+ }
3002
+ }
3003
+ }
3004
+ return channels;
3005
+ }
3006
+ matchSearchScope(target, scope) {
3007
+ scope = scope !== null && scope !== void 0 ? scope : SearchScope.All;
3008
+ return ((target.type === NotificationTargetType.Channel && (scope & SearchScope.Channel) !== 0) ||
3009
+ (target.type === NotificationTargetType.Group && (scope & SearchScope.Group) !== 0) ||
3010
+ (target.type === NotificationTargetType.Person && (scope & SearchScope.Person) !== 0));
3011
+ }
3012
+ }
3013
+ /**
3014
+ * The search scope when calling {@link NotificationBot.findMember} and {@link NotificationBot.findAllMembers}.
3015
+ * The search scope is a flagged enum and it can be combined with `|`.
3016
+ * For example, to search from personal chat and group chat, use `SearchScope.Person | SearchScope.Group`.
3017
+ */
3018
+ var SearchScope;
3019
+ (function (SearchScope) {
3020
+ /**
3021
+ * Search members from the installations in personal chat only.
3022
+ */
3023
+ SearchScope[SearchScope["Person"] = 1] = "Person";
3024
+ /**
3025
+ * Search members from the installations in group chat only.
3026
+ */
3027
+ SearchScope[SearchScope["Group"] = 2] = "Group";
3028
+ /**
3029
+ * Search members from the installations in Teams channel only.
3030
+ */
3031
+ SearchScope[SearchScope["Channel"] = 4] = "Channel";
3032
+ /**
3033
+ * Search members from all installations including personal chat, group chat and Teams channel.
3034
+ */
3035
+ SearchScope[SearchScope["All"] = 7] = "All";
3036
+ })(SearchScope || (SearchScope = {}));
3037
+
3038
+ // Copyright (c) Microsoft Corporation.
3039
+ let DIALOG_NAME = "BotSsoExecutionDialog";
3040
+ let TEAMS_SSO_PROMPT_ID = "TeamsFxSsoPrompt";
3041
+ let COMMAND_ROUTE_DIALOG = "CommandRouteDialog";
3042
+ /**
3043
+ * Sso execution dialog, use to handle sso command
3044
+ */
3045
+ class BotSsoExecutionDialog extends ComponentDialog {
3046
+ /**
3047
+ * Creates a new instance of the BotSsoExecutionDialog.
3048
+ * @param dedupStorage Helper storage to remove duplicated messages
3049
+ * @param settings The list of scopes for which the token will have access
3050
+ * @param teamsfx {@link TeamsFx} instance for authentication
3051
+ */
3052
+ constructor(dedupStorage, ssoPromptSettings, teamsfx, dialogName) {
3053
+ super(dialogName !== null && dialogName !== void 0 ? dialogName : DIALOG_NAME);
3054
+ this.dedupStorageKeys = [];
3055
+ // Map to store the commandId and triggerPatterns, key: commandId, value: triggerPatterns
3056
+ this.commandMapping = new Map();
3057
+ if (dialogName) {
3058
+ DIALOG_NAME = dialogName;
3059
+ TEAMS_SSO_PROMPT_ID = dialogName + TEAMS_SSO_PROMPT_ID;
3060
+ COMMAND_ROUTE_DIALOG = dialogName + COMMAND_ROUTE_DIALOG;
3061
+ }
3062
+ this.initialDialogId = COMMAND_ROUTE_DIALOG;
3063
+ this.dedupStorage = dedupStorage;
3064
+ this.dedupStorageKeys = [];
3065
+ const ssoDialog = new TeamsBotSsoPrompt(teamsfx, TEAMS_SSO_PROMPT_ID, ssoPromptSettings);
3066
+ this.addDialog(ssoDialog);
3067
+ const commandRouteDialog = new WaterfallDialog(COMMAND_ROUTE_DIALOG, [
3068
+ this.commandRouteStep.bind(this),
3069
+ ]);
3070
+ this.addDialog(commandRouteDialog);
3071
+ }
3072
+ /**
3073
+ * Add TeamsFxBotSsoCommandHandler instance
3074
+ * @param handler {@link BotSsoExecutionDialogHandler} callback function
3075
+ * @param triggerPatterns The trigger pattern
3076
+ */
3077
+ addCommand(handler, triggerPatterns) {
3078
+ const commandId = this.getCommandHash(triggerPatterns);
3079
+ const dialog = new WaterfallDialog(commandId, [
3080
+ this.ssoStep.bind(this),
3081
+ this.dedupStep.bind(this),
3082
+ async (stepContext) => {
3083
+ const tokenResponse = stepContext.result.tokenResponse;
3084
+ const context = stepContext.context;
3085
+ const message = stepContext.result.message;
3086
+ try {
3087
+ if (tokenResponse) {
3088
+ await handler(context, tokenResponse, message);
3089
+ }
3090
+ else {
3091
+ throw new Error(ErrorMessage.FailedToRetrieveSsoToken);
3092
+ }
3093
+ return await stepContext.endDialog();
3094
+ }
3095
+ catch (error) {
3096
+ const errorMsg = formatString(ErrorMessage.FailedToProcessSsoHandler, error.message);
3097
+ internalLogger.error(errorMsg);
3098
+ return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToProcessSsoHandler));
3099
+ }
3100
+ },
3101
+ ]);
3102
+ this.commandMapping.set(commandId, triggerPatterns);
3103
+ this.addDialog(dialog);
3104
+ }
3105
+ getCommandHash(patterns) {
3106
+ const expressions = Array.isArray(patterns) ? patterns : [patterns];
3107
+ const patternStr = expressions.join();
3108
+ const patternStrWithoutSpecialChar = patternStr.replace(/[^a-zA-Z0-9]/g, "");
3109
+ const hash = createHash("sha256").update(patternStr).digest("hex").toLowerCase();
3110
+ return patternStrWithoutSpecialChar + hash;
3111
+ }
3112
+ /**
3113
+ * The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system.
3114
+ *
3115
+ * @param context The context object for the current turn.
3116
+ * @param accessor The instance of StatePropertyAccessor for dialog system.
3117
+ */
3118
+ async run(context, accessor) {
3119
+ const dialogSet = new DialogSet(accessor);
3120
+ dialogSet.add(this);
3121
+ const dialogContext = await dialogSet.createContext(context);
3122
+ this.ensureMsTeamsChannel(dialogContext);
3123
+ const results = await dialogContext.continueDialog();
3124
+ if (results && results.status === DialogTurnStatus.empty) {
3125
+ await dialogContext.beginDialog(this.id);
3126
+ }
3127
+ else if (results &&
3128
+ results.status === DialogTurnStatus.complete &&
3129
+ results.result instanceof Error) {
3130
+ throw results.result;
3131
+ }
3132
+ }
3133
+ getActivityText(activity) {
3134
+ let text = activity.text;
3135
+ const removedMentionText = TurnContext.removeRecipientMention(activity);
3136
+ if (removedMentionText) {
3137
+ text = removedMentionText
3138
+ .toLowerCase()
3139
+ .replace(/\n|\r\n/g, "")
3140
+ .trim();
3141
+ }
3142
+ return text;
3143
+ }
3144
+ async commandRouteStep(stepContext) {
3145
+ const turnContext = stepContext.context;
3146
+ const text = this.getActivityText(turnContext.activity);
3147
+ const commandId = this.getMatchesCommandId(text);
3148
+ if (commandId) {
3149
+ return await stepContext.beginDialog(commandId);
3150
+ }
3151
+ const errorMsg = formatString(ErrorMessage.CannotFindCommand, turnContext.activity.text);
3152
+ internalLogger.error(errorMsg);
3153
+ throw new ErrorWithCode(errorMsg, ErrorCode.CannotFindCommand);
3154
+ }
3155
+ async ssoStep(stepContext) {
3156
+ try {
3157
+ const turnContext = stepContext.context;
3158
+ const text = this.getActivityText(turnContext.activity);
3159
+ const message = {
3160
+ text,
3161
+ };
3162
+ stepContext.options.commandMessage = message;
3163
+ return await stepContext.beginDialog(TEAMS_SSO_PROMPT_ID);
3164
+ }
3165
+ catch (error) {
3166
+ const errorMsg = formatString(ErrorMessage.FailedToRunSsoStep, error.message);
3167
+ internalLogger.error(errorMsg);
3168
+ return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunSsoStep));
3169
+ }
3170
+ }
3171
+ async dedupStep(stepContext) {
3172
+ const tokenResponse = stepContext.result;
3173
+ if (!tokenResponse) {
3174
+ internalLogger.error(ErrorMessage.FailedToRetrieveSsoToken);
3175
+ return await stepContext.endDialog(new ErrorWithCode(ErrorMessage.FailedToRetrieveSsoToken, ErrorCode.FailedToRunSsoStep));
3176
+ }
3177
+ try {
3178
+ // Only dedup after ssoStep to make sure that all Teams client would receive the login request
3179
+ if (tokenResponse && (await this.shouldDedup(stepContext.context))) {
3180
+ return Dialog.EndOfTurn;
3181
+ }
3182
+ return await stepContext.next({
3183
+ tokenResponse,
3184
+ message: stepContext.options.commandMessage,
3185
+ });
3186
+ }
3187
+ catch (error) {
3188
+ const errorMsg = formatString(ErrorMessage.FailedToRunDedupStep, error.message);
3189
+ internalLogger.error(errorMsg);
3190
+ return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunDedupStep));
3191
+ }
3192
+ }
3193
+ /**
3194
+ * Called when the component is ending.
3195
+ *
3196
+ * @param context Context for the current turn of conversation.
3197
+ */
3198
+ async onEndDialog(context) {
3199
+ const conversationId = context.activity.conversation.id;
3200
+ const currentDedupKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) > 0);
3201
+ await this.dedupStorage.delete(currentDedupKeys);
3202
+ this.dedupStorageKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) < 0);
3203
+ }
3204
+ /**
3205
+ * If a user is signed into multiple Teams clients, the Bot might receive a "signin/tokenExchange" from each client.
3206
+ * Each token exchange request for a specific user login will have an identical activity.value.Id.
3207
+ * Only one of these token exchange requests should be processed by the bot. For a distributed bot in production,
3208
+ * this requires a distributed storage to ensure only one token exchange is processed.
3209
+ * @param context Context for the current turn of conversation.
3210
+ * @returns boolean value indicate whether the message should be removed
3211
+ */
3212
+ async shouldDedup(context) {
3213
+ const storeItem = {
3214
+ eTag: context.activity.value.id,
3215
+ };
3216
+ const key = this.getStorageKey(context);
3217
+ const storeItems = { [key]: storeItem };
3218
+ try {
3219
+ await this.dedupStorage.write(storeItems);
3220
+ this.dedupStorageKeys.push(key);
3221
+ }
3222
+ catch (err) {
3223
+ if (err instanceof Error && err.message.indexOf("eTag conflict")) {
3224
+ return true;
3225
+ }
3226
+ throw err;
3227
+ }
3228
+ return false;
3229
+ }
3230
+ getStorageKey(context) {
3231
+ if (!context || !context.activity || !context.activity.conversation) {
3232
+ throw new Error("Invalid context, can not get storage key!");
3233
+ }
3234
+ const activity = context.activity;
3235
+ const channelId = activity.channelId;
3236
+ const conversationId = activity.conversation.id;
3237
+ if (activity.type !== ActivityTypes.Invoke || activity.name !== tokenExchangeOperationName) {
3238
+ throw new Error("TokenExchangeState can only be used with Invokes of signin/tokenExchange.");
3239
+ }
3240
+ const value = activity.value;
3241
+ if (!value || !value.id) {
3242
+ throw new Error("Invalid signin/tokenExchange. Missing activity.value.id.");
3243
+ }
3244
+ return `${channelId}/${conversationId}/${value.id}`;
3245
+ }
3246
+ matchPattern(pattern, text) {
3247
+ if (text) {
3248
+ if (typeof pattern === "string") {
3249
+ const regExp = new RegExp(pattern, "i");
3250
+ return regExp.test(text);
3251
+ }
3252
+ if (pattern instanceof RegExp) {
3253
+ const matches = text.match(pattern);
3254
+ return matches !== null && matches !== void 0 ? matches : false;
3255
+ }
3256
+ }
3257
+ return false;
3258
+ }
3259
+ isPatternMatched(patterns, text) {
3260
+ const expressions = Array.isArray(patterns) ? patterns : [patterns];
3261
+ for (const ex of expressions) {
3262
+ const matches = this.matchPattern(ex, text);
3263
+ return !!matches;
3264
+ }
3265
+ return false;
3266
+ }
3267
+ getMatchesCommandId(text) {
3268
+ for (const command of this.commandMapping) {
3269
+ const pattern = command[1];
3270
+ if (this.isPatternMatched(pattern, text)) {
3271
+ return command[0];
3272
+ }
3273
+ }
3274
+ return undefined;
3275
+ }
3276
+ /**
3277
+ * Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
3278
+ * @param dc dialog context
3279
+ * @throws {@link ErrorCode|ChannelNotSupported} if bot channel is not MS Teams
3280
+ * @internal
3281
+ */
3282
+ ensureMsTeamsChannel(dc) {
3283
+ if (dc.context.activity.channelId != Channels.Msteams) {
3284
+ const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "SSO execution dialog");
3285
+ internalLogger.error(errorMsg);
3286
+ throw new ErrorWithCode(errorMsg, ErrorCode.ChannelNotSupported);
3287
+ }
3288
+ }
3289
+ }
3290
+
3291
+ // Copyright (c) Microsoft Corporation.
3292
+ /**
3293
+ * Default SSO execution activity handler
3294
+ */
3295
+ class DefaultBotSsoExecutionActivityHandler extends TeamsActivityHandler {
3296
+ /**
3297
+ * Creates a new instance of the DefaultBotSsoExecutionActivityHandler.
3298
+ * @param ssoConfig configuration for SSO command bot
3299
+ *
3300
+ * @remarks
3301
+ * In the constructor, it uses BotSsoConfig parameter which from {@link ConversationBot} options to initialize {@link BotSsoExecutionDialog}.
3302
+ * It also need to register an event handler for the message event which trigger {@link BotSsoExecutionDialog} instance.
3303
+ */
3304
+ constructor(ssoConfig) {
3305
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3306
+ super();
3307
+ const memoryStorage = new MemoryStorage();
3308
+ const userState = (_b = (_a = ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.userState) !== null && _b !== void 0 ? _b : new UserState(memoryStorage);
3309
+ const conversationState = (_d = (_c = ssoConfig.dialog) === null || _c === void 0 ? void 0 : _c.conversationState) !== null && _d !== void 0 ? _d : new ConversationState(memoryStorage);
3310
+ const dedupStorage = (_f = (_e = ssoConfig.dialog) === null || _e === void 0 ? void 0 : _e.dedupStorage) !== null && _f !== void 0 ? _f : memoryStorage;
3311
+ const _l = ssoConfig.aad, { scopes } = _l, customConfig = __rest(_l, ["scopes"]);
3312
+ const settings = {
3313
+ scopes: scopes,
3314
+ timeout: (_h = (_g = ssoConfig.dialog) === null || _g === void 0 ? void 0 : _g.ssoPromptConfig) === null || _h === void 0 ? void 0 : _h.timeout,
3315
+ endOnInvalidMessage: (_k = (_j = ssoConfig.dialog) === null || _j === void 0 ? void 0 : _j.ssoPromptConfig) === null || _k === void 0 ? void 0 : _k.endOnInvalidMessage,
3316
+ };
3317
+ const teamsfx = new TeamsFx(IdentityType.User, Object.assign({}, customConfig));
3318
+ this.ssoExecutionDialog = new BotSsoExecutionDialog(dedupStorage, settings, teamsfx);
3319
+ this.conversationState = conversationState;
3320
+ this.dialogState = conversationState.createProperty("DialogState");
3321
+ this.userState = userState;
3322
+ this.onMessage(async (context, next) => {
3323
+ await this.ssoExecutionDialog.run(context, this.dialogState);
3324
+ await next();
3325
+ });
3326
+ }
3327
+ /**
3328
+ * Add TeamsFxBotSsoCommandHandler instance to SSO execution dialog
3329
+ * @param handler {@link BotSsoExecutionDialogHandler} callback function
3330
+ * @param triggerPatterns The trigger pattern
3331
+ *
3332
+ * @remarks
3333
+ * This function is used to add SSO command to {@link BotSsoExecutionDialog} instance.
3334
+ */
3335
+ addCommand(handler, triggerPatterns) {
3336
+ this.ssoExecutionDialog.addCommand(handler, triggerPatterns);
3337
+ }
3338
+ /**
3339
+ * Called to initiate the event emission process.
3340
+ * @param context The context object for the current turn.
3341
+ */
3342
+ async run(context) {
3343
+ try {
3344
+ await super.run(context);
3345
+ }
3346
+ finally {
3347
+ await this.conversationState.saveChanges(context, false);
3348
+ await this.userState.saveChanges(context, false);
3349
+ }
3350
+ }
3351
+ /**
3352
+ * Receives invoke activities with Activity name of 'signin/verifyState'.
3353
+ * @param context A context object for this turn.
3354
+ * @param query Signin state (part of signin action auth flow) verification invoke query.
3355
+ * @returns A promise that represents the work queued.
3356
+ *
3357
+ * @remarks
3358
+ * It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
3359
+ */
3360
+ async handleTeamsSigninVerifyState(context, query) {
3361
+ await this.ssoExecutionDialog.run(context, this.dialogState);
3362
+ }
3363
+ /**
3364
+ * Receives invoke activities with Activity name of 'signin/tokenExchange'
3365
+ * @param context A context object for this turn.
3366
+ * @param query Signin state (part of signin action auth flow) verification invoke query
3367
+ * @returns A promise that represents the work queued.
3368
+ *
3369
+ * @remark
3370
+ * It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
3371
+ */
3372
+ async handleTeamsSigninTokenExchange(context, query) {
3373
+ await this.ssoExecutionDialog.run(context, this.dialogState);
3374
+ }
2798
3375
  }
2799
3376
 
2800
3377
  // Copyright (c) Microsoft Corporation.
@@ -2859,20 +3436,30 @@ class ConversationBot {
2859
3436
  * @param options - initialize options
2860
3437
  */
2861
3438
  constructor(options) {
2862
- var _a, _b, _c;
3439
+ var _a, _b, _c, _d;
2863
3440
  if (options.adapter) {
2864
3441
  this.adapter = options.adapter;
2865
3442
  }
2866
3443
  else {
2867
3444
  this.adapter = this.createDefaultAdapter(options.adapterConfig);
2868
3445
  }
2869
- if ((_a = options.command) === null || _a === void 0 ? void 0 : _a.enabled) {
2870
- this.command = new CommandBot(this.adapter, options.command);
3446
+ let ssoCommandActivityHandler;
3447
+ if (options === null || options === void 0 ? void 0 : options.ssoConfig) {
3448
+ if ((_a = options.ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.CustomBotSsoExecutionActivityHandler) {
3449
+ ssoCommandActivityHandler =
3450
+ new options.ssoConfig.dialog.CustomBotSsoExecutionActivityHandler(options.ssoConfig);
3451
+ }
3452
+ else {
3453
+ ssoCommandActivityHandler = new DefaultBotSsoExecutionActivityHandler(options.ssoConfig);
3454
+ }
3455
+ }
3456
+ if ((_b = options.command) === null || _b === void 0 ? void 0 : _b.enabled) {
3457
+ this.command = new CommandBot(this.adapter, options.command, ssoCommandActivityHandler, options.ssoConfig);
2871
3458
  }
2872
- if ((_b = options.notification) === null || _b === void 0 ? void 0 : _b.enabled) {
3459
+ if ((_c = options.notification) === null || _c === void 0 ? void 0 : _c.enabled) {
2873
3460
  this.notification = new NotificationBot(this.adapter, options.notification);
2874
3461
  }
2875
- if ((_c = options.cardAction) === null || _c === void 0 ? void 0 : _c.enabled) {
3462
+ if ((_d = options.cardAction) === null || _d === void 0 ? void 0 : _d.enabled) {
2876
3463
  this.cardAction = new CardActionBot(this.adapter, options.cardAction);
2877
3464
  }
2878
3465
  }
@@ -3061,5 +3648,105 @@ class MessageBuilder {
3061
3648
  }
3062
3649
  }
3063
3650
 
3064
- export { AdaptiveCardResponse, ApiKeyLocation, ApiKeyProvider, AppCredential, BasicAuthProvider, BearerTokenAuthProvider, CardActionBot, CertificateAuthProvider, Channel, CommandBot, ConversationBot, ErrorCode, ErrorWithCode, IdentityType, InvokeResponseErrorCode, InvokeResponseFactory, LogLevel, Member, MessageBuilder, MsGraphAuthProvider, NotificationBot, NotificationTargetType, OnBehalfOfUserCredential, TeamsBotInstallation, TeamsBotSsoPrompt, TeamsFx, TeamsUserCredential, createApiClient, createMicrosoftGraphClient, createPemCertOption, createPfxCertOption, getLogLevel, getTediousConnectionConfig, sendAdaptiveCard, sendMessage, setLogFunction, setLogLevel, setLogger };
3651
+ // Copyright (c) Microsoft Corporation.
3652
+ /**
3653
+ * Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions.
3654
+ * This method only work on MessageExtension with Query now.
3655
+ *
3656
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
3657
+ * @param {string | string[]} scopes - The list of scopes for which the token will have access.
3658
+ *
3659
+ * @returns SignIn link CardAction with 200 status code.
3660
+ */
3661
+ function getSignInResponseForMessageExtension(teamsfx, scopes) {
3662
+ const scopesArray = getScopesArray(scopes);
3663
+ const signInLink = `${teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(scopesArray.join(" "))}&clientId=${teamsfx.getConfig("clientId")}&tenantId=${teamsfx.getConfig("tenantId")}`;
3664
+ return {
3665
+ composeExtension: {
3666
+ type: "silentAuth",
3667
+ suggestedActions: {
3668
+ actions: [
3669
+ {
3670
+ type: "openUrl",
3671
+ value: signInLink,
3672
+ title: "Message Extension OAuth",
3673
+ },
3674
+ ],
3675
+ },
3676
+ },
3677
+ };
3678
+ }
3679
+ /**
3680
+ * execution in message extension with SSO token.
3681
+ *
3682
+ * @param {TurnContext} context - The context object for the current turn.
3683
+ * @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
3684
+ * @param {string[]} scopes - The list of scopes for which the token will have access.
3685
+ * @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
3686
+ *
3687
+ * @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
3688
+ * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
3689
+ * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
3690
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
3691
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
3692
+ *
3693
+ * @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
3694
+ */
3695
+ async function executionWithToken(context, config, scopes, logic) {
3696
+ const valueObj = context.activity.value;
3697
+ if (!valueObj.authentication || !valueObj.authentication.token) {
3698
+ internalLogger.verbose("No AccessToken in request, return silentAuth for AccessToken");
3699
+ return getSignInResponseForMessageExtension(new TeamsFx(IdentityType.User, config), scopes);
3700
+ }
3701
+ try {
3702
+ const teamsfx = new TeamsFx(IdentityType.User, config).setSsoToken(valueObj.authentication.token);
3703
+ const token = await teamsfx.getCredential().getToken(scopes);
3704
+ const ssoTokenExpiration = parseJwt(valueObj.authentication.token).exp;
3705
+ const tokenRes = {
3706
+ ssoToken: valueObj.authentication.token,
3707
+ ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
3708
+ token: token.token,
3709
+ expiration: token.expiresOnTimestamp.toString(),
3710
+ connectionName: "",
3711
+ };
3712
+ if (logic) {
3713
+ return await logic(tokenRes);
3714
+ }
3715
+ }
3716
+ catch (err) {
3717
+ if (err instanceof ErrorWithCode && err.code === ErrorCode.UiRequiredError) {
3718
+ internalLogger.verbose("User not consent yet, return 412 to user consent first.");
3719
+ const response = { status: 412 };
3720
+ await context.sendActivity({ value: response, type: ActivityTypes.InvokeResponse });
3721
+ return;
3722
+ }
3723
+ throw err;
3724
+ }
3725
+ }
3726
+ /**
3727
+ * Users execute query in message extension with SSO or access token.
3728
+ *
3729
+ * @param {TurnContext} context - The context object for the current turn.
3730
+ * @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
3731
+ * @param {string| string[]} scopes - The list of scopes for which the token will have access.
3732
+ * @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
3733
+ *
3734
+ * @throws {@link ErrorCode|InternalError} when User invoke not response to message extension query.
3735
+ * @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
3736
+ * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
3737
+ * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
3738
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
3739
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
3740
+ *
3741
+ * @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
3742
+ */
3743
+ async function handleMessageExtensionQueryWithToken(context, config, scopes, logic) {
3744
+ if (context.activity.name != "composeExtension/query") {
3745
+ internalLogger.error(ErrorMessage.OnlySupportInQueryActivity);
3746
+ throw new ErrorWithCode(formatString(ErrorMessage.OnlySupportInQueryActivity), ErrorCode.FailedOperation);
3747
+ }
3748
+ return await executionWithToken(context, config !== null && config !== void 0 ? config : {}, scopes, logic);
3749
+ }
3750
+
3751
+ export { AdaptiveCardResponse, ApiKeyLocation, ApiKeyProvider, AppCredential, BasicAuthProvider, BearerTokenAuthProvider, BotSsoExecutionDialog, CardActionBot, CertificateAuthProvider, Channel, CommandBot, ConversationBot, DefaultBotSsoExecutionActivityHandler, ErrorCode, ErrorWithCode, IdentityType, InvokeResponseErrorCode, InvokeResponseFactory, LogLevel, Member, MessageBuilder, MsGraphAuthProvider, NotificationBot, NotificationTargetType, OnBehalfOfUserCredential, SearchScope, TeamsBotInstallation, TeamsBotSsoPrompt, TeamsFx, TeamsUserCredential, createApiClient, createMicrosoftGraphClient, createPemCertOption, createPfxCertOption, getLogLevel, getTediousConnectionConfig, handleMessageExtensionQueryWithToken, sendAdaptiveCard, sendMessage, setLogFunction, setLogLevel, setLogger };
3065
3752
  //# sourceMappingURL=index.esm2017.mjs.map