11// Copyright (c) Microsoft Corporation. All rights reserved.
22// Licensed under the MIT License.
33
4+ using System . Linq ;
5+ using System . Security . Claims ;
6+ using System . Security . Principal ;
47using System . Threading ;
58using System . Threading . Tasks ;
9+ using Microsoft . Bot . Builder . TraceExtensions ;
10+ using Microsoft . Bot . Connector . Authentication ;
11+ using Microsoft . Bot . Schema ;
612
713namespace Microsoft . Bot . Builder . Dialogs
814{
@@ -24,16 +30,76 @@ public static class DialogExtensions
2430 /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
2531 public static async Task RunAsync ( this Dialog dialog , ITurnContext turnContext , IStatePropertyAccessor < DialogState > accessor , CancellationToken cancellationToken )
2632 {
27- var dialogSet = new DialogSet ( accessor ) ;
28- dialogSet . TelemetryClient = dialog . TelemetryClient ;
33+ var dialogSet = new DialogSet ( accessor ) { TelemetryClient = dialog . TelemetryClient } ;
2934 dialogSet . Add ( dialog ) ;
3035
3136 var dialogContext = await dialogSet . CreateContextAsync ( turnContext , cancellationToken ) . ConfigureAwait ( false ) ;
32- var results = await dialogContext . ContinueDialogAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
33- if ( results . Status == DialogTurnStatus . Empty )
37+
38+ if ( turnContext . TurnState . Get < IIdentity > ( BotAdapter . BotIdentityKey ) is ClaimsIdentity claimIdentity && SkillValidation . IsSkillClaim ( claimIdentity . Claims ) )
39+ {
40+ // The bot is running as a skill.
41+ if ( turnContext . Activity . Type == ActivityTypes . EndOfConversation && dialogContext . Stack . Any ( ) )
42+ {
43+ // Handle remote cancellation request if we have something in the stack.
44+ var activeDialogContext = GetActiveDialogContext ( dialogContext ) ;
45+
46+ var remoteCancelText = "Skill was canceled by a request from the host." ;
47+ await turnContext . TraceActivityAsync ( $ "{ typeof ( Dialog ) . Name } .RunAsync()", label : $ "{ remoteCancelText } ", cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
48+
49+ // Send cancellation message to the top dialog in the stack to ensure all the parents are canceled in the right order.
50+ await activeDialogContext . CancelAllDialogsAsync ( true , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
51+ }
52+ else
53+ {
54+ // Process a reprompt event sent from the parent.
55+ if ( turnContext . Activity . Type == ActivityTypes . Event && turnContext . Activity . Name == DialogEvents . RepromptDialog && dialogContext . Stack . Any ( ) )
56+ {
57+ await dialogContext . RepromptDialogAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
58+ return ;
59+ }
60+
61+ // Run the Dialog with the new message Activity and capture the results so we can send end of conversation if needed.
62+ var result = await dialogContext . ContinueDialogAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
63+ if ( result . Status == DialogTurnStatus . Empty )
64+ {
65+ var startMessageText = $ "Starting { dialog . Id } .";
66+ await turnContext . TraceActivityAsync ( $ "{ typeof ( Dialog ) . Name } .RunAsync()", label : $ "{ startMessageText } ", cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
67+ result = await dialogContext . BeginDialogAsync ( dialog . Id , null , cancellationToken ) . ConfigureAwait ( false ) ;
68+ }
69+
70+ // Send end of conversation if it is completed or cancelled.
71+ if ( result . Status == DialogTurnStatus . Complete || result . Status == DialogTurnStatus . Cancelled )
72+ {
73+ var endMessageText = $ "Dialog { dialog . Id } has **completed**. Sending EndOfConversation.";
74+ await turnContext . TraceActivityAsync ( $ "{ typeof ( Dialog ) . Name } .RunAsync()", label : $ "{ endMessageText } ", value : result . Result , cancellationToken : cancellationToken ) . ConfigureAwait ( false ) ;
75+
76+ // Send End of conversation at the end.
77+ var activity = new Activity ( ActivityTypes . EndOfConversation ) { Value = result . Result } ;
78+ await turnContext . SendActivityAsync ( activity , cancellationToken ) . ConfigureAwait ( false ) ;
79+ }
80+ }
81+ }
82+ else
3483 {
35- await dialogContext . BeginDialogAsync ( dialog . Id , null , cancellationToken ) . ConfigureAwait ( false ) ;
84+ // The bot is running as a standard bot.
85+ var results = await dialogContext . ContinueDialogAsync ( cancellationToken ) . ConfigureAwait ( false ) ;
86+ if ( results . Status == DialogTurnStatus . Empty )
87+ {
88+ await dialogContext . BeginDialogAsync ( dialog . Id , null , cancellationToken ) . ConfigureAwait ( false ) ;
89+ }
3690 }
3791 }
92+
93+ // Recursively walk up the DC stack to find the active DC.
94+ private static DialogContext GetActiveDialogContext ( DialogContext dialogContext )
95+ {
96+ var child = dialogContext . Child ;
97+ if ( child == null )
98+ {
99+ return dialogContext ;
100+ }
101+
102+ return GetActiveDialogContext ( child ) ;
103+ }
38104 }
39105}
0 commit comments