@microsoft/teamsfx 2.0.0-experimental.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.
- package/LICENSE +21 -21
- package/dist/index.esm2017.js +429 -229
- package/dist/index.esm2017.js.map +1 -1
- package/dist/index.esm2017.mjs +1644 -549
- package/dist/index.esm2017.mjs.map +1 -1
- package/dist/index.esm5.js +447 -229
- package/dist/index.esm5.js.map +1 -1
- package/dist/index.node.cjs.js +1700 -547
- package/dist/index.node.cjs.js.map +1 -1
- package/package.json +11 -10
- package/types/teamsfx.d.ts +2197 -1648
- package/CHANGELOG.md +0 -28
- package/NOTICE.txt +0 -9242
package/dist/index.esm2017.mjs
CHANGED
|
@@ -3,20 +3,20 @@ import { ConfidentialClientApplication } from '@azure/msal-node';
|
|
|
3
3
|
import { createHash } from 'crypto';
|
|
4
4
|
import { Client } from '@microsoft/microsoft-graph-client';
|
|
5
5
|
import { ManagedIdentityCredential } from '@azure/identity';
|
|
6
|
-
import { ActivityTypes, Channels, TeamsInfo, CardFactory, ActionTypes, MessageFactory, StatusCodes, verifyStateOperationName, tokenExchangeOperationName, TurnContext, BotFrameworkAdapter } from 'botbuilder';
|
|
7
|
-
import { Dialog } from 'botbuilder-dialogs';
|
|
6
|
+
import { ActivityTypes, Channels, TeamsInfo, CardFactory, ActionTypes, MessageFactory, StatusCodes, verifyStateOperationName, tokenExchangeOperationName, TurnContext, TeamsActivityHandler, MemoryStorage, UserState, ConversationState, BotFrameworkAdapter } from 'botbuilder';
|
|
7
|
+
import { Dialog, ComponentDialog, WaterfallDialog, DialogSet, DialogTurnStatus } from 'botbuilder-dialogs';
|
|
8
8
|
import { v4 } from 'uuid';
|
|
9
9
|
import axios from 'axios';
|
|
10
10
|
import { Agent } from 'https';
|
|
11
11
|
import * as path from 'path';
|
|
12
12
|
import * as fs from 'fs';
|
|
13
|
+
import { __rest } from 'tslib';
|
|
13
14
|
import { AdaptiveCards } from '@microsoft/adaptivecards-tools';
|
|
14
15
|
|
|
15
16
|
// Copyright (c) Microsoft Corporation.
|
|
16
17
|
// Licensed under the MIT license.
|
|
17
18
|
/**
|
|
18
19
|
* Error code to trace the error types.
|
|
19
|
-
* @beta
|
|
20
20
|
*/
|
|
21
21
|
var ErrorCode;
|
|
22
22
|
(function (ErrorCode) {
|
|
@@ -40,6 +40,30 @@ var ErrorCode;
|
|
|
40
40
|
* Channel is not supported error.
|
|
41
41
|
*/
|
|
42
42
|
ErrorCode["ChannelNotSupported"] = "ChannelNotSupported";
|
|
43
|
+
/**
|
|
44
|
+
* Failed to retrieve sso token
|
|
45
|
+
*/
|
|
46
|
+
ErrorCode["FailedToRetrieveSsoToken"] = "FailedToRetrieveSsoToken";
|
|
47
|
+
/**
|
|
48
|
+
* Failed to process sso handler
|
|
49
|
+
*/
|
|
50
|
+
ErrorCode["FailedToProcessSsoHandler"] = "FailedToProcessSsoHandler";
|
|
51
|
+
/**
|
|
52
|
+
* Cannot find command
|
|
53
|
+
*/
|
|
54
|
+
ErrorCode["CannotFindCommand"] = "CannotFindCommand";
|
|
55
|
+
/**
|
|
56
|
+
* Failed to run sso step
|
|
57
|
+
*/
|
|
58
|
+
ErrorCode["FailedToRunSsoStep"] = "FailedToRunSsoStep";
|
|
59
|
+
/**
|
|
60
|
+
* Failed to run dedup step
|
|
61
|
+
*/
|
|
62
|
+
ErrorCode["FailedToRunDedupStep"] = "FailedToRunDedupStep";
|
|
63
|
+
/**
|
|
64
|
+
* Sso activity handler is undefined
|
|
65
|
+
*/
|
|
66
|
+
ErrorCode["SsoActivityHandlerIsUndefined"] = "SsoActivityHandlerIsUndefined";
|
|
43
67
|
/**
|
|
44
68
|
* Runtime is not supported error.
|
|
45
69
|
*/
|
|
@@ -95,6 +119,15 @@ ErrorMessage.NodejsRuntimeNotSupported = "{0} is not supported in Node.";
|
|
|
95
119
|
ErrorMessage.FailToAcquireTokenOnBehalfOfUser = "Failed to acquire access token on behalf of user: {0}";
|
|
96
120
|
// ChannelNotSupported Error
|
|
97
121
|
ErrorMessage.OnlyMSTeamsChannelSupported = "{0} is only supported in MS Teams Channel";
|
|
122
|
+
ErrorMessage.FailedToProcessSsoHandler = "Failed to process sso handler: {0}";
|
|
123
|
+
// FailedToRetrieveSsoToken Error
|
|
124
|
+
ErrorMessage.FailedToRetrieveSsoToken = "Failed to retrieve sso token, user failed to finish the AAD consent flow.";
|
|
125
|
+
// CannotFindCommand Error
|
|
126
|
+
ErrorMessage.CannotFindCommand = "Cannot find command: {0}";
|
|
127
|
+
ErrorMessage.FailedToRunSsoStep = "Failed to run dialog to retrieve sso token: {0}";
|
|
128
|
+
ErrorMessage.FailedToRunDedupStep = "Failed to run dialog to remove duplicated messages: {0}";
|
|
129
|
+
// SsoActivityHandlerIsUndefined Error
|
|
130
|
+
ErrorMessage.SsoActivityHandlerIsNull = "Sso command can only be used or added when sso activity handler is not undefined";
|
|
98
131
|
// IdentityTypeNotSupported Error
|
|
99
132
|
ErrorMessage.IdentityTypeNotSupported = "{0} identity is not supported in {1}";
|
|
100
133
|
// AuthorizationInfoError
|
|
@@ -105,10 +138,9 @@ ErrorMessage.EmptyParameter = "Parameter {0} is empty";
|
|
|
105
138
|
ErrorMessage.DuplicateHttpsOptionProperty = "Axios HTTPS agent already defined value for property {0}";
|
|
106
139
|
ErrorMessage.DuplicateApiKeyInHeader = "The request already defined api key in request header with name {0}.";
|
|
107
140
|
ErrorMessage.DuplicateApiKeyInQueryParam = "The request already defined api key in query parameter with name {0}.";
|
|
141
|
+
ErrorMessage.OnlySupportInQueryActivity = "The handleMessageExtensionQueryWithToken only support in handleTeamsMessagingExtensionQuery with composeExtension/query type.";
|
|
108
142
|
/**
|
|
109
143
|
* Error class with code and message thrown by the SDK.
|
|
110
|
-
*
|
|
111
|
-
* @beta
|
|
112
144
|
*/
|
|
113
145
|
class ErrorWithCode extends Error {
|
|
114
146
|
/**
|
|
@@ -116,8 +148,6 @@ class ErrorWithCode extends Error {
|
|
|
116
148
|
*
|
|
117
149
|
* @param {string} message - error message.
|
|
118
150
|
* @param {ErrorCode} code - error code.
|
|
119
|
-
*
|
|
120
|
-
* @beta
|
|
121
151
|
*/
|
|
122
152
|
constructor(message, code) {
|
|
123
153
|
if (!code) {
|
|
@@ -135,8 +165,6 @@ class ErrorWithCode extends Error {
|
|
|
135
165
|
// Licensed under the MIT license.
|
|
136
166
|
/**
|
|
137
167
|
* Log level.
|
|
138
|
-
*
|
|
139
|
-
* @beta
|
|
140
168
|
*/
|
|
141
169
|
var LogLevel;
|
|
142
170
|
(function (LogLevel) {
|
|
@@ -161,8 +189,6 @@ var LogLevel;
|
|
|
161
189
|
* Update log level helper.
|
|
162
190
|
*
|
|
163
191
|
* @param { LogLevel } level - log level in configuration
|
|
164
|
-
*
|
|
165
|
-
* @beta
|
|
166
192
|
*/
|
|
167
193
|
function setLogLevel(level) {
|
|
168
194
|
internalLogger.level = level;
|
|
@@ -171,8 +197,6 @@ function setLogLevel(level) {
|
|
|
171
197
|
* Get log level.
|
|
172
198
|
*
|
|
173
199
|
* @returns Log level
|
|
174
|
-
*
|
|
175
|
-
* @beta
|
|
176
200
|
*/
|
|
177
201
|
function getLogLevel() {
|
|
178
202
|
return internalLogger.level;
|
|
@@ -247,8 +271,6 @@ const internalLogger = new InternalLogger();
|
|
|
247
271
|
* error: console.error,
|
|
248
272
|
* });
|
|
249
273
|
* ```
|
|
250
|
-
*
|
|
251
|
-
* @beta
|
|
252
274
|
*/
|
|
253
275
|
function setLogger(logger) {
|
|
254
276
|
internalLogger.customLogger = logger;
|
|
@@ -266,8 +288,6 @@ function setLogger(logger) {
|
|
|
266
288
|
* }
|
|
267
289
|
* });
|
|
268
290
|
* ```
|
|
269
|
-
*
|
|
270
|
-
* @beta
|
|
271
291
|
*/
|
|
272
292
|
function setLogFunction(logFunction) {
|
|
273
293
|
internalLogger.customLogFunction = logFunction;
|
|
@@ -310,6 +330,7 @@ function getUserInfoFromSsoToken(ssoToken) {
|
|
|
310
330
|
const userInfo = {
|
|
311
331
|
displayName: tokenObject.name,
|
|
312
332
|
objectId: tokenObject.oid,
|
|
333
|
+
tenantId: tokenObject.tid,
|
|
313
334
|
preferredUserName: "",
|
|
314
335
|
};
|
|
315
336
|
if (tokenObject.ver === "2.0") {
|
|
@@ -431,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 &&
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
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 (
|
|
1249
|
-
const errorMsg = formatString(ErrorMessage.IdentityTypeNotSupported,
|
|
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 (!
|
|
1275
|
+
if (!teamsfx.hasConfig("initiateLoginEndpoint")) {
|
|
1255
1276
|
missingConfigurations.push("initiateLoginEndpoint");
|
|
1256
1277
|
}
|
|
1257
|
-
if (!
|
|
1278
|
+
if (!teamsfx.hasConfig("clientId")) {
|
|
1258
1279
|
missingConfigurations.push("clientId");
|
|
1259
1280
|
}
|
|
1260
|
-
if (!
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1584
|
-
if (config.
|
|
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
|
-
|
|
1746
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
1912
|
-
|
|
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
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
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
|
-
|
|
1924
|
-
|
|
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
|
-
|
|
1927
|
-
|
|
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
|
-
|
|
1930
|
-
|
|
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
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
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
|
-
|
|
2034
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
* @
|
|
2274
|
-
*
|
|
2275
|
-
* @
|
|
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
|
-
* @
|
|
2286
|
-
*
|
|
2287
|
-
* @
|
|
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 =
|
|
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
|
-
* @
|
|
2327
|
-
*
|
|
2328
|
-
* @
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
* @
|
|
2343
|
-
*
|
|
2344
|
-
* @
|
|
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
|
-
|
|
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
|
-
|
|
2351
|
-
|
|
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 =
|
|
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
|
-
* @
|
|
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
|
-
* @
|
|
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
|
-
|
|
2405
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
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
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
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
|
-
*
|
|
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
|
|
3432
|
+
class DefaultBotSsoExecutionActivityHandler extends TeamsActivityHandler {
|
|
2460
3433
|
/**
|
|
2461
|
-
*
|
|
3434
|
+
* Creates a new instance of the DefaultBotSsoExecutionActivityHandler.
|
|
3435
|
+
* @param ssoConfig configuration for SSO command bot
|
|
2462
3436
|
*
|
|
2463
3437
|
* @remarks
|
|
2464
|
-
*
|
|
2465
|
-
*
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
|
|
2479
|
-
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2486
|
-
await context.
|
|
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
|
-
*
|
|
2491
|
-
*
|
|
2492
|
-
* @param
|
|
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
|
-
* @
|
|
3469
|
+
* @remarks
|
|
3470
|
+
* This function is used to add SSO command to {@link BotSsoExecutionDialog} instance.
|
|
2496
3471
|
*/
|
|
2497
|
-
|
|
2498
|
-
|
|
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
|
-
*
|
|
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
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
|
|
2565
|
-
|
|
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
|
-
*
|
|
2574
|
-
*
|
|
2575
|
-
* @
|
|
2576
|
-
*
|
|
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
|
-
* @
|
|
3506
|
+
* @remark
|
|
3507
|
+
* It should trigger {@link BotSsoExecutionDialog} instance to handle signin process
|
|
2581
3508
|
*/
|
|
2582
|
-
async
|
|
2583
|
-
|
|
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
|
-
|
|
2687
|
-
|
|
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 ((
|
|
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
|
-
|
|
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
|