@microsoft/teamsfx 1.1.2-alpha.3113a0b87.0 → 1.1.2-alpha.79f6a1d84.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.
- package/dist/index.esm2017.js +221 -18
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +732 -34
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +237 -18
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +767 -31
- package/dist/index.node.cjs.js.map +1 -1
- package/package.json +7 -5
- package/types/teamsfx.d.ts +384 -29
package/dist/index.esm2017.mjs
CHANGED
|
@@ -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
|
*/
|
|
@@ -694,10 +729,13 @@ class TeamsUserCredential {
|
|
|
694
729
|
}
|
|
695
730
|
/**
|
|
696
731
|
* Popup login page to get user's access token with specific scopes.
|
|
732
|
+
*
|
|
733
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
734
|
+
*
|
|
697
735
|
* @remarks
|
|
698
736
|
* Can only be used within Teams.
|
|
699
737
|
*/
|
|
700
|
-
async login(scopes) {
|
|
738
|
+
async login(scopes, resources) {
|
|
701
739
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
702
740
|
}
|
|
703
741
|
/**
|
|
@@ -710,10 +748,13 @@ class TeamsUserCredential {
|
|
|
710
748
|
}
|
|
711
749
|
/**
|
|
712
750
|
* Get basic user info from SSO token
|
|
751
|
+
*
|
|
752
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
753
|
+
*
|
|
713
754
|
* @remarks
|
|
714
755
|
* Can only be used within Teams.
|
|
715
756
|
*/
|
|
716
|
-
getUserInfo() {
|
|
757
|
+
getUserInfo(resources) {
|
|
717
758
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
718
759
|
}
|
|
719
760
|
}
|
|
@@ -1661,8 +1702,9 @@ class TeamsFx {
|
|
|
1661
1702
|
this.configuration = new Map();
|
|
1662
1703
|
this.loadFromEnv();
|
|
1663
1704
|
if (customConfig) {
|
|
1664
|
-
|
|
1665
|
-
|
|
1705
|
+
const myConfig = Object.assign({}, customConfig);
|
|
1706
|
+
for (const key of Object.keys(myConfig)) {
|
|
1707
|
+
const value = myConfig[key];
|
|
1666
1708
|
if (value) {
|
|
1667
1709
|
this.configuration.set(key, value);
|
|
1668
1710
|
}
|
|
@@ -1704,9 +1746,10 @@ class TeamsFx {
|
|
|
1704
1746
|
}
|
|
1705
1747
|
/**
|
|
1706
1748
|
* Get user information.
|
|
1749
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
1707
1750
|
* @returns UserInfo object.
|
|
1708
1751
|
*/
|
|
1709
|
-
async getUserInfo() {
|
|
1752
|
+
async getUserInfo(resources) {
|
|
1710
1753
|
if (this.identityType !== IdentityType.User) {
|
|
1711
1754
|
const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.identityType.toString(), "TeamsFx");
|
|
1712
1755
|
internalLogger.error(errorMsg);
|
|
@@ -1728,13 +1771,14 @@ class TeamsFx {
|
|
|
1728
1771
|
* await teamsfx.login("https://graph.microsoft.com/User.Read Calendars.Read"); // multiple scopes using string
|
|
1729
1772
|
* ```
|
|
1730
1773
|
* @param scopes - The list of scopes for which the token will have access, before that, we will request user to consent.
|
|
1774
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
1731
1775
|
*
|
|
1732
1776
|
* @throws {@link ErrorCode|InternalError} when failed to login with unknown error.
|
|
1733
1777
|
* @throws {@link ErrorCode|ConsentFailed} when user canceled or failed to consent.
|
|
1734
1778
|
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
1735
1779
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
1736
1780
|
*/
|
|
1737
|
-
async login(scopes) {
|
|
1781
|
+
async login(scopes, resources) {
|
|
1738
1782
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "login"), ErrorCode.RuntimeNotSupported);
|
|
1739
1783
|
}
|
|
1740
1784
|
/**
|
|
@@ -2113,41 +2157,82 @@ class CardActionBot {
|
|
|
2113
2157
|
* @internal
|
|
2114
2158
|
*/
|
|
2115
2159
|
class CommandResponseMiddleware {
|
|
2116
|
-
constructor(handlers) {
|
|
2160
|
+
constructor(handlers, ssoHandlers, activityHandler) {
|
|
2117
2161
|
this.commandHandlers = [];
|
|
2118
|
-
|
|
2119
|
-
|
|
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);
|
|
2120
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.hasSsoCommand = true;
|
|
2121
2186
|
}
|
|
2122
2187
|
async onTurn(context, next) {
|
|
2188
|
+
var _a, _b;
|
|
2123
2189
|
if (context.activity.type === ActivityTypes.Message) {
|
|
2124
2190
|
// Invoke corresponding command handler for the command response
|
|
2125
2191
|
const commandText = this.getActivityText(context.activity);
|
|
2126
|
-
|
|
2127
|
-
text: commandText,
|
|
2128
|
-
};
|
|
2192
|
+
let alreadyProcessed = false;
|
|
2129
2193
|
for (const handler of this.commandHandlers) {
|
|
2130
2194
|
const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
|
|
2131
2195
|
// It is important to note that the command bot will stop processing handlers
|
|
2132
2196
|
// when the first command handler is matched.
|
|
2133
2197
|
if (!!matchResult) {
|
|
2198
|
+
const message = {
|
|
2199
|
+
text: commandText,
|
|
2200
|
+
};
|
|
2134
2201
|
message.matches = Array.isArray(matchResult) ? matchResult : void 0;
|
|
2135
2202
|
const response = await handler.handleCommandReceived(context, message);
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
}
|
|
2139
|
-
else {
|
|
2140
|
-
const replyActivity = response;
|
|
2141
|
-
if (replyActivity) {
|
|
2142
|
-
await context.sendActivity(replyActivity);
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2203
|
+
await this.processResponse(context, response);
|
|
2204
|
+
alreadyProcessed = true;
|
|
2145
2205
|
break;
|
|
2146
2206
|
}
|
|
2147
2207
|
}
|
|
2208
|
+
if (!alreadyProcessed) {
|
|
2209
|
+
for (const handler of this.ssoCommandHandlers) {
|
|
2210
|
+
const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
|
|
2211
|
+
if (!!matchResult) {
|
|
2212
|
+
await ((_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.run(context));
|
|
2213
|
+
break;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
else {
|
|
2219
|
+
if (this.hasSsoCommand) {
|
|
2220
|
+
await ((_b = this.ssoActivityHandler) === null || _b === void 0 ? void 0 : _b.run(context));
|
|
2221
|
+
}
|
|
2148
2222
|
}
|
|
2149
2223
|
await next();
|
|
2150
2224
|
}
|
|
2225
|
+
async processResponse(context, response) {
|
|
2226
|
+
if (typeof response === "string") {
|
|
2227
|
+
await context.sendActivity(response);
|
|
2228
|
+
}
|
|
2229
|
+
else {
|
|
2230
|
+
const replyActivity = response;
|
|
2231
|
+
if (replyActivity) {
|
|
2232
|
+
await context.sendActivity(replyActivity);
|
|
2233
|
+
}
|
|
2234
|
+
}
|
|
2235
|
+
}
|
|
2151
2236
|
matchPattern(pattern, text) {
|
|
2152
2237
|
if (text) {
|
|
2153
2238
|
if (typeof pattern === "string") {
|
|
@@ -2197,14 +2282,15 @@ class CommandBot {
|
|
|
2197
2282
|
* @param adapter The bound `BotFrameworkAdapter`.
|
|
2198
2283
|
* @param options - initialize options
|
|
2199
2284
|
*/
|
|
2200
|
-
constructor(adapter, options) {
|
|
2201
|
-
this.
|
|
2285
|
+
constructor(adapter, options, ssoCommandActivityHandler, ssoConfig) {
|
|
2286
|
+
this.ssoConfig = ssoConfig;
|
|
2287
|
+
this.middleware = new CommandResponseMiddleware(options === null || options === void 0 ? void 0 : options.commands, options === null || options === void 0 ? void 0 : options.ssoCommands, ssoCommandActivityHandler);
|
|
2202
2288
|
this.adapter = adapter.use(this.middleware);
|
|
2203
2289
|
}
|
|
2204
2290
|
/**
|
|
2205
2291
|
* Registers a command into the command bot.
|
|
2206
2292
|
*
|
|
2207
|
-
* @param command The command to
|
|
2293
|
+
* @param command The command to register.
|
|
2208
2294
|
*/
|
|
2209
2295
|
registerCommand(command) {
|
|
2210
2296
|
if (command) {
|
|
@@ -2214,13 +2300,41 @@ class CommandBot {
|
|
|
2214
2300
|
/**
|
|
2215
2301
|
* Registers commands into the command bot.
|
|
2216
2302
|
*
|
|
2217
|
-
* @param commands The
|
|
2303
|
+
* @param commands The commands to register.
|
|
2218
2304
|
*/
|
|
2219
2305
|
registerCommands(commands) {
|
|
2220
2306
|
if (commands) {
|
|
2221
2307
|
this.middleware.commandHandlers.push(...commands);
|
|
2222
2308
|
}
|
|
2223
2309
|
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Registers a sso command into the command bot.
|
|
2312
|
+
*
|
|
2313
|
+
* @param command The command to register.
|
|
2314
|
+
*/
|
|
2315
|
+
registerSsoCommand(ssoCommand) {
|
|
2316
|
+
this.validateSsoActivityHandler();
|
|
2317
|
+
this.middleware.addSsoCommand(ssoCommand);
|
|
2318
|
+
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Registers commands into the command bot.
|
|
2321
|
+
*
|
|
2322
|
+
* @param commands The commands to register.
|
|
2323
|
+
*/
|
|
2324
|
+
registerSsoCommands(ssoCommands) {
|
|
2325
|
+
if (ssoCommands.length > 0) {
|
|
2326
|
+
this.validateSsoActivityHandler();
|
|
2327
|
+
for (const ssoCommand of ssoCommands) {
|
|
2328
|
+
this.middleware.addSsoCommand(ssoCommand);
|
|
2329
|
+
}
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
validateSsoActivityHandler() {
|
|
2333
|
+
if (!this.middleware.ssoActivityHandler) {
|
|
2334
|
+
internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
|
|
2335
|
+
throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
|
|
2336
|
+
}
|
|
2337
|
+
}
|
|
2224
2338
|
}
|
|
2225
2339
|
|
|
2226
2340
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -2695,6 +2809,10 @@ class TeamsBotInstallation {
|
|
|
2695
2809
|
* @returns an array of channels if bot is installed into a team, otherwise returns an empty array.
|
|
2696
2810
|
*/
|
|
2697
2811
|
async channels() {
|
|
2812
|
+
const channels = [];
|
|
2813
|
+
if (this.type !== NotificationTargetType.Channel) {
|
|
2814
|
+
return channels;
|
|
2815
|
+
}
|
|
2698
2816
|
let teamsChannels = [];
|
|
2699
2817
|
await this.adapter.continueConversation(this.conversationReference, async (context) => {
|
|
2700
2818
|
const teamId = getTeamsBotInstallationId(context);
|
|
@@ -2702,7 +2820,6 @@ class TeamsBotInstallation {
|
|
|
2702
2820
|
teamsChannels = await TeamsInfo.getTeamChannels(context, teamId);
|
|
2703
2821
|
}
|
|
2704
2822
|
});
|
|
2705
|
-
const channels = [];
|
|
2706
2823
|
for (const channel of teamsChannels) {
|
|
2707
2824
|
channels.push(new Channel(this, channel));
|
|
2708
2825
|
}
|
|
@@ -2727,6 +2844,24 @@ class TeamsBotInstallation {
|
|
|
2727
2844
|
});
|
|
2728
2845
|
return members;
|
|
2729
2846
|
}
|
|
2847
|
+
/**
|
|
2848
|
+
* Get team details from this bot installation
|
|
2849
|
+
*
|
|
2850
|
+
* @returns the team details if bot is installed into a team, otherwise returns undefined.
|
|
2851
|
+
*/
|
|
2852
|
+
async getTeamDetails() {
|
|
2853
|
+
if (this.type !== NotificationTargetType.Channel) {
|
|
2854
|
+
return undefined;
|
|
2855
|
+
}
|
|
2856
|
+
let teamDetails;
|
|
2857
|
+
await this.adapter.continueConversation(this.conversationReference, async (context) => {
|
|
2858
|
+
const teamId = getTeamsBotInstallationId(context);
|
|
2859
|
+
if (teamId !== undefined) {
|
|
2860
|
+
teamDetails = await TeamsInfo.getTeamDetails(context, teamId);
|
|
2861
|
+
}
|
|
2862
|
+
});
|
|
2863
|
+
return teamDetails;
|
|
2864
|
+
}
|
|
2730
2865
|
}
|
|
2731
2866
|
/**
|
|
2732
2867
|
* Provide utilities to send notification to varies targets (e.g., member, group, channel).
|
|
@@ -2786,6 +2921,459 @@ class NotificationBot {
|
|
|
2786
2921
|
}
|
|
2787
2922
|
return targets;
|
|
2788
2923
|
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Returns the first {@link Member} where predicate is true, and undefined otherwise.
|
|
2926
|
+
*
|
|
2927
|
+
* @param predicate find calls predicate once for each member of the installation,
|
|
2928
|
+
* until it finds one where predicate returns true. If such a member is found, find
|
|
2929
|
+
* immediately returns that member. Otherwise, find returns undefined.
|
|
2930
|
+
* @param scope the scope to find members from the installations
|
|
2931
|
+
* (personal chat, group chat, Teams channel).
|
|
2932
|
+
* @returns the first {@link Member} where predicate is true, and undefined otherwise.
|
|
2933
|
+
*/
|
|
2934
|
+
async findMember(predicate, scope) {
|
|
2935
|
+
for (const target of await this.installations()) {
|
|
2936
|
+
if (this.matchSearchScope(target, scope)) {
|
|
2937
|
+
for (const member of await target.members()) {
|
|
2938
|
+
if (await predicate(member)) {
|
|
2939
|
+
return member;
|
|
2940
|
+
}
|
|
2941
|
+
}
|
|
2942
|
+
}
|
|
2943
|
+
}
|
|
2944
|
+
return;
|
|
2945
|
+
}
|
|
2946
|
+
/**
|
|
2947
|
+
* Returns the first {@link Channel} where predicate is true, and undefined otherwise.
|
|
2948
|
+
*
|
|
2949
|
+
* @param predicate find calls predicate once for each channel of the installation,
|
|
2950
|
+
* until it finds one where predicate returns true. If such a channel is found, find
|
|
2951
|
+
* immediately returns that channel. Otherwise, find returns undefined.
|
|
2952
|
+
* @returns the first {@link Channel} where predicate is true, and undefined otherwise.
|
|
2953
|
+
*/
|
|
2954
|
+
async findChannel(predicate) {
|
|
2955
|
+
for (const target of await this.installations()) {
|
|
2956
|
+
if (target.type === NotificationTargetType.Channel) {
|
|
2957
|
+
const teamDetails = await target.getTeamDetails();
|
|
2958
|
+
for (const channel of await target.channels()) {
|
|
2959
|
+
if (await predicate(channel, teamDetails)) {
|
|
2960
|
+
return channel;
|
|
2961
|
+
}
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
}
|
|
2965
|
+
return;
|
|
2966
|
+
}
|
|
2967
|
+
/**
|
|
2968
|
+
* Returns all {@link Member} where predicate is true, and empty array otherwise.
|
|
2969
|
+
*
|
|
2970
|
+
* @param predicate find calls predicate for each member of the installation.
|
|
2971
|
+
* @param scope the scope to find members from the installations
|
|
2972
|
+
* (personal chat, group chat, Teams channel).
|
|
2973
|
+
* @returns an array of {@link Member} where predicate is true, and empty array otherwise.
|
|
2974
|
+
*/
|
|
2975
|
+
async findAllMembers(predicate, scope) {
|
|
2976
|
+
const members = [];
|
|
2977
|
+
for (const target of await this.installations()) {
|
|
2978
|
+
if (this.matchSearchScope(target, scope)) {
|
|
2979
|
+
for (const member of await target.members()) {
|
|
2980
|
+
if (await predicate(member)) {
|
|
2981
|
+
members.push(member);
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2984
|
+
}
|
|
2985
|
+
}
|
|
2986
|
+
return members;
|
|
2987
|
+
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Returns all {@link Channel} where predicate is true, and empty array otherwise.
|
|
2990
|
+
*
|
|
2991
|
+
* @param predicate find calls predicate for each channel of the installation.
|
|
2992
|
+
* @returns an array of {@link Channel} where predicate is true, and empty array otherwise.
|
|
2993
|
+
*/
|
|
2994
|
+
async findAllChannels(predicate) {
|
|
2995
|
+
const channels = [];
|
|
2996
|
+
for (const target of await this.installations()) {
|
|
2997
|
+
if (target.type === NotificationTargetType.Channel) {
|
|
2998
|
+
const teamDetails = await target.getTeamDetails();
|
|
2999
|
+
for (const channel of await target.channels()) {
|
|
3000
|
+
if (await predicate(channel, teamDetails)) {
|
|
3001
|
+
channels.push(channel);
|
|
3002
|
+
}
|
|
3003
|
+
}
|
|
3004
|
+
}
|
|
3005
|
+
}
|
|
3006
|
+
return channels;
|
|
3007
|
+
}
|
|
3008
|
+
matchSearchScope(target, scope) {
|
|
3009
|
+
scope = scope !== null && scope !== void 0 ? scope : SearchScope.All;
|
|
3010
|
+
return ((target.type === NotificationTargetType.Channel && (scope & SearchScope.Channel) !== 0) ||
|
|
3011
|
+
(target.type === NotificationTargetType.Group && (scope & SearchScope.Group) !== 0) ||
|
|
3012
|
+
(target.type === NotificationTargetType.Person && (scope & SearchScope.Person) !== 0));
|
|
3013
|
+
}
|
|
3014
|
+
}
|
|
3015
|
+
/**
|
|
3016
|
+
* The search scope when calling {@link NotificationBot.findMember} and {@link NotificationBot.findAllMembers}.
|
|
3017
|
+
* The search scope is a flagged enum and it can be combined with `|`.
|
|
3018
|
+
* For example, to search from personal chat and group chat, use `SearchScope.Person | SearchScope.Group`.
|
|
3019
|
+
*/
|
|
3020
|
+
var SearchScope;
|
|
3021
|
+
(function (SearchScope) {
|
|
3022
|
+
/**
|
|
3023
|
+
* Search members from the installations in personal chat only.
|
|
3024
|
+
*/
|
|
3025
|
+
SearchScope[SearchScope["Person"] = 1] = "Person";
|
|
3026
|
+
/**
|
|
3027
|
+
* Search members from the installations in group chat only.
|
|
3028
|
+
*/
|
|
3029
|
+
SearchScope[SearchScope["Group"] = 2] = "Group";
|
|
3030
|
+
/**
|
|
3031
|
+
* Search members from the installations in Teams channel only.
|
|
3032
|
+
*/
|
|
3033
|
+
SearchScope[SearchScope["Channel"] = 4] = "Channel";
|
|
3034
|
+
/**
|
|
3035
|
+
* Search members from all installations including personal chat, group chat and Teams channel.
|
|
3036
|
+
*/
|
|
3037
|
+
SearchScope[SearchScope["All"] = 7] = "All";
|
|
3038
|
+
})(SearchScope || (SearchScope = {}));
|
|
3039
|
+
|
|
3040
|
+
// Copyright (c) Microsoft Corporation.
|
|
3041
|
+
let DIALOG_NAME = "BotSsoExecutionDialog";
|
|
3042
|
+
let TEAMS_SSO_PROMPT_ID = "TeamsFxSsoPrompt";
|
|
3043
|
+
let COMMAND_ROUTE_DIALOG = "CommandRouteDialog";
|
|
3044
|
+
/**
|
|
3045
|
+
* Sso execution dialog, use to handle sso command
|
|
3046
|
+
*/
|
|
3047
|
+
class BotSsoExecutionDialog extends ComponentDialog {
|
|
3048
|
+
/**
|
|
3049
|
+
* Creates a new instance of the BotSsoExecutionDialog.
|
|
3050
|
+
* @param dedupStorage Helper storage to remove duplicated messages
|
|
3051
|
+
* @param settings The list of scopes for which the token will have access
|
|
3052
|
+
* @param teamsfx {@link TeamsFx} instance for authentication
|
|
3053
|
+
*/
|
|
3054
|
+
constructor(dedupStorage, ssoPromptSettings, teamsfx, dialogName) {
|
|
3055
|
+
super(dialogName !== null && dialogName !== void 0 ? dialogName : DIALOG_NAME);
|
|
3056
|
+
this.dedupStorageKeys = [];
|
|
3057
|
+
// Map to store the commandId and triggerPatterns, key: commandId, value: triggerPatterns
|
|
3058
|
+
this.commandMapping = new Map();
|
|
3059
|
+
if (dialogName) {
|
|
3060
|
+
DIALOG_NAME = dialogName;
|
|
3061
|
+
TEAMS_SSO_PROMPT_ID = dialogName + TEAMS_SSO_PROMPT_ID;
|
|
3062
|
+
COMMAND_ROUTE_DIALOG = dialogName + COMMAND_ROUTE_DIALOG;
|
|
3063
|
+
}
|
|
3064
|
+
this.initialDialogId = COMMAND_ROUTE_DIALOG;
|
|
3065
|
+
this.dedupStorage = dedupStorage;
|
|
3066
|
+
this.dedupStorageKeys = [];
|
|
3067
|
+
const ssoDialog = new TeamsBotSsoPrompt(teamsfx, TEAMS_SSO_PROMPT_ID, ssoPromptSettings);
|
|
3068
|
+
this.addDialog(ssoDialog);
|
|
3069
|
+
const commandRouteDialog = new WaterfallDialog(COMMAND_ROUTE_DIALOG, [
|
|
3070
|
+
this.commandRouteStep.bind(this),
|
|
3071
|
+
]);
|
|
3072
|
+
this.addDialog(commandRouteDialog);
|
|
3073
|
+
}
|
|
3074
|
+
/**
|
|
3075
|
+
* Add TeamsFxBotSsoCommandHandler instance
|
|
3076
|
+
* @param handler {@link BotSsoExecutionDialogHandler} callback function
|
|
3077
|
+
* @param triggerPatterns The trigger pattern
|
|
3078
|
+
*/
|
|
3079
|
+
addCommand(handler, triggerPatterns) {
|
|
3080
|
+
const commandId = this.getCommandHash(triggerPatterns);
|
|
3081
|
+
const dialog = new WaterfallDialog(commandId, [
|
|
3082
|
+
this.ssoStep.bind(this),
|
|
3083
|
+
this.dedupStep.bind(this),
|
|
3084
|
+
async (stepContext) => {
|
|
3085
|
+
const tokenResponse = stepContext.result.tokenResponse;
|
|
3086
|
+
const context = stepContext.context;
|
|
3087
|
+
const message = stepContext.result.message;
|
|
3088
|
+
try {
|
|
3089
|
+
if (tokenResponse) {
|
|
3090
|
+
await handler(context, tokenResponse, message);
|
|
3091
|
+
}
|
|
3092
|
+
else {
|
|
3093
|
+
throw new Error(ErrorMessage.FailedToRetrieveSsoToken);
|
|
3094
|
+
}
|
|
3095
|
+
return await stepContext.endDialog();
|
|
3096
|
+
}
|
|
3097
|
+
catch (error) {
|
|
3098
|
+
const errorMsg = formatString(ErrorMessage.FailedToProcessSsoHandler, error.message);
|
|
3099
|
+
internalLogger.error(errorMsg);
|
|
3100
|
+
return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToProcessSsoHandler));
|
|
3101
|
+
}
|
|
3102
|
+
},
|
|
3103
|
+
]);
|
|
3104
|
+
this.commandMapping.set(commandId, triggerPatterns);
|
|
3105
|
+
this.addDialog(dialog);
|
|
3106
|
+
}
|
|
3107
|
+
getCommandHash(patterns) {
|
|
3108
|
+
const expressions = Array.isArray(patterns) ? patterns : [patterns];
|
|
3109
|
+
const patternStr = expressions.join();
|
|
3110
|
+
const patternStrWithoutSpecialChar = patternStr.replace(/[^a-zA-Z0-9]/g, "");
|
|
3111
|
+
const hash = createHash("sha256").update(patternStr).digest("hex").toLowerCase();
|
|
3112
|
+
return patternStrWithoutSpecialChar + hash;
|
|
3113
|
+
}
|
|
3114
|
+
/**
|
|
3115
|
+
* The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system.
|
|
3116
|
+
*
|
|
3117
|
+
* @param context The context object for the current turn.
|
|
3118
|
+
* @param accessor The instance of StatePropertyAccessor for dialog system.
|
|
3119
|
+
*/
|
|
3120
|
+
async run(context, accessor) {
|
|
3121
|
+
const dialogSet = new DialogSet(accessor);
|
|
3122
|
+
dialogSet.add(this);
|
|
3123
|
+
const dialogContext = await dialogSet.createContext(context);
|
|
3124
|
+
this.ensureMsTeamsChannel(dialogContext);
|
|
3125
|
+
const results = await dialogContext.continueDialog();
|
|
3126
|
+
if (results && results.status === DialogTurnStatus.empty) {
|
|
3127
|
+
await dialogContext.beginDialog(this.id);
|
|
3128
|
+
}
|
|
3129
|
+
else if (results &&
|
|
3130
|
+
results.status === DialogTurnStatus.complete &&
|
|
3131
|
+
results.result instanceof Error) {
|
|
3132
|
+
throw results.result;
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
getActivityText(activity) {
|
|
3136
|
+
let text = activity.text;
|
|
3137
|
+
const removedMentionText = TurnContext.removeRecipientMention(activity);
|
|
3138
|
+
if (removedMentionText) {
|
|
3139
|
+
text = removedMentionText
|
|
3140
|
+
.toLowerCase()
|
|
3141
|
+
.replace(/\n|\r\n/g, "")
|
|
3142
|
+
.trim();
|
|
3143
|
+
}
|
|
3144
|
+
return text;
|
|
3145
|
+
}
|
|
3146
|
+
async commandRouteStep(stepContext) {
|
|
3147
|
+
const turnContext = stepContext.context;
|
|
3148
|
+
const text = this.getActivityText(turnContext.activity);
|
|
3149
|
+
const commandId = this.getMatchesCommandId(text);
|
|
3150
|
+
if (commandId) {
|
|
3151
|
+
return await stepContext.beginDialog(commandId);
|
|
3152
|
+
}
|
|
3153
|
+
const errorMsg = formatString(ErrorMessage.CannotFindCommand, turnContext.activity.text);
|
|
3154
|
+
internalLogger.error(errorMsg);
|
|
3155
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.CannotFindCommand);
|
|
3156
|
+
}
|
|
3157
|
+
async ssoStep(stepContext) {
|
|
3158
|
+
try {
|
|
3159
|
+
const turnContext = stepContext.context;
|
|
3160
|
+
const text = this.getActivityText(turnContext.activity);
|
|
3161
|
+
const message = {
|
|
3162
|
+
text,
|
|
3163
|
+
};
|
|
3164
|
+
stepContext.options.commandMessage = message;
|
|
3165
|
+
return await stepContext.beginDialog(TEAMS_SSO_PROMPT_ID);
|
|
3166
|
+
}
|
|
3167
|
+
catch (error) {
|
|
3168
|
+
const errorMsg = formatString(ErrorMessage.FailedToRunSsoStep, error.message);
|
|
3169
|
+
internalLogger.error(errorMsg);
|
|
3170
|
+
return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunSsoStep));
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
async dedupStep(stepContext) {
|
|
3174
|
+
const tokenResponse = stepContext.result;
|
|
3175
|
+
if (!tokenResponse) {
|
|
3176
|
+
internalLogger.error(ErrorMessage.FailedToRetrieveSsoToken);
|
|
3177
|
+
return await stepContext.endDialog(new ErrorWithCode(ErrorMessage.FailedToRetrieveSsoToken, ErrorCode.FailedToRunSsoStep));
|
|
3178
|
+
}
|
|
3179
|
+
try {
|
|
3180
|
+
// Only dedup after ssoStep to make sure that all Teams client would receive the login request
|
|
3181
|
+
if (tokenResponse && (await this.shouldDedup(stepContext.context))) {
|
|
3182
|
+
return Dialog.EndOfTurn;
|
|
3183
|
+
}
|
|
3184
|
+
return await stepContext.next({
|
|
3185
|
+
tokenResponse,
|
|
3186
|
+
message: stepContext.options.commandMessage,
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
catch (error) {
|
|
3190
|
+
const errorMsg = formatString(ErrorMessage.FailedToRunDedupStep, error.message);
|
|
3191
|
+
internalLogger.error(errorMsg);
|
|
3192
|
+
return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunDedupStep));
|
|
3193
|
+
}
|
|
3194
|
+
}
|
|
3195
|
+
/**
|
|
3196
|
+
* Called when the component is ending.
|
|
3197
|
+
*
|
|
3198
|
+
* @param context Context for the current turn of conversation.
|
|
3199
|
+
*/
|
|
3200
|
+
async onEndDialog(context) {
|
|
3201
|
+
const conversationId = context.activity.conversation.id;
|
|
3202
|
+
const currentDedupKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) > 0);
|
|
3203
|
+
await this.dedupStorage.delete(currentDedupKeys);
|
|
3204
|
+
this.dedupStorageKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) < 0);
|
|
3205
|
+
}
|
|
3206
|
+
/**
|
|
3207
|
+
* If a user is signed into multiple Teams clients, the Bot might receive a "signin/tokenExchange" from each client.
|
|
3208
|
+
* Each token exchange request for a specific user login will have an identical activity.value.Id.
|
|
3209
|
+
* Only one of these token exchange requests should be processed by the bot. For a distributed bot in production,
|
|
3210
|
+
* this requires a distributed storage to ensure only one token exchange is processed.
|
|
3211
|
+
* @param context Context for the current turn of conversation.
|
|
3212
|
+
* @returns boolean value indicate whether the message should be removed
|
|
3213
|
+
*/
|
|
3214
|
+
async shouldDedup(context) {
|
|
3215
|
+
const storeItem = {
|
|
3216
|
+
eTag: context.activity.value.id,
|
|
3217
|
+
};
|
|
3218
|
+
const key = this.getStorageKey(context);
|
|
3219
|
+
const storeItems = { [key]: storeItem };
|
|
3220
|
+
try {
|
|
3221
|
+
await this.dedupStorage.write(storeItems);
|
|
3222
|
+
this.dedupStorageKeys.push(key);
|
|
3223
|
+
}
|
|
3224
|
+
catch (err) {
|
|
3225
|
+
if (err instanceof Error && err.message.indexOf("eTag conflict")) {
|
|
3226
|
+
return true;
|
|
3227
|
+
}
|
|
3228
|
+
throw err;
|
|
3229
|
+
}
|
|
3230
|
+
return false;
|
|
3231
|
+
}
|
|
3232
|
+
getStorageKey(context) {
|
|
3233
|
+
if (!context || !context.activity || !context.activity.conversation) {
|
|
3234
|
+
throw new Error("Invalid context, can not get storage key!");
|
|
3235
|
+
}
|
|
3236
|
+
const activity = context.activity;
|
|
3237
|
+
const channelId = activity.channelId;
|
|
3238
|
+
const conversationId = activity.conversation.id;
|
|
3239
|
+
if (activity.type !== ActivityTypes.Invoke || activity.name !== tokenExchangeOperationName) {
|
|
3240
|
+
throw new Error("TokenExchangeState can only be used with Invokes of signin/tokenExchange.");
|
|
3241
|
+
}
|
|
3242
|
+
const value = activity.value;
|
|
3243
|
+
if (!value || !value.id) {
|
|
3244
|
+
throw new Error("Invalid signin/tokenExchange. Missing activity.value.id.");
|
|
3245
|
+
}
|
|
3246
|
+
return `${channelId}/${conversationId}/${value.id}`;
|
|
3247
|
+
}
|
|
3248
|
+
matchPattern(pattern, text) {
|
|
3249
|
+
if (text) {
|
|
3250
|
+
if (typeof pattern === "string") {
|
|
3251
|
+
const regExp = new RegExp(pattern, "i");
|
|
3252
|
+
return regExp.test(text);
|
|
3253
|
+
}
|
|
3254
|
+
if (pattern instanceof RegExp) {
|
|
3255
|
+
const matches = text.match(pattern);
|
|
3256
|
+
return matches !== null && matches !== void 0 ? matches : false;
|
|
3257
|
+
}
|
|
3258
|
+
}
|
|
3259
|
+
return false;
|
|
3260
|
+
}
|
|
3261
|
+
isPatternMatched(patterns, text) {
|
|
3262
|
+
const expressions = Array.isArray(patterns) ? patterns : [patterns];
|
|
3263
|
+
for (const ex of expressions) {
|
|
3264
|
+
const matches = this.matchPattern(ex, text);
|
|
3265
|
+
return !!matches;
|
|
3266
|
+
}
|
|
3267
|
+
return false;
|
|
3268
|
+
}
|
|
3269
|
+
getMatchesCommandId(text) {
|
|
3270
|
+
for (const command of this.commandMapping) {
|
|
3271
|
+
const pattern = command[1];
|
|
3272
|
+
if (this.isPatternMatched(pattern, text)) {
|
|
3273
|
+
return command[0];
|
|
3274
|
+
}
|
|
3275
|
+
}
|
|
3276
|
+
return undefined;
|
|
3277
|
+
}
|
|
3278
|
+
/**
|
|
3279
|
+
* Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
|
|
3280
|
+
* @param dc dialog context
|
|
3281
|
+
* @throws {@link ErrorCode|ChannelNotSupported} if bot channel is not MS Teams
|
|
3282
|
+
* @internal
|
|
3283
|
+
*/
|
|
3284
|
+
ensureMsTeamsChannel(dc) {
|
|
3285
|
+
if (dc.context.activity.channelId != Channels.Msteams) {
|
|
3286
|
+
const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "SSO execution dialog");
|
|
3287
|
+
internalLogger.error(errorMsg);
|
|
3288
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.ChannelNotSupported);
|
|
3289
|
+
}
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
|
|
3293
|
+
// Copyright (c) Microsoft Corporation.
|
|
3294
|
+
/**
|
|
3295
|
+
* Default SSO execution activity handler
|
|
3296
|
+
*/
|
|
3297
|
+
class DefaultBotSsoExecutionActivityHandler extends TeamsActivityHandler {
|
|
3298
|
+
/**
|
|
3299
|
+
* Creates a new instance of the DefaultBotSsoExecutionActivityHandler.
|
|
3300
|
+
* @param ssoConfig configuration for SSO command bot
|
|
3301
|
+
*
|
|
3302
|
+
* @remarks
|
|
3303
|
+
* In the constructor, it uses BotSsoConfig parameter which from {@link ConversationBot} options to initialize {@link BotSsoExecutionDialog}.
|
|
3304
|
+
* It also need to register an event handler for the message event which trigger {@link BotSsoExecutionDialog} instance.
|
|
3305
|
+
*/
|
|
3306
|
+
constructor(ssoConfig) {
|
|
3307
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
3308
|
+
super();
|
|
3309
|
+
const memoryStorage = new MemoryStorage();
|
|
3310
|
+
const userState = (_b = (_a = ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.userState) !== null && _b !== void 0 ? _b : new UserState(memoryStorage);
|
|
3311
|
+
const conversationState = (_d = (_c = ssoConfig.dialog) === null || _c === void 0 ? void 0 : _c.conversationState) !== null && _d !== void 0 ? _d : new ConversationState(memoryStorage);
|
|
3312
|
+
const dedupStorage = (_f = (_e = ssoConfig.dialog) === null || _e === void 0 ? void 0 : _e.dedupStorage) !== null && _f !== void 0 ? _f : memoryStorage;
|
|
3313
|
+
const _l = ssoConfig.aad, { scopes } = _l, customConfig = __rest(_l, ["scopes"]);
|
|
3314
|
+
const settings = {
|
|
3315
|
+
scopes: scopes,
|
|
3316
|
+
timeout: (_h = (_g = ssoConfig.dialog) === null || _g === void 0 ? void 0 : _g.ssoPromptConfig) === null || _h === void 0 ? void 0 : _h.timeout,
|
|
3317
|
+
endOnInvalidMessage: (_k = (_j = ssoConfig.dialog) === null || _j === void 0 ? void 0 : _j.ssoPromptConfig) === null || _k === void 0 ? void 0 : _k.endOnInvalidMessage,
|
|
3318
|
+
};
|
|
3319
|
+
const teamsfx = new TeamsFx(IdentityType.User, Object.assign({}, customConfig));
|
|
3320
|
+
this.ssoExecutionDialog = new BotSsoExecutionDialog(dedupStorage, settings, teamsfx);
|
|
3321
|
+
this.conversationState = conversationState;
|
|
3322
|
+
this.dialogState = conversationState.createProperty("DialogState");
|
|
3323
|
+
this.userState = userState;
|
|
3324
|
+
this.onMessage(async (context, next) => {
|
|
3325
|
+
await this.ssoExecutionDialog.run(context, this.dialogState);
|
|
3326
|
+
await next();
|
|
3327
|
+
});
|
|
3328
|
+
}
|
|
3329
|
+
/**
|
|
3330
|
+
* Add TeamsFxBotSsoCommandHandler instance to SSO execution dialog
|
|
3331
|
+
* @param handler {@link BotSsoExecutionDialogHandler} callback function
|
|
3332
|
+
* @param triggerPatterns The trigger pattern
|
|
3333
|
+
*
|
|
3334
|
+
* @remarks
|
|
3335
|
+
* This function is used to add SSO command to {@link BotSsoExecutionDialog} instance.
|
|
3336
|
+
*/
|
|
3337
|
+
addCommand(handler, triggerPatterns) {
|
|
3338
|
+
this.ssoExecutionDialog.addCommand(handler, triggerPatterns);
|
|
3339
|
+
}
|
|
3340
|
+
/**
|
|
3341
|
+
* Called to initiate the event emission process.
|
|
3342
|
+
* @param context The context object for the current turn.
|
|
3343
|
+
*/
|
|
3344
|
+
async run(context) {
|
|
3345
|
+
try {
|
|
3346
|
+
await super.run(context);
|
|
3347
|
+
}
|
|
3348
|
+
finally {
|
|
3349
|
+
await this.conversationState.saveChanges(context, false);
|
|
3350
|
+
await this.userState.saveChanges(context, false);
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
/**
|
|
3354
|
+
* Receives invoke activities with Activity name of 'signin/verifyState'.
|
|
3355
|
+
* @param context A context object for this turn.
|
|
3356
|
+
* @param query Signin state (part of signin action auth flow) verification invoke query.
|
|
3357
|
+
* @returns A promise that represents the work queued.
|
|
3358
|
+
*
|
|
3359
|
+
* @remarks
|
|
3360
|
+
* It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
|
|
3361
|
+
*/
|
|
3362
|
+
async handleTeamsSigninVerifyState(context, query) {
|
|
3363
|
+
await this.ssoExecutionDialog.run(context, this.dialogState);
|
|
3364
|
+
}
|
|
3365
|
+
/**
|
|
3366
|
+
* Receives invoke activities with Activity name of 'signin/tokenExchange'
|
|
3367
|
+
* @param context A context object for this turn.
|
|
3368
|
+
* @param query Signin state (part of signin action auth flow) verification invoke query
|
|
3369
|
+
* @returns A promise that represents the work queued.
|
|
3370
|
+
*
|
|
3371
|
+
* @remark
|
|
3372
|
+
* It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
|
|
3373
|
+
*/
|
|
3374
|
+
async handleTeamsSigninTokenExchange(context, query) {
|
|
3375
|
+
await this.ssoExecutionDialog.run(context, this.dialogState);
|
|
3376
|
+
}
|
|
2789
3377
|
}
|
|
2790
3378
|
|
|
2791
3379
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -2850,20 +3438,30 @@ class ConversationBot {
|
|
|
2850
3438
|
* @param options - initialize options
|
|
2851
3439
|
*/
|
|
2852
3440
|
constructor(options) {
|
|
2853
|
-
var _a, _b, _c;
|
|
3441
|
+
var _a, _b, _c, _d;
|
|
2854
3442
|
if (options.adapter) {
|
|
2855
3443
|
this.adapter = options.adapter;
|
|
2856
3444
|
}
|
|
2857
3445
|
else {
|
|
2858
3446
|
this.adapter = this.createDefaultAdapter(options.adapterConfig);
|
|
2859
3447
|
}
|
|
2860
|
-
|
|
2861
|
-
|
|
3448
|
+
let ssoCommandActivityHandler;
|
|
3449
|
+
if (options === null || options === void 0 ? void 0 : options.ssoConfig) {
|
|
3450
|
+
if ((_a = options.ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.CustomBotSsoExecutionActivityHandler) {
|
|
3451
|
+
ssoCommandActivityHandler =
|
|
3452
|
+
new options.ssoConfig.dialog.CustomBotSsoExecutionActivityHandler(options.ssoConfig);
|
|
3453
|
+
}
|
|
3454
|
+
else {
|
|
3455
|
+
ssoCommandActivityHandler = new DefaultBotSsoExecutionActivityHandler(options.ssoConfig);
|
|
3456
|
+
}
|
|
2862
3457
|
}
|
|
2863
|
-
if ((_b = options.
|
|
3458
|
+
if ((_b = options.command) === null || _b === void 0 ? void 0 : _b.enabled) {
|
|
3459
|
+
this.command = new CommandBot(this.adapter, options.command, ssoCommandActivityHandler, options.ssoConfig);
|
|
3460
|
+
}
|
|
3461
|
+
if ((_c = options.notification) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
2864
3462
|
this.notification = new NotificationBot(this.adapter, options.notification);
|
|
2865
3463
|
}
|
|
2866
|
-
if ((
|
|
3464
|
+
if ((_d = options.cardAction) === null || _d === void 0 ? void 0 : _d.enabled) {
|
|
2867
3465
|
this.cardAction = new CardActionBot(this.adapter, options.cardAction);
|
|
2868
3466
|
}
|
|
2869
3467
|
}
|
|
@@ -3052,5 +3650,105 @@ class MessageBuilder {
|
|
|
3052
3650
|
}
|
|
3053
3651
|
}
|
|
3054
3652
|
|
|
3055
|
-
|
|
3653
|
+
// Copyright (c) Microsoft Corporation.
|
|
3654
|
+
/**
|
|
3655
|
+
* Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions.
|
|
3656
|
+
* This method only work on MessageExtension with Query now.
|
|
3657
|
+
*
|
|
3658
|
+
* @param {TeamsFx} teamsfx - Used to provide configuration and auth.
|
|
3659
|
+
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
3660
|
+
*
|
|
3661
|
+
* @returns SignIn link CardAction with 200 status code.
|
|
3662
|
+
*/
|
|
3663
|
+
function getSignInResponseForMessageExtension(teamsfx, scopes) {
|
|
3664
|
+
const scopesArray = getScopesArray(scopes);
|
|
3665
|
+
const signInLink = `${teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(scopesArray.join(" "))}&clientId=${teamsfx.getConfig("clientId")}&tenantId=${teamsfx.getConfig("tenantId")}`;
|
|
3666
|
+
return {
|
|
3667
|
+
composeExtension: {
|
|
3668
|
+
type: "silentAuth",
|
|
3669
|
+
suggestedActions: {
|
|
3670
|
+
actions: [
|
|
3671
|
+
{
|
|
3672
|
+
type: "openUrl",
|
|
3673
|
+
value: signInLink,
|
|
3674
|
+
title: "Message Extension OAuth",
|
|
3675
|
+
},
|
|
3676
|
+
],
|
|
3677
|
+
},
|
|
3678
|
+
},
|
|
3679
|
+
};
|
|
3680
|
+
}
|
|
3681
|
+
/**
|
|
3682
|
+
* execution in message extension with SSO token.
|
|
3683
|
+
*
|
|
3684
|
+
* @param {TurnContext} context - The context object for the current turn.
|
|
3685
|
+
* @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
|
|
3686
|
+
* @param {string[]} scopes - The list of scopes for which the token will have access.
|
|
3687
|
+
* @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
|
|
3688
|
+
*
|
|
3689
|
+
* @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
|
|
3690
|
+
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
3691
|
+
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
|
|
3692
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
3693
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
3694
|
+
*
|
|
3695
|
+
* @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
|
|
3696
|
+
*/
|
|
3697
|
+
async function executionWithToken(context, config, scopes, logic) {
|
|
3698
|
+
const valueObj = context.activity.value;
|
|
3699
|
+
if (!valueObj.authentication || !valueObj.authentication.token) {
|
|
3700
|
+
internalLogger.verbose("No AccessToken in request, return silentAuth for AccessToken");
|
|
3701
|
+
return getSignInResponseForMessageExtension(new TeamsFx(IdentityType.User, config), scopes);
|
|
3702
|
+
}
|
|
3703
|
+
try {
|
|
3704
|
+
const teamsfx = new TeamsFx(IdentityType.User, config).setSsoToken(valueObj.authentication.token);
|
|
3705
|
+
const token = await teamsfx.getCredential().getToken(scopes);
|
|
3706
|
+
const ssoTokenExpiration = parseJwt(valueObj.authentication.token).exp;
|
|
3707
|
+
const tokenRes = {
|
|
3708
|
+
ssoToken: valueObj.authentication.token,
|
|
3709
|
+
ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
|
|
3710
|
+
token: token.token,
|
|
3711
|
+
expiration: token.expiresOnTimestamp.toString(),
|
|
3712
|
+
connectionName: "",
|
|
3713
|
+
};
|
|
3714
|
+
if (logic) {
|
|
3715
|
+
return await logic(tokenRes);
|
|
3716
|
+
}
|
|
3717
|
+
}
|
|
3718
|
+
catch (err) {
|
|
3719
|
+
if (err instanceof ErrorWithCode && err.code === ErrorCode.UiRequiredError) {
|
|
3720
|
+
internalLogger.verbose("User not consent yet, return 412 to user consent first.");
|
|
3721
|
+
const response = { status: 412 };
|
|
3722
|
+
await context.sendActivity({ value: response, type: ActivityTypes.InvokeResponse });
|
|
3723
|
+
return;
|
|
3724
|
+
}
|
|
3725
|
+
throw err;
|
|
3726
|
+
}
|
|
3727
|
+
}
|
|
3728
|
+
/**
|
|
3729
|
+
* Users execute query in message extension with SSO or access token.
|
|
3730
|
+
*
|
|
3731
|
+
* @param {TurnContext} context - The context object for the current turn.
|
|
3732
|
+
* @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
|
|
3733
|
+
* @param {string| string[]} scopes - The list of scopes for which the token will have access.
|
|
3734
|
+
* @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
|
|
3735
|
+
*
|
|
3736
|
+
* @throws {@link ErrorCode|InternalError} when User invoke not response to message extension query.
|
|
3737
|
+
* @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
|
|
3738
|
+
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
3739
|
+
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
|
|
3740
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
3741
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
3742
|
+
*
|
|
3743
|
+
* @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
|
|
3744
|
+
*/
|
|
3745
|
+
async function handleMessageExtensionQueryWithToken(context, config, scopes, logic) {
|
|
3746
|
+
if (context.activity.name != "composeExtension/query") {
|
|
3747
|
+
internalLogger.error(ErrorMessage.OnlySupportInQueryActivity);
|
|
3748
|
+
throw new ErrorWithCode(formatString(ErrorMessage.OnlySupportInQueryActivity), ErrorCode.FailedOperation);
|
|
3749
|
+
}
|
|
3750
|
+
return await executionWithToken(context, config !== null && config !== void 0 ? config : {}, scopes, logic);
|
|
3751
|
+
}
|
|
3752
|
+
|
|
3753
|
+
export { AdaptiveCardResponse, ApiKeyLocation, ApiKeyProvider, AppCredential, BasicAuthProvider, BearerTokenAuthProvider, BotSsoExecutionDialog, CardActionBot, CertificateAuthProvider, Channel, CommandBot, ConversationBot, 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 };
|
|
3056
3754
|
//# sourceMappingURL=index.esm2017.mjs.map
|