1+ # Copyright (c) Microsoft Corporation. All rights reserved.
2+ # Licensed under the MIT License.
3+
4+ from .dialog_set import DialogSet
5+ from .dialog_state import DialogState
6+ from .dialog_turn_result import DialogTurnResult
7+ from .dialog_reason import DialogReason
8+ from botbuilder .core .turn_context import TurnContext
9+
10+
11+ class DialogContext ():
12+ def __init__ (self , dialogs : DialogSet , turn_context : TurnContext , state : DialogState ):
13+ if dialogs is None :
14+ raise TypeError ('DialogContext(): dialogs cannot be None.' )
15+ if turn_context is None :
16+ raise TypeError ('DialogContext(): turn_context cannot be None.' )
17+ self .__turn_context = turn_context ;
18+ self .__dialogs = dialogs ;
19+ self .__id = dialog_id ;
20+ self .__stack = state .dialog_stack ;
21+ self .parent ;
22+
23+ @property
24+ def dialogs (self ):
25+ """Gets the set of dialogs that can be called from this context.
26+
27+ :param:
28+ :return str:
29+ """
30+ return self .__dialogs ;
31+
32+ @property
33+ def context (self ):
34+ """Gets the context for the current turn of conversation.
35+
36+ :param:
37+ :return str:
38+ """
39+ return self .__stack ;
40+
41+ @property
42+ def stack (self ):
43+ """Gets the current dialog stack.
44+
45+ :param:
46+ :return str:
47+ """
48+ return self .__stack ;
49+
50+
51+ @property
52+ def active_dialog (self ):
53+ """Return the container link in the database.
54+
55+ :param:
56+ :return str:
57+ """
58+ if (self .__stack & & self .__stack .size () > 0 ):
59+ return self .__stack [0 ];
60+ return None ;
61+
62+
63+
64+ async def begin_dialog (self , dialog_id : str , options : object = None ):
65+ """
66+ Pushes a new dialog onto the dialog stack.
67+ :param dialog_id: ID of the dialog to start..
68+ :param options: (Optional) additional argument(s) to pass to the dialog being started.
69+ """
70+ if (not dialog_id ):
71+ raise TypeError ('Dialog(): dialogId cannot be None.' )
72+ # Look up dialog
73+ dialog = find_dialog (dialog_id );
74+ if (not dialog ):
75+ raise Exception ("'DialogContext.begin_dialog(): A dialog with an id of '%s' wasn't found."
76+ " The dialog must be included in the current or parent DialogSet."
77+ " For example, if subclassing a ComponentDialog you can call add_dialog() within your constructor." % dialog_id );
78+ # Push new instance onto stack
79+ instance = new DialogInstance ();
80+ {
81+ Id = dialogId ,
82+ State = new Dictionary < string , object > (),
83+ };
84+
85+ stack .insert (0 , instance );
86+
87+ # Call dialog's BeginAsync() method
88+ return await dialog .begin_dialog (this , options );
89+
90+ async def prompt (self , dialog_id : str , options : PromptOptions ) -> DialogTurnResult :
91+ """
92+ Helper function to simplify formatting the options for calling a prompt dialog. This helper will
93+ take a `PromptOptions` argument and then call.
94+ :param dialog_id: ID of the prompt to start.
95+ :param options: Contains a Prompt, potentially a RetryPrompt and if using ChoicePrompt, Choices.
96+ :return:
97+ """
98+ if (not dialog_id ):
99+ raise TypeError ('DialogContext.prompt(): dialogId cannot be None.' );
100+
101+ if (not options ):
102+ raise TypeError ('DialogContext.prompt(): options cannot be None.' );
103+
104+ return await begin_dialog (dialog_id , options );
105+
106+ async def continue_dialog (self , dc : DialogContext , reason : DialogReason , result : object ):
107+ """
108+ Continues execution of the active dialog, if there is one, by passing the context object to
109+ its `Dialog.continue_dialog()` method. You can check `turn_context.responded` after the call completes
110+ to determine if a dialog was run and a reply was sent to the user.
111+ :return:
112+ """
113+ # Check for a dialog on the stack
114+ if not active_dialog :
115+ # Look up dialog
116+ dialog = find_dialog (active_dialog .id );
117+ if not dialog :
118+ raise Exception ("DialogContext.continue_dialog(): Can't continue dialog. A dialog with an id of '%s' wasn't found." % active_dialog .id );
119+
120+ # Continue execution of dialog
121+ return await dialog .continue_dialog (self );
122+ else :
123+ return new DialogTurnResult (DialogTurnStatus .Empty );
124+
125+ async def end_dialog (self , context : TurnContext , instance : DialogInstance ):
126+ """
127+ Ends a dialog by popping it off the stack and returns an optional result to the dialog's
128+ parent. The parent dialog is the dialog that started the dialog being ended via a call to
129+ either "begin_dialog" or "prompt".
130+ The parent dialog will have its `Dialog.resume_dialog()` method invoked with any returned
131+ result. If the parent dialog hasn't implemented a `resume_dialog()` method then it will be
132+ automatically ended as well and the result passed to its parent. If there are no more
133+ parent dialogs on the stack then processing of the turn will end.
134+ :param result: (Optional) result to pass to the parent dialogs.
135+ :return:
136+ """
137+ await end_active_dialog (DialogReason .EndCalled );
138+
139+ # Resume previous dialog
140+ if not active_dialog :
141+ # Look up dialog
142+ dialog = find_dialog (active_dialog .id );
143+ if not dialog :
144+ raise Exception ("DialogContext.EndDialogAsync(): Can't resume previous dialog. A dialog with an id of '%s' wasn't found." % active_dialog .id );
145+
146+ # Return result to previous dialog
147+ return await dialog .resume_dialog (self , DialogReason .EndCalled , result );
148+ else :
149+ return new DialogTurnResult (DialogTurnStatus .Complete , result );
150+
151+
152+ async def cancel_all_dialogs (self ):
153+ """
154+ Deletes any existing dialog stack thus cancelling all dialogs on the stack.
155+ :param result: (Optional) result to pass to the parent dialogs.
156+ :return:
157+ """
158+ if (len (stack ) > 0 ):
159+ while (len (stack ) > 0 ):
160+ await end_active_dialog (DialogReason .CancelCalled );
161+ return DialogTurnResult (DialogTurnStatus .Cancelled );
162+ else :
163+ return DialogTurnResult (DialogTurnStatus .Empty );
164+
165+ async def find_dialog (self , dialog_id : str ) -> Dialog :
166+ """
167+ If the dialog cannot be found within the current `DialogSet`, the parent `DialogContext`
168+ will be searched if there is one.
169+ :param dialog_id: ID of the dialog to search for.
170+ :return:
171+ """
172+ dialog = dialogs .find (dialog_id );
173+ if (not dialog & & parent != None ):
174+ dialog = parent .find_dialog (dialog_id );
175+ return dialog ;
176+
177+ async def replace_dialog (self ) -> DialogTurnResult :
178+ """
179+ Ends the active dialog and starts a new dialog in its place. This is particularly useful
180+ for creating loops or redirecting to another dialog.
181+ :param dialog_id: ID of the dialog to search for.
182+ :param options: (Optional) additional argument(s) to pass to the new dialog.
183+ :return:
184+ """
185+ # End the current dialog and giving the reason.
186+ await end_active_dialog (DialogReason .ReplaceCalled );
187+
188+ # Start replacement dialog
189+ return await begin_dialog (dialogId , options );
190+
191+ async def reprompt_dialog (self ):
192+ """
193+ Calls reprompt on the currently active dialog, if there is one. Used with Prompts that have a reprompt behavior.
194+ :return:
195+ """
196+ # Check for a dialog on the stack
197+ if active_dialog != None :
198+ # Look up dialog
199+ dialog = find_dialog (active_dialog .id );
200+ if not dialog :
201+ raise Exception ("DialogSet.reprompt_dialog(): Can't find A dialog with an id of '%s'." % active_dialog .id );
202+
203+ # Ask dialog to re-prompt if supported
204+ await dialog .reprompt_dialog (context , active_dialog );
205+
206+ async def end_active_dialog (reason : DialogReason ):
207+ instance = active_dialog ;
208+ if instance != None :
209+ # Look up dialog
210+ dialog = find_dialog (instance .id );
211+ if not dialog :
212+ # Notify dialog of end
213+ await dialog .end_dialog (context , instance , reason );
214+
215+ # Pop dialog off stack
216+ stack .pop ());
0 commit comments