@microsoft/teamsfx 2.0.0-experimental.0 → 2.0.1-alpha.ba6cc7dba.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/LICENSE +21 -21
- package/dist/index.esm2017.js +351 -181
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1301 -367
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +367 -181
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1351 -365
- package/dist/index.node.cjs.js.map +1 -1
- package/package.json +11 -10
- package/types/teamsfx.d.ts +649 -293
- package/CHANGELOG.md +0 -28
- package/NOTICE.txt +0 -9242
package/dist/index.esm2017.mjs
CHANGED
|
@@ -3,20 +3,20 @@ 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.
|
|
16
17
|
// Licensed under the MIT license.
|
|
17
18
|
/**
|
|
18
19
|
* Error code to trace the error types.
|
|
19
|
-
* @beta
|
|
20
20
|
*/
|
|
21
21
|
var ErrorCode;
|
|
22
22
|
(function (ErrorCode) {
|
|
@@ -40,6 +40,30 @@ var ErrorCode;
|
|
|
40
40
|
* Channel is not supported error.
|
|
41
41
|
*/
|
|
42
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";
|
|
43
67
|
/**
|
|
44
68
|
* Runtime is not supported error.
|
|
45
69
|
*/
|
|
@@ -95,6 +119,15 @@ ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
|
|
|
95
119
|
ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
|
|
96
120
|
// ChannelNotSupported Error
|
|
97
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";
|
|
98
131
|
// IdentityTypeNotSupported Error
|
|
99
132
|
ErrorMessage.IdentityTypeNotSupported = "{0} identity is not supported in {1}";
|
|
100
133
|
// AuthorizationInfoError
|
|
@@ -105,10 +138,9 @@ ErrorMessage.EmptyParameter = "Parameter {0} is empty";
|
|
|
105
138
|
ErrorMessage.DuplicateHttpsOptionProperty = "Axios HTTPS agent already defined value for property {0}";
|
|
106
139
|
ErrorMessage.DuplicateApiKeyInHeader = "The request already defined api key in request header with name {0}.";
|
|
107
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.";
|
|
108
142
|
/**
|
|
109
143
|
* Error class with code and message thrown by the SDK.
|
|
110
|
-
*
|
|
111
|
-
* @beta
|
|
112
144
|
*/
|
|
113
145
|
class ErrorWithCode extends Error {
|
|
114
146
|
/**
|
|
@@ -116,8 +148,6 @@ class ErrorWithCode extends Error {
|
|
|
116
148
|
*
|
|
117
149
|
* @param {string} message - error message.
|
|
118
150
|
* @param {ErrorCode} code - error code.
|
|
119
|
-
*
|
|
120
|
-
* @beta
|
|
121
151
|
*/
|
|
122
152
|
constructor(message, code) {
|
|
123
153
|
if (!code) {
|
|
@@ -135,8 +165,6 @@ class ErrorWithCode extends Error {
|
|
|
135
165
|
// Licensed under the MIT license.
|
|
136
166
|
/**
|
|
137
167
|
* Log level.
|
|
138
|
-
*
|
|
139
|
-
* @beta
|
|
140
168
|
*/
|
|
141
169
|
var LogLevel;
|
|
142
170
|
(function (LogLevel) {
|
|
@@ -161,8 +189,6 @@ var LogLevel;
|
|
|
161
189
|
* Update log level helper.
|
|
162
190
|
*
|
|
163
191
|
* @param { LogLevel } level - log level in configuration
|
|
164
|
-
*
|
|
165
|
-
* @beta
|
|
166
192
|
*/
|
|
167
193
|
function setLogLevel(level) {
|
|
168
194
|
internalLogger.level = level;
|
|
@@ -171,8 +197,6 @@ function setLogLevel(level) {
|
|
|
171
197
|
* Get log level.
|
|
172
198
|
*
|
|
173
199
|
* @returns Log level
|
|
174
|
-
*
|
|
175
|
-
* @beta
|
|
176
200
|
*/
|
|
177
201
|
function getLogLevel() {
|
|
178
202
|
return internalLogger.level;
|
|
@@ -247,8 +271,6 @@ const internalLogger = new InternalLogger();
|
|
|
247
271
|
* error: console.error,
|
|
248
272
|
* });
|
|
249
273
|
* ```
|
|
250
|
-
*
|
|
251
|
-
* @beta
|
|
252
274
|
*/
|
|
253
275
|
function setLogger(logger) {
|
|
254
276
|
internalLogger.customLogger = logger;
|
|
@@ -266,8 +288,6 @@ function setLogger(logger) {
|
|
|
266
288
|
* }
|
|
267
289
|
* });
|
|
268
290
|
* ```
|
|
269
|
-
*
|
|
270
|
-
* @beta
|
|
271
291
|
*/
|
|
272
292
|
function setLogFunction(logFunction) {
|
|
273
293
|
internalLogger.customLogFunction = logFunction;
|
|
@@ -310,6 +330,7 @@ function getUserInfoFromSsoToken(ssoToken) {
|
|
|
310
330
|
const userInfo = {
|
|
311
331
|
displayName: tokenObject.name,
|
|
312
332
|
objectId: tokenObject.oid,
|
|
333
|
+
tenantId: tokenObject.tid,
|
|
313
334
|
preferredUserName: "",
|
|
314
335
|
};
|
|
315
336
|
if (tokenObject.ver === "2.0") {
|
|
@@ -431,8 +452,6 @@ function parseCertificate(certificateContent) {
|
|
|
431
452
|
*
|
|
432
453
|
* @remarks
|
|
433
454
|
* Only works in in server side.
|
|
434
|
-
*
|
|
435
|
-
* @beta
|
|
436
455
|
*/
|
|
437
456
|
class AppCredential {
|
|
438
457
|
/**
|
|
@@ -445,8 +464,6 @@ class AppCredential {
|
|
|
445
464
|
*
|
|
446
465
|
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.
|
|
447
466
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
448
|
-
*
|
|
449
|
-
* @beta
|
|
450
467
|
*/
|
|
451
468
|
constructor(authConfig) {
|
|
452
469
|
internalLogger.info("Create M365 tenant credential");
|
|
@@ -476,8 +493,6 @@ class AppCredential {
|
|
|
476
493
|
*
|
|
477
494
|
* @returns Access token with expected scopes.
|
|
478
495
|
* Throw error if get access token failed.
|
|
479
|
-
*
|
|
480
|
-
* @beta
|
|
481
496
|
*/
|
|
482
497
|
async getToken(scopes, options) {
|
|
483
498
|
let accessToken;
|
|
@@ -517,7 +532,10 @@ class AppCredential {
|
|
|
517
532
|
*/
|
|
518
533
|
loadAndValidateConfig(config) {
|
|
519
534
|
internalLogger.verbose("Validate authentication configuration");
|
|
520
|
-
if (config.clientId &&
|
|
535
|
+
if (config.clientId &&
|
|
536
|
+
(config.clientSecret || config.certificateContent) &&
|
|
537
|
+
config.tenantId &&
|
|
538
|
+
config.authorityHost) {
|
|
521
539
|
return config;
|
|
522
540
|
}
|
|
523
541
|
const missingValues = [];
|
|
@@ -530,6 +548,9 @@ class AppCredential {
|
|
|
530
548
|
if (!config.tenantId) {
|
|
531
549
|
missingValues.push("tenantId");
|
|
532
550
|
}
|
|
551
|
+
if (!config.authorityHost) {
|
|
552
|
+
missingValues.push("authorityHost");
|
|
553
|
+
}
|
|
533
554
|
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingValues.join(", "), "undefined");
|
|
534
555
|
internalLogger.error(errorMsg);
|
|
535
556
|
throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
|
|
@@ -547,8 +568,6 @@ class AppCredential {
|
|
|
547
568
|
*
|
|
548
569
|
* @remarks
|
|
549
570
|
* Can only be used in server side.
|
|
550
|
-
*
|
|
551
|
-
* @beta
|
|
552
571
|
*/
|
|
553
572
|
class OnBehalfOfUserCredential {
|
|
554
573
|
/**
|
|
@@ -563,8 +582,6 @@ class OnBehalfOfUserCredential {
|
|
|
563
582
|
* @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
|
|
564
583
|
* @throws {@link ErrorCode|InternalError} when SSO token is not valid.
|
|
565
584
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
566
|
-
*
|
|
567
|
-
* @beta
|
|
568
585
|
*/
|
|
569
586
|
constructor(ssoToken, config) {
|
|
570
587
|
internalLogger.info("Get on behalf of user credential");
|
|
@@ -625,8 +642,6 @@ class OnBehalfOfUserCredential {
|
|
|
625
642
|
* @remarks
|
|
626
643
|
* If scopes is empty string or array, it returns SSO token.
|
|
627
644
|
* If scopes is non-empty, it returns access token for target scope.
|
|
628
|
-
*
|
|
629
|
-
* @beta
|
|
630
645
|
*/
|
|
631
646
|
async getToken(scopes, options) {
|
|
632
647
|
validateScopesType(scopes);
|
|
@@ -677,8 +692,6 @@ class OnBehalfOfUserCredential {
|
|
|
677
692
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
678
693
|
*
|
|
679
694
|
* @returns Basic user info with user displayName, objectId and preferredUserName.
|
|
680
|
-
*
|
|
681
|
-
* @beta
|
|
682
695
|
*/
|
|
683
696
|
getUserInfo() {
|
|
684
697
|
internalLogger.info("Get basic user info from SSO token");
|
|
@@ -710,44 +723,44 @@ class OnBehalfOfUserCredential {
|
|
|
710
723
|
*
|
|
711
724
|
* @remarks
|
|
712
725
|
* Can only be used within Teams.
|
|
713
|
-
*
|
|
714
|
-
* @beta
|
|
715
726
|
*/
|
|
716
727
|
class TeamsUserCredential {
|
|
717
728
|
/**
|
|
718
729
|
* Constructor of TeamsUserCredential.
|
|
719
730
|
* @remarks
|
|
720
731
|
* Can only be used within Teams.
|
|
721
|
-
* @beta
|
|
722
732
|
*/
|
|
723
733
|
constructor(authConfig) {
|
|
724
734
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
725
735
|
}
|
|
726
736
|
/**
|
|
727
737
|
* Popup login page to get user's access token with specific scopes.
|
|
738
|
+
*
|
|
739
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
740
|
+
*
|
|
728
741
|
* @remarks
|
|
729
742
|
* Can only be used within Teams.
|
|
730
|
-
* @beta
|
|
731
743
|
*/
|
|
732
|
-
async login(scopes) {
|
|
744
|
+
async login(scopes, resources) {
|
|
733
745
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
734
746
|
}
|
|
735
747
|
/**
|
|
736
748
|
* Get access token from credential.
|
|
737
749
|
* @remarks
|
|
738
750
|
* Can only be used within Teams.
|
|
739
|
-
* @beta
|
|
740
751
|
*/
|
|
741
752
|
async getToken(scopes, options) {
|
|
742
753
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
743
754
|
}
|
|
744
755
|
/**
|
|
745
756
|
* Get basic user info from SSO token
|
|
757
|
+
*
|
|
758
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
759
|
+
*
|
|
746
760
|
* @remarks
|
|
747
761
|
* Can only be used within Teams.
|
|
748
|
-
* @beta
|
|
749
762
|
*/
|
|
750
|
-
getUserInfo() {
|
|
763
|
+
getUserInfo(resources) {
|
|
751
764
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
|
|
752
765
|
}
|
|
753
766
|
}
|
|
@@ -756,8 +769,6 @@ class TeamsUserCredential {
|
|
|
756
769
|
const defaultScope = "https://graph.microsoft.com/.default";
|
|
757
770
|
/**
|
|
758
771
|
* Microsoft Graph auth provider for Teams Framework
|
|
759
|
-
*
|
|
760
|
-
* @beta
|
|
761
772
|
*/
|
|
762
773
|
class MsGraphAuthProvider {
|
|
763
774
|
/**
|
|
@@ -769,8 +780,6 @@ class MsGraphAuthProvider {
|
|
|
769
780
|
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
770
781
|
*
|
|
771
782
|
* @returns An instance of MsGraphAuthProvider.
|
|
772
|
-
*
|
|
773
|
-
* @beta
|
|
774
783
|
*/
|
|
775
784
|
constructor(teamsfx, scopes) {
|
|
776
785
|
this.teamsfx = teamsfx;
|
|
@@ -862,8 +871,6 @@ class MsGraphAuthProvider {
|
|
|
862
871
|
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
863
872
|
*
|
|
864
873
|
* @returns Graph client with specified scopes.
|
|
865
|
-
*
|
|
866
|
-
* @beta
|
|
867
874
|
*/
|
|
868
875
|
function createMicrosoftGraphClient(teamsfx, scopes) {
|
|
869
876
|
internalLogger.info("Create Microsoft Graph Client");
|
|
@@ -891,8 +898,6 @@ const defaultSQLScope = "https://database.windows.net/";
|
|
|
891
898
|
* @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
|
|
892
899
|
* @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
|
|
893
900
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
894
|
-
*
|
|
895
|
-
* @beta
|
|
896
901
|
*/
|
|
897
902
|
async function getTediousConnectionConfig(teamsfx, databaseName) {
|
|
898
903
|
internalLogger.info("Get SQL configuration");
|
|
@@ -1047,8 +1052,6 @@ var TediousAuthenticationType;
|
|
|
1047
1052
|
// Licensed under the MIT license.
|
|
1048
1053
|
/**
|
|
1049
1054
|
* Identity type to use in authentication.
|
|
1050
|
-
*
|
|
1051
|
-
* @beta
|
|
1052
1055
|
*/
|
|
1053
1056
|
var IdentityType;
|
|
1054
1057
|
(function (IdentityType) {
|
|
@@ -1066,8 +1069,6 @@ var IdentityType;
|
|
|
1066
1069
|
const invokeResponseType = "invokeResponse";
|
|
1067
1070
|
/**
|
|
1068
1071
|
* Response body returned for a token exchange invoke activity.
|
|
1069
|
-
*
|
|
1070
|
-
* @beta
|
|
1071
1072
|
*/
|
|
1072
1073
|
class TokenExchangeInvokeResponse {
|
|
1073
1074
|
constructor(id, failureDetail) {
|
|
@@ -1122,8 +1123,6 @@ class TokenExchangeInvokeResponse {
|
|
|
1122
1123
|
* }
|
|
1123
1124
|
* ]));
|
|
1124
1125
|
* ```
|
|
1125
|
-
*
|
|
1126
|
-
* @beta
|
|
1127
1126
|
*/
|
|
1128
1127
|
class TeamsBotSsoPrompt extends Dialog {
|
|
1129
1128
|
/**
|
|
@@ -1135,8 +1134,6 @@ class TeamsBotSsoPrompt extends Dialog {
|
|
|
1135
1134
|
*
|
|
1136
1135
|
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
1137
1136
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1138
|
-
*
|
|
1139
|
-
* @beta
|
|
1140
1137
|
*/
|
|
1141
1138
|
constructor(teamsfx, dialogId, settings) {
|
|
1142
1139
|
super(dialogId);
|
|
@@ -1159,8 +1156,6 @@ class TeamsBotSsoPrompt extends Dialog {
|
|
|
1159
1156
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1160
1157
|
*
|
|
1161
1158
|
* @returns A `Promise` representing the asynchronous operation.
|
|
1162
|
-
*
|
|
1163
|
-
* @beta
|
|
1164
1159
|
*/
|
|
1165
1160
|
async beginDialog(dc) {
|
|
1166
1161
|
var _a;
|
|
@@ -1208,8 +1203,6 @@ class TeamsBotSsoPrompt extends Dialog {
|
|
|
1208
1203
|
*
|
|
1209
1204
|
* @throws {@link ErrorCode|ChannelNotSupported} when bot channel is not MS Teams.
|
|
1210
1205
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1211
|
-
*
|
|
1212
|
-
* @beta
|
|
1213
1206
|
*/
|
|
1214
1207
|
async continueDialog(dc) {
|
|
1215
1208
|
var _a;
|
|
@@ -1260,9 +1253,6 @@ class TeamsBotSsoPrompt extends Dialog {
|
|
|
1260
1253
|
if (!this.teamsfx.hasConfig("tenantId")) {
|
|
1261
1254
|
missingConfigurations.push("tenantId");
|
|
1262
1255
|
}
|
|
1263
|
-
if (!this.teamsfx.hasConfig("applicationIdUri")) {
|
|
1264
|
-
missingConfigurations.push("applicationIdUri");
|
|
1265
|
-
}
|
|
1266
1256
|
if (missingConfigurations.length != 0) {
|
|
1267
1257
|
const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
|
|
1268
1258
|
internalLogger.error(errorMsg);
|
|
@@ -1313,9 +1303,7 @@ class TeamsBotSsoPrompt extends Dialog {
|
|
|
1313
1303
|
internalLogger.verbose("Sign in link: " + signInLink);
|
|
1314
1304
|
const tokenExchangeResource = {
|
|
1315
1305
|
id: v4(),
|
|
1316
|
-
uri: this.teamsfx.getConfig("applicationIdUri").replace(/\/$/, "") + "/access_as_user",
|
|
1317
1306
|
};
|
|
1318
|
-
internalLogger.verbose("Token exchange resource uri: " + tokenExchangeResource.uri);
|
|
1319
1307
|
return {
|
|
1320
1308
|
signInLink: signInLink,
|
|
1321
1309
|
tokenExchangeResource: tokenExchangeResource,
|
|
@@ -1414,8 +1402,6 @@ class TeamsBotSsoPrompt extends Dialog {
|
|
|
1414
1402
|
* ```typescript
|
|
1415
1403
|
* const client = createApiClient("https://my-api-endpoint-base-url", new BasicAuthProvider("xxx","xxx"));
|
|
1416
1404
|
* ```
|
|
1417
|
-
*
|
|
1418
|
-
* @beta
|
|
1419
1405
|
*/
|
|
1420
1406
|
function createApiClient(apiEndpoint, authProvider) {
|
|
1421
1407
|
// Add a request interceptor
|
|
@@ -1431,14 +1417,10 @@ function createApiClient(apiEndpoint, authProvider) {
|
|
|
1431
1417
|
// Copyright (c) Microsoft Corporation.
|
|
1432
1418
|
/**
|
|
1433
1419
|
* Provider that handles Bearer Token authentication
|
|
1434
|
-
*
|
|
1435
|
-
* @beta
|
|
1436
1420
|
*/
|
|
1437
1421
|
class BearerTokenAuthProvider {
|
|
1438
1422
|
/**
|
|
1439
1423
|
* @param { () => Promise<string> } getToken - Function that returns the content of bearer token used in http request
|
|
1440
|
-
*
|
|
1441
|
-
* @beta
|
|
1442
1424
|
*/
|
|
1443
1425
|
constructor(getToken) {
|
|
1444
1426
|
this.getToken = getToken;
|
|
@@ -1452,8 +1434,6 @@ class BearerTokenAuthProvider {
|
|
|
1452
1434
|
* @returns Updated axios request config.
|
|
1453
1435
|
*
|
|
1454
1436
|
* @throws {@link ErrorCode|AuthorizationInfoAlreadyExists} - when Authorization header already exists in request configuration.
|
|
1455
|
-
*
|
|
1456
|
-
* @beta
|
|
1457
1437
|
*/
|
|
1458
1438
|
async AddAuthenticationInfo(config) {
|
|
1459
1439
|
const token = await this.getToken();
|
|
@@ -1471,8 +1451,6 @@ class BearerTokenAuthProvider {
|
|
|
1471
1451
|
// Copyright (c) Microsoft Corporation.
|
|
1472
1452
|
/**
|
|
1473
1453
|
* Provider that handles Basic authentication
|
|
1474
|
-
*
|
|
1475
|
-
* @beta
|
|
1476
1454
|
*/
|
|
1477
1455
|
class BasicAuthProvider {
|
|
1478
1456
|
/**
|
|
@@ -1482,8 +1460,6 @@ class BasicAuthProvider {
|
|
|
1482
1460
|
*
|
|
1483
1461
|
* @throws {@link ErrorCode|InvalidParameter} - when username or password is empty.
|
|
1484
1462
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1485
|
-
*
|
|
1486
|
-
* @beta
|
|
1487
1463
|
*/
|
|
1488
1464
|
constructor(userName, password) {
|
|
1489
1465
|
if (!userName) {
|
|
@@ -1505,8 +1481,6 @@ class BasicAuthProvider {
|
|
|
1505
1481
|
*
|
|
1506
1482
|
* @throws {@link ErrorCode|AuthorizationInfoAlreadyExists} - when Authorization header or auth property already exists in request configuration.
|
|
1507
1483
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1508
|
-
*
|
|
1509
|
-
* @beta
|
|
1510
1484
|
*/
|
|
1511
1485
|
async AddAuthenticationInfo(config) {
|
|
1512
1486
|
if (config.headers && config.headers["Authorization"]) {
|
|
@@ -1526,8 +1500,6 @@ class BasicAuthProvider {
|
|
|
1526
1500
|
// Copyright (c) Microsoft Corporation.
|
|
1527
1501
|
/**
|
|
1528
1502
|
* Provider that handles API Key authentication
|
|
1529
|
-
*
|
|
1530
|
-
* @beta
|
|
1531
1503
|
*/
|
|
1532
1504
|
class ApiKeyProvider {
|
|
1533
1505
|
/**
|
|
@@ -1538,8 +1510,6 @@ class ApiKeyProvider {
|
|
|
1538
1510
|
*
|
|
1539
1511
|
* @throws {@link ErrorCode|InvalidParameter} - when key name or key value is empty.
|
|
1540
1512
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1541
|
-
*
|
|
1542
|
-
* @beta
|
|
1543
1513
|
*/
|
|
1544
1514
|
constructor(keyName, keyValue, keyLocation) {
|
|
1545
1515
|
if (!keyName) {
|
|
@@ -1562,8 +1532,6 @@ class ApiKeyProvider {
|
|
|
1562
1532
|
*
|
|
1563
1533
|
* @throws {@link ErrorCode|AuthorizationInfoAlreadyExists} - when API key already exists in request header or url query parameter.
|
|
1564
1534
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
|
|
1565
|
-
*
|
|
1566
|
-
* @beta
|
|
1567
1535
|
*/
|
|
1568
1536
|
async AddAuthenticationInfo(config) {
|
|
1569
1537
|
switch (this.keyLocation) {
|
|
@@ -1580,8 +1548,12 @@ class ApiKeyProvider {
|
|
|
1580
1548
|
if (!config.params) {
|
|
1581
1549
|
config.params = {};
|
|
1582
1550
|
}
|
|
1583
|
-
|
|
1584
|
-
if (config.
|
|
1551
|
+
let urlHasDefinedApiKey = false;
|
|
1552
|
+
if (config.url) {
|
|
1553
|
+
const url = new URL(config.url, config.baseURL);
|
|
1554
|
+
urlHasDefinedApiKey = url.searchParams.has(this.keyName);
|
|
1555
|
+
}
|
|
1556
|
+
if (config.params[this.keyName] || urlHasDefinedApiKey) {
|
|
1585
1557
|
throw new ErrorWithCode(formatString(ErrorMessage.DuplicateApiKeyInQueryParam, this.keyName), ErrorCode.AuthorizationInfoAlreadyExists);
|
|
1586
1558
|
}
|
|
1587
1559
|
config.params[this.keyName] = this.keyValue;
|
|
@@ -1592,8 +1564,6 @@ class ApiKeyProvider {
|
|
|
1592
1564
|
}
|
|
1593
1565
|
/**
|
|
1594
1566
|
* Define available location for API Key location
|
|
1595
|
-
*
|
|
1596
|
-
* @beta
|
|
1597
1567
|
*/
|
|
1598
1568
|
var ApiKeyLocation;
|
|
1599
1569
|
(function (ApiKeyLocation) {
|
|
@@ -1610,8 +1580,6 @@ var ApiKeyLocation;
|
|
|
1610
1580
|
// Copyright (c) Microsoft Corporation.
|
|
1611
1581
|
/**
|
|
1612
1582
|
* Provider that handles Certificate authentication
|
|
1613
|
-
*
|
|
1614
|
-
* @beta
|
|
1615
1583
|
*/
|
|
1616
1584
|
class CertificateAuthProvider {
|
|
1617
1585
|
/**
|
|
@@ -1619,8 +1587,6 @@ class CertificateAuthProvider {
|
|
|
1619
1587
|
* @param { SecureContextOptions } certOption - information about the cert used in http requests
|
|
1620
1588
|
*
|
|
1621
1589
|
* @throws {@link ErrorCode|InvalidParameter} - when cert option is empty.
|
|
1622
|
-
*
|
|
1623
|
-
* @beta
|
|
1624
1590
|
*/
|
|
1625
1591
|
constructor(certOption) {
|
|
1626
1592
|
if (certOption && Object.keys(certOption).length !== 0) {
|
|
@@ -1639,8 +1605,6 @@ class CertificateAuthProvider {
|
|
|
1639
1605
|
* @returns Updated axios request config.
|
|
1640
1606
|
*
|
|
1641
1607
|
* @throws {@link ErrorCode|InvalidParameter} - when custom httpsAgent in the request has duplicate properties with certOption provided in constructor.
|
|
1642
|
-
*
|
|
1643
|
-
* @beta
|
|
1644
1608
|
*/
|
|
1645
1609
|
async AddAuthenticationInfo(config) {
|
|
1646
1610
|
if (!config.httpsAgent) {
|
|
@@ -1724,7 +1688,6 @@ const ReservedKey = new Set([
|
|
|
1724
1688
|
]);
|
|
1725
1689
|
/**
|
|
1726
1690
|
* A class providing credential and configuration.
|
|
1727
|
-
* @beta
|
|
1728
1691
|
*/
|
|
1729
1692
|
class TeamsFx {
|
|
1730
1693
|
/**
|
|
@@ -1734,16 +1697,15 @@ class TeamsFx {
|
|
|
1734
1697
|
* @param customConfig - key/value pairs of customized configuration that overrides default ones.
|
|
1735
1698
|
*
|
|
1736
1699
|
* @throws {@link ErrorCode|IdentityTypeNotSupported} when setting app identity in browser.
|
|
1737
|
-
*
|
|
1738
|
-
* @beta
|
|
1739
1700
|
*/
|
|
1740
1701
|
constructor(identityType, customConfig) {
|
|
1741
1702
|
this.identityType = identityType !== null && identityType !== void 0 ? identityType : IdentityType.User;
|
|
1742
1703
|
this.configuration = new Map();
|
|
1743
1704
|
this.loadFromEnv();
|
|
1744
1705
|
if (customConfig) {
|
|
1745
|
-
|
|
1746
|
-
|
|
1706
|
+
const myConfig = Object.assign({}, customConfig);
|
|
1707
|
+
for (const key of Object.keys(myConfig)) {
|
|
1708
|
+
const value = myConfig[key];
|
|
1747
1709
|
if (value) {
|
|
1748
1710
|
this.configuration.set(key, value);
|
|
1749
1711
|
}
|
|
@@ -1754,7 +1716,6 @@ class TeamsFx {
|
|
|
1754
1716
|
* Identity type set by user.
|
|
1755
1717
|
*
|
|
1756
1718
|
* @returns identity type.
|
|
1757
|
-
* @beta
|
|
1758
1719
|
*/
|
|
1759
1720
|
getIdentityType() {
|
|
1760
1721
|
return this.identityType;
|
|
@@ -1767,7 +1728,6 @@ class TeamsFx {
|
|
|
1767
1728
|
* identity is chose, will return {@link AppCredential}.
|
|
1768
1729
|
*
|
|
1769
1730
|
* @returns instance implements TokenCredential interface.
|
|
1770
|
-
* @beta
|
|
1771
1731
|
*/
|
|
1772
1732
|
getCredential() {
|
|
1773
1733
|
if (this.identityType === IdentityType.User) {
|
|
@@ -1787,10 +1747,10 @@ class TeamsFx {
|
|
|
1787
1747
|
}
|
|
1788
1748
|
/**
|
|
1789
1749
|
* Get user information.
|
|
1750
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
1790
1751
|
* @returns UserInfo object.
|
|
1791
|
-
* @beta
|
|
1792
1752
|
*/
|
|
1793
|
-
async getUserInfo() {
|
|
1753
|
+
async getUserInfo(resources) {
|
|
1794
1754
|
if (this.identityType !== IdentityType.User) {
|
|
1795
1755
|
const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.identityType.toString(), "TeamsFx");
|
|
1796
1756
|
internalLogger.error(errorMsg);
|
|
@@ -1812,22 +1772,20 @@ class TeamsFx {
|
|
|
1812
1772
|
* await teamsfx.login("https://graph.microsoft.com/User.Read Calendars.Read"); // multiple scopes using string
|
|
1813
1773
|
* ```
|
|
1814
1774
|
* @param scopes - The list of scopes for which the token will have access, before that, we will request user to consent.
|
|
1775
|
+
* @param {string[]} resources - The optional list of resources for full trust Teams apps.
|
|
1815
1776
|
*
|
|
1816
1777
|
* @throws {@link ErrorCode|InternalError} when failed to login with unknown error.
|
|
1817
1778
|
* @throws {@link ErrorCode|ConsentFailed} when user canceled or failed to consent.
|
|
1818
1779
|
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
1819
1780
|
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
1820
|
-
*
|
|
1821
|
-
* @beta
|
|
1822
1781
|
*/
|
|
1823
|
-
async login(scopes) {
|
|
1782
|
+
async login(scopes, resources) {
|
|
1824
1783
|
throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "login"), ErrorCode.RuntimeNotSupported);
|
|
1825
1784
|
}
|
|
1826
1785
|
/**
|
|
1827
1786
|
* Set SSO token when using user identity in NodeJS.
|
|
1828
1787
|
* @param {string} ssoToken - used for on behalf of user flow.
|
|
1829
1788
|
* @returns self instance.
|
|
1830
|
-
* @beta
|
|
1831
1789
|
*/
|
|
1832
1790
|
setSsoToken(ssoToken) {
|
|
1833
1791
|
if (this.identityType !== IdentityType.User) {
|
|
@@ -1840,7 +1798,6 @@ class TeamsFx {
|
|
|
1840
1798
|
* Usually used by service plugins to retrieve specific config
|
|
1841
1799
|
* @param {string} key - configuration key.
|
|
1842
1800
|
* @returns value in configuration.
|
|
1843
|
-
* @beta
|
|
1844
1801
|
*/
|
|
1845
1802
|
getConfig(key) {
|
|
1846
1803
|
const value = this.configuration.get(key);
|
|
@@ -1855,7 +1812,6 @@ class TeamsFx {
|
|
|
1855
1812
|
* Check the value of specific key.
|
|
1856
1813
|
* @param {string} key - configuration key.
|
|
1857
1814
|
* @returns true if corresponding value is not empty string.
|
|
1858
|
-
* @beta
|
|
1859
1815
|
*/
|
|
1860
1816
|
hasConfig(key) {
|
|
1861
1817
|
const value = this.configuration.get(key);
|
|
@@ -1864,7 +1820,6 @@ class TeamsFx {
|
|
|
1864
1820
|
/**
|
|
1865
1821
|
* Get all configurations.
|
|
1866
1822
|
* @returns key value mappings.
|
|
1867
|
-
* @beta
|
|
1868
1823
|
*/
|
|
1869
1824
|
getConfigs() {
|
|
1870
1825
|
const config = {};
|
|
@@ -1906,171 +1861,388 @@ class TeamsFx {
|
|
|
1906
1861
|
// Copyright (c) Microsoft Corporation.
|
|
1907
1862
|
// Licensed under the MIT license.
|
|
1908
1863
|
/**
|
|
1909
|
-
*
|
|
1864
|
+
* The target type where the notification will be sent to.
|
|
1865
|
+
*
|
|
1866
|
+
* @remarks
|
|
1867
|
+
* - "Channel" means to a team channel. (By default, notification to a team will be sent to its "General" channel.)
|
|
1868
|
+
* - "Group" means to a group chat.
|
|
1869
|
+
* - "Person" means to a personal chat.
|
|
1910
1870
|
*/
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1871
|
+
var NotificationTargetType;
|
|
1872
|
+
(function (NotificationTargetType) {
|
|
1873
|
+
/**
|
|
1874
|
+
* The notification will be sent to a team channel.
|
|
1875
|
+
* (By default, notification to a team will be sent to its "General" channel.)
|
|
1876
|
+
*/
|
|
1877
|
+
NotificationTargetType["Channel"] = "Channel";
|
|
1878
|
+
/**
|
|
1879
|
+
* The notification will be sent to a group chat.
|
|
1880
|
+
*/
|
|
1881
|
+
NotificationTargetType["Group"] = "Group";
|
|
1882
|
+
/**
|
|
1883
|
+
* The notification will be sent to a personal chat.
|
|
1884
|
+
*/
|
|
1885
|
+
NotificationTargetType["Person"] = "Person";
|
|
1886
|
+
})(NotificationTargetType || (NotificationTargetType = {}));
|
|
1887
|
+
/**
|
|
1888
|
+
* Options used to control how the response card will be sent to users.
|
|
1889
|
+
*/
|
|
1890
|
+
var AdaptiveCardResponse;
|
|
1891
|
+
(function (AdaptiveCardResponse) {
|
|
1892
|
+
/**
|
|
1893
|
+
* The response card will be replaced the current one for the interactor who trigger the action.
|
|
1894
|
+
*/
|
|
1895
|
+
AdaptiveCardResponse[AdaptiveCardResponse["ReplaceForInteractor"] = 0] = "ReplaceForInteractor";
|
|
1896
|
+
/**
|
|
1897
|
+
* The response card will be replaced the current one for all users in the chat.
|
|
1898
|
+
*/
|
|
1899
|
+
AdaptiveCardResponse[AdaptiveCardResponse["ReplaceForAll"] = 1] = "ReplaceForAll";
|
|
1900
|
+
/**
|
|
1901
|
+
* The response card will be sent as a new message for all users in the chat.
|
|
1902
|
+
*/
|
|
1903
|
+
AdaptiveCardResponse[AdaptiveCardResponse["NewForAll"] = 2] = "NewForAll";
|
|
1904
|
+
})(AdaptiveCardResponse || (AdaptiveCardResponse = {}));
|
|
1905
|
+
/**
|
|
1906
|
+
* Status code for an `application/vnd.microsoft.error` invoke response.
|
|
1907
|
+
*/
|
|
1908
|
+
var InvokeResponseErrorCode;
|
|
1909
|
+
(function (InvokeResponseErrorCode) {
|
|
1910
|
+
/**
|
|
1911
|
+
* Invalid request.
|
|
1912
|
+
*/
|
|
1913
|
+
InvokeResponseErrorCode[InvokeResponseErrorCode["BadRequest"] = 400] = "BadRequest";
|
|
1914
|
+
/**
|
|
1915
|
+
* Internal server error.
|
|
1916
|
+
*/
|
|
1917
|
+
InvokeResponseErrorCode[InvokeResponseErrorCode["InternalServerError"] = 500] = "InternalServerError";
|
|
1918
|
+
})(InvokeResponseErrorCode || (InvokeResponseErrorCode = {}));
|
|
1919
|
+
|
|
1914
1920
|
/**
|
|
1921
|
+
* Available response type for an adaptive card invoke response.
|
|
1915
1922
|
* @internal
|
|
1916
1923
|
*/
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1924
|
+
var InvokeResponseType;
|
|
1925
|
+
(function (InvokeResponseType) {
|
|
1926
|
+
InvokeResponseType["AdaptiveCard"] = "application/vnd.microsoft.card.adaptive";
|
|
1927
|
+
InvokeResponseType["Message"] = "application/vnd.microsoft.activity.message";
|
|
1928
|
+
InvokeResponseType["Error"] = "application/vnd.microsoft.error";
|
|
1929
|
+
})(InvokeResponseType || (InvokeResponseType = {}));
|
|
1930
|
+
/**
|
|
1931
|
+
* Provides methods for formatting various invoke responses a bot can send to respond to an invoke request.
|
|
1932
|
+
*
|
|
1933
|
+
* @remarks
|
|
1934
|
+
* All of these functions return an {@link InvokeResponse} object, which can be
|
|
1935
|
+
* passed as input to generate a new `invokeResponse` activity.
|
|
1936
|
+
*
|
|
1937
|
+
* This example sends an invoke response that contains an adaptive card.
|
|
1938
|
+
*
|
|
1939
|
+
* ```typescript
|
|
1940
|
+
*
|
|
1941
|
+
* const myCard = {
|
|
1942
|
+
* type: "AdaptiveCard",
|
|
1943
|
+
* body: [
|
|
1944
|
+
* {
|
|
1945
|
+
* "type": "TextBlock",
|
|
1946
|
+
* "text": "This is a sample card"
|
|
1947
|
+
* }],
|
|
1948
|
+
* $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
|
|
1949
|
+
* version: "1.4"
|
|
1950
|
+
* };
|
|
1951
|
+
*
|
|
1952
|
+
* const invokeResponse = InvokeResponseFactory.adaptiveCard(myCard);
|
|
1953
|
+
* await context.sendActivity({
|
|
1954
|
+
* type: ActivityTypes.InvokeResponse,
|
|
1955
|
+
* value: invokeResponse,
|
|
1956
|
+
* });
|
|
1957
|
+
* ```
|
|
1958
|
+
*/
|
|
1959
|
+
class InvokeResponseFactory {
|
|
1960
|
+
/**
|
|
1961
|
+
* Create an invoke response from a text message.
|
|
1962
|
+
* The type of the invoke response is `application/vnd.microsoft.activity.message`
|
|
1963
|
+
* indicates the request was successfully processed.
|
|
1964
|
+
*
|
|
1965
|
+
* @param message A text message included in a invoke response.
|
|
1966
|
+
*
|
|
1967
|
+
* @returns {InvokeResponse} An InvokeResponse object.
|
|
1968
|
+
*/
|
|
1969
|
+
static textMessage(message) {
|
|
1970
|
+
if (!message) {
|
|
1971
|
+
throw new Error("The text message cannot be null or empty");
|
|
1972
|
+
}
|
|
1973
|
+
return {
|
|
1974
|
+
status: StatusCodes.OK,
|
|
1975
|
+
body: {
|
|
1976
|
+
statusCode: StatusCodes.OK,
|
|
1977
|
+
type: InvokeResponseType.Message,
|
|
1978
|
+
value: message,
|
|
1979
|
+
},
|
|
1980
|
+
};
|
|
1922
1981
|
}
|
|
1923
|
-
|
|
1924
|
-
|
|
1982
|
+
/**
|
|
1983
|
+
* Create an invoke response from an adaptive card.
|
|
1984
|
+
*
|
|
1985
|
+
* The type of the invoke response is `application/vnd.microsoft.card.adaptive` indicates
|
|
1986
|
+
* the request was successfully processed, and the response includes an adaptive card
|
|
1987
|
+
* that the client should display in place of the current one.
|
|
1988
|
+
*
|
|
1989
|
+
* @param card The adaptive card JSON payload.
|
|
1990
|
+
*
|
|
1991
|
+
* @returns {InvokeResponse} An InvokeResponse object.
|
|
1992
|
+
*/
|
|
1993
|
+
static adaptiveCard(card) {
|
|
1994
|
+
if (!card) {
|
|
1995
|
+
throw new Error("The adaptive card content cannot be null or undefined");
|
|
1996
|
+
}
|
|
1997
|
+
return {
|
|
1998
|
+
status: StatusCodes.OK,
|
|
1999
|
+
body: {
|
|
2000
|
+
statusCode: StatusCodes.OK,
|
|
2001
|
+
type: InvokeResponseType.AdaptiveCard,
|
|
2002
|
+
value: card,
|
|
2003
|
+
},
|
|
2004
|
+
};
|
|
1925
2005
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
2006
|
+
/**
|
|
2007
|
+
* Create an invoke response with error code and message.
|
|
2008
|
+
*
|
|
2009
|
+
* The type of the invoke response is `application/vnd.microsoft.error` indicates
|
|
2010
|
+
* the request was failed to processed.
|
|
2011
|
+
*
|
|
2012
|
+
* @param errorCode The status code indicates error, available values:
|
|
2013
|
+
* - 400 (BadRequest): indicate the incoming request was invalid.
|
|
2014
|
+
* - 500 (InternalServerError): indicate an unexpected error occurred.
|
|
2015
|
+
* @param errorMessage The error message.
|
|
2016
|
+
*
|
|
2017
|
+
* @returns {InvokeResponse} An InvokeResponse object.
|
|
2018
|
+
*/
|
|
2019
|
+
static errorResponse(errorCode, errorMessage) {
|
|
2020
|
+
return {
|
|
2021
|
+
status: StatusCodes.OK,
|
|
2022
|
+
body: {
|
|
2023
|
+
statusCode: errorCode,
|
|
2024
|
+
type: InvokeResponseType.Error,
|
|
2025
|
+
value: {
|
|
2026
|
+
code: errorCode.toString(),
|
|
2027
|
+
message: errorMessage,
|
|
2028
|
+
},
|
|
2029
|
+
},
|
|
2030
|
+
};
|
|
1928
2031
|
}
|
|
1929
|
-
|
|
1930
|
-
|
|
2032
|
+
/**
|
|
2033
|
+
* Create an invoke response with status code and response value.
|
|
2034
|
+
* @param statusCode The status code.
|
|
2035
|
+
* @param body The value of the response body.
|
|
2036
|
+
*
|
|
2037
|
+
* @returns {InvokeResponse} An InvokeResponse object.
|
|
2038
|
+
*/
|
|
2039
|
+
static createInvokeResponse(statusCode, body) {
|
|
2040
|
+
return {
|
|
2041
|
+
status: statusCode,
|
|
2042
|
+
body: body,
|
|
2043
|
+
};
|
|
1931
2044
|
}
|
|
1932
|
-
}
|
|
2045
|
+
}
|
|
2046
|
+
|
|
1933
2047
|
/**
|
|
1934
2048
|
* @internal
|
|
1935
2049
|
*/
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
2050
|
+
class CardActionMiddleware {
|
|
2051
|
+
constructor(handlers) {
|
|
2052
|
+
this.actionHandlers = [];
|
|
2053
|
+
this.defaultMessage = "Your response was sent to the app";
|
|
2054
|
+
if (handlers && handlers.length > 0) {
|
|
2055
|
+
this.actionHandlers.push(...handlers);
|
|
2056
|
+
}
|
|
2057
|
+
}
|
|
2058
|
+
async onTurn(context, next) {
|
|
2059
|
+
var _a, _b, _c;
|
|
2060
|
+
if (context.activity.name === "adaptiveCard/action") {
|
|
2061
|
+
const action = context.activity.value.action;
|
|
2062
|
+
const actionVerb = action.verb;
|
|
2063
|
+
for (const handler of this.actionHandlers) {
|
|
2064
|
+
if (((_a = handler.triggerVerb) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === (actionVerb === null || actionVerb === void 0 ? void 0 : actionVerb.toLowerCase())) {
|
|
2065
|
+
let response;
|
|
2066
|
+
try {
|
|
2067
|
+
response = await handler.handleActionInvoked(context, action.data);
|
|
2068
|
+
}
|
|
2069
|
+
catch (error) {
|
|
2070
|
+
const errorResponse = InvokeResponseFactory.errorResponse(InvokeResponseErrorCode.InternalServerError, error.message);
|
|
2071
|
+
await this.sendInvokeResponse(context, errorResponse);
|
|
2072
|
+
throw error;
|
|
2073
|
+
}
|
|
2074
|
+
const responseType = (_b = response.body) === null || _b === void 0 ? void 0 : _b.type;
|
|
2075
|
+
switch (responseType) {
|
|
2076
|
+
case InvokeResponseType.AdaptiveCard:
|
|
2077
|
+
const card = (_c = response.body) === null || _c === void 0 ? void 0 : _c.value;
|
|
2078
|
+
if (!card) {
|
|
2079
|
+
const errorMessage = "Adaptive card content cannot be found in the response body";
|
|
2080
|
+
await this.sendInvokeResponse(context, InvokeResponseFactory.errorResponse(InvokeResponseErrorCode.InternalServerError, errorMessage));
|
|
2081
|
+
throw new Error(errorMessage);
|
|
2082
|
+
}
|
|
2083
|
+
if (card.refresh && handler.adaptiveCardResponse !== AdaptiveCardResponse.NewForAll) {
|
|
2084
|
+
// Card won't be refreshed with AdaptiveCardResponse.ReplaceForInteractor.
|
|
2085
|
+
// So set to AdaptiveCardResponse.ReplaceForAll here.
|
|
2086
|
+
handler.adaptiveCardResponse = AdaptiveCardResponse.ReplaceForAll;
|
|
2087
|
+
}
|
|
2088
|
+
const activity = MessageFactory.attachment(CardFactory.adaptiveCard(card));
|
|
2089
|
+
if (handler.adaptiveCardResponse === AdaptiveCardResponse.NewForAll) {
|
|
2090
|
+
await this.sendInvokeResponse(context, InvokeResponseFactory.textMessage(this.defaultMessage));
|
|
2091
|
+
await context.sendActivity(activity);
|
|
2092
|
+
}
|
|
2093
|
+
else if (handler.adaptiveCardResponse === AdaptiveCardResponse.ReplaceForAll) {
|
|
2094
|
+
activity.id = context.activity.replyToId;
|
|
2095
|
+
await context.updateActivity(activity);
|
|
2096
|
+
await this.sendInvokeResponse(context, response);
|
|
2097
|
+
}
|
|
2098
|
+
else {
|
|
2099
|
+
await this.sendInvokeResponse(context, response);
|
|
2100
|
+
}
|
|
2101
|
+
break;
|
|
2102
|
+
case InvokeResponseType.Message:
|
|
2103
|
+
case InvokeResponseType.Error:
|
|
2104
|
+
default:
|
|
2105
|
+
await this.sendInvokeResponse(context, response);
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2108
|
+
break;
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
await next();
|
|
2113
|
+
}
|
|
2114
|
+
async sendInvokeResponse(context, response) {
|
|
2115
|
+
await context.sendActivity({
|
|
2116
|
+
type: ActivityTypes.InvokeResponse,
|
|
2117
|
+
value: response,
|
|
2118
|
+
});
|
|
2119
|
+
}
|
|
1939
2120
|
}
|
|
1940
2121
|
|
|
1941
|
-
// Copyright (c) Microsoft Corporation.
|
|
1942
2122
|
/**
|
|
1943
|
-
*
|
|
2123
|
+
* A card action bot to respond to adaptive card universal actions.
|
|
1944
2124
|
*/
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
2125
|
+
class CardActionBot {
|
|
2126
|
+
/**
|
|
2127
|
+
* Creates a new instance of the `CardActionBot`.
|
|
2128
|
+
*
|
|
2129
|
+
* @param adapter The bound `BotFrameworkAdapter`.
|
|
2130
|
+
* @param options - initialize options
|
|
2131
|
+
*/
|
|
2132
|
+
constructor(adapter, options) {
|
|
2133
|
+
this.middleware = new CardActionMiddleware(options === null || options === void 0 ? void 0 : options.actions);
|
|
2134
|
+
this.adapter = adapter.use(this.middleware);
|
|
2135
|
+
}
|
|
2136
|
+
/**
|
|
2137
|
+
* Registers a card action handler to the bot.
|
|
2138
|
+
* @param actionHandler A card action handler to be registered.
|
|
2139
|
+
*/
|
|
2140
|
+
registerHandler(actionHandler) {
|
|
2141
|
+
if (actionHandler) {
|
|
2142
|
+
this.middleware.actionHandlers.push(actionHandler);
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
/**
|
|
2146
|
+
* Registers card action handlers to the bot.
|
|
2147
|
+
* @param actionHandlers A set of card action handlers to be registered.
|
|
2148
|
+
*/
|
|
2149
|
+
registerHandlers(actionHandlers) {
|
|
2150
|
+
if (actionHandlers) {
|
|
2151
|
+
this.middleware.actionHandlers.push(...actionHandlers);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
|
|
2156
|
+
// Copyright (c) Microsoft Corporation.
|
|
1954
2157
|
/**
|
|
1955
2158
|
* @internal
|
|
1956
2159
|
*/
|
|
1957
|
-
class
|
|
1958
|
-
constructor(
|
|
1959
|
-
this.
|
|
2160
|
+
class CommandResponseMiddleware {
|
|
2161
|
+
constructor(handlers, ssoHandlers, activityHandler) {
|
|
2162
|
+
this.commandHandlers = [];
|
|
2163
|
+
this.ssoCommandHandlers = [];
|
|
2164
|
+
handlers = handlers !== null && handlers !== void 0 ? handlers : [];
|
|
2165
|
+
ssoHandlers = ssoHandlers !== null && ssoHandlers !== void 0 ? ssoHandlers : [];
|
|
2166
|
+
this.hasSsoCommand = ssoHandlers.length > 0;
|
|
2167
|
+
this.ssoActivityHandler = activityHandler;
|
|
2168
|
+
if (this.hasSsoCommand && !this.ssoActivityHandler) {
|
|
2169
|
+
internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
|
|
2170
|
+
throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
|
|
2171
|
+
}
|
|
2172
|
+
this.commandHandlers.push(...handlers);
|
|
2173
|
+
for (const ssoHandler of ssoHandlers) {
|
|
2174
|
+
this.addSsoCommand(ssoHandler);
|
|
2175
|
+
}
|
|
2176
|
+
}
|
|
2177
|
+
addSsoCommand(ssoHandler) {
|
|
2178
|
+
var _a;
|
|
2179
|
+
(_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.addCommand(async (context, tokenResponse, message) => {
|
|
2180
|
+
const matchResult = this.shouldTrigger(ssoHandler.triggerPatterns, message.text);
|
|
2181
|
+
message.matches = Array.isArray(matchResult) ? matchResult : void 0;
|
|
2182
|
+
const response = await ssoHandler.handleCommandReceived(context, message, tokenResponse);
|
|
2183
|
+
await this.processResponse(context, response);
|
|
2184
|
+
}, ssoHandler.triggerPatterns);
|
|
2185
|
+
this.ssoCommandHandlers.push(ssoHandler);
|
|
2186
|
+
this.hasSsoCommand = true;
|
|
1960
2187
|
}
|
|
1961
2188
|
async onTurn(context, next) {
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
2189
|
+
var _a, _b;
|
|
2190
|
+
if (context.activity.type === ActivityTypes.Message) {
|
|
2191
|
+
// Invoke corresponding command handler for the command response
|
|
2192
|
+
const commandText = this.getActivityText(context.activity);
|
|
2193
|
+
let alreadyProcessed = false;
|
|
2194
|
+
for (const handler of this.commandHandlers) {
|
|
2195
|
+
const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
|
|
2196
|
+
// It is important to note that the command bot will stop processing handlers
|
|
2197
|
+
// when the first command handler is matched.
|
|
2198
|
+
if (!!matchResult) {
|
|
2199
|
+
const message = {
|
|
2200
|
+
text: commandText,
|
|
2201
|
+
};
|
|
2202
|
+
message.matches = Array.isArray(matchResult) ? matchResult : void 0;
|
|
2203
|
+
const response = await handler.handleCommandReceived(context, message);
|
|
2204
|
+
await this.processResponse(context, response);
|
|
2205
|
+
alreadyProcessed = true;
|
|
2206
|
+
break;
|
|
2207
|
+
}
|
|
1969
2208
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
2209
|
+
if (!alreadyProcessed) {
|
|
2210
|
+
for (const handler of this.ssoCommandHandlers) {
|
|
2211
|
+
const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
|
|
2212
|
+
if (!!matchResult) {
|
|
2213
|
+
await ((_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.run(context));
|
|
2214
|
+
break;
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
1973
2217
|
}
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
await this.
|
|
1978
|
-
break;
|
|
2218
|
+
}
|
|
2219
|
+
else {
|
|
2220
|
+
if (this.hasSsoCommand) {
|
|
2221
|
+
await ((_b = this.ssoActivityHandler) === null || _b === void 0 ? void 0 : _b.run(context));
|
|
1979
2222
|
}
|
|
1980
2223
|
}
|
|
1981
2224
|
await next();
|
|
1982
2225
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
else {
|
|
1992
|
-
return ActivityType.CurrentBotUninstalled;
|
|
2226
|
+
async processResponse(context, response) {
|
|
2227
|
+
if (typeof response === "string") {
|
|
2228
|
+
await context.sendActivity(response);
|
|
2229
|
+
}
|
|
2230
|
+
else {
|
|
2231
|
+
const replyActivity = response;
|
|
2232
|
+
if (replyActivity) {
|
|
2233
|
+
await context.sendActivity(replyActivity);
|
|
1993
2234
|
}
|
|
1994
2235
|
}
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
2236
|
+
}
|
|
2237
|
+
matchPattern(pattern, text) {
|
|
2238
|
+
if (text) {
|
|
2239
|
+
if (typeof pattern === "string") {
|
|
2240
|
+
const regExp = new RegExp(pattern, "i");
|
|
2241
|
+
return regExp.test(text);
|
|
1999
2242
|
}
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
}
|
|
2004
|
-
else if (activityType === "message") {
|
|
2005
|
-
return ActivityType.CurrentBotMessaged;
|
|
2006
|
-
}
|
|
2007
|
-
return ActivityType.Unknown;
|
|
2008
|
-
}
|
|
2009
|
-
async tryAddMessagedReference(context) {
|
|
2010
|
-
var _a, _b, _c, _d;
|
|
2011
|
-
const reference = TurnContext.getConversationReference(context.activity);
|
|
2012
|
-
const conversationType = (_a = reference === null || reference === void 0 ? void 0 : reference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType;
|
|
2013
|
-
if (conversationType === "personal" || conversationType === "groupChat") {
|
|
2014
|
-
if (!(await this.conversationReferenceStore.check(reference))) {
|
|
2015
|
-
await this.conversationReferenceStore.set(reference);
|
|
2016
|
-
}
|
|
2017
|
-
}
|
|
2018
|
-
else if (conversationType === "channel") {
|
|
2019
|
-
const teamId = (_d = (_c = (_b = context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.team) === null || _d === void 0 ? void 0 : _d.id;
|
|
2020
|
-
if (teamId !== undefined) {
|
|
2021
|
-
const teamReference = cloneConversation(reference);
|
|
2022
|
-
teamReference.conversation.id = teamId;
|
|
2023
|
-
if (!(await this.conversationReferenceStore.check(teamReference))) {
|
|
2024
|
-
await this.conversationReferenceStore.set(teamReference);
|
|
2025
|
-
}
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
2028
|
-
}
|
|
2029
|
-
}
|
|
2030
|
-
class CommandResponseMiddleware {
|
|
2031
|
-
constructor(handlers) {
|
|
2032
|
-
this.commandHandlers = [];
|
|
2033
|
-
if (handlers && handlers.length > 0) {
|
|
2034
|
-
this.commandHandlers.push(...handlers);
|
|
2035
|
-
}
|
|
2036
|
-
}
|
|
2037
|
-
async onTurn(context, next) {
|
|
2038
|
-
if (context.activity.type === ActivityTypes.Message) {
|
|
2039
|
-
// Invoke corresponding command handler for the command response
|
|
2040
|
-
const commandText = this.getActivityText(context.activity);
|
|
2041
|
-
const message = {
|
|
2042
|
-
text: commandText,
|
|
2043
|
-
};
|
|
2044
|
-
for (const handler of this.commandHandlers) {
|
|
2045
|
-
const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
|
|
2046
|
-
// It is important to note that the command bot will stop processing handlers
|
|
2047
|
-
// when the first command handler is matched.
|
|
2048
|
-
if (!!matchResult) {
|
|
2049
|
-
message.matches = Array.isArray(matchResult) ? matchResult : void 0;
|
|
2050
|
-
const response = await handler.handleCommandReceived(context, message);
|
|
2051
|
-
if (typeof response === "string") {
|
|
2052
|
-
await context.sendActivity(response);
|
|
2053
|
-
}
|
|
2054
|
-
else {
|
|
2055
|
-
const replyActivity = response;
|
|
2056
|
-
if (replyActivity) {
|
|
2057
|
-
await context.sendActivity(replyActivity);
|
|
2058
|
-
}
|
|
2059
|
-
}
|
|
2060
|
-
}
|
|
2061
|
-
}
|
|
2062
|
-
}
|
|
2063
|
-
await next();
|
|
2064
|
-
}
|
|
2065
|
-
matchPattern(pattern, text) {
|
|
2066
|
-
if (text) {
|
|
2067
|
-
if (typeof pattern === "string") {
|
|
2068
|
-
const regExp = new RegExp(pattern, "i");
|
|
2069
|
-
return regExp.test(text);
|
|
2070
|
-
}
|
|
2071
|
-
if (pattern instanceof RegExp) {
|
|
2072
|
-
const matches = text.match(pattern);
|
|
2073
|
-
return matches !== null && matches !== void 0 ? matches : false;
|
|
2243
|
+
if (pattern instanceof RegExp) {
|
|
2244
|
+
const matches = text.match(pattern);
|
|
2245
|
+
return matches !== null && matches !== void 0 ? matches : false;
|
|
2074
2246
|
}
|
|
2075
2247
|
}
|
|
2076
2248
|
return false;
|
|
@@ -2103,8 +2275,6 @@ class CommandResponseMiddleware {
|
|
|
2103
2275
|
*
|
|
2104
2276
|
* @remarks
|
|
2105
2277
|
* Ensure each command should ONLY be registered with the command once, otherwise it'll cause unexpected behavior if you register the same command more than once.
|
|
2106
|
-
*
|
|
2107
|
-
* @beta
|
|
2108
2278
|
*/
|
|
2109
2279
|
class CommandBot {
|
|
2110
2280
|
/**
|
|
@@ -2112,19 +2282,16 @@ class CommandBot {
|
|
|
2112
2282
|
*
|
|
2113
2283
|
* @param adapter The bound `BotFrameworkAdapter`.
|
|
2114
2284
|
* @param options - initialize options
|
|
2115
|
-
*
|
|
2116
|
-
* @beta
|
|
2117
2285
|
*/
|
|
2118
|
-
constructor(adapter, options) {
|
|
2119
|
-
this.
|
|
2286
|
+
constructor(adapter, options, ssoCommandActivityHandler, ssoConfig) {
|
|
2287
|
+
this.ssoConfig = ssoConfig;
|
|
2288
|
+
this.middleware = new CommandResponseMiddleware(options === null || options === void 0 ? void 0 : options.commands, options === null || options === void 0 ? void 0 : options.ssoCommands, ssoCommandActivityHandler);
|
|
2120
2289
|
this.adapter = adapter.use(this.middleware);
|
|
2121
2290
|
}
|
|
2122
2291
|
/**
|
|
2123
2292
|
* Registers a command into the command bot.
|
|
2124
2293
|
*
|
|
2125
|
-
* @param command The command to
|
|
2126
|
-
*
|
|
2127
|
-
* @beta
|
|
2294
|
+
* @param command The command to register.
|
|
2128
2295
|
*/
|
|
2129
2296
|
registerCommand(command) {
|
|
2130
2297
|
if (command) {
|
|
@@ -2134,15 +2301,165 @@ class CommandBot {
|
|
|
2134
2301
|
/**
|
|
2135
2302
|
* Registers commands into the command bot.
|
|
2136
2303
|
*
|
|
2137
|
-
* @param commands The
|
|
2138
|
-
*
|
|
2139
|
-
* @beta
|
|
2304
|
+
* @param commands The commands to register.
|
|
2140
2305
|
*/
|
|
2141
2306
|
registerCommands(commands) {
|
|
2142
2307
|
if (commands) {
|
|
2143
2308
|
this.middleware.commandHandlers.push(...commands);
|
|
2144
2309
|
}
|
|
2145
2310
|
}
|
|
2311
|
+
/**
|
|
2312
|
+
* Registers a sso command into the command bot.
|
|
2313
|
+
*
|
|
2314
|
+
* @param command The command to register.
|
|
2315
|
+
*/
|
|
2316
|
+
registerSsoCommand(ssoCommand) {
|
|
2317
|
+
this.validateSsoActivityHandler();
|
|
2318
|
+
this.middleware.addSsoCommand(ssoCommand);
|
|
2319
|
+
}
|
|
2320
|
+
/**
|
|
2321
|
+
* Registers commands into the command bot.
|
|
2322
|
+
*
|
|
2323
|
+
* @param commands The commands to register.
|
|
2324
|
+
*/
|
|
2325
|
+
registerSsoCommands(ssoCommands) {
|
|
2326
|
+
if (ssoCommands.length > 0) {
|
|
2327
|
+
this.validateSsoActivityHandler();
|
|
2328
|
+
for (const ssoCommand of ssoCommands) {
|
|
2329
|
+
this.middleware.addSsoCommand(ssoCommand);
|
|
2330
|
+
}
|
|
2331
|
+
}
|
|
2332
|
+
}
|
|
2333
|
+
validateSsoActivityHandler() {
|
|
2334
|
+
if (!this.middleware.ssoActivityHandler) {
|
|
2335
|
+
internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
|
|
2336
|
+
throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
|
|
2337
|
+
}
|
|
2338
|
+
}
|
|
2339
|
+
}
|
|
2340
|
+
|
|
2341
|
+
// Copyright (c) Microsoft Corporation.
|
|
2342
|
+
/**
|
|
2343
|
+
* @internal
|
|
2344
|
+
*/
|
|
2345
|
+
function cloneConversation(conversation) {
|
|
2346
|
+
return JSON.parse(JSON.stringify(conversation));
|
|
2347
|
+
}
|
|
2348
|
+
/**
|
|
2349
|
+
* @internal
|
|
2350
|
+
*/
|
|
2351
|
+
function getTargetType(conversationReference) {
|
|
2352
|
+
var _a;
|
|
2353
|
+
const conversationType = (_a = conversationReference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType;
|
|
2354
|
+
if (conversationType === "personal") {
|
|
2355
|
+
return NotificationTargetType.Person;
|
|
2356
|
+
}
|
|
2357
|
+
else if (conversationType === "groupChat") {
|
|
2358
|
+
return NotificationTargetType.Group;
|
|
2359
|
+
}
|
|
2360
|
+
else if (conversationType === "channel") {
|
|
2361
|
+
return NotificationTargetType.Channel;
|
|
2362
|
+
}
|
|
2363
|
+
else {
|
|
2364
|
+
return undefined;
|
|
2365
|
+
}
|
|
2366
|
+
}
|
|
2367
|
+
/**
|
|
2368
|
+
* @internal
|
|
2369
|
+
*/
|
|
2370
|
+
function getTeamsBotInstallationId(context) {
|
|
2371
|
+
var _a, _b, _c, _d;
|
|
2372
|
+
return (_d = (_c = (_b = (_a = context.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 : context.activity.conversation.id;
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
// Copyright (c) Microsoft Corporation.
|
|
2376
|
+
/**
|
|
2377
|
+
* @internal
|
|
2378
|
+
*/
|
|
2379
|
+
var ActivityType;
|
|
2380
|
+
(function (ActivityType) {
|
|
2381
|
+
ActivityType[ActivityType["CurrentBotInstalled"] = 0] = "CurrentBotInstalled";
|
|
2382
|
+
ActivityType[ActivityType["CurrentBotMessaged"] = 1] = "CurrentBotMessaged";
|
|
2383
|
+
ActivityType[ActivityType["CurrentBotUninstalled"] = 2] = "CurrentBotUninstalled";
|
|
2384
|
+
ActivityType[ActivityType["TeamDeleted"] = 3] = "TeamDeleted";
|
|
2385
|
+
ActivityType[ActivityType["TeamRestored"] = 4] = "TeamRestored";
|
|
2386
|
+
ActivityType[ActivityType["Unknown"] = 5] = "Unknown";
|
|
2387
|
+
})(ActivityType || (ActivityType = {}));
|
|
2388
|
+
/**
|
|
2389
|
+
* @internal
|
|
2390
|
+
*/
|
|
2391
|
+
class NotificationMiddleware {
|
|
2392
|
+
constructor(options) {
|
|
2393
|
+
this.conversationReferenceStore = options.conversationReferenceStore;
|
|
2394
|
+
}
|
|
2395
|
+
async onTurn(context, next) {
|
|
2396
|
+
const type = this.classifyActivity(context.activity);
|
|
2397
|
+
switch (type) {
|
|
2398
|
+
case ActivityType.CurrentBotInstalled:
|
|
2399
|
+
case ActivityType.TeamRestored: {
|
|
2400
|
+
const reference = TurnContext.getConversationReference(context.activity);
|
|
2401
|
+
await this.conversationReferenceStore.set(reference);
|
|
2402
|
+
break;
|
|
2403
|
+
}
|
|
2404
|
+
case ActivityType.CurrentBotMessaged: {
|
|
2405
|
+
await this.tryAddMessagedReference(context);
|
|
2406
|
+
break;
|
|
2407
|
+
}
|
|
2408
|
+
case ActivityType.CurrentBotUninstalled:
|
|
2409
|
+
case ActivityType.TeamDeleted: {
|
|
2410
|
+
const reference = TurnContext.getConversationReference(context.activity);
|
|
2411
|
+
await this.conversationReferenceStore.delete(reference);
|
|
2412
|
+
break;
|
|
2413
|
+
}
|
|
2414
|
+
}
|
|
2415
|
+
await next();
|
|
2416
|
+
}
|
|
2417
|
+
classifyActivity(activity) {
|
|
2418
|
+
var _a, _b;
|
|
2419
|
+
const activityType = activity.type;
|
|
2420
|
+
if (activityType === "installationUpdate") {
|
|
2421
|
+
const action = (_a = activity.action) === null || _a === void 0 ? void 0 : _a.toLowerCase();
|
|
2422
|
+
if (action === "add") {
|
|
2423
|
+
return ActivityType.CurrentBotInstalled;
|
|
2424
|
+
}
|
|
2425
|
+
else {
|
|
2426
|
+
return ActivityType.CurrentBotUninstalled;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
else if (activityType === "conversationUpdate") {
|
|
2430
|
+
const eventType = (_b = activity.channelData) === null || _b === void 0 ? void 0 : _b.eventType;
|
|
2431
|
+
if (eventType === "teamDeleted") {
|
|
2432
|
+
return ActivityType.TeamDeleted;
|
|
2433
|
+
}
|
|
2434
|
+
else if (eventType === "teamRestored") {
|
|
2435
|
+
return ActivityType.TeamRestored;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
else if (activityType === "message") {
|
|
2439
|
+
return ActivityType.CurrentBotMessaged;
|
|
2440
|
+
}
|
|
2441
|
+
return ActivityType.Unknown;
|
|
2442
|
+
}
|
|
2443
|
+
async tryAddMessagedReference(context) {
|
|
2444
|
+
var _a, _b, _c, _d;
|
|
2445
|
+
const reference = TurnContext.getConversationReference(context.activity);
|
|
2446
|
+
const conversationType = (_a = reference === null || reference === void 0 ? void 0 : reference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType;
|
|
2447
|
+
if (conversationType === "personal" || conversationType === "groupChat") {
|
|
2448
|
+
if (!(await this.conversationReferenceStore.check(reference))) {
|
|
2449
|
+
await this.conversationReferenceStore.set(reference);
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
else if (conversationType === "channel") {
|
|
2453
|
+
const teamId = (_d = (_c = (_b = context.activity) === null || _b === void 0 ? void 0 : _b.channelData) === null || _c === void 0 ? void 0 : _c.team) === null || _d === void 0 ? void 0 : _d.id;
|
|
2454
|
+
if (teamId !== undefined) {
|
|
2455
|
+
const teamReference = cloneConversation(reference);
|
|
2456
|
+
teamReference.conversation.id = teamId;
|
|
2457
|
+
if (!(await this.conversationReferenceStore.check(teamReference))) {
|
|
2458
|
+
await this.conversationReferenceStore.set(teamReference);
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2146
2463
|
}
|
|
2147
2464
|
|
|
2148
2465
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -2270,32 +2587,30 @@ class ConversationReferenceStore {
|
|
|
2270
2587
|
*
|
|
2271
2588
|
* @param target - the notification target.
|
|
2272
2589
|
* @param text - the plain text message.
|
|
2273
|
-
* @
|
|
2274
|
-
*
|
|
2275
|
-
* @
|
|
2590
|
+
* @param onError - an optional error handler that can catch exceptions during message sending.
|
|
2591
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2592
|
+
* @returns the response of sending message.
|
|
2276
2593
|
*/
|
|
2277
|
-
function sendMessage(target, text) {
|
|
2278
|
-
return target.sendMessage(text);
|
|
2594
|
+
function sendMessage(target, text, onError) {
|
|
2595
|
+
return target.sendMessage(text, onError);
|
|
2279
2596
|
}
|
|
2280
2597
|
/**
|
|
2281
2598
|
* Send an adaptive card message to a notification target.
|
|
2282
2599
|
*
|
|
2283
2600
|
* @param target - the notification target.
|
|
2284
2601
|
* @param card - the adaptive card raw JSON.
|
|
2285
|
-
* @
|
|
2286
|
-
*
|
|
2287
|
-
* @
|
|
2602
|
+
* @param onError - an optional error handler that can catch exceptions during adaptive card sending.
|
|
2603
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2604
|
+
* @returns the response of sending adaptive card message.
|
|
2288
2605
|
*/
|
|
2289
|
-
function sendAdaptiveCard(target, card) {
|
|
2290
|
-
return target.sendAdaptiveCard(card);
|
|
2606
|
+
function sendAdaptiveCard(target, card, onError) {
|
|
2607
|
+
return target.sendAdaptiveCard(card, onError);
|
|
2291
2608
|
}
|
|
2292
2609
|
/**
|
|
2293
2610
|
* A {@link NotificationTarget} that represents a team channel.
|
|
2294
2611
|
*
|
|
2295
2612
|
* @remarks
|
|
2296
2613
|
* It's recommended to get channels from {@link TeamsBotInstallation.channels()}.
|
|
2297
|
-
*
|
|
2298
|
-
* @beta
|
|
2299
2614
|
*/
|
|
2300
2615
|
class Channel {
|
|
2301
2616
|
/**
|
|
@@ -2306,16 +2621,12 @@ class Channel {
|
|
|
2306
2621
|
*
|
|
2307
2622
|
* @param parent - The parent {@link TeamsBotInstallation} where this channel is created from.
|
|
2308
2623
|
* @param info - Detailed channel information.
|
|
2309
|
-
*
|
|
2310
|
-
* @beta
|
|
2311
2624
|
*/
|
|
2312
2625
|
constructor(parent, info) {
|
|
2313
2626
|
/**
|
|
2314
2627
|
* Notification target type. For channel it's always "Channel".
|
|
2315
|
-
*
|
|
2316
|
-
* @beta
|
|
2317
2628
|
*/
|
|
2318
|
-
this.type =
|
|
2629
|
+
this.type = NotificationTargetType.Channel;
|
|
2319
2630
|
this.parent = parent;
|
|
2320
2631
|
this.info = info;
|
|
2321
2632
|
}
|
|
@@ -2323,35 +2634,61 @@ class Channel {
|
|
|
2323
2634
|
* Send a plain text message.
|
|
2324
2635
|
*
|
|
2325
2636
|
* @param text - the plain text message.
|
|
2326
|
-
* @
|
|
2327
|
-
*
|
|
2328
|
-
* @
|
|
2637
|
+
* @param onError - an optional error handler that can catch exceptions during message sending.
|
|
2638
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2639
|
+
* @returns the response of sending message.
|
|
2329
2640
|
*/
|
|
2330
|
-
sendMessage(text) {
|
|
2331
|
-
|
|
2641
|
+
async sendMessage(text, onError) {
|
|
2642
|
+
const response = {};
|
|
2643
|
+
await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
|
|
2332
2644
|
const conversation = await this.newConversation(context);
|
|
2333
2645
|
await this.parent.adapter.continueConversation(conversation, async (ctx) => {
|
|
2334
|
-
|
|
2646
|
+
try {
|
|
2647
|
+
const res = await ctx.sendActivity(text);
|
|
2648
|
+
response.id = res === null || res === void 0 ? void 0 : res.id;
|
|
2649
|
+
}
|
|
2650
|
+
catch (error) {
|
|
2651
|
+
if (onError) {
|
|
2652
|
+
await onError(ctx, error);
|
|
2653
|
+
}
|
|
2654
|
+
else {
|
|
2655
|
+
throw error;
|
|
2656
|
+
}
|
|
2657
|
+
}
|
|
2335
2658
|
});
|
|
2336
2659
|
});
|
|
2660
|
+
return response;
|
|
2337
2661
|
}
|
|
2338
2662
|
/**
|
|
2339
2663
|
* Send an adaptive card message.
|
|
2340
2664
|
*
|
|
2341
2665
|
* @param card - the adaptive card raw JSON.
|
|
2342
|
-
* @
|
|
2343
|
-
*
|
|
2344
|
-
* @
|
|
2666
|
+
* @param onError - an optional error handler that can catch exceptions during adaptive card sending.
|
|
2667
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2668
|
+
* @returns the response of sending adaptive card message.
|
|
2345
2669
|
*/
|
|
2346
|
-
async sendAdaptiveCard(card) {
|
|
2347
|
-
|
|
2670
|
+
async sendAdaptiveCard(card, onError) {
|
|
2671
|
+
const response = {};
|
|
2672
|
+
await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
|
|
2348
2673
|
const conversation = await this.newConversation(context);
|
|
2349
2674
|
await this.parent.adapter.continueConversation(conversation, async (ctx) => {
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2675
|
+
try {
|
|
2676
|
+
const res = await ctx.sendActivity({
|
|
2677
|
+
attachments: [CardFactory.adaptiveCard(card)],
|
|
2678
|
+
});
|
|
2679
|
+
response.id = res === null || res === void 0 ? void 0 : res.id;
|
|
2680
|
+
}
|
|
2681
|
+
catch (error) {
|
|
2682
|
+
if (onError) {
|
|
2683
|
+
await onError(ctx, error);
|
|
2684
|
+
}
|
|
2685
|
+
else {
|
|
2686
|
+
throw error;
|
|
2687
|
+
}
|
|
2688
|
+
}
|
|
2353
2689
|
});
|
|
2354
2690
|
});
|
|
2691
|
+
return response;
|
|
2355
2692
|
}
|
|
2356
2693
|
/**
|
|
2357
2694
|
* @internal
|
|
@@ -2368,8 +2705,6 @@ class Channel {
|
|
|
2368
2705
|
*
|
|
2369
2706
|
* @remarks
|
|
2370
2707
|
* It's recommended to get members from {@link TeamsBotInstallation.members()}.
|
|
2371
|
-
*
|
|
2372
|
-
* @beta
|
|
2373
2708
|
*/
|
|
2374
2709
|
class Member {
|
|
2375
2710
|
/**
|
|
@@ -2380,16 +2715,12 @@ class Member {
|
|
|
2380
2715
|
*
|
|
2381
2716
|
* @param parent - The parent {@link TeamsBotInstallation} where this member is created from.
|
|
2382
2717
|
* @param account - Detailed member account information.
|
|
2383
|
-
*
|
|
2384
|
-
* @beta
|
|
2385
2718
|
*/
|
|
2386
2719
|
constructor(parent, account) {
|
|
2387
2720
|
/**
|
|
2388
2721
|
* Notification target type. For member it's always "Person".
|
|
2389
|
-
*
|
|
2390
|
-
* @beta
|
|
2391
2722
|
*/
|
|
2392
|
-
this.type =
|
|
2723
|
+
this.type = NotificationTargetType.Person;
|
|
2393
2724
|
this.parent = parent;
|
|
2394
2725
|
this.account = account;
|
|
2395
2726
|
}
|
|
@@ -2397,35 +2728,61 @@ class Member {
|
|
|
2397
2728
|
* Send a plain text message.
|
|
2398
2729
|
*
|
|
2399
2730
|
* @param text - the plain text message.
|
|
2400
|
-
* @
|
|
2401
|
-
*
|
|
2402
|
-
* @
|
|
2731
|
+
* @param onError - an optional error handler that can catch exceptions during message sending.
|
|
2732
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2733
|
+
* @returns the response of sending message.
|
|
2403
2734
|
*/
|
|
2404
|
-
sendMessage(text) {
|
|
2405
|
-
|
|
2735
|
+
async sendMessage(text, onError) {
|
|
2736
|
+
const response = {};
|
|
2737
|
+
await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
|
|
2406
2738
|
const conversation = await this.newConversation(context);
|
|
2407
2739
|
await this.parent.adapter.continueConversation(conversation, async (ctx) => {
|
|
2408
|
-
|
|
2740
|
+
try {
|
|
2741
|
+
const res = await ctx.sendActivity(text);
|
|
2742
|
+
response.id = res === null || res === void 0 ? void 0 : res.id;
|
|
2743
|
+
}
|
|
2744
|
+
catch (error) {
|
|
2745
|
+
if (onError) {
|
|
2746
|
+
await onError(ctx, error);
|
|
2747
|
+
}
|
|
2748
|
+
else {
|
|
2749
|
+
throw error;
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2409
2752
|
});
|
|
2410
2753
|
});
|
|
2754
|
+
return response;
|
|
2411
2755
|
}
|
|
2412
2756
|
/**
|
|
2413
2757
|
* Send an adaptive card message.
|
|
2414
2758
|
*
|
|
2415
2759
|
* @param card - the adaptive card raw JSON.
|
|
2416
|
-
* @
|
|
2417
|
-
*
|
|
2418
|
-
* @
|
|
2760
|
+
* @param onError - an optional error handler that can catch exceptions during adaptive card sending.
|
|
2761
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2762
|
+
* @returns the response of sending adaptive card message.
|
|
2419
2763
|
*/
|
|
2420
|
-
async sendAdaptiveCard(card) {
|
|
2421
|
-
|
|
2764
|
+
async sendAdaptiveCard(card, onError) {
|
|
2765
|
+
const response = {};
|
|
2766
|
+
await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
|
|
2422
2767
|
const conversation = await this.newConversation(context);
|
|
2423
2768
|
await this.parent.adapter.continueConversation(conversation, async (ctx) => {
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2769
|
+
try {
|
|
2770
|
+
const res = await ctx.sendActivity({
|
|
2771
|
+
attachments: [CardFactory.adaptiveCard(card)],
|
|
2772
|
+
});
|
|
2773
|
+
response.id = res === null || res === void 0 ? void 0 : res.id;
|
|
2774
|
+
}
|
|
2775
|
+
catch (error) {
|
|
2776
|
+
if (onError) {
|
|
2777
|
+
await onError(ctx, error);
|
|
2778
|
+
}
|
|
2779
|
+
else {
|
|
2780
|
+
throw error;
|
|
2781
|
+
}
|
|
2782
|
+
}
|
|
2427
2783
|
});
|
|
2428
2784
|
});
|
|
2785
|
+
return response;
|
|
2429
2786
|
}
|
|
2430
2787
|
/**
|
|
2431
2788
|
* @internal
|
|
@@ -2453,8 +2810,6 @@ class Member {
|
|
|
2453
2810
|
*
|
|
2454
2811
|
* @remarks
|
|
2455
2812
|
* It's recommended to get bot installations from {@link ConversationBot.installations()}.
|
|
2456
|
-
*
|
|
2457
|
-
* @beta
|
|
2458
2813
|
*/
|
|
2459
2814
|
class TeamsBotInstallation {
|
|
2460
2815
|
/**
|
|
@@ -2465,8 +2820,6 @@ class TeamsBotInstallation {
|
|
|
2465
2820
|
*
|
|
2466
2821
|
* @param adapter - the bound `BotFrameworkAdapter`.
|
|
2467
2822
|
* @param conversationReference - the bound `ConversationReference`.
|
|
2468
|
-
*
|
|
2469
|
-
* @beta
|
|
2470
2823
|
*/
|
|
2471
2824
|
constructor(adapter, conversationReference) {
|
|
2472
2825
|
this.adapter = adapter;
|
|
@@ -2477,38 +2830,66 @@ class TeamsBotInstallation {
|
|
|
2477
2830
|
* Send a plain text message.
|
|
2478
2831
|
*
|
|
2479
2832
|
* @param text - the plain text message.
|
|
2480
|
-
* @
|
|
2481
|
-
*
|
|
2482
|
-
* @
|
|
2833
|
+
* @param onError - an optional error handler that can catch exceptions during message sending.
|
|
2834
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2835
|
+
* @returns the response of sending message.
|
|
2483
2836
|
*/
|
|
2484
|
-
sendMessage(text) {
|
|
2485
|
-
|
|
2486
|
-
|
|
2837
|
+
async sendMessage(text, onError) {
|
|
2838
|
+
const response = {};
|
|
2839
|
+
await this.adapter.continueConversation(this.conversationReference, async (context) => {
|
|
2840
|
+
try {
|
|
2841
|
+
const res = await context.sendActivity(text);
|
|
2842
|
+
response.id = res === null || res === void 0 ? void 0 : res.id;
|
|
2843
|
+
}
|
|
2844
|
+
catch (error) {
|
|
2845
|
+
if (onError) {
|
|
2846
|
+
await onError(context, error);
|
|
2847
|
+
}
|
|
2848
|
+
else {
|
|
2849
|
+
throw error;
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2487
2852
|
});
|
|
2853
|
+
return response;
|
|
2488
2854
|
}
|
|
2489
2855
|
/**
|
|
2490
2856
|
* Send an adaptive card message.
|
|
2491
2857
|
*
|
|
2492
2858
|
* @param card - the adaptive card raw JSON.
|
|
2493
|
-
* @
|
|
2494
|
-
*
|
|
2495
|
-
* @
|
|
2859
|
+
* @param onError - an optional error handler that can catch exceptions during adaptive card sending.
|
|
2860
|
+
* If not defined, error will be handled by `BotAdapter.onTurnError`.
|
|
2861
|
+
* @returns the response of sending adaptive card message.
|
|
2496
2862
|
*/
|
|
2497
|
-
sendAdaptiveCard(card) {
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2863
|
+
async sendAdaptiveCard(card, onError) {
|
|
2864
|
+
const response = {};
|
|
2865
|
+
await this.adapter.continueConversation(this.conversationReference, async (context) => {
|
|
2866
|
+
try {
|
|
2867
|
+
const res = await context.sendActivity({
|
|
2868
|
+
attachments: [CardFactory.adaptiveCard(card)],
|
|
2869
|
+
});
|
|
2870
|
+
response.id = res === null || res === void 0 ? void 0 : res.id;
|
|
2871
|
+
}
|
|
2872
|
+
catch (error) {
|
|
2873
|
+
if (onError) {
|
|
2874
|
+
await onError(context, error);
|
|
2875
|
+
}
|
|
2876
|
+
else {
|
|
2877
|
+
throw error;
|
|
2878
|
+
}
|
|
2879
|
+
}
|
|
2502
2880
|
});
|
|
2881
|
+
return response;
|
|
2503
2882
|
}
|
|
2504
2883
|
/**
|
|
2505
2884
|
* Get channels from this bot installation.
|
|
2506
2885
|
*
|
|
2507
2886
|
* @returns an array of channels if bot is installed into a team, otherwise returns an empty array.
|
|
2508
|
-
*
|
|
2509
|
-
* @beta
|
|
2510
2887
|
*/
|
|
2511
2888
|
async channels() {
|
|
2889
|
+
const channels = [];
|
|
2890
|
+
if (this.type !== NotificationTargetType.Channel) {
|
|
2891
|
+
return channels;
|
|
2892
|
+
}
|
|
2512
2893
|
let teamsChannels = [];
|
|
2513
2894
|
await this.adapter.continueConversation(this.conversationReference, async (context) => {
|
|
2514
2895
|
const teamId = getTeamsBotInstallationId(context);
|
|
@@ -2516,7 +2897,6 @@ class TeamsBotInstallation {
|
|
|
2516
2897
|
teamsChannels = await TeamsInfo.getTeamChannels(context, teamId);
|
|
2517
2898
|
}
|
|
2518
2899
|
});
|
|
2519
|
-
const channels = [];
|
|
2520
2900
|
for (const channel of teamsChannels) {
|
|
2521
2901
|
channels.push(new Channel(this, channel));
|
|
2522
2902
|
}
|
|
@@ -2526,8 +2906,6 @@ class TeamsBotInstallation {
|
|
|
2526
2906
|
* Get members from this bot installation.
|
|
2527
2907
|
*
|
|
2528
2908
|
* @returns an array of members from where the bot is installed.
|
|
2529
|
-
*
|
|
2530
|
-
* @beta
|
|
2531
2909
|
*/
|
|
2532
2910
|
async members() {
|
|
2533
2911
|
const members = [];
|
|
@@ -2543,11 +2921,27 @@ class TeamsBotInstallation {
|
|
|
2543
2921
|
});
|
|
2544
2922
|
return members;
|
|
2545
2923
|
}
|
|
2924
|
+
/**
|
|
2925
|
+
* Get team details from this bot installation
|
|
2926
|
+
*
|
|
2927
|
+
* @returns the team details if bot is installed into a team, otherwise returns undefined.
|
|
2928
|
+
*/
|
|
2929
|
+
async getTeamDetails() {
|
|
2930
|
+
if (this.type !== NotificationTargetType.Channel) {
|
|
2931
|
+
return undefined;
|
|
2932
|
+
}
|
|
2933
|
+
let teamDetails;
|
|
2934
|
+
await this.adapter.continueConversation(this.conversationReference, async (context) => {
|
|
2935
|
+
const teamId = getTeamsBotInstallationId(context);
|
|
2936
|
+
if (teamId !== undefined) {
|
|
2937
|
+
teamDetails = await TeamsInfo.getTeamDetails(context, teamId);
|
|
2938
|
+
}
|
|
2939
|
+
});
|
|
2940
|
+
return teamDetails;
|
|
2941
|
+
}
|
|
2546
2942
|
}
|
|
2547
2943
|
/**
|
|
2548
2944
|
* Provide utilities to send notification to varies targets (e.g., member, group, channel).
|
|
2549
|
-
*
|
|
2550
|
-
* @beta
|
|
2551
2945
|
*/
|
|
2552
2946
|
class NotificationBot {
|
|
2553
2947
|
/**
|
|
@@ -2558,8 +2952,6 @@ class NotificationBot {
|
|
|
2558
2952
|
*
|
|
2559
2953
|
* @param adapter - the bound `BotFrameworkAdapter`
|
|
2560
2954
|
* @param options - initialize options
|
|
2561
|
-
*
|
|
2562
|
-
* @beta
|
|
2563
2955
|
*/
|
|
2564
2956
|
constructor(adapter, options) {
|
|
2565
2957
|
var _a, _b;
|
|
@@ -2576,8 +2968,6 @@ class NotificationBot {
|
|
|
2576
2968
|
* The result is retrieving from the persisted storage.
|
|
2577
2969
|
*
|
|
2578
2970
|
* @returns - an array of {@link TeamsBotInstallation}.
|
|
2579
|
-
*
|
|
2580
|
-
* @beta
|
|
2581
2971
|
*/
|
|
2582
2972
|
async installations() {
|
|
2583
2973
|
if (this.conversationReferenceStore === undefined || this.adapter === undefined) {
|
|
@@ -2608,6 +2998,459 @@ class NotificationBot {
|
|
|
2608
2998
|
}
|
|
2609
2999
|
return targets;
|
|
2610
3000
|
}
|
|
3001
|
+
/**
|
|
3002
|
+
* Returns the first {@link Member} where predicate is true, and undefined otherwise.
|
|
3003
|
+
*
|
|
3004
|
+
* @param predicate find calls predicate once for each member of the installation,
|
|
3005
|
+
* until it finds one where predicate returns true. If such a member is found, find
|
|
3006
|
+
* immediately returns that member. Otherwise, find returns undefined.
|
|
3007
|
+
* @param scope the scope to find members from the installations
|
|
3008
|
+
* (personal chat, group chat, Teams channel).
|
|
3009
|
+
* @returns the first {@link Member} where predicate is true, and undefined otherwise.
|
|
3010
|
+
*/
|
|
3011
|
+
async findMember(predicate, scope) {
|
|
3012
|
+
for (const target of await this.installations()) {
|
|
3013
|
+
if (this.matchSearchScope(target, scope)) {
|
|
3014
|
+
for (const member of await target.members()) {
|
|
3015
|
+
if (await predicate(member)) {
|
|
3016
|
+
return member;
|
|
3017
|
+
}
|
|
3018
|
+
}
|
|
3019
|
+
}
|
|
3020
|
+
}
|
|
3021
|
+
return;
|
|
3022
|
+
}
|
|
3023
|
+
/**
|
|
3024
|
+
* Returns the first {@link Channel} where predicate is true, and undefined otherwise.
|
|
3025
|
+
*
|
|
3026
|
+
* @param predicate find calls predicate once for each channel of the installation,
|
|
3027
|
+
* until it finds one where predicate returns true. If such a channel is found, find
|
|
3028
|
+
* immediately returns that channel. Otherwise, find returns undefined.
|
|
3029
|
+
* @returns the first {@link Channel} where predicate is true, and undefined otherwise.
|
|
3030
|
+
*/
|
|
3031
|
+
async findChannel(predicate) {
|
|
3032
|
+
for (const target of await this.installations()) {
|
|
3033
|
+
if (target.type === NotificationTargetType.Channel) {
|
|
3034
|
+
const teamDetails = await target.getTeamDetails();
|
|
3035
|
+
for (const channel of await target.channels()) {
|
|
3036
|
+
if (await predicate(channel, teamDetails)) {
|
|
3037
|
+
return channel;
|
|
3038
|
+
}
|
|
3039
|
+
}
|
|
3040
|
+
}
|
|
3041
|
+
}
|
|
3042
|
+
return;
|
|
3043
|
+
}
|
|
3044
|
+
/**
|
|
3045
|
+
* Returns all {@link Member} where predicate is true, and empty array otherwise.
|
|
3046
|
+
*
|
|
3047
|
+
* @param predicate find calls predicate for each member of the installation.
|
|
3048
|
+
* @param scope the scope to find members from the installations
|
|
3049
|
+
* (personal chat, group chat, Teams channel).
|
|
3050
|
+
* @returns an array of {@link Member} where predicate is true, and empty array otherwise.
|
|
3051
|
+
*/
|
|
3052
|
+
async findAllMembers(predicate, scope) {
|
|
3053
|
+
const members = [];
|
|
3054
|
+
for (const target of await this.installations()) {
|
|
3055
|
+
if (this.matchSearchScope(target, scope)) {
|
|
3056
|
+
for (const member of await target.members()) {
|
|
3057
|
+
if (await predicate(member)) {
|
|
3058
|
+
members.push(member);
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3063
|
+
return members;
|
|
3064
|
+
}
|
|
3065
|
+
/**
|
|
3066
|
+
* Returns all {@link Channel} where predicate is true, and empty array otherwise.
|
|
3067
|
+
*
|
|
3068
|
+
* @param predicate find calls predicate for each channel of the installation.
|
|
3069
|
+
* @returns an array of {@link Channel} where predicate is true, and empty array otherwise.
|
|
3070
|
+
*/
|
|
3071
|
+
async findAllChannels(predicate) {
|
|
3072
|
+
const channels = [];
|
|
3073
|
+
for (const target of await this.installations()) {
|
|
3074
|
+
if (target.type === NotificationTargetType.Channel) {
|
|
3075
|
+
const teamDetails = await target.getTeamDetails();
|
|
3076
|
+
for (const channel of await target.channels()) {
|
|
3077
|
+
if (await predicate(channel, teamDetails)) {
|
|
3078
|
+
channels.push(channel);
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
return channels;
|
|
3084
|
+
}
|
|
3085
|
+
matchSearchScope(target, scope) {
|
|
3086
|
+
scope = scope !== null && scope !== void 0 ? scope : SearchScope.All;
|
|
3087
|
+
return ((target.type === NotificationTargetType.Channel && (scope & SearchScope.Channel) !== 0) ||
|
|
3088
|
+
(target.type === NotificationTargetType.Group && (scope & SearchScope.Group) !== 0) ||
|
|
3089
|
+
(target.type === NotificationTargetType.Person && (scope & SearchScope.Person) !== 0));
|
|
3090
|
+
}
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* The search scope when calling {@link NotificationBot.findMember} and {@link NotificationBot.findAllMembers}.
|
|
3094
|
+
* The search scope is a flagged enum and it can be combined with `|`.
|
|
3095
|
+
* For example, to search from personal chat and group chat, use `SearchScope.Person | SearchScope.Group`.
|
|
3096
|
+
*/
|
|
3097
|
+
var SearchScope;
|
|
3098
|
+
(function (SearchScope) {
|
|
3099
|
+
/**
|
|
3100
|
+
* Search members from the installations in personal chat only.
|
|
3101
|
+
*/
|
|
3102
|
+
SearchScope[SearchScope["Person"] = 1] = "Person";
|
|
3103
|
+
/**
|
|
3104
|
+
* Search members from the installations in group chat only.
|
|
3105
|
+
*/
|
|
3106
|
+
SearchScope[SearchScope["Group"] = 2] = "Group";
|
|
3107
|
+
/**
|
|
3108
|
+
* Search members from the installations in Teams channel only.
|
|
3109
|
+
*/
|
|
3110
|
+
SearchScope[SearchScope["Channel"] = 4] = "Channel";
|
|
3111
|
+
/**
|
|
3112
|
+
* Search members from all installations including personal chat, group chat and Teams channel.
|
|
3113
|
+
*/
|
|
3114
|
+
SearchScope[SearchScope["All"] = 7] = "All";
|
|
3115
|
+
})(SearchScope || (SearchScope = {}));
|
|
3116
|
+
|
|
3117
|
+
// Copyright (c) Microsoft Corporation.
|
|
3118
|
+
let DIALOG_NAME = "BotSsoExecutionDialog";
|
|
3119
|
+
let TEAMS_SSO_PROMPT_ID = "TeamsFxSsoPrompt";
|
|
3120
|
+
let COMMAND_ROUTE_DIALOG = "CommandRouteDialog";
|
|
3121
|
+
/**
|
|
3122
|
+
* Sso execution dialog, use to handle sso command
|
|
3123
|
+
*/
|
|
3124
|
+
class BotSsoExecutionDialog extends ComponentDialog {
|
|
3125
|
+
/**
|
|
3126
|
+
* Creates a new instance of the BotSsoExecutionDialog.
|
|
3127
|
+
* @param dedupStorage Helper storage to remove duplicated messages
|
|
3128
|
+
* @param settings The list of scopes for which the token will have access
|
|
3129
|
+
* @param teamsfx {@link TeamsFx} instance for authentication
|
|
3130
|
+
*/
|
|
3131
|
+
constructor(dedupStorage, ssoPromptSettings, teamsfx, dialogName) {
|
|
3132
|
+
super(dialogName !== null && dialogName !== void 0 ? dialogName : DIALOG_NAME);
|
|
3133
|
+
this.dedupStorageKeys = [];
|
|
3134
|
+
// Map to store the commandId and triggerPatterns, key: commandId, value: triggerPatterns
|
|
3135
|
+
this.commandMapping = new Map();
|
|
3136
|
+
if (dialogName) {
|
|
3137
|
+
DIALOG_NAME = dialogName;
|
|
3138
|
+
TEAMS_SSO_PROMPT_ID = dialogName + TEAMS_SSO_PROMPT_ID;
|
|
3139
|
+
COMMAND_ROUTE_DIALOG = dialogName + COMMAND_ROUTE_DIALOG;
|
|
3140
|
+
}
|
|
3141
|
+
this.initialDialogId = COMMAND_ROUTE_DIALOG;
|
|
3142
|
+
this.dedupStorage = dedupStorage;
|
|
3143
|
+
this.dedupStorageKeys = [];
|
|
3144
|
+
const ssoDialog = new TeamsBotSsoPrompt(teamsfx, TEAMS_SSO_PROMPT_ID, ssoPromptSettings);
|
|
3145
|
+
this.addDialog(ssoDialog);
|
|
3146
|
+
const commandRouteDialog = new WaterfallDialog(COMMAND_ROUTE_DIALOG, [
|
|
3147
|
+
this.commandRouteStep.bind(this),
|
|
3148
|
+
]);
|
|
3149
|
+
this.addDialog(commandRouteDialog);
|
|
3150
|
+
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Add TeamsFxBotSsoCommandHandler instance
|
|
3153
|
+
* @param handler {@link BotSsoExecutionDialogHandler} callback function
|
|
3154
|
+
* @param triggerPatterns The trigger pattern
|
|
3155
|
+
*/
|
|
3156
|
+
addCommand(handler, triggerPatterns) {
|
|
3157
|
+
const commandId = this.getCommandHash(triggerPatterns);
|
|
3158
|
+
const dialog = new WaterfallDialog(commandId, [
|
|
3159
|
+
this.ssoStep.bind(this),
|
|
3160
|
+
this.dedupStep.bind(this),
|
|
3161
|
+
async (stepContext) => {
|
|
3162
|
+
const tokenResponse = stepContext.result.tokenResponse;
|
|
3163
|
+
const context = stepContext.context;
|
|
3164
|
+
const message = stepContext.result.message;
|
|
3165
|
+
try {
|
|
3166
|
+
if (tokenResponse) {
|
|
3167
|
+
await handler(context, tokenResponse, message);
|
|
3168
|
+
}
|
|
3169
|
+
else {
|
|
3170
|
+
throw new Error(ErrorMessage.FailedToRetrieveSsoToken);
|
|
3171
|
+
}
|
|
3172
|
+
return await stepContext.endDialog();
|
|
3173
|
+
}
|
|
3174
|
+
catch (error) {
|
|
3175
|
+
const errorMsg = formatString(ErrorMessage.FailedToProcessSsoHandler, error.message);
|
|
3176
|
+
internalLogger.error(errorMsg);
|
|
3177
|
+
return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToProcessSsoHandler));
|
|
3178
|
+
}
|
|
3179
|
+
},
|
|
3180
|
+
]);
|
|
3181
|
+
this.commandMapping.set(commandId, triggerPatterns);
|
|
3182
|
+
this.addDialog(dialog);
|
|
3183
|
+
}
|
|
3184
|
+
getCommandHash(patterns) {
|
|
3185
|
+
const expressions = Array.isArray(patterns) ? patterns : [patterns];
|
|
3186
|
+
const patternStr = expressions.join();
|
|
3187
|
+
const patternStrWithoutSpecialChar = patternStr.replace(/[^a-zA-Z0-9]/g, "");
|
|
3188
|
+
const hash = createHash("sha256").update(patternStr).digest("hex").toLowerCase();
|
|
3189
|
+
return patternStrWithoutSpecialChar + hash;
|
|
3190
|
+
}
|
|
3191
|
+
/**
|
|
3192
|
+
* The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system.
|
|
3193
|
+
*
|
|
3194
|
+
* @param context The context object for the current turn.
|
|
3195
|
+
* @param accessor The instance of StatePropertyAccessor for dialog system.
|
|
3196
|
+
*/
|
|
3197
|
+
async run(context, accessor) {
|
|
3198
|
+
const dialogSet = new DialogSet(accessor);
|
|
3199
|
+
dialogSet.add(this);
|
|
3200
|
+
const dialogContext = await dialogSet.createContext(context);
|
|
3201
|
+
this.ensureMsTeamsChannel(dialogContext);
|
|
3202
|
+
const results = await dialogContext.continueDialog();
|
|
3203
|
+
if (results && results.status === DialogTurnStatus.empty) {
|
|
3204
|
+
await dialogContext.beginDialog(this.id);
|
|
3205
|
+
}
|
|
3206
|
+
else if (results &&
|
|
3207
|
+
results.status === DialogTurnStatus.complete &&
|
|
3208
|
+
results.result instanceof Error) {
|
|
3209
|
+
throw results.result;
|
|
3210
|
+
}
|
|
3211
|
+
}
|
|
3212
|
+
getActivityText(activity) {
|
|
3213
|
+
let text = activity.text;
|
|
3214
|
+
const removedMentionText = TurnContext.removeRecipientMention(activity);
|
|
3215
|
+
if (removedMentionText) {
|
|
3216
|
+
text = removedMentionText
|
|
3217
|
+
.toLowerCase()
|
|
3218
|
+
.replace(/\n|\r\n/g, "")
|
|
3219
|
+
.trim();
|
|
3220
|
+
}
|
|
3221
|
+
return text;
|
|
3222
|
+
}
|
|
3223
|
+
async commandRouteStep(stepContext) {
|
|
3224
|
+
const turnContext = stepContext.context;
|
|
3225
|
+
const text = this.getActivityText(turnContext.activity);
|
|
3226
|
+
const commandId = this.getMatchesCommandId(text);
|
|
3227
|
+
if (commandId) {
|
|
3228
|
+
return await stepContext.beginDialog(commandId);
|
|
3229
|
+
}
|
|
3230
|
+
const errorMsg = formatString(ErrorMessage.CannotFindCommand, turnContext.activity.text);
|
|
3231
|
+
internalLogger.error(errorMsg);
|
|
3232
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.CannotFindCommand);
|
|
3233
|
+
}
|
|
3234
|
+
async ssoStep(stepContext) {
|
|
3235
|
+
try {
|
|
3236
|
+
const turnContext = stepContext.context;
|
|
3237
|
+
const text = this.getActivityText(turnContext.activity);
|
|
3238
|
+
const message = {
|
|
3239
|
+
text,
|
|
3240
|
+
};
|
|
3241
|
+
stepContext.options.commandMessage = message;
|
|
3242
|
+
return await stepContext.beginDialog(TEAMS_SSO_PROMPT_ID);
|
|
3243
|
+
}
|
|
3244
|
+
catch (error) {
|
|
3245
|
+
const errorMsg = formatString(ErrorMessage.FailedToRunSsoStep, error.message);
|
|
3246
|
+
internalLogger.error(errorMsg);
|
|
3247
|
+
return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunSsoStep));
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
async dedupStep(stepContext) {
|
|
3251
|
+
const tokenResponse = stepContext.result;
|
|
3252
|
+
if (!tokenResponse) {
|
|
3253
|
+
internalLogger.error(ErrorMessage.FailedToRetrieveSsoToken);
|
|
3254
|
+
return await stepContext.endDialog(new ErrorWithCode(ErrorMessage.FailedToRetrieveSsoToken, ErrorCode.FailedToRunSsoStep));
|
|
3255
|
+
}
|
|
3256
|
+
try {
|
|
3257
|
+
// Only dedup after ssoStep to make sure that all Teams client would receive the login request
|
|
3258
|
+
if (tokenResponse && (await this.shouldDedup(stepContext.context))) {
|
|
3259
|
+
return Dialog.EndOfTurn;
|
|
3260
|
+
}
|
|
3261
|
+
return await stepContext.next({
|
|
3262
|
+
tokenResponse,
|
|
3263
|
+
message: stepContext.options.commandMessage,
|
|
3264
|
+
});
|
|
3265
|
+
}
|
|
3266
|
+
catch (error) {
|
|
3267
|
+
const errorMsg = formatString(ErrorMessage.FailedToRunDedupStep, error.message);
|
|
3268
|
+
internalLogger.error(errorMsg);
|
|
3269
|
+
return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunDedupStep));
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
/**
|
|
3273
|
+
* Called when the component is ending.
|
|
3274
|
+
*
|
|
3275
|
+
* @param context Context for the current turn of conversation.
|
|
3276
|
+
*/
|
|
3277
|
+
async onEndDialog(context) {
|
|
3278
|
+
const conversationId = context.activity.conversation.id;
|
|
3279
|
+
const currentDedupKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) > 0);
|
|
3280
|
+
await this.dedupStorage.delete(currentDedupKeys);
|
|
3281
|
+
this.dedupStorageKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) < 0);
|
|
3282
|
+
}
|
|
3283
|
+
/**
|
|
3284
|
+
* If a user is signed into multiple Teams clients, the Bot might receive a "signin/tokenExchange" from each client.
|
|
3285
|
+
* Each token exchange request for a specific user login will have an identical activity.value.Id.
|
|
3286
|
+
* Only one of these token exchange requests should be processed by the bot. For a distributed bot in production,
|
|
3287
|
+
* this requires a distributed storage to ensure only one token exchange is processed.
|
|
3288
|
+
* @param context Context for the current turn of conversation.
|
|
3289
|
+
* @returns boolean value indicate whether the message should be removed
|
|
3290
|
+
*/
|
|
3291
|
+
async shouldDedup(context) {
|
|
3292
|
+
const storeItem = {
|
|
3293
|
+
eTag: context.activity.value.id,
|
|
3294
|
+
};
|
|
3295
|
+
const key = this.getStorageKey(context);
|
|
3296
|
+
const storeItems = { [key]: storeItem };
|
|
3297
|
+
try {
|
|
3298
|
+
await this.dedupStorage.write(storeItems);
|
|
3299
|
+
this.dedupStorageKeys.push(key);
|
|
3300
|
+
}
|
|
3301
|
+
catch (err) {
|
|
3302
|
+
if (err instanceof Error && err.message.indexOf("eTag conflict")) {
|
|
3303
|
+
return true;
|
|
3304
|
+
}
|
|
3305
|
+
throw err;
|
|
3306
|
+
}
|
|
3307
|
+
return false;
|
|
3308
|
+
}
|
|
3309
|
+
getStorageKey(context) {
|
|
3310
|
+
if (!context || !context.activity || !context.activity.conversation) {
|
|
3311
|
+
throw new Error("Invalid context, can not get storage key!");
|
|
3312
|
+
}
|
|
3313
|
+
const activity = context.activity;
|
|
3314
|
+
const channelId = activity.channelId;
|
|
3315
|
+
const conversationId = activity.conversation.id;
|
|
3316
|
+
if (activity.type !== ActivityTypes.Invoke || activity.name !== tokenExchangeOperationName) {
|
|
3317
|
+
throw new Error("TokenExchangeState can only be used with Invokes of signin/tokenExchange.");
|
|
3318
|
+
}
|
|
3319
|
+
const value = activity.value;
|
|
3320
|
+
if (!value || !value.id) {
|
|
3321
|
+
throw new Error("Invalid signin/tokenExchange. Missing activity.value.id.");
|
|
3322
|
+
}
|
|
3323
|
+
return `${channelId}/${conversationId}/${value.id}`;
|
|
3324
|
+
}
|
|
3325
|
+
matchPattern(pattern, text) {
|
|
3326
|
+
if (text) {
|
|
3327
|
+
if (typeof pattern === "string") {
|
|
3328
|
+
const regExp = new RegExp(pattern, "i");
|
|
3329
|
+
return regExp.test(text);
|
|
3330
|
+
}
|
|
3331
|
+
if (pattern instanceof RegExp) {
|
|
3332
|
+
const matches = text.match(pattern);
|
|
3333
|
+
return matches !== null && matches !== void 0 ? matches : false;
|
|
3334
|
+
}
|
|
3335
|
+
}
|
|
3336
|
+
return false;
|
|
3337
|
+
}
|
|
3338
|
+
isPatternMatched(patterns, text) {
|
|
3339
|
+
const expressions = Array.isArray(patterns) ? patterns : [patterns];
|
|
3340
|
+
for (const ex of expressions) {
|
|
3341
|
+
const matches = this.matchPattern(ex, text);
|
|
3342
|
+
return !!matches;
|
|
3343
|
+
}
|
|
3344
|
+
return false;
|
|
3345
|
+
}
|
|
3346
|
+
getMatchesCommandId(text) {
|
|
3347
|
+
for (const command of this.commandMapping) {
|
|
3348
|
+
const pattern = command[1];
|
|
3349
|
+
if (this.isPatternMatched(pattern, text)) {
|
|
3350
|
+
return command[0];
|
|
3351
|
+
}
|
|
3352
|
+
}
|
|
3353
|
+
return undefined;
|
|
3354
|
+
}
|
|
3355
|
+
/**
|
|
3356
|
+
* Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
|
|
3357
|
+
* @param dc dialog context
|
|
3358
|
+
* @throws {@link ErrorCode|ChannelNotSupported} if bot channel is not MS Teams
|
|
3359
|
+
* @internal
|
|
3360
|
+
*/
|
|
3361
|
+
ensureMsTeamsChannel(dc) {
|
|
3362
|
+
if (dc.context.activity.channelId != Channels.Msteams) {
|
|
3363
|
+
const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "SSO execution dialog");
|
|
3364
|
+
internalLogger.error(errorMsg);
|
|
3365
|
+
throw new ErrorWithCode(errorMsg, ErrorCode.ChannelNotSupported);
|
|
3366
|
+
}
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
|
|
3370
|
+
// Copyright (c) Microsoft Corporation.
|
|
3371
|
+
/**
|
|
3372
|
+
* Default SSO execution activity handler
|
|
3373
|
+
*/
|
|
3374
|
+
class DefaultBotSsoExecutionActivityHandler extends TeamsActivityHandler {
|
|
3375
|
+
/**
|
|
3376
|
+
* Creates a new instance of the DefaultBotSsoExecutionActivityHandler.
|
|
3377
|
+
* @param ssoConfig configuration for SSO command bot
|
|
3378
|
+
*
|
|
3379
|
+
* @remarks
|
|
3380
|
+
* In the constructor, it uses BotSsoConfig parameter which from {@link ConversationBot} options to initialize {@link BotSsoExecutionDialog}.
|
|
3381
|
+
* It also need to register an event handler for the message event which trigger {@link BotSsoExecutionDialog} instance.
|
|
3382
|
+
*/
|
|
3383
|
+
constructor(ssoConfig) {
|
|
3384
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
|
|
3385
|
+
super();
|
|
3386
|
+
const memoryStorage = new MemoryStorage();
|
|
3387
|
+
const userState = (_b = (_a = ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.userState) !== null && _b !== void 0 ? _b : new UserState(memoryStorage);
|
|
3388
|
+
const conversationState = (_d = (_c = ssoConfig.dialog) === null || _c === void 0 ? void 0 : _c.conversationState) !== null && _d !== void 0 ? _d : new ConversationState(memoryStorage);
|
|
3389
|
+
const dedupStorage = (_f = (_e = ssoConfig.dialog) === null || _e === void 0 ? void 0 : _e.dedupStorage) !== null && _f !== void 0 ? _f : memoryStorage;
|
|
3390
|
+
const _l = ssoConfig.aad, { scopes } = _l, customConfig = __rest(_l, ["scopes"]);
|
|
3391
|
+
const settings = {
|
|
3392
|
+
scopes: scopes,
|
|
3393
|
+
timeout: (_h = (_g = ssoConfig.dialog) === null || _g === void 0 ? void 0 : _g.ssoPromptConfig) === null || _h === void 0 ? void 0 : _h.timeout,
|
|
3394
|
+
endOnInvalidMessage: (_k = (_j = ssoConfig.dialog) === null || _j === void 0 ? void 0 : _j.ssoPromptConfig) === null || _k === void 0 ? void 0 : _k.endOnInvalidMessage,
|
|
3395
|
+
};
|
|
3396
|
+
const teamsfx = new TeamsFx(IdentityType.User, Object.assign({}, customConfig));
|
|
3397
|
+
this.ssoExecutionDialog = new BotSsoExecutionDialog(dedupStorage, settings, teamsfx);
|
|
3398
|
+
this.conversationState = conversationState;
|
|
3399
|
+
this.dialogState = conversationState.createProperty("DialogState");
|
|
3400
|
+
this.userState = userState;
|
|
3401
|
+
this.onMessage(async (context, next) => {
|
|
3402
|
+
await this.ssoExecutionDialog.run(context, this.dialogState);
|
|
3403
|
+
await next();
|
|
3404
|
+
});
|
|
3405
|
+
}
|
|
3406
|
+
/**
|
|
3407
|
+
* Add TeamsFxBotSsoCommandHandler instance to SSO execution dialog
|
|
3408
|
+
* @param handler {@link BotSsoExecutionDialogHandler} callback function
|
|
3409
|
+
* @param triggerPatterns The trigger pattern
|
|
3410
|
+
*
|
|
3411
|
+
* @remarks
|
|
3412
|
+
* This function is used to add SSO command to {@link BotSsoExecutionDialog} instance.
|
|
3413
|
+
*/
|
|
3414
|
+
addCommand(handler, triggerPatterns) {
|
|
3415
|
+
this.ssoExecutionDialog.addCommand(handler, triggerPatterns);
|
|
3416
|
+
}
|
|
3417
|
+
/**
|
|
3418
|
+
* Called to initiate the event emission process.
|
|
3419
|
+
* @param context The context object for the current turn.
|
|
3420
|
+
*/
|
|
3421
|
+
async run(context) {
|
|
3422
|
+
try {
|
|
3423
|
+
await super.run(context);
|
|
3424
|
+
}
|
|
3425
|
+
finally {
|
|
3426
|
+
await this.conversationState.saveChanges(context, false);
|
|
3427
|
+
await this.userState.saveChanges(context, false);
|
|
3428
|
+
}
|
|
3429
|
+
}
|
|
3430
|
+
/**
|
|
3431
|
+
* Receives invoke activities with Activity name of 'signin/verifyState'.
|
|
3432
|
+
* @param context A context object for this turn.
|
|
3433
|
+
* @param query Signin state (part of signin action auth flow) verification invoke query.
|
|
3434
|
+
* @returns A promise that represents the work queued.
|
|
3435
|
+
*
|
|
3436
|
+
* @remarks
|
|
3437
|
+
* It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
|
|
3438
|
+
*/
|
|
3439
|
+
async handleTeamsSigninVerifyState(context, query) {
|
|
3440
|
+
await this.ssoExecutionDialog.run(context, this.dialogState);
|
|
3441
|
+
}
|
|
3442
|
+
/**
|
|
3443
|
+
* Receives invoke activities with Activity name of 'signin/tokenExchange'
|
|
3444
|
+
* @param context A context object for this turn.
|
|
3445
|
+
* @param query Signin state (part of signin action auth flow) verification invoke query
|
|
3446
|
+
* @returns A promise that represents the work queued.
|
|
3447
|
+
*
|
|
3448
|
+
* @remark
|
|
3449
|
+
* It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
|
|
3450
|
+
*/
|
|
3451
|
+
async handleTeamsSigninTokenExchange(context, query) {
|
|
3452
|
+
await this.ssoExecutionDialog.run(context, this.dialogState);
|
|
3453
|
+
}
|
|
2611
3454
|
}
|
|
2612
3455
|
|
|
2613
3456
|
// Copyright (c) Microsoft Corporation.
|
|
@@ -2661,8 +3504,6 @@ class NotificationBot {
|
|
|
2661
3504
|
* For command and response, ensure each command should ONLY be registered with the command once, otherwise it'll cause unexpected behavior if you register the same command more than once.
|
|
2662
3505
|
*
|
|
2663
3506
|
* For notification, set `notification.storage` in {@link ConversationOptions} to use your own storage implementation.
|
|
2664
|
-
*
|
|
2665
|
-
* @beta
|
|
2666
3507
|
*/
|
|
2667
3508
|
class ConversationBot {
|
|
2668
3509
|
/**
|
|
@@ -2672,23 +3513,34 @@ class ConversationBot {
|
|
|
2672
3513
|
* It's recommended to create your own adapter and storage for production environment instead of the default one.
|
|
2673
3514
|
*
|
|
2674
3515
|
* @param options - initialize options
|
|
2675
|
-
*
|
|
2676
|
-
* @beta
|
|
2677
3516
|
*/
|
|
2678
3517
|
constructor(options) {
|
|
2679
|
-
var _a, _b;
|
|
3518
|
+
var _a, _b, _c, _d;
|
|
2680
3519
|
if (options.adapter) {
|
|
2681
3520
|
this.adapter = options.adapter;
|
|
2682
3521
|
}
|
|
2683
3522
|
else {
|
|
2684
3523
|
this.adapter = this.createDefaultAdapter(options.adapterConfig);
|
|
2685
3524
|
}
|
|
2686
|
-
|
|
2687
|
-
|
|
3525
|
+
let ssoCommandActivityHandler;
|
|
3526
|
+
if (options === null || options === void 0 ? void 0 : options.ssoConfig) {
|
|
3527
|
+
if ((_a = options.ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.CustomBotSsoExecutionActivityHandler) {
|
|
3528
|
+
ssoCommandActivityHandler =
|
|
3529
|
+
new options.ssoConfig.dialog.CustomBotSsoExecutionActivityHandler(options.ssoConfig);
|
|
3530
|
+
}
|
|
3531
|
+
else {
|
|
3532
|
+
ssoCommandActivityHandler = new DefaultBotSsoExecutionActivityHandler(options.ssoConfig);
|
|
3533
|
+
}
|
|
3534
|
+
}
|
|
3535
|
+
if ((_b = options.command) === null || _b === void 0 ? void 0 : _b.enabled) {
|
|
3536
|
+
this.command = new CommandBot(this.adapter, options.command, ssoCommandActivityHandler, options.ssoConfig);
|
|
2688
3537
|
}
|
|
2689
|
-
if ((
|
|
3538
|
+
if ((_c = options.notification) === null || _c === void 0 ? void 0 : _c.enabled) {
|
|
2690
3539
|
this.notification = new NotificationBot(this.adapter, options.notification);
|
|
2691
3540
|
}
|
|
3541
|
+
if ((_d = options.cardAction) === null || _d === void 0 ? void 0 : _d.enabled) {
|
|
3542
|
+
this.cardAction = new CardActionBot(this.adapter, options.cardAction);
|
|
3543
|
+
}
|
|
2692
3544
|
}
|
|
2693
3545
|
createDefaultAdapter(adapterConfig) {
|
|
2694
3546
|
const adapter = adapterConfig === undefined
|
|
@@ -2729,8 +3581,6 @@ class ConversationBot {
|
|
|
2729
3581
|
* });
|
|
2730
3582
|
* });
|
|
2731
3583
|
* ```
|
|
2732
|
-
*
|
|
2733
|
-
* @beta
|
|
2734
3584
|
*/
|
|
2735
3585
|
async requestHandler(req, res, logic) {
|
|
2736
3586
|
if (logic === undefined) {
|
|
@@ -2781,8 +3631,6 @@ class MessageBuilder {
|
|
|
2781
3631
|
* description: "sample card description"
|
|
2782
3632
|
* });
|
|
2783
3633
|
* ```
|
|
2784
|
-
*
|
|
2785
|
-
* @beta
|
|
2786
3634
|
*/
|
|
2787
3635
|
static attachAdaptiveCard(cardTemplate, data) {
|
|
2788
3636
|
return {
|
|
@@ -2794,8 +3642,6 @@ class MessageBuilder {
|
|
|
2794
3642
|
*
|
|
2795
3643
|
* @param card The adaptive card content.
|
|
2796
3644
|
* @returns A bot message activity attached with an adaptive card.
|
|
2797
|
-
*
|
|
2798
|
-
* @beta
|
|
2799
3645
|
*/
|
|
2800
3646
|
static attachAdaptiveCardWithoutData(card) {
|
|
2801
3647
|
return {
|
|
@@ -2821,8 +3667,6 @@ class MessageBuilder {
|
|
|
2821
3667
|
* ['action']
|
|
2822
3668
|
* );
|
|
2823
3669
|
* ```
|
|
2824
|
-
*
|
|
2825
|
-
* @beta
|
|
2826
3670
|
*/
|
|
2827
3671
|
static attachHeroCard(title, images, buttons, other) {
|
|
2828
3672
|
return MessageBuilder.attachContent(CardFactory.heroCard(title, images, buttons, other));
|
|
@@ -2838,8 +3682,6 @@ class MessageBuilder {
|
|
|
2838
3682
|
*
|
|
2839
3683
|
* @remarks
|
|
2840
3684
|
* For channels that don't natively support sign-in cards, an alternative message is rendered.
|
|
2841
|
-
*
|
|
2842
|
-
* @beta
|
|
2843
3685
|
*/
|
|
2844
3686
|
static attachSigninCard(title, url, text) {
|
|
2845
3687
|
return MessageBuilder.attachContent(CardFactory.signinCard(title, url, text));
|
|
@@ -2849,8 +3691,6 @@ class MessageBuilder {
|
|
|
2849
3691
|
*
|
|
2850
3692
|
* @param card A description of the Office 365 connector card.
|
|
2851
3693
|
* @returns A bot message activity attached with an Office 365 connector card.
|
|
2852
|
-
*
|
|
2853
|
-
* @beta
|
|
2854
3694
|
*/
|
|
2855
3695
|
static attachO365ConnectorCard(card) {
|
|
2856
3696
|
return MessageBuilder.attachContent(CardFactory.o365ConnectorCard(card));
|
|
@@ -2859,8 +3699,6 @@ class MessageBuilder {
|
|
|
2859
3699
|
* Build a message activity attached with a receipt card.
|
|
2860
3700
|
* @param card A description of the receipt card.
|
|
2861
3701
|
* @returns A message activity attached with a receipt card.
|
|
2862
|
-
*
|
|
2863
|
-
* @beta
|
|
2864
3702
|
*/
|
|
2865
3703
|
static AttachReceiptCard(card) {
|
|
2866
3704
|
return MessageBuilder.attachContent(CardFactory.receiptCard(card));
|
|
@@ -2873,8 +3711,6 @@ class MessageBuilder {
|
|
|
2873
3711
|
* is converted to an `imBack` button with a title and value set to the value of the string.
|
|
2874
3712
|
* @param other Optional. Any additional properties to include on the card.
|
|
2875
3713
|
* @returns A message activity attached with a thumbnail card
|
|
2876
|
-
*
|
|
2877
|
-
* @beta
|
|
2878
3714
|
*/
|
|
2879
3715
|
static attachThumbnailCard(title, images, buttons, other) {
|
|
2880
3716
|
return MessageBuilder.attachContent(CardFactory.thumbnailCard(title, images, buttons, other));
|
|
@@ -2883,8 +3719,6 @@ class MessageBuilder {
|
|
|
2883
3719
|
* Add an attachement to a bot activity.
|
|
2884
3720
|
* @param attachement The attachment object to attach.
|
|
2885
3721
|
* @returns A message activity with an attachment.
|
|
2886
|
-
*
|
|
2887
|
-
* @beta
|
|
2888
3722
|
*/
|
|
2889
3723
|
static attachContent(attachement) {
|
|
2890
3724
|
return {
|
|
@@ -2893,5 +3727,105 @@ class MessageBuilder {
|
|
|
2893
3727
|
}
|
|
2894
3728
|
}
|
|
2895
3729
|
|
|
2896
|
-
|
|
3730
|
+
// Copyright (c) Microsoft Corporation.
|
|
3731
|
+
/**
|
|
3732
|
+
* Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions.
|
|
3733
|
+
* This method only work on MessageExtension with Query now.
|
|
3734
|
+
*
|
|
3735
|
+
* @param {TeamsFx} teamsfx - Used to provide configuration and auth.
|
|
3736
|
+
* @param {string | string[]} scopes - The list of scopes for which the token will have access.
|
|
3737
|
+
*
|
|
3738
|
+
* @returns SignIn link CardAction with 200 status code.
|
|
3739
|
+
*/
|
|
3740
|
+
function getSignInResponseForMessageExtension(teamsfx, scopes) {
|
|
3741
|
+
const scopesArray = getScopesArray(scopes);
|
|
3742
|
+
const signInLink = `${teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(scopesArray.join(" "))}&clientId=${teamsfx.getConfig("clientId")}&tenantId=${teamsfx.getConfig("tenantId")}`;
|
|
3743
|
+
return {
|
|
3744
|
+
composeExtension: {
|
|
3745
|
+
type: "silentAuth",
|
|
3746
|
+
suggestedActions: {
|
|
3747
|
+
actions: [
|
|
3748
|
+
{
|
|
3749
|
+
type: "openUrl",
|
|
3750
|
+
value: signInLink,
|
|
3751
|
+
title: "Message Extension OAuth",
|
|
3752
|
+
},
|
|
3753
|
+
],
|
|
3754
|
+
},
|
|
3755
|
+
},
|
|
3756
|
+
};
|
|
3757
|
+
}
|
|
3758
|
+
/**
|
|
3759
|
+
* execution in message extension with SSO token.
|
|
3760
|
+
*
|
|
3761
|
+
* @param {TurnContext} context - The context object for the current turn.
|
|
3762
|
+
* @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
|
|
3763
|
+
* @param {string[]} scopes - The list of scopes for which the token will have access.
|
|
3764
|
+
* @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
|
|
3765
|
+
*
|
|
3766
|
+
* @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
|
|
3767
|
+
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
3768
|
+
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
|
|
3769
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
3770
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
3771
|
+
*
|
|
3772
|
+
* @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
|
|
3773
|
+
*/
|
|
3774
|
+
async function executionWithToken(context, config, scopes, logic) {
|
|
3775
|
+
const valueObj = context.activity.value;
|
|
3776
|
+
if (!valueObj.authentication || !valueObj.authentication.token) {
|
|
3777
|
+
internalLogger.verbose("No AccessToken in request, return silentAuth for AccessToken");
|
|
3778
|
+
return getSignInResponseForMessageExtension(new TeamsFx(IdentityType.User, config), scopes);
|
|
3779
|
+
}
|
|
3780
|
+
try {
|
|
3781
|
+
const teamsfx = new TeamsFx(IdentityType.User, config).setSsoToken(valueObj.authentication.token);
|
|
3782
|
+
const token = await teamsfx.getCredential().getToken(scopes);
|
|
3783
|
+
const ssoTokenExpiration = parseJwt(valueObj.authentication.token).exp;
|
|
3784
|
+
const tokenRes = {
|
|
3785
|
+
ssoToken: valueObj.authentication.token,
|
|
3786
|
+
ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
|
|
3787
|
+
token: token.token,
|
|
3788
|
+
expiration: token.expiresOnTimestamp.toString(),
|
|
3789
|
+
connectionName: "",
|
|
3790
|
+
};
|
|
3791
|
+
if (logic) {
|
|
3792
|
+
return await logic(tokenRes);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
catch (err) {
|
|
3796
|
+
if (err instanceof ErrorWithCode && err.code === ErrorCode.UiRequiredError) {
|
|
3797
|
+
internalLogger.verbose("User not consent yet, return 412 to user consent first.");
|
|
3798
|
+
const response = { status: 412 };
|
|
3799
|
+
await context.sendActivity({ value: response, type: ActivityTypes.InvokeResponse });
|
|
3800
|
+
return;
|
|
3801
|
+
}
|
|
3802
|
+
throw err;
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
/**
|
|
3806
|
+
* Users execute query in message extension with SSO or access token.
|
|
3807
|
+
*
|
|
3808
|
+
* @param {TurnContext} context - The context object for the current turn.
|
|
3809
|
+
* @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
|
|
3810
|
+
* @param {string| string[]} scopes - The list of scopes for which the token will have access.
|
|
3811
|
+
* @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
|
|
3812
|
+
*
|
|
3813
|
+
* @throws {@link ErrorCode|InternalError} when User invoke not response to message extension query.
|
|
3814
|
+
* @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
|
|
3815
|
+
* @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
|
|
3816
|
+
* @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
|
|
3817
|
+
* @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
|
|
3818
|
+
* @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
|
|
3819
|
+
*
|
|
3820
|
+
* @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
|
|
3821
|
+
*/
|
|
3822
|
+
async function handleMessageExtensionQueryWithToken(context, config, scopes, logic) {
|
|
3823
|
+
if (context.activity.name != "composeExtension/query") {
|
|
3824
|
+
internalLogger.error(ErrorMessage.OnlySupportInQueryActivity);
|
|
3825
|
+
throw new ErrorWithCode(formatString(ErrorMessage.OnlySupportInQueryActivity), ErrorCode.FailedOperation);
|
|
3826
|
+
}
|
|
3827
|
+
return await executionWithToken(context, config !== null && config !== void 0 ? config : {}, scopes, logic);
|
|
3828
|
+
}
|
|
3829
|
+
|
|
3830
|
+
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 };
|
|
2897
3831
|
//# sourceMappingURL=index.esm2017.mjs.map
|