6
6
* Licensed under the MIT License.
7
7
*/
8
8
9
- import { Activity , ActivityTypes , BotFrameworkSkill , ConversationState , ConversationReference , StatePropertyAccessor , TurnContext } from 'botbuilder-core' ;
10
- import { Dialog , DialogTurnResult } from './dialog' ;
9
+ import {
10
+ Activity ,
11
+ ActivityTypes ,
12
+ ConversationReference ,
13
+ TurnContext
14
+ } from 'botbuilder-core' ;
15
+ import {
16
+ Dialog ,
17
+ DialogInstance ,
18
+ DialogReason ,
19
+ DialogTurnResult
20
+ } from './dialog' ;
11
21
import { DialogContext } from './dialogContext' ;
12
22
import { SkillDialogArgs } from './skillDialogArgs' ;
13
23
import { SkillDialogOptions } from './skillDialogOptions' ;
14
24
15
25
export class SkillDialog extends Dialog {
16
- private readonly _activeSkillProperty : StatePropertyAccessor ;
17
- private readonly _conversationState : ConversationState ; // Why is this restricted to ConversationState?
18
- private readonly _dialogOptions : SkillDialogOptions ;
26
+ protected dialogOptions : SkillDialogOptions ;
19
27
20
28
/**
21
29
* A sample dialog that can wrap remote calls to a skill.
@@ -24,51 +32,36 @@ export class SkillDialog extends Dialog {
24
32
* The options parameter in `beginDialog()` must be a `SkillDialogArgs` object with the initial parameters
25
33
* for the dialog.
26
34
*
27
- * @param SkillDialogOptions
28
35
* @param dialogOptions
29
- * @param ConversationState
30
- * @param conversationState
36
+ * @param dialogId
31
37
*/
32
- public constructor ( dialogOptions : SkillDialogOptions , conversationState : ConversationState ) {
33
- super ( 'SkillDialog' ) ; // What about Dialog Id?
38
+ public constructor ( dialogOptions : SkillDialogOptions , dialogId ?: string ) {
39
+ super ( dialogId ) ;
34
40
if ( ! dialogOptions ) {
35
41
throw new TypeError ( 'Missing dialogOptions parameter' ) ;
36
42
}
37
-
38
- if ( ! conversationState ) {
39
- throw new TypeError ( 'Missing conversationState parameter' ) ;
40
- }
41
-
42
- this . _dialogOptions = dialogOptions ;
43
- this . _conversationState = conversationState ;
44
-
45
- this . _activeSkillProperty = conversationState . createProperty < BotFrameworkSkill > ( 'botbuilder-dialogs.SkillDialog.ActiveSkillProperty' ) ;
43
+ this . dialogOptions = dialogOptions ;
46
44
}
47
45
48
46
public async beginDialog ( dc : DialogContext , options ?: { } ) : Promise < DialogTurnResult > {
49
- const dialogArgs = SkillDialog . validateBeginDialogOptions ( options ) ;
47
+ const dialogArgs = SkillDialog . validateBeginDialogArgs ( options ) ;
50
48
51
49
await dc . context . sendTraceActivity ( `${ this . id } .beginDialog()` , undefined , undefined , `Using activity of type: ${ dialogArgs . activity . type } ` ) ;
52
50
53
- // Store Skill information for this dialog instance
54
- await this . _activeSkillProperty . set ( dc . context , dialogArgs . skill ) ;
55
-
56
51
// Create deep clone of the original activity to avoid altering it before forwarding it.
57
52
const clonedActivity = this . cloneActivity ( dialogArgs . activity ) ;
58
53
59
54
// Apply conversation reference and common properties from incoming activity before sending.
60
55
const skillActivity = TurnContext . applyConversationReference ( clonedActivity , TurnContext . getConversationReference ( dc . context . activity ) , true ) as Activity ;
61
56
62
57
// Send the activity to the skill.
63
- await this . sendToSkill ( dc , skillActivity , dialogArgs . skill ) ;
58
+ await this . sendToSkill ( dc . context , skillActivity ) ;
64
59
return Dialog . EndOfTurn ;
65
60
}
66
61
67
62
public async continueDialog ( dc : DialogContext ) : Promise < DialogTurnResult > {
68
63
await dc . context . sendTraceActivity ( `${ this . id } .continueDialog()` , undefined , undefined , `ActivityType: ${ dc . context . activity . type } ` ) ;
69
64
70
- // Retrieve the current skill information from ConversationState
71
- var skillInfo = await this . _activeSkillProperty . get ( dc . context , null ) ;
72
65
73
66
// Handle EndOfConversation from the skill (this will be sent to the this dialog by the SkillHandler if received from the Skill)
74
67
if ( dc . context . activity . type === ActivityTypes . EndOfConversation ) {
@@ -79,12 +72,28 @@ export class SkillDialog extends Dialog {
79
72
// Forward only Message and Event activities to the skill
80
73
if ( dc . context . activity . type === ActivityTypes . Message || dc . context . activity . type === ActivityTypes . Event ) {
81
74
// Just forward to the remote skill
82
- await this . sendToSkill ( dc , dc . context . activity , skillInfo ) ;
75
+ await this . sendToSkill ( dc . context , dc . context . activity ) ;
83
76
}
84
77
85
78
return Dialog . EndOfTurn ;
86
79
}
87
80
81
+ public async endDialog ( context : TurnContext , instance : DialogInstance , reason : DialogReason ) : Promise < void > {
82
+ // Send of of conversation to the skill if the dialog has been cancelled.
83
+ if ( reason == DialogReason . cancelCalled || reason == DialogReason . replaceCalled ) {
84
+ await context . sendTraceActivity ( `${ this . id } .EndDialogAsync()` , undefined , undefined , `ActivityType: ${ context . activity . type } ` ) ;
85
+
86
+ const reference = TurnContext . getConversationReference ( context . activity ) ;
87
+ // Apply conversation reference and common properties from incoming activity before sending.
88
+ const activity = TurnContext . applyConversationReference ( { type : ActivityTypes . EndOfConversation } , reference , true ) ;
89
+ activity . channelData = context . activity . channelData ;
90
+
91
+ await this . sendToSkill ( context , activity as Activity ) ;
92
+ }
93
+
94
+ await super . endDialog ( context , instance , reason ) ;
95
+ }
96
+
88
97
/**
89
98
* Clones the Activity entity.
90
99
* @param activity Activity to clone.
@@ -93,18 +102,14 @@ export class SkillDialog extends Dialog {
93
102
return Object . assign ( { } as Activity , activity ) ;
94
103
}
95
104
96
- private static validateBeginDialogOptions ( options : any ) : SkillDialogArgs {
105
+ private static validateBeginDialogArgs ( options : any ) : SkillDialogArgs {
97
106
if ( ! options ) {
98
107
throw new TypeError ( 'Missing options parameter' ) ;
99
108
}
100
109
101
110
const dialogArgs = options as SkillDialogArgs ;
102
111
103
- if ( dialogArgs . skill == undefined || dialogArgs . skill == null ) {
104
- throw new TypeError ( `"skill" undefined or null in options` ) ;
105
- }
106
-
107
- if ( dialogArgs . activity === undefined || dialogArgs . activity === null ) {
112
+ if ( ! dialogArgs . activity ) {
108
113
throw new TypeError ( `"activity" is undefined or null in options.` ) ;
109
114
}
110
115
@@ -117,15 +122,16 @@ export class SkillDialog extends Dialog {
117
122
return dialogArgs ;
118
123
}
119
124
120
- private async sendToSkill ( dc : DialogContext , activity : Activity , skillInfo : BotFrameworkSkill ) : Promise < void > {
125
+ private async sendToSkill ( context : TurnContext , activity : Activity ) : Promise < void > {
126
+ // Create a conversationId to interact with the skill and send the activity
127
+ const skillConversationId = await this . dialogOptions . conversationIdFactory . createSkillConversationId ( TurnContext . getConversationReference ( activity ) as ConversationReference ) ;
128
+
121
129
// Always save state before forwarding
122
130
// (the dialog stack won't get updated with the skillDialog and things won't work if you don't)
123
- await this . _conversationState . saveChanges ( dc . context , true ) ;
124
-
125
- // Create a conversationId to interact with the skill and send the activity
126
- const skillConversationId = await this . _dialogOptions . conversationIdFactory . createSkillConversationId ( TurnContext . getConversationReference ( activity ) as ConversationReference ) ;
127
- const response = await this . _dialogOptions . skillClient . postActivity ( this . _dialogOptions . botId , skillInfo . appId , skillInfo . skillEndpoint , this . _dialogOptions . skillHostEndpoint , skillConversationId , activity ) ;
128
-
131
+ await this . dialogOptions . conversationState . saveChanges ( context , true ) ;
132
+ const skillInfo = this . dialogOptions . skill ;
133
+ const response = await this . dialogOptions . skillClient . postActivity ( this . dialogOptions . botId , skillInfo . appId , skillInfo . skillEndpoint , skillInfo . skillEndpoint , skillConversationId , activity ) ;
134
+
129
135
// Inspect the skill response status
130
136
if ( ! ( response . status >= 200 && response . status <= 299 ) ) {
131
137
throw new Error ( `Error invoking the skill id: "${ skillInfo . id } " at "${ skillInfo . skillEndpoint } " (status is ${ response . status } ). \r\n ${ response . body } ` ) ;
0 commit comments