@microsoft/teamsfx 2.0.0-beta.0 → 2.0.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,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,23 +452,8 @@ 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
- /**
439
- * Constructor of AppCredential.
440
- *
441
- * @remarks
442
- * Only works in in server side.
443
- *
444
- * @param {AuthenticationConfiguration} authConfig - The authentication configuration. Use environment variables if not provided.
445
- *
446
- * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret or tenant id is not found in config.
447
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
448
- *
449
- * @beta
450
- */
451
457
  constructor(authConfig) {
452
458
  internalLogger.info("Create M365 tenant credential");
453
459
  const config = this.loadAndValidateConfig(authConfig);
@@ -476,8 +482,6 @@ class AppCredential {
476
482
  *
477
483
  * @returns Access token with expected scopes.
478
484
  * Throw error if get access token failed.
479
- *
480
- * @beta
481
485
  */
482
486
  async getToken(scopes, options) {
483
487
  let accessToken;
@@ -517,7 +521,10 @@ class AppCredential {
517
521
  */
518
522
  loadAndValidateConfig(config) {
519
523
  internalLogger.verbose("Validate authentication configuration");
520
- if (config.clientId && (config.clientSecret || config.certificateContent) && config.tenantId) {
524
+ if (config.clientId &&
525
+ (config.clientSecret || config.certificateContent) &&
526
+ config.tenantId &&
527
+ config.authorityHost) {
521
528
  return config;
522
529
  }
523
530
  const missingValues = [];
@@ -530,6 +537,9 @@ class AppCredential {
530
537
  if (!config.tenantId) {
531
538
  missingValues.push("tenantId");
532
539
  }
540
+ if (!config.authorityHost) {
541
+ missingValues.push("authorityHost");
542
+ }
533
543
  const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingValues.join(", "), "undefined");
534
544
  internalLogger.error(errorMsg);
535
545
  throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
@@ -547,25 +557,8 @@ class AppCredential {
547
557
  *
548
558
  * @remarks
549
559
  * Can only be used in server side.
550
- *
551
- * @beta
552
560
  */
553
561
  class OnBehalfOfUserCredential {
554
- /**
555
- * Constructor of OnBehalfOfUserCredential
556
- *
557
- * @remarks
558
- * Only works in in server side.
559
- *
560
- * @param {string} ssoToken - User token provided by Teams SSO feature.
561
- * @param {AuthenticationConfiguration} config - The authentication configuration. Use environment variables if not provided.
562
- *
563
- * @throws {@link ErrorCode|InvalidConfiguration} when client id, client secret, certificate content, authority host or tenant id is not found in config.
564
- * @throws {@link ErrorCode|InternalError} when SSO token is not valid.
565
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
566
- *
567
- * @beta
568
- */
569
562
  constructor(ssoToken, config) {
570
563
  internalLogger.info("Get on behalf of user credential");
571
564
  const missingConfigurations = [];
@@ -625,8 +618,6 @@ class OnBehalfOfUserCredential {
625
618
  * @remarks
626
619
  * If scopes is empty string or array, it returns SSO token.
627
620
  * If scopes is non-empty, it returns access token for target scope.
628
- *
629
- * @beta
630
621
  */
631
622
  async getToken(scopes, options) {
632
623
  validateScopesType(scopes);
@@ -677,8 +668,6 @@ class OnBehalfOfUserCredential {
677
668
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
678
669
  *
679
670
  * @returns Basic user info with user displayName, objectId and preferredUserName.
680
- *
681
- * @beta
682
671
  */
683
672
  getUserInfo() {
684
673
  internalLogger.info("Get basic user info from SSO token");
@@ -710,44 +699,39 @@ class OnBehalfOfUserCredential {
710
699
  *
711
700
  * @remarks
712
701
  * Can only be used within Teams.
713
- *
714
- * @beta
715
702
  */
716
703
  class TeamsUserCredential {
717
- /**
718
- * Constructor of TeamsUserCredential.
719
- * @remarks
720
- * Can only be used within Teams.
721
- * @beta
722
- */
723
704
  constructor(authConfig) {
724
705
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
725
706
  }
726
707
  /**
727
708
  * Popup login page to get user's access token with specific scopes.
709
+ *
710
+ * @param {string[]} resources - The optional list of resources for full trust Teams apps.
711
+ *
728
712
  * @remarks
729
713
  * Can only be used within Teams.
730
- * @beta
731
714
  */
732
- async login(scopes) {
715
+ async login(scopes, resources) {
733
716
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
734
717
  }
735
718
  /**
736
719
  * Get access token from credential.
737
720
  * @remarks
738
721
  * Can only be used within Teams.
739
- * @beta
740
722
  */
741
723
  async getToken(scopes, options) {
742
724
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
743
725
  }
744
726
  /**
745
727
  * Get basic user info from SSO token
728
+ *
729
+ * @param {string[]} resources - The optional list of resources for full trust Teams apps.
730
+ *
746
731
  * @remarks
747
732
  * Can only be used within Teams.
748
- * @beta
749
733
  */
750
- getUserInfo() {
734
+ getUserInfo(resources) {
751
735
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "TeamsUserCredential"), ErrorCode.RuntimeNotSupported);
752
736
  }
753
737
  }
@@ -756,24 +740,10 @@ class TeamsUserCredential {
756
740
  const defaultScope = "https://graph.microsoft.com/.default";
757
741
  /**
758
742
  * Microsoft Graph auth provider for Teams Framework
759
- *
760
- * @beta
761
743
  */
762
744
  class MsGraphAuthProvider {
763
- /**
764
- * Constructor of MsGraphAuthProvider.
765
- *
766
- * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
767
- * @param {string | string[]} scopes - The list of scopes for which the token will have access.
768
- *
769
- * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
770
- *
771
- * @returns An instance of MsGraphAuthProvider.
772
- *
773
- * @beta
774
- */
775
- constructor(teamsfx, scopes) {
776
- this.teamsfx = teamsfx;
745
+ constructor(credentialOrTeamsFx, scopes) {
746
+ this.credentialOrTeamsFx = credentialOrTeamsFx;
777
747
  let scopesStr = defaultScope;
778
748
  if (scopes) {
779
749
  validateScopesType(scopes);
@@ -799,7 +769,15 @@ class MsGraphAuthProvider {
799
769
  */
800
770
  async getAccessToken() {
801
771
  internalLogger.info(`Get Graph Access token with scopes: '${this.scopes}'`);
802
- const accessToken = await this.teamsfx.getCredential().getToken(this.scopes);
772
+ let accessToken;
773
+ if (this.credentialOrTeamsFx.getCredential) {
774
+ accessToken = await this.credentialOrTeamsFx
775
+ .getCredential()
776
+ .getToken(this.scopes);
777
+ }
778
+ else {
779
+ accessToken = await this.credentialOrTeamsFx.getToken(this.scopes);
780
+ }
803
781
  return new Promise((resolve, reject) => {
804
782
  if (accessToken) {
805
783
  resolve(accessToken.token);
@@ -816,7 +794,6 @@ class MsGraphAuthProvider {
816
794
  // Copyright (c) Microsoft Corporation.
817
795
  /**
818
796
  * Get Microsoft graph client.
819
- *
820
797
  * @example
821
798
  * Get Microsoft graph client by TokenCredential
822
799
  * ```typescript
@@ -862,8 +839,6 @@ class MsGraphAuthProvider {
862
839
  * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
863
840
  *
864
841
  * @returns Graph client with specified scopes.
865
- *
866
- * @beta
867
842
  */
868
843
  function createMicrosoftGraphClient(teamsfx, scopes) {
869
844
  internalLogger.info("Create Microsoft Graph Client");
@@ -872,6 +847,66 @@ function createMicrosoftGraphClient(teamsfx, scopes) {
872
847
  authProvider,
873
848
  });
874
849
  return graphClient;
850
+ }
851
+ // eslint-disable-next-line no-secrets/no-secrets
852
+ /**
853
+ * Get Microsoft graph client.
854
+ * @example
855
+ * Get Microsoft graph client by TokenCredential
856
+ * ```typescript
857
+ * // In browser: TeamsUserCredential
858
+ * const authConfig: TeamsUserCredentialAuthConfig = {
859
+ * clientId: "xxx",
860
+ initiateLoginEndpoint: "https://xxx/auth-start.html",
861
+ * };
862
+
863
+ * const credential = new TeamsUserCredential(authConfig);
864
+
865
+ * const scope = "User.Read";
866
+ * await credential.login(scope);
867
+
868
+ * const client = createMicrosoftGraphClientWithCredential(credential, scope);
869
+
870
+ * // In node: OnBehalfOfUserCredential
871
+ * const oboAuthConfig: OnBehalfOfCredentialAuthConfig = {
872
+ * authorityHost: "xxx",
873
+ * clientId: "xxx",
874
+ * tenantId: "xxx",
875
+ * clientSecret: "xxx",
876
+ * };
877
+
878
+ * const oboCredential = new OnBehalfOfUserCredential(ssoToken, oboAuthConfig);
879
+ * const scope = "User.Read";
880
+ * const client = createMicrosoftGraphClientWithCredential(oboCredential, scope);
881
+
882
+ * // In node: AppCredential
883
+ * const appAuthConfig: AppCredentialAuthConfig = {
884
+ * authorityHost: "xxx",
885
+ * clientId: "xxx",
886
+ * tenantId: "xxx",
887
+ * clientSecret: "xxx",
888
+ * };
889
+ * const appCredential = new AppCredential(appAuthConfig);
890
+ * const scope = "User.Read";
891
+ * const client = createMicrosoftGraphClientWithCredential(appCredential, scope);
892
+ *
893
+ * const profile = await client.api("/me").get();
894
+ * ```
895
+ *
896
+ * @param {TokenCredential} credential - Used to provide configuration and auth.
897
+ * @param scopes - The array of Microsoft Token scope of access. Default value is `[.default]`.
898
+ *
899
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
900
+ *
901
+ * @returns Graph client with specified scopes.
902
+ */
903
+ function createMicrosoftGraphClientWithCredential(credential, scopes) {
904
+ internalLogger.info("Create Microsoft Graph Client");
905
+ const authProvider = new MsGraphAuthProvider(credential, scopes);
906
+ const graphClient = Client.initWithMiddleware({
907
+ authProvider,
908
+ });
909
+ return graphClient;
875
910
  }
876
911
 
877
912
  // Copyright (c) Microsoft Corporation.
@@ -883,6 +918,8 @@ const defaultSQLScope = "https://database.windows.net/";
883
918
  /**
884
919
  * Generate connection configuration consumed by tedious.
885
920
  *
921
+ * @deprecated we recommend you compose your own Tedious configuration for better flexibility.
922
+ *
886
923
  * @param {TeamsFx} teamsfx - Used to provide configuration and auth
887
924
  * @param { string? } databaseName - specify database name to override default one if there are multiple databases.
888
925
  *
@@ -891,8 +928,6 @@ const defaultSQLScope = "https://database.windows.net/";
891
928
  * @throws {@link ErrorCode|InvalidConfiguration} when SQL config resource configuration is invalid.
892
929
  * @throws {@link ErrorCode|InternalError} when get user MSI token failed or MSI token is invalid.
893
930
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
894
- *
895
- * @beta
896
931
  */
897
932
  async function getTediousConnectionConfig(teamsfx, databaseName) {
898
933
  internalLogger.info("Get SQL configuration");
@@ -1047,8 +1082,6 @@ var TediousAuthenticationType;
1047
1082
  // Licensed under the MIT license.
1048
1083
  /**
1049
1084
  * Identity type to use in authentication.
1050
- *
1051
- * @beta
1052
1085
  */
1053
1086
  var IdentityType;
1054
1087
  (function (IdentityType) {
@@ -1066,8 +1099,6 @@ var IdentityType;
1066
1099
  const invokeResponseType = "invokeResponse";
1067
1100
  /**
1068
1101
  * Response body returned for a token exchange invoke activity.
1069
- *
1070
- * @beta
1071
1102
  */
1072
1103
  class TokenExchangeInvokeResponse {
1073
1104
  constructor(id, failureDetail) {
@@ -1122,28 +1153,22 @@ class TokenExchangeInvokeResponse {
1122
1153
  * }
1123
1154
  * ]));
1124
1155
  * ```
1125
- *
1126
- * @beta
1127
1156
  */
1128
1157
  class TeamsBotSsoPrompt extends Dialog {
1129
- /**
1130
- * Constructor of TeamsBotSsoPrompt.
1131
- *
1132
- * @param {TeamsFx} teamsfx - Used to provide configuration and auth
1133
- * @param dialogId Unique ID of the dialog within its parent `DialogSet` or `ComponentDialog`.
1134
- * @param settings Settings used to configure the prompt.
1135
- *
1136
- * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
1137
- * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1138
- *
1139
- * @beta
1140
- */
1141
- constructor(teamsfx, dialogId, settings) {
1142
- super(dialogId);
1143
- this.teamsfx = teamsfx;
1144
- this.settings = settings;
1145
- validateScopesType(settings.scopes);
1146
- this.loadAndValidateConfig();
1158
+ constructor(authConfig, ...args) {
1159
+ super(arguments.length === 3 ? args[0] : args[1]);
1160
+ if (authConfig.getCredential) {
1161
+ const teamsfx = authConfig;
1162
+ this.authConfig = this.loadAndValidateConfig(teamsfx);
1163
+ this.initiateLoginEndpoint = teamsfx.getConfig("initiateLoginEndpoint");
1164
+ this.settings = args[1];
1165
+ }
1166
+ else {
1167
+ this.initiateLoginEndpoint = args[0];
1168
+ this.authConfig = authConfig;
1169
+ this.settings = args[2];
1170
+ }
1171
+ validateScopesType(this.settings.scopes);
1147
1172
  internalLogger.info("Create a new Teams Bot SSO Prompt");
1148
1173
  }
1149
1174
  /**
@@ -1159,8 +1184,6 @@ class TeamsBotSsoPrompt extends Dialog {
1159
1184
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1160
1185
  *
1161
1186
  * @returns A `Promise` representing the asynchronous operation.
1162
- *
1163
- * @beta
1164
1187
  */
1165
1188
  async beginDialog(dc) {
1166
1189
  var _a;
@@ -1208,8 +1231,6 @@ class TeamsBotSsoPrompt extends Dialog {
1208
1231
  *
1209
1232
  * @throws {@link ErrorCode|ChannelNotSupported} when bot channel is not MS Teams.
1210
1233
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1211
- *
1212
- * @beta
1213
1234
  */
1214
1235
  async continueDialog(dc) {
1215
1236
  var _a;
@@ -1244,30 +1265,45 @@ class TeamsBotSsoPrompt extends Dialog {
1244
1265
  return Dialog.EndOfTurn;
1245
1266
  }
1246
1267
  }
1247
- loadAndValidateConfig() {
1248
- if (this.teamsfx.getIdentityType() !== IdentityType.User) {
1249
- const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.teamsfx.getIdentityType().toString(), "TeamsBotSsoPrompt");
1268
+ loadAndValidateConfig(teamsfx) {
1269
+ if (teamsfx.getIdentityType() !== IdentityType.User) {
1270
+ const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, teamsfx.getIdentityType().toString(), "TeamsBotSsoPrompt");
1250
1271
  internalLogger.error(errorMsg);
1251
1272
  throw new ErrorWithCode(errorMsg, ErrorCode.IdentityTypeNotSupported);
1252
1273
  }
1253
1274
  const missingConfigurations = [];
1254
- if (!this.teamsfx.hasConfig("initiateLoginEndpoint")) {
1275
+ if (!teamsfx.hasConfig("initiateLoginEndpoint")) {
1255
1276
  missingConfigurations.push("initiateLoginEndpoint");
1256
1277
  }
1257
- if (!this.teamsfx.hasConfig("clientId")) {
1278
+ if (!teamsfx.hasConfig("clientId")) {
1258
1279
  missingConfigurations.push("clientId");
1259
1280
  }
1260
- if (!this.teamsfx.hasConfig("tenantId")) {
1281
+ if (!teamsfx.hasConfig("tenantId")) {
1261
1282
  missingConfigurations.push("tenantId");
1262
1283
  }
1263
- if (!this.teamsfx.hasConfig("applicationIdUri")) {
1264
- missingConfigurations.push("applicationIdUri");
1265
- }
1266
1284
  if (missingConfigurations.length != 0) {
1267
1285
  const errorMsg = formatString(ErrorMessage.InvalidConfiguration, missingConfigurations.join(", "), "undefined");
1268
1286
  internalLogger.error(errorMsg);
1269
1287
  throw new ErrorWithCode(errorMsg, ErrorCode.InvalidConfiguration);
1270
1288
  }
1289
+ let authConfig;
1290
+ if (teamsfx.getConfig("clientSecret")) {
1291
+ authConfig = {
1292
+ authorityHost: teamsfx.getConfig("authorityHost"),
1293
+ clientId: teamsfx.getConfig("clientId"),
1294
+ tenantId: teamsfx.getConfig("tenantId"),
1295
+ clientSecret: teamsfx.getConfig("clientSecret"),
1296
+ };
1297
+ }
1298
+ else {
1299
+ authConfig = {
1300
+ authorityHost: teamsfx.getConfig("authorityHost"),
1301
+ clientId: teamsfx.getConfig("clientId"),
1302
+ tenantId: teamsfx.getConfig("tenantId"),
1303
+ certificateContent: teamsfx.getConfig("certificateContent"),
1304
+ };
1305
+ }
1306
+ return authConfig;
1271
1307
  }
1272
1308
  /**
1273
1309
  * Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
@@ -1309,13 +1345,11 @@ class TeamsBotSsoPrompt extends Dialog {
1309
1345
  */
1310
1346
  getSignInResource(loginHint) {
1311
1347
  internalLogger.verbose("Get sign in authentication configuration");
1312
- const signInLink = `${this.teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${this.teamsfx.getConfig("clientId")}&tenantId=${this.teamsfx.getConfig("tenantId")}&loginHint=${loginHint}`;
1348
+ const signInLink = `${this.initiateLoginEndpoint}?scope=${encodeURI(this.settings.scopes.join(" "))}&clientId=${this.authConfig.clientId}&tenantId=${this.authConfig.tenantId}&loginHint=${loginHint}`;
1313
1349
  internalLogger.verbose("Sign in link: " + signInLink);
1314
1350
  const tokenExchangeResource = {
1315
1351
  id: v4(),
1316
- uri: this.teamsfx.getConfig("applicationIdUri").replace(/\/$/, "") + "/access_as_user",
1317
1352
  };
1318
- internalLogger.verbose("Token exchange resource uri: " + tokenExchangeResource.uri);
1319
1353
  return {
1320
1354
  signInLink: signInLink,
1321
1355
  tokenExchangeResource: tokenExchangeResource,
@@ -1337,8 +1371,7 @@ class TeamsBotSsoPrompt extends Dialog {
1337
1371
  }
1338
1372
  else {
1339
1373
  const ssoToken = context.activity.value.token;
1340
- this.teamsfx.setSsoToken(ssoToken);
1341
- const credential = this.teamsfx.getCredential();
1374
+ const credential = new OnBehalfOfUserCredential(ssoToken, this.authConfig);
1342
1375
  let exchangedToken;
1343
1376
  try {
1344
1377
  exchangedToken = await credential.getToken(this.settings.scopes);
@@ -1414,8 +1447,6 @@ class TeamsBotSsoPrompt extends Dialog {
1414
1447
  * ```typescript
1415
1448
  * const client = createApiClient("https://my-api-endpoint-base-url", new BasicAuthProvider("xxx","xxx"));
1416
1449
  * ```
1417
- *
1418
- * @beta
1419
1450
  */
1420
1451
  function createApiClient(apiEndpoint, authProvider) {
1421
1452
  // Add a request interceptor
@@ -1431,14 +1462,10 @@ function createApiClient(apiEndpoint, authProvider) {
1431
1462
  // Copyright (c) Microsoft Corporation.
1432
1463
  /**
1433
1464
  * Provider that handles Bearer Token authentication
1434
- *
1435
- * @beta
1436
1465
  */
1437
1466
  class BearerTokenAuthProvider {
1438
1467
  /**
1439
1468
  * @param { () => Promise<string> } getToken - Function that returns the content of bearer token used in http request
1440
- *
1441
- * @beta
1442
1469
  */
1443
1470
  constructor(getToken) {
1444
1471
  this.getToken = getToken;
@@ -1452,8 +1479,6 @@ class BearerTokenAuthProvider {
1452
1479
  * @returns Updated axios request config.
1453
1480
  *
1454
1481
  * @throws {@link ErrorCode|AuthorizationInfoAlreadyExists} - when Authorization header already exists in request configuration.
1455
- *
1456
- * @beta
1457
1482
  */
1458
1483
  async AddAuthenticationInfo(config) {
1459
1484
  const token = await this.getToken();
@@ -1471,8 +1496,6 @@ class BearerTokenAuthProvider {
1471
1496
  // Copyright (c) Microsoft Corporation.
1472
1497
  /**
1473
1498
  * Provider that handles Basic authentication
1474
- *
1475
- * @beta
1476
1499
  */
1477
1500
  class BasicAuthProvider {
1478
1501
  /**
@@ -1482,8 +1505,6 @@ class BasicAuthProvider {
1482
1505
  *
1483
1506
  * @throws {@link ErrorCode|InvalidParameter} - when username or password is empty.
1484
1507
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1485
- *
1486
- * @beta
1487
1508
  */
1488
1509
  constructor(userName, password) {
1489
1510
  if (!userName) {
@@ -1505,8 +1526,6 @@ class BasicAuthProvider {
1505
1526
  *
1506
1527
  * @throws {@link ErrorCode|AuthorizationInfoAlreadyExists} - when Authorization header or auth property already exists in request configuration.
1507
1528
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1508
- *
1509
- * @beta
1510
1529
  */
1511
1530
  async AddAuthenticationInfo(config) {
1512
1531
  if (config.headers && config.headers["Authorization"]) {
@@ -1526,8 +1545,6 @@ class BasicAuthProvider {
1526
1545
  // Copyright (c) Microsoft Corporation.
1527
1546
  /**
1528
1547
  * Provider that handles API Key authentication
1529
- *
1530
- * @beta
1531
1548
  */
1532
1549
  class ApiKeyProvider {
1533
1550
  /**
@@ -1538,8 +1555,6 @@ class ApiKeyProvider {
1538
1555
  *
1539
1556
  * @throws {@link ErrorCode|InvalidParameter} - when key name or key value is empty.
1540
1557
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1541
- *
1542
- * @beta
1543
1558
  */
1544
1559
  constructor(keyName, keyValue, keyLocation) {
1545
1560
  if (!keyName) {
@@ -1562,8 +1577,6 @@ class ApiKeyProvider {
1562
1577
  *
1563
1578
  * @throws {@link ErrorCode|AuthorizationInfoAlreadyExists} - when API key already exists in request header or url query parameter.
1564
1579
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is browser.
1565
- *
1566
- * @beta
1567
1580
  */
1568
1581
  async AddAuthenticationInfo(config) {
1569
1582
  switch (this.keyLocation) {
@@ -1580,8 +1593,12 @@ class ApiKeyProvider {
1580
1593
  if (!config.params) {
1581
1594
  config.params = {};
1582
1595
  }
1583
- const url = new URL(config.url, config.baseURL);
1584
- if (config.params[this.keyName] || url.searchParams.has(this.keyName)) {
1596
+ let urlHasDefinedApiKey = false;
1597
+ if (config.url) {
1598
+ const url = new URL(config.url, config.baseURL);
1599
+ urlHasDefinedApiKey = url.searchParams.has(this.keyName);
1600
+ }
1601
+ if (config.params[this.keyName] || urlHasDefinedApiKey) {
1585
1602
  throw new ErrorWithCode(formatString(ErrorMessage.DuplicateApiKeyInQueryParam, this.keyName), ErrorCode.AuthorizationInfoAlreadyExists);
1586
1603
  }
1587
1604
  config.params[this.keyName] = this.keyValue;
@@ -1592,8 +1609,6 @@ class ApiKeyProvider {
1592
1609
  }
1593
1610
  /**
1594
1611
  * Define available location for API Key location
1595
- *
1596
- * @beta
1597
1612
  */
1598
1613
  var ApiKeyLocation;
1599
1614
  (function (ApiKeyLocation) {
@@ -1610,8 +1625,6 @@ var ApiKeyLocation;
1610
1625
  // Copyright (c) Microsoft Corporation.
1611
1626
  /**
1612
1627
  * Provider that handles Certificate authentication
1613
- *
1614
- * @beta
1615
1628
  */
1616
1629
  class CertificateAuthProvider {
1617
1630
  /**
@@ -1619,8 +1632,6 @@ class CertificateAuthProvider {
1619
1632
  * @param { SecureContextOptions } certOption - information about the cert used in http requests
1620
1633
  *
1621
1634
  * @throws {@link ErrorCode|InvalidParameter} - when cert option is empty.
1622
- *
1623
- * @beta
1624
1635
  */
1625
1636
  constructor(certOption) {
1626
1637
  if (certOption && Object.keys(certOption).length !== 0) {
@@ -1639,8 +1650,6 @@ class CertificateAuthProvider {
1639
1650
  * @returns Updated axios request config.
1640
1651
  *
1641
1652
  * @throws {@link ErrorCode|InvalidParameter} - when custom httpsAgent in the request has duplicate properties with certOption provided in constructor.
1642
- *
1643
- * @beta
1644
1653
  */
1645
1654
  async AddAuthenticationInfo(config) {
1646
1655
  if (!config.httpsAgent) {
@@ -1724,7 +1733,6 @@ const ReservedKey = new Set([
1724
1733
  ]);
1725
1734
  /**
1726
1735
  * A class providing credential and configuration.
1727
- * @beta
1728
1736
  */
1729
1737
  class TeamsFx {
1730
1738
  /**
@@ -1734,16 +1742,15 @@ class TeamsFx {
1734
1742
  * @param customConfig - key/value pairs of customized configuration that overrides default ones.
1735
1743
  *
1736
1744
  * @throws {@link ErrorCode|IdentityTypeNotSupported} when setting app identity in browser.
1737
- *
1738
- * @beta
1739
1745
  */
1740
1746
  constructor(identityType, customConfig) {
1741
1747
  this.identityType = identityType !== null && identityType !== void 0 ? identityType : IdentityType.User;
1742
1748
  this.configuration = new Map();
1743
1749
  this.loadFromEnv();
1744
1750
  if (customConfig) {
1745
- for (const key of Object.keys(customConfig)) {
1746
- const value = customConfig[key];
1751
+ const myConfig = Object.assign({}, customConfig);
1752
+ for (const key of Object.keys(myConfig)) {
1753
+ const value = myConfig[key];
1747
1754
  if (value) {
1748
1755
  this.configuration.set(key, value);
1749
1756
  }
@@ -1754,7 +1761,6 @@ class TeamsFx {
1754
1761
  * Identity type set by user.
1755
1762
  *
1756
1763
  * @returns identity type.
1757
- * @beta
1758
1764
  */
1759
1765
  getIdentityType() {
1760
1766
  return this.identityType;
@@ -1767,7 +1773,6 @@ class TeamsFx {
1767
1773
  * identity is chose, will return {@link AppCredential}.
1768
1774
  *
1769
1775
  * @returns instance implements TokenCredential interface.
1770
- * @beta
1771
1776
  */
1772
1777
  getCredential() {
1773
1778
  if (this.identityType === IdentityType.User) {
@@ -1787,10 +1792,10 @@ class TeamsFx {
1787
1792
  }
1788
1793
  /**
1789
1794
  * Get user information.
1795
+ * @param {string[]} resources - The optional list of resources for full trust Teams apps.
1790
1796
  * @returns UserInfo object.
1791
- * @beta
1792
1797
  */
1793
- async getUserInfo() {
1798
+ async getUserInfo(resources) {
1794
1799
  if (this.identityType !== IdentityType.User) {
1795
1800
  const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported, this.identityType.toString(), "TeamsFx");
1796
1801
  internalLogger.error(errorMsg);
@@ -1812,22 +1817,20 @@ class TeamsFx {
1812
1817
  * await teamsfx.login("https://graph.microsoft.com/User.Read Calendars.Read"); // multiple scopes using string
1813
1818
  * ```
1814
1819
  * @param scopes - The list of scopes for which the token will have access, before that, we will request user to consent.
1820
+ * @param {string[]} resources - The optional list of resources for full trust Teams apps.
1815
1821
  *
1816
1822
  * @throws {@link ErrorCode|InternalError} when failed to login with unknown error.
1817
1823
  * @throws {@link ErrorCode|ConsentFailed} when user canceled or failed to consent.
1818
1824
  * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
1819
1825
  * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
1820
- *
1821
- * @beta
1822
1826
  */
1823
- async login(scopes) {
1827
+ async login(scopes, resources) {
1824
1828
  throw new ErrorWithCode(formatString(ErrorMessage.NodejsRuntimeNotSupported, "login"), ErrorCode.RuntimeNotSupported);
1825
1829
  }
1826
1830
  /**
1827
1831
  * Set SSO token when using user identity in NodeJS.
1828
1832
  * @param {string} ssoToken - used for on behalf of user flow.
1829
1833
  * @returns self instance.
1830
- * @beta
1831
1834
  */
1832
1835
  setSsoToken(ssoToken) {
1833
1836
  if (this.identityType !== IdentityType.User) {
@@ -1840,7 +1843,6 @@ class TeamsFx {
1840
1843
  * Usually used by service plugins to retrieve specific config
1841
1844
  * @param {string} key - configuration key.
1842
1845
  * @returns value in configuration.
1843
- * @beta
1844
1846
  */
1845
1847
  getConfig(key) {
1846
1848
  const value = this.configuration.get(key);
@@ -1855,7 +1857,6 @@ class TeamsFx {
1855
1857
  * Check the value of specific key.
1856
1858
  * @param {string} key - configuration key.
1857
1859
  * @returns true if corresponding value is not empty string.
1858
- * @beta
1859
1860
  */
1860
1861
  hasConfig(key) {
1861
1862
  const value = this.configuration.get(key);
@@ -1864,7 +1865,6 @@ class TeamsFx {
1864
1865
  /**
1865
1866
  * Get all configurations.
1866
1867
  * @returns key value mappings.
1867
- * @beta
1868
1868
  */
1869
1869
  getConfigs() {
1870
1870
  const config = {};
@@ -1906,162 +1906,379 @@ class TeamsFx {
1906
1906
  // Copyright (c) Microsoft Corporation.
1907
1907
  // Licensed under the MIT license.
1908
1908
  /**
1909
- * @internal
1909
+ * The target type where the notification will be sent to.
1910
+ *
1911
+ * @remarks
1912
+ * - "Channel" means to a team channel. (By default, notification to a team will be sent to its "General" channel.)
1913
+ * - "Group" means to a group chat.
1914
+ * - "Person" means to a personal chat.
1910
1915
  */
1911
- function cloneConversation(conversation) {
1912
- return JSON.parse(JSON.stringify(conversation));
1913
- }
1916
+ var NotificationTargetType;
1917
+ (function (NotificationTargetType) {
1918
+ /**
1919
+ * The notification will be sent to a team channel.
1920
+ * (By default, notification to a team will be sent to its "General" channel.)
1921
+ */
1922
+ NotificationTargetType["Channel"] = "Channel";
1923
+ /**
1924
+ * The notification will be sent to a group chat.
1925
+ */
1926
+ NotificationTargetType["Group"] = "Group";
1927
+ /**
1928
+ * The notification will be sent to a personal chat.
1929
+ */
1930
+ NotificationTargetType["Person"] = "Person";
1931
+ })(NotificationTargetType || (NotificationTargetType = {}));
1932
+ /**
1933
+ * Options used to control how the response card will be sent to users.
1934
+ */
1935
+ var AdaptiveCardResponse;
1936
+ (function (AdaptiveCardResponse) {
1937
+ /**
1938
+ * The response card will be replaced the current one for the interactor who trigger the action.
1939
+ */
1940
+ AdaptiveCardResponse[AdaptiveCardResponse["ReplaceForInteractor"] = 0] = "ReplaceForInteractor";
1941
+ /**
1942
+ * The response card will be replaced the current one for all users in the chat.
1943
+ */
1944
+ AdaptiveCardResponse[AdaptiveCardResponse["ReplaceForAll"] = 1] = "ReplaceForAll";
1945
+ /**
1946
+ * The response card will be sent as a new message for all users in the chat.
1947
+ */
1948
+ AdaptiveCardResponse[AdaptiveCardResponse["NewForAll"] = 2] = "NewForAll";
1949
+ })(AdaptiveCardResponse || (AdaptiveCardResponse = {}));
1950
+ /**
1951
+ * Status code for an `application/vnd.microsoft.error` invoke response.
1952
+ */
1953
+ var InvokeResponseErrorCode;
1954
+ (function (InvokeResponseErrorCode) {
1955
+ /**
1956
+ * Invalid request.
1957
+ */
1958
+ InvokeResponseErrorCode[InvokeResponseErrorCode["BadRequest"] = 400] = "BadRequest";
1959
+ /**
1960
+ * Internal server error.
1961
+ */
1962
+ InvokeResponseErrorCode[InvokeResponseErrorCode["InternalServerError"] = 500] = "InternalServerError";
1963
+ })(InvokeResponseErrorCode || (InvokeResponseErrorCode = {}));
1964
+
1914
1965
  /**
1966
+ * Available response type for an adaptive card invoke response.
1915
1967
  * @internal
1916
1968
  */
1917
- function getTargetType(conversationReference) {
1918
- var _a;
1919
- const conversationType = (_a = conversationReference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType;
1920
- if (conversationType === "personal") {
1921
- return "Person";
1969
+ var InvokeResponseType;
1970
+ (function (InvokeResponseType) {
1971
+ InvokeResponseType["AdaptiveCard"] = "application/vnd.microsoft.card.adaptive";
1972
+ InvokeResponseType["Message"] = "application/vnd.microsoft.activity.message";
1973
+ InvokeResponseType["Error"] = "application/vnd.microsoft.error";
1974
+ })(InvokeResponseType || (InvokeResponseType = {}));
1975
+ /**
1976
+ * Provides methods for formatting various invoke responses a bot can send to respond to an invoke request.
1977
+ *
1978
+ * @remarks
1979
+ * All of these functions return an {@link InvokeResponse} object, which can be
1980
+ * passed as input to generate a new `invokeResponse` activity.
1981
+ *
1982
+ * This example sends an invoke response that contains an adaptive card.
1983
+ *
1984
+ * ```typescript
1985
+ *
1986
+ * const myCard = {
1987
+ * type: "AdaptiveCard",
1988
+ * body: [
1989
+ * {
1990
+ * "type": "TextBlock",
1991
+ * "text": "This is a sample card"
1992
+ * }],
1993
+ * $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
1994
+ * version: "1.4"
1995
+ * };
1996
+ *
1997
+ * const invokeResponse = InvokeResponseFactory.adaptiveCard(myCard);
1998
+ * await context.sendActivity({
1999
+ * type: ActivityTypes.InvokeResponse,
2000
+ * value: invokeResponse,
2001
+ * });
2002
+ * ```
2003
+ */
2004
+ class InvokeResponseFactory {
2005
+ /**
2006
+ * Create an invoke response from a text message.
2007
+ * The type of the invoke response is `application/vnd.microsoft.activity.message`
2008
+ * indicates the request was successfully processed.
2009
+ *
2010
+ * @param message A text message included in a invoke response.
2011
+ *
2012
+ * @returns {InvokeResponse} An InvokeResponse object.
2013
+ */
2014
+ static textMessage(message) {
2015
+ if (!message) {
2016
+ throw new Error("The text message cannot be null or empty");
2017
+ }
2018
+ return {
2019
+ status: StatusCodes.OK,
2020
+ body: {
2021
+ statusCode: StatusCodes.OK,
2022
+ type: InvokeResponseType.Message,
2023
+ value: message,
2024
+ },
2025
+ };
1922
2026
  }
1923
- else if (conversationType === "groupChat") {
1924
- return "Group";
2027
+ /**
2028
+ * Create an invoke response from an adaptive card.
2029
+ *
2030
+ * The type of the invoke response is `application/vnd.microsoft.card.adaptive` indicates
2031
+ * the request was successfully processed, and the response includes an adaptive card
2032
+ * that the client should display in place of the current one.
2033
+ *
2034
+ * @param card The adaptive card JSON payload.
2035
+ *
2036
+ * @returns {InvokeResponse} An InvokeResponse object.
2037
+ */
2038
+ static adaptiveCard(card) {
2039
+ if (!card) {
2040
+ throw new Error("The adaptive card content cannot be null or undefined");
2041
+ }
2042
+ return {
2043
+ status: StatusCodes.OK,
2044
+ body: {
2045
+ statusCode: StatusCodes.OK,
2046
+ type: InvokeResponseType.AdaptiveCard,
2047
+ value: card,
2048
+ },
2049
+ };
1925
2050
  }
1926
- else if (conversationType === "channel") {
1927
- return "Channel";
2051
+ /**
2052
+ * Create an invoke response with error code and message.
2053
+ *
2054
+ * The type of the invoke response is `application/vnd.microsoft.error` indicates
2055
+ * the request was failed to processed.
2056
+ *
2057
+ * @param errorCode The status code indicates error, available values:
2058
+ * - 400 (BadRequest): indicate the incoming request was invalid.
2059
+ * - 500 (InternalServerError): indicate an unexpected error occurred.
2060
+ * @param errorMessage The error message.
2061
+ *
2062
+ * @returns {InvokeResponse} An InvokeResponse object.
2063
+ */
2064
+ static errorResponse(errorCode, errorMessage) {
2065
+ return {
2066
+ status: StatusCodes.OK,
2067
+ body: {
2068
+ statusCode: errorCode,
2069
+ type: InvokeResponseType.Error,
2070
+ value: {
2071
+ code: errorCode.toString(),
2072
+ message: errorMessage,
2073
+ },
2074
+ },
2075
+ };
1928
2076
  }
1929
- else {
1930
- return undefined;
2077
+ /**
2078
+ * Create an invoke response with status code and response value.
2079
+ * @param statusCode The status code.
2080
+ * @param body The value of the response body.
2081
+ *
2082
+ * @returns {InvokeResponse} An InvokeResponse object.
2083
+ */
2084
+ static createInvokeResponse(statusCode, body) {
2085
+ return {
2086
+ status: statusCode,
2087
+ body: body,
2088
+ };
1931
2089
  }
1932
- }
2090
+ }
2091
+
1933
2092
  /**
1934
2093
  * @internal
1935
2094
  */
1936
- function getTeamsBotInstallationId(context) {
1937
- var _a, _b, _c, _d;
1938
- 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;
2095
+ class CardActionMiddleware {
2096
+ constructor(handlers) {
2097
+ this.actionHandlers = [];
2098
+ this.defaultMessage = "Your response was sent to the app";
2099
+ if (handlers && handlers.length > 0) {
2100
+ this.actionHandlers.push(...handlers);
2101
+ }
2102
+ }
2103
+ async onTurn(context, next) {
2104
+ var _a, _b, _c;
2105
+ if (context.activity.name === "adaptiveCard/action") {
2106
+ const action = context.activity.value.action;
2107
+ const actionVerb = action.verb;
2108
+ for (const handler of this.actionHandlers) {
2109
+ if (((_a = handler.triggerVerb) === null || _a === void 0 ? void 0 : _a.toLowerCase()) === (actionVerb === null || actionVerb === void 0 ? void 0 : actionVerb.toLowerCase())) {
2110
+ let response;
2111
+ try {
2112
+ response = await handler.handleActionInvoked(context, action.data);
2113
+ }
2114
+ catch (error) {
2115
+ const errorResponse = InvokeResponseFactory.errorResponse(InvokeResponseErrorCode.InternalServerError, error.message);
2116
+ await this.sendInvokeResponse(context, errorResponse);
2117
+ throw error;
2118
+ }
2119
+ const responseType = (_b = response.body) === null || _b === void 0 ? void 0 : _b.type;
2120
+ switch (responseType) {
2121
+ case InvokeResponseType.AdaptiveCard:
2122
+ const card = (_c = response.body) === null || _c === void 0 ? void 0 : _c.value;
2123
+ if (!card) {
2124
+ const errorMessage = "Adaptive card content cannot be found in the response body";
2125
+ await this.sendInvokeResponse(context, InvokeResponseFactory.errorResponse(InvokeResponseErrorCode.InternalServerError, errorMessage));
2126
+ throw new Error(errorMessage);
2127
+ }
2128
+ if (card.refresh && handler.adaptiveCardResponse !== AdaptiveCardResponse.NewForAll) {
2129
+ // Card won't be refreshed with AdaptiveCardResponse.ReplaceForInteractor.
2130
+ // So set to AdaptiveCardResponse.ReplaceForAll here.
2131
+ handler.adaptiveCardResponse = AdaptiveCardResponse.ReplaceForAll;
2132
+ }
2133
+ const activity = MessageFactory.attachment(CardFactory.adaptiveCard(card));
2134
+ if (handler.adaptiveCardResponse === AdaptiveCardResponse.NewForAll) {
2135
+ await this.sendInvokeResponse(context, InvokeResponseFactory.textMessage(this.defaultMessage));
2136
+ await context.sendActivity(activity);
2137
+ }
2138
+ else if (handler.adaptiveCardResponse === AdaptiveCardResponse.ReplaceForAll) {
2139
+ activity.id = context.activity.replyToId;
2140
+ await context.updateActivity(activity);
2141
+ await this.sendInvokeResponse(context, response);
2142
+ }
2143
+ else {
2144
+ await this.sendInvokeResponse(context, response);
2145
+ }
2146
+ break;
2147
+ case InvokeResponseType.Message:
2148
+ case InvokeResponseType.Error:
2149
+ default:
2150
+ await this.sendInvokeResponse(context, response);
2151
+ break;
2152
+ }
2153
+ break;
2154
+ }
2155
+ }
2156
+ }
2157
+ await next();
2158
+ }
2159
+ async sendInvokeResponse(context, response) {
2160
+ await context.sendActivity({
2161
+ type: ActivityTypes.InvokeResponse,
2162
+ value: response,
2163
+ });
2164
+ }
2165
+ }
2166
+
2167
+ /**
2168
+ * A card action bot to respond to adaptive card universal actions.
2169
+ */
2170
+ class CardActionBot {
2171
+ /**
2172
+ * Creates a new instance of the `CardActionBot`.
2173
+ *
2174
+ * @param adapter The bound `BotFrameworkAdapter`.
2175
+ * @param options - initialize options
2176
+ */
2177
+ constructor(adapter, options) {
2178
+ this.middleware = new CardActionMiddleware(options === null || options === void 0 ? void 0 : options.actions);
2179
+ this.adapter = adapter.use(this.middleware);
2180
+ }
2181
+ /**
2182
+ * Registers a card action handler to the bot.
2183
+ * @param actionHandler A card action handler to be registered.
2184
+ */
2185
+ registerHandler(actionHandler) {
2186
+ if (actionHandler) {
2187
+ this.middleware.actionHandlers.push(actionHandler);
2188
+ }
2189
+ }
2190
+ /**
2191
+ * Registers card action handlers to the bot.
2192
+ * @param actionHandlers A set of card action handlers to be registered.
2193
+ */
2194
+ registerHandlers(actionHandlers) {
2195
+ if (actionHandlers) {
2196
+ this.middleware.actionHandlers.push(...actionHandlers);
2197
+ }
2198
+ }
1939
2199
  }
1940
2200
 
1941
2201
  // Copyright (c) Microsoft Corporation.
1942
2202
  /**
1943
2203
  * @internal
1944
2204
  */
1945
- var ActivityType;
1946
- (function (ActivityType) {
1947
- ActivityType[ActivityType["CurrentBotInstalled"] = 0] = "CurrentBotInstalled";
1948
- ActivityType[ActivityType["CurrentBotMessaged"] = 1] = "CurrentBotMessaged";
1949
- ActivityType[ActivityType["CurrentBotUninstalled"] = 2] = "CurrentBotUninstalled";
1950
- ActivityType[ActivityType["TeamDeleted"] = 3] = "TeamDeleted";
1951
- ActivityType[ActivityType["TeamRestored"] = 4] = "TeamRestored";
1952
- ActivityType[ActivityType["Unknown"] = 5] = "Unknown";
1953
- })(ActivityType || (ActivityType = {}));
1954
- /**
1955
- * @internal
1956
- */
1957
- class NotificationMiddleware {
1958
- constructor(options) {
1959
- this.conversationReferenceStore = options.conversationReferenceStore;
1960
- }
1961
- async onTurn(context, next) {
1962
- const type = this.classifyActivity(context.activity);
1963
- switch (type) {
1964
- case ActivityType.CurrentBotInstalled:
1965
- case ActivityType.TeamRestored: {
1966
- const reference = TurnContext.getConversationReference(context.activity);
1967
- await this.conversationReferenceStore.set(reference);
1968
- break;
1969
- }
1970
- case ActivityType.CurrentBotMessaged: {
1971
- await this.tryAddMessagedReference(context);
1972
- break;
1973
- }
1974
- case ActivityType.CurrentBotUninstalled:
1975
- case ActivityType.TeamDeleted: {
1976
- const reference = TurnContext.getConversationReference(context.activity);
1977
- await this.conversationReferenceStore.delete(reference);
1978
- break;
1979
- }
1980
- }
1981
- await next();
1982
- }
1983
- classifyActivity(activity) {
1984
- var _a, _b;
1985
- const activityType = activity.type;
1986
- if (activityType === "installationUpdate") {
1987
- const action = (_a = activity.action) === null || _a === void 0 ? void 0 : _a.toLowerCase();
1988
- if (action === "add") {
1989
- return ActivityType.CurrentBotInstalled;
1990
- }
1991
- else {
1992
- return ActivityType.CurrentBotUninstalled;
1993
- }
1994
- }
1995
- else if (activityType === "conversationUpdate") {
1996
- const eventType = (_b = activity.channelData) === null || _b === void 0 ? void 0 : _b.eventType;
1997
- if (eventType === "teamDeleted") {
1998
- return ActivityType.TeamDeleted;
1999
- }
2000
- else if (eventType === "teamRestored") {
2001
- return ActivityType.TeamRestored;
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
2205
  class CommandResponseMiddleware {
2031
- constructor(handlers) {
2206
+ constructor(handlers, ssoHandlers, activityHandler) {
2032
2207
  this.commandHandlers = [];
2033
- if (handlers && handlers.length > 0) {
2034
- this.commandHandlers.push(...handlers);
2208
+ this.ssoCommandHandlers = [];
2209
+ handlers = handlers !== null && handlers !== void 0 ? handlers : [];
2210
+ ssoHandlers = ssoHandlers !== null && ssoHandlers !== void 0 ? ssoHandlers : [];
2211
+ this.hasSsoCommand = ssoHandlers.length > 0;
2212
+ this.ssoActivityHandler = activityHandler;
2213
+ if (this.hasSsoCommand && !this.ssoActivityHandler) {
2214
+ internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
2215
+ throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
2035
2216
  }
2217
+ this.commandHandlers.push(...handlers);
2218
+ for (const ssoHandler of ssoHandlers) {
2219
+ this.addSsoCommand(ssoHandler);
2220
+ }
2221
+ }
2222
+ addSsoCommand(ssoHandler) {
2223
+ var _a;
2224
+ (_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.addCommand(async (context, tokenResponse, message) => {
2225
+ const matchResult = this.shouldTrigger(ssoHandler.triggerPatterns, message.text);
2226
+ message.matches = Array.isArray(matchResult) ? matchResult : void 0;
2227
+ const response = await ssoHandler.handleCommandReceived(context, message, tokenResponse);
2228
+ await this.processResponse(context, response);
2229
+ }, ssoHandler.triggerPatterns);
2230
+ this.ssoCommandHandlers.push(ssoHandler);
2231
+ this.hasSsoCommand = true;
2036
2232
  }
2037
2233
  async onTurn(context, next) {
2234
+ var _a, _b;
2038
2235
  if (context.activity.type === ActivityTypes.Message) {
2039
2236
  // Invoke corresponding command handler for the command response
2040
2237
  const commandText = this.getActivityText(context.activity);
2041
- const message = {
2042
- text: commandText,
2043
- };
2238
+ let alreadyProcessed = false;
2044
2239
  for (const handler of this.commandHandlers) {
2045
2240
  const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
2046
2241
  // It is important to note that the command bot will stop processing handlers
2047
2242
  // when the first command handler is matched.
2048
2243
  if (!!matchResult) {
2244
+ const message = {
2245
+ text: commandText,
2246
+ };
2049
2247
  message.matches = Array.isArray(matchResult) ? matchResult : void 0;
2050
2248
  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
- }
2249
+ await this.processResponse(context, response);
2250
+ alreadyProcessed = true;
2251
+ break;
2252
+ }
2253
+ }
2254
+ if (!alreadyProcessed) {
2255
+ for (const handler of this.ssoCommandHandlers) {
2256
+ const matchResult = this.shouldTrigger(handler.triggerPatterns, commandText);
2257
+ if (!!matchResult) {
2258
+ await ((_a = this.ssoActivityHandler) === null || _a === void 0 ? void 0 : _a.run(context));
2259
+ break;
2059
2260
  }
2060
2261
  }
2061
2262
  }
2062
2263
  }
2264
+ else {
2265
+ if (this.hasSsoCommand) {
2266
+ await ((_b = this.ssoActivityHandler) === null || _b === void 0 ? void 0 : _b.run(context));
2267
+ }
2268
+ }
2063
2269
  await next();
2064
2270
  }
2271
+ async processResponse(context, response) {
2272
+ if (typeof response === "string") {
2273
+ await context.sendActivity(response);
2274
+ }
2275
+ else {
2276
+ const replyActivity = response;
2277
+ if (replyActivity) {
2278
+ await context.sendActivity(replyActivity);
2279
+ }
2280
+ }
2281
+ }
2065
2282
  matchPattern(pattern, text) {
2066
2283
  if (text) {
2067
2284
  if (typeof pattern === "string") {
@@ -2103,8 +2320,6 @@ class CommandResponseMiddleware {
2103
2320
  *
2104
2321
  * @remarks
2105
2322
  * 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
2323
  */
2109
2324
  class CommandBot {
2110
2325
  /**
@@ -2112,19 +2327,16 @@ class CommandBot {
2112
2327
  *
2113
2328
  * @param adapter The bound `BotFrameworkAdapter`.
2114
2329
  * @param options - initialize options
2115
- *
2116
- * @beta
2117
2330
  */
2118
- constructor(adapter, options) {
2119
- this.middleware = new CommandResponseMiddleware(options === null || options === void 0 ? void 0 : options.commands);
2331
+ constructor(adapter, options, ssoCommandActivityHandler, ssoConfig) {
2332
+ this.ssoConfig = ssoConfig;
2333
+ this.middleware = new CommandResponseMiddleware(options === null || options === void 0 ? void 0 : options.commands, options === null || options === void 0 ? void 0 : options.ssoCommands, ssoCommandActivityHandler);
2120
2334
  this.adapter = adapter.use(this.middleware);
2121
2335
  }
2122
2336
  /**
2123
2337
  * Registers a command into the command bot.
2124
2338
  *
2125
- * @param command The command to registered.
2126
- *
2127
- * @beta
2339
+ * @param command The command to register.
2128
2340
  */
2129
2341
  registerCommand(command) {
2130
2342
  if (command) {
@@ -2134,15 +2346,174 @@ class CommandBot {
2134
2346
  /**
2135
2347
  * Registers commands into the command bot.
2136
2348
  *
2137
- * @param commands The command to registered.
2138
- *
2139
- * @beta
2349
+ * @param commands The commands to register.
2140
2350
  */
2141
2351
  registerCommands(commands) {
2142
2352
  if (commands) {
2143
2353
  this.middleware.commandHandlers.push(...commands);
2144
2354
  }
2145
2355
  }
2356
+ /**
2357
+ * Registers a sso command into the command bot.
2358
+ *
2359
+ * @param command The command to register.
2360
+ */
2361
+ registerSsoCommand(ssoCommand) {
2362
+ this.validateSsoActivityHandler();
2363
+ this.middleware.addSsoCommand(ssoCommand);
2364
+ }
2365
+ /**
2366
+ * Registers commands into the command bot.
2367
+ *
2368
+ * @param commands The commands to register.
2369
+ */
2370
+ registerSsoCommands(ssoCommands) {
2371
+ if (ssoCommands.length > 0) {
2372
+ this.validateSsoActivityHandler();
2373
+ for (const ssoCommand of ssoCommands) {
2374
+ this.middleware.addSsoCommand(ssoCommand);
2375
+ }
2376
+ }
2377
+ }
2378
+ validateSsoActivityHandler() {
2379
+ if (!this.middleware.ssoActivityHandler) {
2380
+ internalLogger.error(ErrorMessage.SsoActivityHandlerIsNull);
2381
+ throw new ErrorWithCode(ErrorMessage.SsoActivityHandlerIsNull, ErrorCode.SsoActivityHandlerIsUndefined);
2382
+ }
2383
+ }
2384
+ }
2385
+
2386
+ // Copyright (c) Microsoft Corporation.
2387
+ /**
2388
+ * @internal
2389
+ */
2390
+ function cloneConversation(conversation) {
2391
+ return JSON.parse(JSON.stringify(conversation));
2392
+ }
2393
+ /**
2394
+ * @internal
2395
+ */
2396
+ function getTargetType(conversationReference) {
2397
+ var _a;
2398
+ const conversationType = (_a = conversationReference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType;
2399
+ if (conversationType === "personal") {
2400
+ return NotificationTargetType.Person;
2401
+ }
2402
+ else if (conversationType === "groupChat") {
2403
+ return NotificationTargetType.Group;
2404
+ }
2405
+ else if (conversationType === "channel") {
2406
+ return NotificationTargetType.Channel;
2407
+ }
2408
+ else {
2409
+ return undefined;
2410
+ }
2411
+ }
2412
+ /**
2413
+ * @internal
2414
+ */
2415
+ function getTeamsBotInstallationId(context) {
2416
+ var _a, _b, _c;
2417
+ const teamId = (_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;
2418
+ if (teamId) {
2419
+ return teamId;
2420
+ }
2421
+ // Fallback to use conversation id.
2422
+ // the conversation id is equal to team id only when the bot app is installed into the General channel.
2423
+ if (context.activity.conversation.name === undefined) {
2424
+ return context.activity.conversation.id;
2425
+ }
2426
+ return undefined;
2427
+ }
2428
+
2429
+ // Copyright (c) Microsoft Corporation.
2430
+ /**
2431
+ * @internal
2432
+ */
2433
+ var ActivityType;
2434
+ (function (ActivityType) {
2435
+ ActivityType[ActivityType["CurrentBotInstalled"] = 0] = "CurrentBotInstalled";
2436
+ ActivityType[ActivityType["CurrentBotMessaged"] = 1] = "CurrentBotMessaged";
2437
+ ActivityType[ActivityType["CurrentBotUninstalled"] = 2] = "CurrentBotUninstalled";
2438
+ ActivityType[ActivityType["TeamDeleted"] = 3] = "TeamDeleted";
2439
+ ActivityType[ActivityType["TeamRestored"] = 4] = "TeamRestored";
2440
+ ActivityType[ActivityType["Unknown"] = 5] = "Unknown";
2441
+ })(ActivityType || (ActivityType = {}));
2442
+ /**
2443
+ * @internal
2444
+ */
2445
+ class NotificationMiddleware {
2446
+ constructor(options) {
2447
+ this.conversationReferenceStore = options.conversationReferenceStore;
2448
+ }
2449
+ async onTurn(context, next) {
2450
+ const type = this.classifyActivity(context.activity);
2451
+ switch (type) {
2452
+ case ActivityType.CurrentBotInstalled:
2453
+ case ActivityType.TeamRestored: {
2454
+ const reference = TurnContext.getConversationReference(context.activity);
2455
+ await this.conversationReferenceStore.set(reference);
2456
+ break;
2457
+ }
2458
+ case ActivityType.CurrentBotMessaged: {
2459
+ await this.tryAddMessagedReference(context);
2460
+ break;
2461
+ }
2462
+ case ActivityType.CurrentBotUninstalled:
2463
+ case ActivityType.TeamDeleted: {
2464
+ const reference = TurnContext.getConversationReference(context.activity);
2465
+ await this.conversationReferenceStore.delete(reference);
2466
+ break;
2467
+ }
2468
+ }
2469
+ await next();
2470
+ }
2471
+ classifyActivity(activity) {
2472
+ var _a, _b;
2473
+ const activityType = activity.type;
2474
+ if (activityType === "installationUpdate") {
2475
+ const action = (_a = activity.action) === null || _a === void 0 ? void 0 : _a.toLowerCase();
2476
+ if (action === "add") {
2477
+ return ActivityType.CurrentBotInstalled;
2478
+ }
2479
+ else {
2480
+ return ActivityType.CurrentBotUninstalled;
2481
+ }
2482
+ }
2483
+ else if (activityType === "conversationUpdate") {
2484
+ const eventType = (_b = activity.channelData) === null || _b === void 0 ? void 0 : _b.eventType;
2485
+ if (eventType === "teamDeleted") {
2486
+ return ActivityType.TeamDeleted;
2487
+ }
2488
+ else if (eventType === "teamRestored") {
2489
+ return ActivityType.TeamRestored;
2490
+ }
2491
+ }
2492
+ else if (activityType === "message") {
2493
+ return ActivityType.CurrentBotMessaged;
2494
+ }
2495
+ return ActivityType.Unknown;
2496
+ }
2497
+ async tryAddMessagedReference(context) {
2498
+ var _a, _b, _c, _d;
2499
+ const reference = TurnContext.getConversationReference(context.activity);
2500
+ const conversationType = (_a = reference === null || reference === void 0 ? void 0 : reference.conversation) === null || _a === void 0 ? void 0 : _a.conversationType;
2501
+ if (conversationType === "personal" || conversationType === "groupChat") {
2502
+ if (!(await this.conversationReferenceStore.check(reference))) {
2503
+ await this.conversationReferenceStore.set(reference);
2504
+ }
2505
+ }
2506
+ else if (conversationType === "channel") {
2507
+ 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;
2508
+ if (teamId !== undefined) {
2509
+ const teamReference = cloneConversation(reference);
2510
+ teamReference.conversation.id = teamId;
2511
+ if (!(await this.conversationReferenceStore.check(teamReference))) {
2512
+ await this.conversationReferenceStore.set(teamReference);
2513
+ }
2514
+ }
2515
+ }
2516
+ }
2146
2517
  }
2147
2518
 
2148
2519
  // Copyright (c) Microsoft Corporation.
@@ -2270,32 +2641,30 @@ class ConversationReferenceStore {
2270
2641
  *
2271
2642
  * @param target - the notification target.
2272
2643
  * @param text - the plain text message.
2273
- * @returns A `Promise` representing the asynchronous operation.
2274
- *
2275
- * @beta
2644
+ * @param onError - an optional error handler that can catch exceptions during message sending.
2645
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2646
+ * @returns the response of sending message.
2276
2647
  */
2277
- function sendMessage(target, text) {
2278
- return target.sendMessage(text);
2648
+ function sendMessage(target, text, onError) {
2649
+ return target.sendMessage(text, onError);
2279
2650
  }
2280
2651
  /**
2281
2652
  * Send an adaptive card message to a notification target.
2282
2653
  *
2283
2654
  * @param target - the notification target.
2284
2655
  * @param card - the adaptive card raw JSON.
2285
- * @returns A `Promise` representing the asynchronous operation.
2286
- *
2287
- * @beta
2656
+ * @param onError - an optional error handler that can catch exceptions during adaptive card sending.
2657
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2658
+ * @returns the response of sending adaptive card message.
2288
2659
  */
2289
- function sendAdaptiveCard(target, card) {
2290
- return target.sendAdaptiveCard(card);
2660
+ function sendAdaptiveCard(target, card, onError) {
2661
+ return target.sendAdaptiveCard(card, onError);
2291
2662
  }
2292
2663
  /**
2293
2664
  * A {@link NotificationTarget} that represents a team channel.
2294
2665
  *
2295
2666
  * @remarks
2296
2667
  * It's recommended to get channels from {@link TeamsBotInstallation.channels()}.
2297
- *
2298
- * @beta
2299
2668
  */
2300
2669
  class Channel {
2301
2670
  /**
@@ -2306,16 +2675,12 @@ class Channel {
2306
2675
  *
2307
2676
  * @param parent - The parent {@link TeamsBotInstallation} where this channel is created from.
2308
2677
  * @param info - Detailed channel information.
2309
- *
2310
- * @beta
2311
2678
  */
2312
2679
  constructor(parent, info) {
2313
2680
  /**
2314
2681
  * Notification target type. For channel it's always "Channel".
2315
- *
2316
- * @beta
2317
2682
  */
2318
- this.type = "Channel";
2683
+ this.type = NotificationTargetType.Channel;
2319
2684
  this.parent = parent;
2320
2685
  this.info = info;
2321
2686
  }
@@ -2323,35 +2688,61 @@ class Channel {
2323
2688
  * Send a plain text message.
2324
2689
  *
2325
2690
  * @param text - the plain text message.
2326
- * @returns A `Promise` representing the asynchronous operation.
2327
- *
2328
- * @beta
2691
+ * @param onError - an optional error handler that can catch exceptions during message sending.
2692
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2693
+ * @returns the response of sending message.
2329
2694
  */
2330
- sendMessage(text) {
2331
- return this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2695
+ async sendMessage(text, onError) {
2696
+ const response = {};
2697
+ await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2332
2698
  const conversation = await this.newConversation(context);
2333
2699
  await this.parent.adapter.continueConversation(conversation, async (ctx) => {
2334
- await ctx.sendActivity(text);
2700
+ try {
2701
+ const res = await ctx.sendActivity(text);
2702
+ response.id = res === null || res === void 0 ? void 0 : res.id;
2703
+ }
2704
+ catch (error) {
2705
+ if (onError) {
2706
+ await onError(ctx, error);
2707
+ }
2708
+ else {
2709
+ throw error;
2710
+ }
2711
+ }
2335
2712
  });
2336
2713
  });
2714
+ return response;
2337
2715
  }
2338
2716
  /**
2339
2717
  * Send an adaptive card message.
2340
2718
  *
2341
2719
  * @param card - the adaptive card raw JSON.
2342
- * @returns A `Promise` representing the asynchronous operation.
2343
- *
2344
- * @beta
2720
+ * @param onError - an optional error handler that can catch exceptions during adaptive card sending.
2721
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2722
+ * @returns the response of sending adaptive card message.
2345
2723
  */
2346
- async sendAdaptiveCard(card) {
2347
- return this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2724
+ async sendAdaptiveCard(card, onError) {
2725
+ const response = {};
2726
+ await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2348
2727
  const conversation = await this.newConversation(context);
2349
2728
  await this.parent.adapter.continueConversation(conversation, async (ctx) => {
2350
- await ctx.sendActivity({
2351
- attachments: [CardFactory.adaptiveCard(card)],
2352
- });
2729
+ try {
2730
+ const res = await ctx.sendActivity({
2731
+ attachments: [CardFactory.adaptiveCard(card)],
2732
+ });
2733
+ response.id = res === null || res === void 0 ? void 0 : res.id;
2734
+ }
2735
+ catch (error) {
2736
+ if (onError) {
2737
+ await onError(ctx, error);
2738
+ }
2739
+ else {
2740
+ throw error;
2741
+ }
2742
+ }
2353
2743
  });
2354
2744
  });
2745
+ return response;
2355
2746
  }
2356
2747
  /**
2357
2748
  * @internal
@@ -2368,8 +2759,6 @@ class Channel {
2368
2759
  *
2369
2760
  * @remarks
2370
2761
  * It's recommended to get members from {@link TeamsBotInstallation.members()}.
2371
- *
2372
- * @beta
2373
2762
  */
2374
2763
  class Member {
2375
2764
  /**
@@ -2380,16 +2769,12 @@ class Member {
2380
2769
  *
2381
2770
  * @param parent - The parent {@link TeamsBotInstallation} where this member is created from.
2382
2771
  * @param account - Detailed member account information.
2383
- *
2384
- * @beta
2385
2772
  */
2386
2773
  constructor(parent, account) {
2387
2774
  /**
2388
2775
  * Notification target type. For member it's always "Person".
2389
- *
2390
- * @beta
2391
2776
  */
2392
- this.type = "Person";
2777
+ this.type = NotificationTargetType.Person;
2393
2778
  this.parent = parent;
2394
2779
  this.account = account;
2395
2780
  }
@@ -2397,216 +2782,732 @@ class Member {
2397
2782
  * Send a plain text message.
2398
2783
  *
2399
2784
  * @param text - the plain text message.
2400
- * @returns A `Promise` representing the asynchronous operation.
2785
+ * @param onError - an optional error handler that can catch exceptions during message sending.
2786
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2787
+ * @returns the response of sending message.
2788
+ */
2789
+ async sendMessage(text, onError) {
2790
+ const response = {};
2791
+ await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2792
+ const conversation = await this.newConversation(context);
2793
+ await this.parent.adapter.continueConversation(conversation, async (ctx) => {
2794
+ try {
2795
+ const res = await ctx.sendActivity(text);
2796
+ response.id = res === null || res === void 0 ? void 0 : res.id;
2797
+ }
2798
+ catch (error) {
2799
+ if (onError) {
2800
+ await onError(ctx, error);
2801
+ }
2802
+ else {
2803
+ throw error;
2804
+ }
2805
+ }
2806
+ });
2807
+ });
2808
+ return response;
2809
+ }
2810
+ /**
2811
+ * Send an adaptive card message.
2401
2812
  *
2402
- * @beta
2813
+ * @param card - the adaptive card raw JSON.
2814
+ * @param onError - an optional error handler that can catch exceptions during adaptive card sending.
2815
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2816
+ * @returns the response of sending adaptive card message.
2403
2817
  */
2404
- sendMessage(text) {
2405
- return this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2818
+ async sendAdaptiveCard(card, onError) {
2819
+ const response = {};
2820
+ await this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2406
2821
  const conversation = await this.newConversation(context);
2407
2822
  await this.parent.adapter.continueConversation(conversation, async (ctx) => {
2408
- await ctx.sendActivity(text);
2823
+ try {
2824
+ const res = await ctx.sendActivity({
2825
+ attachments: [CardFactory.adaptiveCard(card)],
2826
+ });
2827
+ response.id = res === null || res === void 0 ? void 0 : res.id;
2828
+ }
2829
+ catch (error) {
2830
+ if (onError) {
2831
+ await onError(ctx, error);
2832
+ }
2833
+ else {
2834
+ throw error;
2835
+ }
2836
+ }
2409
2837
  });
2410
2838
  });
2839
+ return response;
2840
+ }
2841
+ /**
2842
+ * @internal
2843
+ */
2844
+ async newConversation(context) {
2845
+ const reference = TurnContext.getConversationReference(context.activity);
2846
+ const personalConversation = cloneConversation(reference);
2847
+ const connectorClient = context.turnState.get(this.parent.adapter.ConnectorClientKey);
2848
+ const conversation = await connectorClient.conversations.createConversation({
2849
+ isGroup: false,
2850
+ tenantId: context.activity.conversation.tenantId,
2851
+ bot: context.activity.recipient,
2852
+ members: [this.account],
2853
+ channelData: {},
2854
+ });
2855
+ personalConversation.conversation.id = conversation.id;
2856
+ return personalConversation;
2857
+ }
2858
+ }
2859
+ /**
2860
+ * A {@link NotificationTarget} that represents a bot installation. Teams Bot could be installed into
2861
+ * - Personal chat
2862
+ * - Group chat
2863
+ * - Team (by default the `General` channel)
2864
+ *
2865
+ * @remarks
2866
+ * It's recommended to get bot installations from {@link ConversationBot.installations()}.
2867
+ */
2868
+ class TeamsBotInstallation {
2869
+ /**
2870
+ * Constructor
2871
+ *
2872
+ * @remarks
2873
+ * It's recommended to get bot installations from {@link ConversationBot.installations()}, instead of using this constructor.
2874
+ *
2875
+ * @param adapter - the bound `BotFrameworkAdapter`.
2876
+ * @param conversationReference - the bound `ConversationReference`.
2877
+ */
2878
+ constructor(adapter, conversationReference) {
2879
+ this.adapter = adapter;
2880
+ this.conversationReference = conversationReference;
2881
+ this.type = getTargetType(conversationReference);
2882
+ }
2883
+ /**
2884
+ * Send a plain text message.
2885
+ *
2886
+ * @param text - the plain text message.
2887
+ * @param onError - an optional error handler that can catch exceptions during message sending.
2888
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2889
+ * @returns the response of sending message.
2890
+ */
2891
+ async sendMessage(text, onError) {
2892
+ const response = {};
2893
+ await this.adapter.continueConversation(this.conversationReference, async (context) => {
2894
+ try {
2895
+ const res = await context.sendActivity(text);
2896
+ response.id = res === null || res === void 0 ? void 0 : res.id;
2897
+ }
2898
+ catch (error) {
2899
+ if (onError) {
2900
+ await onError(context, error);
2901
+ }
2902
+ else {
2903
+ throw error;
2904
+ }
2905
+ }
2906
+ });
2907
+ return response;
2908
+ }
2909
+ /**
2910
+ * Send an adaptive card message.
2911
+ *
2912
+ * @param card - the adaptive card raw JSON.
2913
+ * @param onError - an optional error handler that can catch exceptions during adaptive card sending.
2914
+ * If not defined, error will be handled by `BotAdapter.onTurnError`.
2915
+ * @returns the response of sending adaptive card message.
2916
+ */
2917
+ async sendAdaptiveCard(card, onError) {
2918
+ const response = {};
2919
+ await this.adapter.continueConversation(this.conversationReference, async (context) => {
2920
+ try {
2921
+ const res = await context.sendActivity({
2922
+ attachments: [CardFactory.adaptiveCard(card)],
2923
+ });
2924
+ response.id = res === null || res === void 0 ? void 0 : res.id;
2925
+ }
2926
+ catch (error) {
2927
+ if (onError) {
2928
+ await onError(context, error);
2929
+ }
2930
+ else {
2931
+ throw error;
2932
+ }
2933
+ }
2934
+ });
2935
+ return response;
2936
+ }
2937
+ /**
2938
+ * Get channels from this bot installation.
2939
+ *
2940
+ * @returns an array of channels if bot is installed into a team, otherwise returns an empty array.
2941
+ */
2942
+ async channels() {
2943
+ const channels = [];
2944
+ if (this.type !== NotificationTargetType.Channel) {
2945
+ return channels;
2946
+ }
2947
+ let teamsChannels = [];
2948
+ await this.adapter.continueConversation(this.conversationReference, async (context) => {
2949
+ const teamId = getTeamsBotInstallationId(context);
2950
+ if (teamId !== undefined) {
2951
+ teamsChannels = await TeamsInfo.getTeamChannels(context, teamId);
2952
+ }
2953
+ });
2954
+ for (const channel of teamsChannels) {
2955
+ channels.push(new Channel(this, channel));
2956
+ }
2957
+ return channels;
2958
+ }
2959
+ /**
2960
+ * Get members from this bot installation.
2961
+ *
2962
+ * @returns an array of members from where the bot is installed.
2963
+ */
2964
+ async members() {
2965
+ const members = [];
2966
+ await this.adapter.continueConversation(this.conversationReference, async (context) => {
2967
+ let continuationToken;
2968
+ do {
2969
+ const pagedMembers = await TeamsInfo.getPagedMembers(context, undefined, continuationToken);
2970
+ continuationToken = pagedMembers.continuationToken;
2971
+ for (const member of pagedMembers.members) {
2972
+ members.push(new Member(this, member));
2973
+ }
2974
+ } while (continuationToken !== undefined);
2975
+ });
2976
+ return members;
2977
+ }
2978
+ /**
2979
+ * Get team details from this bot installation
2980
+ *
2981
+ * @returns the team details if bot is installed into a team, otherwise returns undefined.
2982
+ */
2983
+ async getTeamDetails() {
2984
+ if (this.type !== NotificationTargetType.Channel) {
2985
+ return undefined;
2986
+ }
2987
+ let teamDetails;
2988
+ await this.adapter.continueConversation(this.conversationReference, async (context) => {
2989
+ const teamId = getTeamsBotInstallationId(context);
2990
+ if (teamId !== undefined) {
2991
+ teamDetails = await TeamsInfo.getTeamDetails(context, teamId);
2992
+ }
2993
+ });
2994
+ return teamDetails;
2995
+ }
2996
+ }
2997
+ /**
2998
+ * Provide utilities to send notification to varies targets (e.g., member, group, channel).
2999
+ */
3000
+ class NotificationBot {
3001
+ /**
3002
+ * constructor of the notification bot.
3003
+ *
3004
+ * @remarks
3005
+ * To ensure accuracy, it's recommended to initialize before handling any message.
3006
+ *
3007
+ * @param adapter - the bound `BotFrameworkAdapter`
3008
+ * @param options - initialize options
3009
+ */
3010
+ constructor(adapter, options) {
3011
+ var _a, _b;
3012
+ const storage = (_a = options === null || options === void 0 ? void 0 : options.storage) !== null && _a !== void 0 ? _a : new LocalFileStorage(path.resolve(process.env.RUNNING_ON_AZURE === "1" ? (_b = process.env.TEMP) !== null && _b !== void 0 ? _b : "./" : "./"));
3013
+ this.conversationReferenceStore = new ConversationReferenceStore(storage);
3014
+ this.adapter = adapter.use(new NotificationMiddleware({
3015
+ conversationReferenceStore: this.conversationReferenceStore,
3016
+ }));
3017
+ }
3018
+ /**
3019
+ * Get all targets where the bot is installed.
3020
+ *
3021
+ * @remarks
3022
+ * The result is retrieving from the persisted storage.
3023
+ *
3024
+ * @returns - an array of {@link TeamsBotInstallation}.
3025
+ */
3026
+ async installations() {
3027
+ if (this.conversationReferenceStore === undefined || this.adapter === undefined) {
3028
+ throw new Error("NotificationBot has not been initialized.");
3029
+ }
3030
+ const references = await this.conversationReferenceStore.getAll();
3031
+ const targets = [];
3032
+ for (const reference of references) {
3033
+ // validate connection
3034
+ let valid = true;
3035
+ await this.adapter.continueConversation(reference, async (context) => {
3036
+ try {
3037
+ // try get member to see if the installation is still valid
3038
+ await TeamsInfo.getPagedMembers(context, 1);
3039
+ }
3040
+ catch (error) {
3041
+ if (error.code === "BotNotInConversationRoster") {
3042
+ valid = false;
3043
+ }
3044
+ }
3045
+ });
3046
+ if (valid) {
3047
+ targets.push(new TeamsBotInstallation(this.adapter, reference));
3048
+ }
3049
+ else {
3050
+ await this.conversationReferenceStore.delete(reference);
3051
+ }
3052
+ }
3053
+ return targets;
3054
+ }
3055
+ /**
3056
+ * Returns the first {@link Member} where predicate is true, and undefined otherwise.
3057
+ *
3058
+ * @param predicate find calls predicate once for each member of the installation,
3059
+ * until it finds one where predicate returns true. If such a member is found, find
3060
+ * immediately returns that member. Otherwise, find returns undefined.
3061
+ * @param scope the scope to find members from the installations
3062
+ * (personal chat, group chat, Teams channel).
3063
+ * @returns the first {@link Member} where predicate is true, and undefined otherwise.
3064
+ */
3065
+ async findMember(predicate, scope) {
3066
+ for (const target of await this.installations()) {
3067
+ if (this.matchSearchScope(target, scope)) {
3068
+ for (const member of await target.members()) {
3069
+ if (await predicate(member)) {
3070
+ return member;
3071
+ }
3072
+ }
3073
+ }
3074
+ }
3075
+ return;
3076
+ }
3077
+ /**
3078
+ * Returns the first {@link Channel} where predicate is true, and undefined otherwise.
3079
+ * (Ensure the bot app is installed into the `General` channel, otherwise undefined will be returned.)
3080
+ *
3081
+ * @param predicate find calls predicate once for each channel of the installation,
3082
+ * until it finds one where predicate returns true. If such a channel is found, find
3083
+ * immediately returns that channel. Otherwise, find returns undefined.
3084
+ * @returns the first {@link Channel} where predicate is true, and undefined otherwise.
3085
+ */
3086
+ async findChannel(predicate) {
3087
+ for (const target of await this.installations()) {
3088
+ if (target.type === NotificationTargetType.Channel) {
3089
+ const teamDetails = await target.getTeamDetails();
3090
+ for (const channel of await target.channels()) {
3091
+ if (await predicate(channel, teamDetails)) {
3092
+ return channel;
3093
+ }
3094
+ }
3095
+ }
3096
+ }
3097
+ return;
3098
+ }
3099
+ /**
3100
+ * Returns all {@link Member} where predicate is true, and empty array otherwise.
3101
+ *
3102
+ * @param predicate find calls predicate for each member of the installation.
3103
+ * @param scope the scope to find members from the installations
3104
+ * (personal chat, group chat, Teams channel).
3105
+ * @returns an array of {@link Member} where predicate is true, and empty array otherwise.
3106
+ */
3107
+ async findAllMembers(predicate, scope) {
3108
+ const members = [];
3109
+ for (const target of await this.installations()) {
3110
+ if (this.matchSearchScope(target, scope)) {
3111
+ for (const member of await target.members()) {
3112
+ if (await predicate(member)) {
3113
+ members.push(member);
3114
+ }
3115
+ }
3116
+ }
3117
+ }
3118
+ return members;
3119
+ }
3120
+ /**
3121
+ * Returns all {@link Channel} where predicate is true, and empty array otherwise.
3122
+ * (Ensure the bot app is installed into the `General` channel, otherwise empty array will be returned.)
3123
+ *
3124
+ * @param predicate find calls predicate for each channel of the installation.
3125
+ * @returns an array of {@link Channel} where predicate is true, and empty array otherwise.
3126
+ */
3127
+ async findAllChannels(predicate) {
3128
+ const channels = [];
3129
+ for (const target of await this.installations()) {
3130
+ if (target.type === NotificationTargetType.Channel) {
3131
+ const teamDetails = await target.getTeamDetails();
3132
+ for (const channel of await target.channels()) {
3133
+ if (await predicate(channel, teamDetails)) {
3134
+ channels.push(channel);
3135
+ }
3136
+ }
3137
+ }
3138
+ }
3139
+ return channels;
3140
+ }
3141
+ matchSearchScope(target, scope) {
3142
+ scope = scope !== null && scope !== void 0 ? scope : SearchScope.All;
3143
+ return ((target.type === NotificationTargetType.Channel && (scope & SearchScope.Channel) !== 0) ||
3144
+ (target.type === NotificationTargetType.Group && (scope & SearchScope.Group) !== 0) ||
3145
+ (target.type === NotificationTargetType.Person && (scope & SearchScope.Person) !== 0));
3146
+ }
3147
+ }
3148
+ /**
3149
+ * The search scope when calling {@link NotificationBot.findMember} and {@link NotificationBot.findAllMembers}.
3150
+ * The search scope is a flagged enum and it can be combined with `|`.
3151
+ * For example, to search from personal chat and group chat, use `SearchScope.Person | SearchScope.Group`.
3152
+ */
3153
+ var SearchScope;
3154
+ (function (SearchScope) {
3155
+ /**
3156
+ * Search members from the installations in personal chat only.
3157
+ */
3158
+ SearchScope[SearchScope["Person"] = 1] = "Person";
3159
+ /**
3160
+ * Search members from the installations in group chat only.
3161
+ */
3162
+ SearchScope[SearchScope["Group"] = 2] = "Group";
3163
+ /**
3164
+ * Search members from the installations in Teams channel only.
3165
+ */
3166
+ SearchScope[SearchScope["Channel"] = 4] = "Channel";
3167
+ /**
3168
+ * Search members from all installations including personal chat, group chat and Teams channel.
3169
+ */
3170
+ SearchScope[SearchScope["All"] = 7] = "All";
3171
+ })(SearchScope || (SearchScope = {}));
3172
+
3173
+ // Copyright (c) Microsoft Corporation.
3174
+ let DIALOG_NAME = "BotSsoExecutionDialog";
3175
+ let TEAMS_SSO_PROMPT_ID = "TeamsFxSsoPrompt";
3176
+ let COMMAND_ROUTE_DIALOG = "CommandRouteDialog";
3177
+ /**
3178
+ * Sso execution dialog, use to handle sso command
3179
+ */
3180
+ class BotSsoExecutionDialog extends ComponentDialog {
3181
+ constructor(dedupStorage, ssoPromptSettings, authConfig, ...args) {
3182
+ var _a;
3183
+ super((_a = (authConfig.getCredential ? args[0] : args[1])) !== null && _a !== void 0 ? _a : DIALOG_NAME);
3184
+ this.dedupStorageKeys = [];
3185
+ // Map to store the commandId and triggerPatterns, key: commandId, value: triggerPatterns
3186
+ this.commandMapping = new Map();
3187
+ const dialogName = authConfig.getCredential ? args[0] : args[1];
3188
+ if (dialogName) {
3189
+ DIALOG_NAME = dialogName;
3190
+ TEAMS_SSO_PROMPT_ID = dialogName + TEAMS_SSO_PROMPT_ID;
3191
+ COMMAND_ROUTE_DIALOG = dialogName + COMMAND_ROUTE_DIALOG;
3192
+ }
3193
+ let ssoDialog;
3194
+ if (authConfig.getCredential) {
3195
+ ssoDialog = new TeamsBotSsoPrompt(authConfig, TEAMS_SSO_PROMPT_ID, ssoPromptSettings);
3196
+ }
3197
+ else {
3198
+ ssoDialog = new TeamsBotSsoPrompt(authConfig, args[0], TEAMS_SSO_PROMPT_ID, ssoPromptSettings);
3199
+ }
3200
+ this.addDialog(ssoDialog);
3201
+ this.initialDialogId = COMMAND_ROUTE_DIALOG;
3202
+ this.dedupStorage = dedupStorage;
3203
+ this.dedupStorageKeys = [];
3204
+ const commandRouteDialog = new WaterfallDialog(COMMAND_ROUTE_DIALOG, [
3205
+ this.commandRouteStep.bind(this),
3206
+ ]);
3207
+ this.addDialog(commandRouteDialog);
3208
+ }
3209
+ /**
3210
+ * Add TeamsFxBotSsoCommandHandler instance
3211
+ * @param handler {@link BotSsoExecutionDialogHandler} callback function
3212
+ * @param triggerPatterns The trigger pattern
3213
+ */
3214
+ addCommand(handler, triggerPatterns) {
3215
+ const commandId = this.getCommandHash(triggerPatterns);
3216
+ const dialog = new WaterfallDialog(commandId, [
3217
+ this.ssoStep.bind(this),
3218
+ this.dedupStep.bind(this),
3219
+ async (stepContext) => {
3220
+ const tokenResponse = stepContext.result.tokenResponse;
3221
+ const context = stepContext.context;
3222
+ const message = stepContext.result.message;
3223
+ try {
3224
+ if (tokenResponse) {
3225
+ await handler(context, tokenResponse, message);
3226
+ }
3227
+ else {
3228
+ throw new Error(ErrorMessage.FailedToRetrieveSsoToken);
3229
+ }
3230
+ return await stepContext.endDialog();
3231
+ }
3232
+ catch (error) {
3233
+ const errorMsg = formatString(ErrorMessage.FailedToProcessSsoHandler, error.message);
3234
+ internalLogger.error(errorMsg);
3235
+ return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToProcessSsoHandler));
3236
+ }
3237
+ },
3238
+ ]);
3239
+ this.commandMapping.set(commandId, triggerPatterns);
3240
+ this.addDialog(dialog);
3241
+ }
3242
+ getCommandHash(patterns) {
3243
+ const expressions = Array.isArray(patterns) ? patterns : [patterns];
3244
+ const patternStr = expressions.join();
3245
+ const patternStrWithoutSpecialChar = patternStr.replace(/[^a-zA-Z0-9]/g, "");
3246
+ const hash = createHash("sha256").update(patternStr).digest("hex").toLowerCase();
3247
+ return patternStrWithoutSpecialChar + hash;
3248
+ }
3249
+ /**
3250
+ * The run method handles the incoming activity (in the form of a DialogContext) and passes it through the dialog system.
3251
+ *
3252
+ * @param context The context object for the current turn.
3253
+ * @param accessor The instance of StatePropertyAccessor for dialog system.
3254
+ */
3255
+ async run(context, accessor) {
3256
+ const dialogSet = new DialogSet(accessor);
3257
+ dialogSet.add(this);
3258
+ const dialogContext = await dialogSet.createContext(context);
3259
+ this.ensureMsTeamsChannel(dialogContext);
3260
+ const results = await dialogContext.continueDialog();
3261
+ if (results && results.status === DialogTurnStatus.empty) {
3262
+ await dialogContext.beginDialog(this.id);
3263
+ }
3264
+ else if (results &&
3265
+ results.status === DialogTurnStatus.complete &&
3266
+ results.result instanceof Error) {
3267
+ throw results.result;
3268
+ }
3269
+ }
3270
+ getActivityText(activity) {
3271
+ let text = activity.text;
3272
+ const removedMentionText = TurnContext.removeRecipientMention(activity);
3273
+ if (removedMentionText) {
3274
+ text = removedMentionText
3275
+ .toLowerCase()
3276
+ .replace(/\n|\r\n/g, "")
3277
+ .trim();
3278
+ }
3279
+ return text;
3280
+ }
3281
+ async commandRouteStep(stepContext) {
3282
+ const turnContext = stepContext.context;
3283
+ const text = this.getActivityText(turnContext.activity);
3284
+ const commandId = this.getMatchesCommandId(text);
3285
+ if (commandId) {
3286
+ return await stepContext.beginDialog(commandId);
3287
+ }
3288
+ const errorMsg = formatString(ErrorMessage.CannotFindCommand, turnContext.activity.text);
3289
+ internalLogger.error(errorMsg);
3290
+ throw new ErrorWithCode(errorMsg, ErrorCode.CannotFindCommand);
3291
+ }
3292
+ async ssoStep(stepContext) {
3293
+ try {
3294
+ const turnContext = stepContext.context;
3295
+ const text = this.getActivityText(turnContext.activity);
3296
+ const message = {
3297
+ text,
3298
+ };
3299
+ stepContext.options.commandMessage = message;
3300
+ return await stepContext.beginDialog(TEAMS_SSO_PROMPT_ID);
3301
+ }
3302
+ catch (error) {
3303
+ const errorMsg = formatString(ErrorMessage.FailedToRunSsoStep, error.message);
3304
+ internalLogger.error(errorMsg);
3305
+ return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunSsoStep));
3306
+ }
3307
+ }
3308
+ async dedupStep(stepContext) {
3309
+ const tokenResponse = stepContext.result;
3310
+ if (!tokenResponse) {
3311
+ internalLogger.error(ErrorMessage.FailedToRetrieveSsoToken);
3312
+ return await stepContext.endDialog(new ErrorWithCode(ErrorMessage.FailedToRetrieveSsoToken, ErrorCode.FailedToRunSsoStep));
3313
+ }
3314
+ try {
3315
+ // Only dedup after ssoStep to make sure that all Teams client would receive the login request
3316
+ if (tokenResponse && (await this.shouldDedup(stepContext.context))) {
3317
+ return Dialog.EndOfTurn;
3318
+ }
3319
+ return await stepContext.next({
3320
+ tokenResponse,
3321
+ message: stepContext.options.commandMessage,
3322
+ });
3323
+ }
3324
+ catch (error) {
3325
+ const errorMsg = formatString(ErrorMessage.FailedToRunDedupStep, error.message);
3326
+ internalLogger.error(errorMsg);
3327
+ return await stepContext.endDialog(new ErrorWithCode(errorMsg, ErrorCode.FailedToRunDedupStep));
3328
+ }
3329
+ }
3330
+ /**
3331
+ * Called when the component is ending.
3332
+ *
3333
+ * @param context Context for the current turn of conversation.
3334
+ */
3335
+ async onEndDialog(context) {
3336
+ const conversationId = context.activity.conversation.id;
3337
+ const currentDedupKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) > 0);
3338
+ await this.dedupStorage.delete(currentDedupKeys);
3339
+ this.dedupStorageKeys = this.dedupStorageKeys.filter((key) => key.indexOf(conversationId) < 0);
3340
+ }
3341
+ /**
3342
+ * If a user is signed into multiple Teams clients, the Bot might receive a "signin/tokenExchange" from each client.
3343
+ * Each token exchange request for a specific user login will have an identical activity.value.Id.
3344
+ * Only one of these token exchange requests should be processed by the bot. For a distributed bot in production,
3345
+ * this requires a distributed storage to ensure only one token exchange is processed.
3346
+ * @param context Context for the current turn of conversation.
3347
+ * @returns boolean value indicate whether the message should be removed
3348
+ */
3349
+ async shouldDedup(context) {
3350
+ const storeItem = {
3351
+ eTag: context.activity.value.id,
3352
+ };
3353
+ const key = this.getStorageKey(context);
3354
+ const storeItems = { [key]: storeItem };
3355
+ try {
3356
+ await this.dedupStorage.write(storeItems);
3357
+ this.dedupStorageKeys.push(key);
3358
+ }
3359
+ catch (err) {
3360
+ if (err instanceof Error && err.message.indexOf("eTag conflict")) {
3361
+ return true;
3362
+ }
3363
+ throw err;
3364
+ }
3365
+ return false;
3366
+ }
3367
+ getStorageKey(context) {
3368
+ if (!context || !context.activity || !context.activity.conversation) {
3369
+ throw new Error("Invalid context, can not get storage key!");
3370
+ }
3371
+ const activity = context.activity;
3372
+ const channelId = activity.channelId;
3373
+ const conversationId = activity.conversation.id;
3374
+ if (activity.type !== ActivityTypes.Invoke || activity.name !== tokenExchangeOperationName) {
3375
+ throw new Error("TokenExchangeState can only be used with Invokes of signin/tokenExchange.");
3376
+ }
3377
+ const value = activity.value;
3378
+ if (!value || !value.id) {
3379
+ throw new Error("Invalid signin/tokenExchange. Missing activity.value.id.");
3380
+ }
3381
+ return `${channelId}/${conversationId}/${value.id}`;
3382
+ }
3383
+ matchPattern(pattern, text) {
3384
+ if (text) {
3385
+ if (typeof pattern === "string") {
3386
+ const regExp = new RegExp(pattern, "i");
3387
+ return regExp.test(text);
3388
+ }
3389
+ if (pattern instanceof RegExp) {
3390
+ const matches = text.match(pattern);
3391
+ return matches !== null && matches !== void 0 ? matches : false;
3392
+ }
3393
+ }
3394
+ return false;
3395
+ }
3396
+ isPatternMatched(patterns, text) {
3397
+ const expressions = Array.isArray(patterns) ? patterns : [patterns];
3398
+ for (const ex of expressions) {
3399
+ const matches = this.matchPattern(ex, text);
3400
+ return !!matches;
3401
+ }
3402
+ return false;
2411
3403
  }
2412
- /**
2413
- * Send an adaptive card message.
2414
- *
2415
- * @param card - the adaptive card raw JSON.
2416
- * @returns A `Promise` representing the asynchronous operation.
2417
- *
2418
- * @beta
2419
- */
2420
- async sendAdaptiveCard(card) {
2421
- return this.parent.adapter.continueConversation(this.parent.conversationReference, async (context) => {
2422
- const conversation = await this.newConversation(context);
2423
- await this.parent.adapter.continueConversation(conversation, async (ctx) => {
2424
- await ctx.sendActivity({
2425
- attachments: [CardFactory.adaptiveCard(card)],
2426
- });
2427
- });
2428
- });
3404
+ getMatchesCommandId(text) {
3405
+ for (const command of this.commandMapping) {
3406
+ const pattern = command[1];
3407
+ if (this.isPatternMatched(pattern, text)) {
3408
+ return command[0];
3409
+ }
3410
+ }
3411
+ return undefined;
2429
3412
  }
2430
3413
  /**
3414
+ * Ensure bot is running in MS Teams since TeamsBotSsoPrompt is only supported in MS Teams channel.
3415
+ * @param dc dialog context
3416
+ * @throws {@link ErrorCode|ChannelNotSupported} if bot channel is not MS Teams
2431
3417
  * @internal
2432
3418
  */
2433
- async newConversation(context) {
2434
- const reference = TurnContext.getConversationReference(context.activity);
2435
- const personalConversation = cloneConversation(reference);
2436
- const connectorClient = context.turnState.get(this.parent.adapter.ConnectorClientKey);
2437
- const conversation = await connectorClient.conversations.createConversation({
2438
- isGroup: false,
2439
- tenantId: context.activity.conversation.tenantId,
2440
- bot: context.activity.recipient,
2441
- members: [this.account],
2442
- channelData: {},
2443
- });
2444
- personalConversation.conversation.id = conversation.id;
2445
- return personalConversation;
3419
+ ensureMsTeamsChannel(dc) {
3420
+ if (dc.context.activity.channelId != Channels.Msteams) {
3421
+ const errorMsg = formatString(ErrorMessage.OnlyMSTeamsChannelSupported, "SSO execution dialog");
3422
+ internalLogger.error(errorMsg);
3423
+ throw new ErrorWithCode(errorMsg, ErrorCode.ChannelNotSupported);
3424
+ }
2446
3425
  }
2447
- }
3426
+ }
3427
+
3428
+ // Copyright (c) Microsoft Corporation.
2448
3429
  /**
2449
- * A {@link NotificationTarget} that represents a bot installation. Teams Bot could be installed into
2450
- * - Personal chat
2451
- * - Group chat
2452
- * - Team (by default the `General` channel)
2453
- *
2454
- * @remarks
2455
- * It's recommended to get bot installations from {@link ConversationBot.installations()}.
2456
- *
2457
- * @beta
3430
+ * Default SSO execution activity handler
2458
3431
  */
2459
- class TeamsBotInstallation {
3432
+ class DefaultBotSsoExecutionActivityHandler extends TeamsActivityHandler {
2460
3433
  /**
2461
- * Constructor
3434
+ * Creates a new instance of the DefaultBotSsoExecutionActivityHandler.
3435
+ * @param ssoConfig configuration for SSO command bot
2462
3436
  *
2463
3437
  * @remarks
2464
- * It's recommended to get bot installations from {@link ConversationBot.installations()}, instead of using this constructor.
2465
- *
2466
- * @param adapter - the bound `BotFrameworkAdapter`.
2467
- * @param conversationReference - the bound `ConversationReference`.
2468
- *
2469
- * @beta
2470
- */
2471
- constructor(adapter, conversationReference) {
2472
- this.adapter = adapter;
2473
- this.conversationReference = conversationReference;
2474
- this.type = getTargetType(conversationReference);
2475
- }
2476
- /**
2477
- * Send a plain text message.
2478
- *
2479
- * @param text - the plain text message.
2480
- * @returns A `Promise` representing the asynchronous operation.
2481
- *
2482
- * @beta
2483
- */
2484
- sendMessage(text) {
2485
- return this.adapter.continueConversation(this.conversationReference, async (context) => {
2486
- await context.sendActivity(text);
3438
+ * In the constructor, it uses BotSsoConfig parameter which from {@link ConversationBot} options to initialize {@link BotSsoExecutionDialog}.
3439
+ * It also need to register an event handler for the message event which trigger {@link BotSsoExecutionDialog} instance.
3440
+ */
3441
+ constructor(ssoConfig) {
3442
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
3443
+ super();
3444
+ const memoryStorage = new MemoryStorage();
3445
+ const userState = (_b = (_a = ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.userState) !== null && _b !== void 0 ? _b : new UserState(memoryStorage);
3446
+ const conversationState = (_d = (_c = ssoConfig.dialog) === null || _c === void 0 ? void 0 : _c.conversationState) !== null && _d !== void 0 ? _d : new ConversationState(memoryStorage);
3447
+ const dedupStorage = (_f = (_e = ssoConfig.dialog) === null || _e === void 0 ? void 0 : _e.dedupStorage) !== null && _f !== void 0 ? _f : memoryStorage;
3448
+ const _l = ssoConfig.aad, { scopes } = _l, customConfig = __rest(_l, ["scopes"]);
3449
+ const settings = {
3450
+ scopes: scopes,
3451
+ timeout: (_h = (_g = ssoConfig.dialog) === null || _g === void 0 ? void 0 : _g.ssoPromptConfig) === null || _h === void 0 ? void 0 : _h.timeout,
3452
+ endOnInvalidMessage: (_k = (_j = ssoConfig.dialog) === null || _j === void 0 ? void 0 : _j.ssoPromptConfig) === null || _k === void 0 ? void 0 : _k.endOnInvalidMessage,
3453
+ };
3454
+ const teamsfx = new TeamsFx(IdentityType.User, Object.assign({}, customConfig));
3455
+ this.ssoExecutionDialog = new BotSsoExecutionDialog(dedupStorage, settings, teamsfx);
3456
+ this.conversationState = conversationState;
3457
+ this.dialogState = conversationState.createProperty("DialogState");
3458
+ this.userState = userState;
3459
+ this.onMessage(async (context, next) => {
3460
+ await this.ssoExecutionDialog.run(context, this.dialogState);
3461
+ await next();
2487
3462
  });
2488
3463
  }
2489
3464
  /**
2490
- * Send an adaptive card message.
2491
- *
2492
- * @param card - the adaptive card raw JSON.
2493
- * @returns A `Promise` representing the asynchronous operation.
3465
+ * Add TeamsFxBotSsoCommandHandler instance to SSO execution dialog
3466
+ * @param handler {@link BotSsoExecutionDialogHandler} callback function
3467
+ * @param triggerPatterns The trigger pattern
2494
3468
  *
2495
- * @beta
3469
+ * @remarks
3470
+ * This function is used to add SSO command to {@link BotSsoExecutionDialog} instance.
2496
3471
  */
2497
- sendAdaptiveCard(card) {
2498
- return this.adapter.continueConversation(this.conversationReference, async (context) => {
2499
- await context.sendActivity({
2500
- attachments: [CardFactory.adaptiveCard(card)],
2501
- });
2502
- });
3472
+ addCommand(handler, triggerPatterns) {
3473
+ this.ssoExecutionDialog.addCommand(handler, triggerPatterns);
2503
3474
  }
2504
3475
  /**
2505
- * Get channels from this bot installation.
2506
- *
2507
- * @returns an array of channels if bot is installed into a team, otherwise returns an empty array.
2508
- *
2509
- * @beta
3476
+ * Called to initiate the event emission process.
3477
+ * @param context The context object for the current turn.
2510
3478
  */
2511
- async channels() {
2512
- let teamsChannels = [];
2513
- await this.adapter.continueConversation(this.conversationReference, async (context) => {
2514
- const teamId = getTeamsBotInstallationId(context);
2515
- if (teamId !== undefined) {
2516
- teamsChannels = await TeamsInfo.getTeamChannels(context, teamId);
2517
- }
2518
- });
2519
- const channels = [];
2520
- for (const channel of teamsChannels) {
2521
- channels.push(new Channel(this, channel));
3479
+ async run(context) {
3480
+ try {
3481
+ await super.run(context);
3482
+ }
3483
+ finally {
3484
+ await this.conversationState.saveChanges(context, false);
3485
+ await this.userState.saveChanges(context, false);
2522
3486
  }
2523
- return channels;
2524
- }
2525
- /**
2526
- * Get members from this bot installation.
2527
- *
2528
- * @returns an array of members from where the bot is installed.
2529
- *
2530
- * @beta
2531
- */
2532
- async members() {
2533
- const members = [];
2534
- await this.adapter.continueConversation(this.conversationReference, async (context) => {
2535
- let continuationToken;
2536
- do {
2537
- const pagedMembers = await TeamsInfo.getPagedMembers(context, undefined, continuationToken);
2538
- continuationToken = pagedMembers.continuationToken;
2539
- for (const member of pagedMembers.members) {
2540
- members.push(new Member(this, member));
2541
- }
2542
- } while (continuationToken !== undefined);
2543
- });
2544
- return members;
2545
3487
  }
2546
- }
2547
- /**
2548
- * Provide utilities to send notification to varies targets (e.g., member, group, channel).
2549
- *
2550
- * @beta
2551
- */
2552
- class NotificationBot {
2553
3488
  /**
2554
- * constructor of the notification bot.
3489
+ * Receives invoke activities with Activity name of 'signin/verifyState'.
3490
+ * @param context A context object for this turn.
3491
+ * @param query Signin state (part of signin action auth flow) verification invoke query.
3492
+ * @returns A promise that represents the work queued.
2555
3493
  *
2556
3494
  * @remarks
2557
- * To ensure accuracy, it's recommended to initialize before handling any message.
2558
- *
2559
- * @param adapter - the bound `BotFrameworkAdapter`
2560
- * @param options - initialize options
2561
- *
2562
- * @beta
3495
+ * It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
2563
3496
  */
2564
- constructor(adapter, options) {
2565
- var _a, _b;
2566
- const storage = (_a = options === null || options === void 0 ? void 0 : options.storage) !== null && _a !== void 0 ? _a : new LocalFileStorage(path.resolve(process.env.RUNNING_ON_AZURE === "1" ? (_b = process.env.TEMP) !== null && _b !== void 0 ? _b : "./" : "./"));
2567
- this.conversationReferenceStore = new ConversationReferenceStore(storage);
2568
- this.adapter = adapter.use(new NotificationMiddleware({
2569
- conversationReferenceStore: this.conversationReferenceStore,
2570
- }));
3497
+ async handleTeamsSigninVerifyState(context, query) {
3498
+ await this.ssoExecutionDialog.run(context, this.dialogState);
2571
3499
  }
2572
3500
  /**
2573
- * Get all targets where the bot is installed.
2574
- *
2575
- * @remarks
2576
- * The result is retrieving from the persisted storage.
2577
- *
2578
- * @returns - an array of {@link TeamsBotInstallation}.
3501
+ * Receives invoke activities with Activity name of 'signin/tokenExchange'
3502
+ * @param context A context object for this turn.
3503
+ * @param query Signin state (part of signin action auth flow) verification invoke query
3504
+ * @returns A promise that represents the work queued.
2579
3505
  *
2580
- * @beta
3506
+ * @remark
3507
+ * It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
2581
3508
  */
2582
- async installations() {
2583
- if (this.conversationReferenceStore === undefined || this.adapter === undefined) {
2584
- throw new Error("NotificationBot has not been initialized.");
2585
- }
2586
- const references = await this.conversationReferenceStore.getAll();
2587
- const targets = [];
2588
- for (const reference of references) {
2589
- // validate connection
2590
- let valid = true;
2591
- await this.adapter.continueConversation(reference, async (context) => {
2592
- try {
2593
- // try get member to see if the installation is still valid
2594
- await TeamsInfo.getPagedMembers(context, 1);
2595
- }
2596
- catch (error) {
2597
- if (error.code === "BotNotInConversationRoster") {
2598
- valid = false;
2599
- }
2600
- }
2601
- });
2602
- if (valid) {
2603
- targets.push(new TeamsBotInstallation(this.adapter, reference));
2604
- }
2605
- else {
2606
- await this.conversationReferenceStore.delete(reference);
2607
- }
2608
- }
2609
- return targets;
3509
+ async handleTeamsSigninTokenExchange(context, query) {
3510
+ await this.ssoExecutionDialog.run(context, this.dialogState);
2610
3511
  }
2611
3512
  }
2612
3513
 
@@ -2661,8 +3562,6 @@ class NotificationBot {
2661
3562
  * 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
3563
  *
2663
3564
  * For notification, set `notification.storage` in {@link ConversationOptions} to use your own storage implementation.
2664
- *
2665
- * @beta
2666
3565
  */
2667
3566
  class ConversationBot {
2668
3567
  /**
@@ -2672,23 +3571,34 @@ class ConversationBot {
2672
3571
  * It's recommended to create your own adapter and storage for production environment instead of the default one.
2673
3572
  *
2674
3573
  * @param options - initialize options
2675
- *
2676
- * @beta
2677
3574
  */
2678
3575
  constructor(options) {
2679
- var _a, _b;
3576
+ var _a, _b, _c, _d;
2680
3577
  if (options.adapter) {
2681
3578
  this.adapter = options.adapter;
2682
3579
  }
2683
3580
  else {
2684
3581
  this.adapter = this.createDefaultAdapter(options.adapterConfig);
2685
3582
  }
2686
- if ((_a = options.command) === null || _a === void 0 ? void 0 : _a.enabled) {
2687
- this.command = new CommandBot(this.adapter, options.command);
3583
+ let ssoCommandActivityHandler;
3584
+ if (options === null || options === void 0 ? void 0 : options.ssoConfig) {
3585
+ if ((_a = options.ssoConfig.dialog) === null || _a === void 0 ? void 0 : _a.CustomBotSsoExecutionActivityHandler) {
3586
+ ssoCommandActivityHandler =
3587
+ new options.ssoConfig.dialog.CustomBotSsoExecutionActivityHandler(options.ssoConfig);
3588
+ }
3589
+ else {
3590
+ ssoCommandActivityHandler = new DefaultBotSsoExecutionActivityHandler(options.ssoConfig);
3591
+ }
3592
+ }
3593
+ if ((_b = options.command) === null || _b === void 0 ? void 0 : _b.enabled) {
3594
+ this.command = new CommandBot(this.adapter, options.command, ssoCommandActivityHandler, options.ssoConfig);
2688
3595
  }
2689
- if ((_b = options.notification) === null || _b === void 0 ? void 0 : _b.enabled) {
3596
+ if ((_c = options.notification) === null || _c === void 0 ? void 0 : _c.enabled) {
2690
3597
  this.notification = new NotificationBot(this.adapter, options.notification);
2691
3598
  }
3599
+ if ((_d = options.cardAction) === null || _d === void 0 ? void 0 : _d.enabled) {
3600
+ this.cardAction = new CardActionBot(this.adapter, options.cardAction);
3601
+ }
2692
3602
  }
2693
3603
  createDefaultAdapter(adapterConfig) {
2694
3604
  const adapter = adapterConfig === undefined
@@ -2729,8 +3639,6 @@ class ConversationBot {
2729
3639
  * });
2730
3640
  * });
2731
3641
  * ```
2732
- *
2733
- * @beta
2734
3642
  */
2735
3643
  async requestHandler(req, res, logic) {
2736
3644
  if (logic === undefined) {
@@ -2781,8 +3689,6 @@ class MessageBuilder {
2781
3689
  * description: "sample card description"
2782
3690
  * });
2783
3691
  * ```
2784
- *
2785
- * @beta
2786
3692
  */
2787
3693
  static attachAdaptiveCard(cardTemplate, data) {
2788
3694
  return {
@@ -2794,8 +3700,6 @@ class MessageBuilder {
2794
3700
  *
2795
3701
  * @param card The adaptive card content.
2796
3702
  * @returns A bot message activity attached with an adaptive card.
2797
- *
2798
- * @beta
2799
3703
  */
2800
3704
  static attachAdaptiveCardWithoutData(card) {
2801
3705
  return {
@@ -2821,8 +3725,6 @@ class MessageBuilder {
2821
3725
  * ['action']
2822
3726
  * );
2823
3727
  * ```
2824
- *
2825
- * @beta
2826
3728
  */
2827
3729
  static attachHeroCard(title, images, buttons, other) {
2828
3730
  return MessageBuilder.attachContent(CardFactory.heroCard(title, images, buttons, other));
@@ -2838,8 +3740,6 @@ class MessageBuilder {
2838
3740
  *
2839
3741
  * @remarks
2840
3742
  * For channels that don't natively support sign-in cards, an alternative message is rendered.
2841
- *
2842
- * @beta
2843
3743
  */
2844
3744
  static attachSigninCard(title, url, text) {
2845
3745
  return MessageBuilder.attachContent(CardFactory.signinCard(title, url, text));
@@ -2849,8 +3749,6 @@ class MessageBuilder {
2849
3749
  *
2850
3750
  * @param card A description of the Office 365 connector card.
2851
3751
  * @returns A bot message activity attached with an Office 365 connector card.
2852
- *
2853
- * @beta
2854
3752
  */
2855
3753
  static attachO365ConnectorCard(card) {
2856
3754
  return MessageBuilder.attachContent(CardFactory.o365ConnectorCard(card));
@@ -2859,8 +3757,6 @@ class MessageBuilder {
2859
3757
  * Build a message activity attached with a receipt card.
2860
3758
  * @param card A description of the receipt card.
2861
3759
  * @returns A message activity attached with a receipt card.
2862
- *
2863
- * @beta
2864
3760
  */
2865
3761
  static AttachReceiptCard(card) {
2866
3762
  return MessageBuilder.attachContent(CardFactory.receiptCard(card));
@@ -2873,8 +3769,6 @@ class MessageBuilder {
2873
3769
  * is converted to an `imBack` button with a title and value set to the value of the string.
2874
3770
  * @param other Optional. Any additional properties to include on the card.
2875
3771
  * @returns A message activity attached with a thumbnail card
2876
- *
2877
- * @beta
2878
3772
  */
2879
3773
  static attachThumbnailCard(title, images, buttons, other) {
2880
3774
  return MessageBuilder.attachContent(CardFactory.thumbnailCard(title, images, buttons, other));
@@ -2883,8 +3777,6 @@ class MessageBuilder {
2883
3777
  * Add an attachement to a bot activity.
2884
3778
  * @param attachement The attachment object to attach.
2885
3779
  * @returns A message activity with an attachment.
2886
- *
2887
- * @beta
2888
3780
  */
2889
3781
  static attachContent(attachement) {
2890
3782
  return {
@@ -2893,5 +3785,208 @@ class MessageBuilder {
2893
3785
  }
2894
3786
  }
2895
3787
 
2896
- export { ApiKeyLocation, ApiKeyProvider, AppCredential, BasicAuthProvider, BearerTokenAuthProvider, CertificateAuthProvider, Channel, CommandBot, ConversationBot, ErrorCode, ErrorWithCode, IdentityType, LogLevel, Member, MessageBuilder, MsGraphAuthProvider, NotificationBot, OnBehalfOfUserCredential, TeamsBotInstallation, TeamsBotSsoPrompt, TeamsFx, TeamsUserCredential, createApiClient, createMicrosoftGraphClient, createPemCertOption, createPfxCertOption, getLogLevel, getTediousConnectionConfig, sendAdaptiveCard, sendMessage, setLogFunction, setLogLevel, setLogger };
3788
+ // Copyright (c) Microsoft Corporation.
3789
+ /**
3790
+ * Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions.
3791
+ * This method only work on MessageExtension with Query now.
3792
+ *
3793
+ * @param {OnBehalfOfCredentialAuthConfig} authConfig - User custom the message extension authentication configuration.
3794
+ * @param {initiateLoginEndpoint} initiateLoginEndpoint - Login page for Teams to redirect to.
3795
+ * @param {string | string[]} scopes - The list of scopes for which the token will have access.
3796
+ *
3797
+ * @returns SignIn link CardAction with 200 status code.
3798
+ */
3799
+ function getSignInResponseForMessageExtensionWithAuthConfig(authConfig, initiateLoginEndpoint, scopes) {
3800
+ const scopesArray = getScopesArray(scopes);
3801
+ const signInLink = `${initiateLoginEndpoint}?scope=${encodeURI(scopesArray.join(" "))}&clientId=${authConfig.clientId}&tenantId=${authConfig.tenantId}`;
3802
+ return {
3803
+ composeExtension: {
3804
+ type: "silentAuth",
3805
+ suggestedActions: {
3806
+ actions: [
3807
+ {
3808
+ type: "openUrl",
3809
+ value: signInLink,
3810
+ title: "Message Extension OAuth",
3811
+ },
3812
+ ],
3813
+ },
3814
+ },
3815
+ };
3816
+ }
3817
+ /**
3818
+ * Retrieve the OAuth Sign in Link to use in the MessagingExtensionResult Suggested Actions.
3819
+ * This method only work on MessageExtension with Query now.
3820
+ *
3821
+ * @param {TeamsFx} teamsfx - Used to provide configuration and auth.
3822
+ * @param {string | string[]} scopes - The list of scopes for which the token will have access.
3823
+ *
3824
+ * @returns SignIn link CardAction with 200 status code.
3825
+ */
3826
+ function getSignInResponseForMessageExtension(teamsfx, scopes) {
3827
+ const scopesArray = getScopesArray(scopes);
3828
+ const signInLink = `${teamsfx.getConfig("initiateLoginEndpoint")}?scope=${encodeURI(scopesArray.join(" "))}&clientId=${teamsfx.getConfig("clientId")}&tenantId=${teamsfx.getConfig("tenantId")}`;
3829
+ return {
3830
+ composeExtension: {
3831
+ type: "silentAuth",
3832
+ suggestedActions: {
3833
+ actions: [
3834
+ {
3835
+ type: "openUrl",
3836
+ value: signInLink,
3837
+ title: "Message Extension OAuth",
3838
+ },
3839
+ ],
3840
+ },
3841
+ },
3842
+ };
3843
+ }
3844
+ /**
3845
+ * execution in message extension with SSO token.
3846
+ *
3847
+ * @param {TurnContext} context - The context object for the current turn.
3848
+ * @param {OnBehalfOfCredentialAuthConfig} authConfig - User custom the message extension authentication configuration.
3849
+ * @param {initiateLoginEndpoint} initiateLoginEndpoint - Login page for Teams to redirect to.
3850
+ * @param {string[]} scopes - The list of scopes for which the token will have access.
3851
+ * @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
3852
+ *
3853
+ * @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
3854
+ * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
3855
+ * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
3856
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
3857
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
3858
+ *
3859
+ * @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
3860
+ */
3861
+ async function executionWithTokenAndConfig(context, authConfig, initiateLoginEndpoint, scopes, logic) {
3862
+ const valueObj = context.activity.value;
3863
+ if (!valueObj.authentication || !valueObj.authentication.token) {
3864
+ internalLogger.verbose("No AccessToken in request, return silentAuth for AccessToken");
3865
+ return getSignInResponseForMessageExtensionWithAuthConfig(authConfig, initiateLoginEndpoint, scopes);
3866
+ }
3867
+ try {
3868
+ const credential = new OnBehalfOfUserCredential(valueObj.authentication.token, authConfig);
3869
+ const token = await credential.getToken(scopes);
3870
+ const ssoTokenExpiration = parseJwt(valueObj.authentication.token).exp;
3871
+ const tokenRes = {
3872
+ ssoToken: valueObj.authentication.token,
3873
+ ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
3874
+ token: token.token,
3875
+ expiration: token.expiresOnTimestamp.toString(),
3876
+ connectionName: "",
3877
+ };
3878
+ if (logic) {
3879
+ return await logic(tokenRes);
3880
+ }
3881
+ }
3882
+ catch (err) {
3883
+ if (err instanceof ErrorWithCode && err.code === ErrorCode.UiRequiredError) {
3884
+ internalLogger.verbose("User not consent yet, return 412 to user consent first.");
3885
+ const response = { status: 412 };
3886
+ await context.sendActivity({ value: response, type: ActivityTypes.InvokeResponse });
3887
+ return;
3888
+ }
3889
+ throw err;
3890
+ }
3891
+ }
3892
+ /**
3893
+ * execution in message extension with SSO token.
3894
+ *
3895
+ * @param {TurnContext} context - The context object for the current turn.
3896
+ * @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
3897
+ * @param {string[]} scopes - The list of scopes for which the token will have access.
3898
+ * @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
3899
+ *
3900
+ * @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
3901
+ * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
3902
+ * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
3903
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
3904
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
3905
+ *
3906
+ * @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
3907
+ */
3908
+ async function executionWithToken(context, config, scopes, logic) {
3909
+ const valueObj = context.activity.value;
3910
+ if (!valueObj.authentication || !valueObj.authentication.token) {
3911
+ internalLogger.verbose("No AccessToken in request, return silentAuth for AccessToken");
3912
+ return getSignInResponseForMessageExtension(new TeamsFx(IdentityType.User, config), scopes);
3913
+ }
3914
+ try {
3915
+ const teamsfx = new TeamsFx(IdentityType.User, config).setSsoToken(valueObj.authentication.token);
3916
+ const token = await teamsfx.getCredential().getToken(scopes);
3917
+ const ssoTokenExpiration = parseJwt(valueObj.authentication.token).exp;
3918
+ const tokenRes = {
3919
+ ssoToken: valueObj.authentication.token,
3920
+ ssoTokenExpiration: new Date(ssoTokenExpiration * 1000).toISOString(),
3921
+ token: token.token,
3922
+ expiration: token.expiresOnTimestamp.toString(),
3923
+ connectionName: "",
3924
+ };
3925
+ if (logic) {
3926
+ return await logic(tokenRes);
3927
+ }
3928
+ }
3929
+ catch (err) {
3930
+ if (err instanceof ErrorWithCode && err.code === ErrorCode.UiRequiredError) {
3931
+ internalLogger.verbose("User not consent yet, return 412 to user consent first.");
3932
+ const response = { status: 412 };
3933
+ await context.sendActivity({ value: response, type: ActivityTypes.InvokeResponse });
3934
+ return;
3935
+ }
3936
+ throw err;
3937
+ }
3938
+ }
3939
+ // eslint-disable-next-line no-secrets/no-secrets
3940
+ /**
3941
+ * Users execute query in message extension with SSO or access token.
3942
+ *
3943
+ *
3944
+ * @param {TurnContext} context - The context object for the current turn.
3945
+ * @param {AuthenticationConfiguration} config - User custom the message extension authentication configuration.
3946
+ * @param {string| string[]} scopes - The list of scopes for which the token will have access.
3947
+ * @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
3948
+ *
3949
+ * @throws {@link ErrorCode|InternalError} when User invoke not response to message extension query.
3950
+ * @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
3951
+ * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
3952
+ * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
3953
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
3954
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
3955
+ *
3956
+ * @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
3957
+ */
3958
+ async function handleMessageExtensionQueryWithToken(context, config, scopes, logic) {
3959
+ if (context.activity.name != "composeExtension/query") {
3960
+ internalLogger.error(ErrorMessage.OnlySupportInQueryActivity);
3961
+ throw new ErrorWithCode(formatString(ErrorMessage.OnlySupportInQueryActivity), ErrorCode.FailedOperation);
3962
+ }
3963
+ return await executionWithToken(context, config !== null && config !== void 0 ? config : {}, scopes, logic);
3964
+ }
3965
+ /**
3966
+ * Users execute query in message extension with SSO or access token.
3967
+ *
3968
+ * @param {TurnContext} context - The context object for the current turn.
3969
+ * @param {OnBehalfOfCredentialAuthConfig} config - User custom the message extension authentication configuration.
3970
+ * @param {initiateLoginEndpoint} initiateLoginEndpoint - Login page for Teams to redirect to.
3971
+ * @param {string| string[]} scopes - The list of scopes for which the token will have access.
3972
+ * @param {function} logic - Business logic when executing the query in message extension with SSO or access token.
3973
+ *
3974
+ * @throws {@link ErrorCode|InternalError} when User invoke not response to message extension query.
3975
+ * @throws {@link ErrorCode|InternalError} when failed to get access token with unknown error.
3976
+ * @throws {@link ErrorCode|TokenExpiredError} when SSO token has already expired.
3977
+ * @throws {@link ErrorCode|ServiceError} when failed to get access token from simple auth server.
3978
+ * @throws {@link ErrorCode|InvalidParameter} when scopes is not a valid string or string array.
3979
+ * @throws {@link ErrorCode|RuntimeNotSupported} when runtime is nodeJS.
3980
+ *
3981
+ * @returns A MessageExtension Response for the activity. If the logic not return any, return void instead.
3982
+ */
3983
+ async function handleMessageExtensionQueryWithSSO(context, config, initiateLoginEndpoint, scopes, logic) {
3984
+ if (context.activity.name != "composeExtension/query") {
3985
+ internalLogger.error(ErrorMessage.OnlySupportInQueryActivity);
3986
+ throw new ErrorWithCode(formatString(ErrorMessage.OnlySupportInQueryActivity), ErrorCode.FailedOperation);
3987
+ }
3988
+ return await executionWithTokenAndConfig(context, config !== null && config !== void 0 ? config : {}, initiateLoginEndpoint, scopes, logic);
3989
+ }
3990
+
3991
+ 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, createMicrosoftGraphClientWithCredential, createPemCertOption, createPfxCertOption, getLogLevel, getTediousConnectionConfig, handleMessageExtensionQueryWithSSO, handleMessageExtensionQueryWithToken, sendAdaptiveCard, sendMessage, setLogFunction, setLogLevel, setLogger };
2897
3992
  //# sourceMappingURL=index.esm2017.mjs.map