CallingBotService.cs
5 using System;
6 using System.Collections.Generic;
7 using System.Diagnostics;
8 using System.IO;
9 using System.Threading.Tasks;
10 using System.Threading;
11 
12 namespace Microsoft.Bot.Builder.Calling
13 {
15  {
16  private readonly string _callbackUrl;
17 
18  #region Implementation of ICallingBotService
19 
23  public event Func<IncomingCallEvent, Task> OnIncomingCallReceived;
24 
28  public event Func<AnswerOutcomeEvent, Task> OnAnswerCompleted;
29 
33  public event Func<HangupOutcomeEvent, Task> OnHangupCompleted;
34 
38  public event Func<PlayPromptOutcomeEvent, Task> OnPlayPromptCompleted;
39 
43  public event Func<RecognizeOutcomeEvent, Task> OnRecognizeCompleted;
44 
48  public event Func<RecordOutcomeEvent, Task> OnRecordCompleted;
49 
53  public event Func<RejectOutcomeEvent, Task> OnRejectCompleted;
54 
58  public event Func<WorkflowValidationOutcomeEvent, Task> OnWorkflowValidationFailed;
59 
65  {
66  if (settings == null)
67  throw new ArgumentNullException(nameof(settings));
68 
69  _callbackUrl = settings.CallbackUrl;
70  }
71 
78  public async Task<string> ProcessCallbackAsync(string content, Task<Stream> additionalData)
79  {
80  if (content == null)
81  throw new ArgumentNullException(nameof(content));
82 
83  ConversationResult conversationResult = ConvertToConversationResult(content);
84  return await ProcessConversationResult(conversationResult, additionalData).ConfigureAwait(false);
85  }
86 
92  public string ProcessIncomingCall(string content)
93  {
94  return Task.Factory.StartNew(s => ((ICallingBotService)s).ProcessIncomingCallAsync(content), this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
95  }
96 
102  public async Task<string> ProcessIncomingCallAsync(string content)
103  {
104  if (content == null)
105  throw new ArgumentNullException(nameof(content));
106  var conversation = Serializer.DeserializeFromJson<Conversation>(content);
107  conversation.Validate();
108  var workflow = await HandleIncomingCall(conversation).ConfigureAwait(false);
109  if (workflow == null)
110  throw new BotCallingServiceException("Incoming call not handled. No workflow produced for incoming call.");
111  workflow.Validate();
112  var serializedResponse = Serializer.SerializeToJson(workflow);
113  return serializedResponse;
114  }
115 
122  public string ProcessCallback(string content, Task<Stream> additionalData)
123  {
124  return Task.Factory.StartNew(s => ((ICallingBotService)s).ProcessCallbackAsync(content, additionalData), this, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.Default).Unwrap().GetAwaiter().GetResult();
125  }
126 
127  private async Task<string> ProcessConversationResult(ConversationResult conversationResult, Task<Stream> additionalData)
128  {
129  conversationResult.Validate();
130  var newWorkflowResult = await PassActionResultToHandler(conversationResult, additionalData).ConfigureAwait(false);
131  if (newWorkflowResult == null)
132  return "";
133  newWorkflowResult.Validate();
134  return Serializer.SerializeToJson(newWorkflowResult);
135  }
136 
137  private Task<Workflow> PassActionResultToHandler(ConversationResult receivedConversationResult, Task<Stream> additionalData)
138  {
139  Trace.TraceInformation(
140  $"CallingBotService: Received the outcome for {receivedConversationResult.OperationOutcome.Type} operation, callId: {receivedConversationResult.OperationOutcome.Id}");
141 
142  switch (receivedConversationResult.OperationOutcome.Type)
143  {
145  return HandleAnswerOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as AnswerOutcome);
147  return HandleHangupOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as HangupOutcome);
149  return HandlePlayPromptOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as PlayPromptOutcome);
151  return HandleRecognizeOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as RecognizeOutcome);
153  return HandleRecordOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as RecordOutcome, additionalData);
155  return HandleRejectOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as RejectOutcome);
157  return HandleWorkflowValidationOutcome(receivedConversationResult, receivedConversationResult.OperationOutcome as WorkflowValidationOutcome);
158  }
159  throw new BotCallingServiceException($"Unknown conversation result type {receivedConversationResult.OperationOutcome.Type}");
160  }
161 
162  private async Task<Workflow> HandleIncomingCall(Conversation conversation)
163  {
164  Trace.TraceInformation($"CallingBotService: Received incoming call, callId: {conversation.Id}");
165  var incomingCall = new IncomingCallEvent(conversation, CreateInitialWorkflow());
166  var eventHandler = OnIncomingCallReceived;
167  if (eventHandler != null)
168  await eventHandler.Invoke(incomingCall).ConfigureAwait(false);
169  else
170  {
171  Trace.TraceInformation($"CallingBotService: No handler specified for incoming call");
172  return null;
173  }
174 
175  return incomingCall.ResultingWorkflow;
176  }
177 
178  private Task<Workflow> HandleAnswerOutcome(ConversationResult conversationResult, AnswerOutcome answerOutcome)
179  {
180  var outcomeEvent = new AnswerOutcomeEvent(conversationResult, CreateInitialWorkflow(), answerOutcome);
181  var eventHandler = OnAnswerCompleted;
182  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
183  }
184 
185  private Task<Workflow> HandleHangupOutcome(ConversationResult conversationResult, HangupOutcome hangupOutcome)
186  {
187  var outcomeEvent = new HangupOutcomeEvent(conversationResult, CreateInitialWorkflow(), hangupOutcome);
188  var eventHandler = OnHangupCompleted;
189  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
190  }
191 
192  private Task<Workflow> HandlePlayPromptOutcome(ConversationResult conversationResult, PlayPromptOutcome playPromptOutcome)
193  {
194  var outcomeEvent = new PlayPromptOutcomeEvent(conversationResult, CreateInitialWorkflow(), playPromptOutcome);
195  var eventHandler = OnPlayPromptCompleted;
196  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
197  }
198 
199  private Task<Workflow> HandleRecognizeOutcome(ConversationResult conversationResult, RecognizeOutcome recognizeOutcome)
200  {
201  var outcomeEvent = new RecognizeOutcomeEvent(conversationResult, CreateInitialWorkflow(), recognizeOutcome);
202  var eventHandler = OnRecognizeCompleted;
203  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
204  }
205 
206  private Task<Workflow> HandleRecordOutcome(ConversationResult conversationResult, RecordOutcome recordOutcome, Task<Stream> recordedContent)
207  {
208  var outcomeEvent = new RecordOutcomeEvent(conversationResult, CreateInitialWorkflow(), recordOutcome, recordedContent);
209  var eventHandler = OnRecordCompleted;
210  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
211  }
212 
213  private Task<Workflow> HandleRejectOutcome(ConversationResult conversationResult, RejectOutcome rejectOutcome)
214  {
215  var outcomeEvent = new RejectOutcomeEvent(conversationResult, CreateInitialWorkflow(), rejectOutcome);
216  var eventHandler = OnRejectCompleted;
217  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
218  }
219 
220  private Task<Workflow> HandleWorkflowValidationOutcome(
221  ConversationResult conversationResult,
222  WorkflowValidationOutcome workflowValidationOutcome)
223  {
224 
225  var outcomeEvent = new WorkflowValidationOutcomeEvent(conversationResult, CreateInitialWorkflow(), workflowValidationOutcome);
226  var eventHandler = OnWorkflowValidationFailed;
227  return InvokeHandlerIfSet(eventHandler, outcomeEvent);
228  }
229 
230  private async Task<Workflow> InvokeHandlerIfSet<T>(Func<T, Task> action, T outcomeEventBase) where T : OutcomeEventBase
231  {
232  if (action != null)
233  {
234  await action.Invoke(outcomeEventBase).ConfigureAwait(false);
235  return outcomeEventBase.ResultingWorkflow;
236  }
237  throw new BotCallingServiceException($"No event handler set for {outcomeEventBase.ConversationResult.OperationOutcome.Type} outcome");
238  }
239 
240  private Workflow CreateInitialWorkflow()
241  {
242  var workflow = new Workflow();
243  workflow.Links = new CallbackLink { Callback = GetCallbackUri() };
244  workflow.Actions = new List<ActionBase>();
245 
246  return workflow;
247  }
248 
249  private Uri GetCallbackUri()
250  {
251  return new Uri(_callbackUrl);
252  }
253 
254  private static ConversationResult ConvertToConversationResult(string content)
255  {
256  try
257  {
258  var conversationResult = Serializer.DeserializeFromJson<ConversationResult>(content);
259  return conversationResult;
260  }
261  catch (Exception e)
262  {
263  throw new BotCallingServiceException("Failed to deserialize Calling Service callback content", e);
264  }
265  }
266 
267  #endregion
268  }
269 }
This is the outcome of the "recognize" action. This is conveyed to the customer as POST to the custom...
If the customer&#39;s "response" fails validation, this is the outcome conveyed to the customer as POST t...
OperationOutcomeBase OperationOutcome
a. We would always return the outcome : i. of the last operation if all operations were performed suc...
This class contains the workflow the customer sent for the OnInComingCall POST or any subsequent POST...
Definition: Workflow.cs:14
string ProcessIncomingCall(string content)
Method responsible for processing the data sent with POST request to incoming call URL ...
CallingBotService(CallingBotServiceSettings settings)
Instantiates CallingBotService using provided settings
async Task< string > ProcessIncomingCallAsync(string content)
Method responsible for processing the data sent with POST request to incoming call URL ...
This is the outcome of the "playPrompt" action. This is conveyed to the customer as POST to the custo...
Root namespace for the Microsoft Bot Builder Calling SDK object model.
string CallbackUrl
The url where the Callingcallbacks from Skype Bot platform will be sent. Needs to match the domain na...
Func< RecordOutcomeEvent, Task > OnRecordCompleted
Event raised when the bot gets the outcome of Record action
This is a helper class for validating outcomes. This can be used by customers or by us (before we sen...
Helper class for serializing/deserializing
Definition: Serializer.cs:14
Func< RecognizeOutcomeEvent, Task > OnRecognizeCompleted
Event raised when the bot gets the outcome of Recognize action
This is the outcome of the "record" action. This is conveyed to the customer as POST to the customer ...
This defines the set of the properties that define a conversation. A conversation includes participan...
Func< HangupOutcomeEvent, Task > OnHangupCompleted
Event raised when the bot gets the outcome of Hangup action
This is the outcome of the "reject" action. This is conveyed to the customer as POST to the customer ...
Definition: RejectOutcome.cs:9
Func< RejectOutcomeEvent, Task > OnRejectCompleted
Event raised when the bot gets the outcome of Reject action
static string SerializeToJson(object obj, bool forLogging=false)
Serialize input object to string
Definition: Serializer.cs:22
Func< IncomingCallEvent, Task > OnIncomingCallReceived
Event raised when bot receives incoming call
Root namespace for the Microsoft Bot Builder Calling SDK.
Namespace for contracts used by the Microsoft Bot Builder Calling SDK.
Definition: ActionBase.cs:7
Namespace for events generated by the Microsoft Bot Builder Calling SDK.
Once we have peformed the "actions" requested by the customer, we POST back to customer callback Url ...
const string WorkflowValidationOutcome
WorkflowValidationOutcome
Func< AnswerOutcomeEvent, Task > OnAnswerCompleted
Event raised when the bot gets the outcome of Answer action. If the operation was successful the call...
This is the outcome of the "answer" action. This is conveyed to the customer as POST to the customer ...
Definition: AnswerOutcome.cs:9
Namespace for exceptions raised by the Microsoft Bot Builder Calling SDK.
async Task< string > ProcessCallbackAsync(string content, Task< Stream > additionalData)
Method responsible for processing the data sent with POST request to callback URL ...
This is the outcome of the "hangup" action. This is conveyed to the customer as POST to the customer ...
Definition: HangupOutcome.cs:9
string Type
The type of outcome. Various concrete outcome classes specify their name. This is used to deserialize...
Namespace for utilities and helpers in the Microsoft Bot Builder Calling SDK.
string ProcessCallback(string content, Task< Stream > additionalData)
Method responsible for processing the data sent with POST request to callback URL ...
Func< PlayPromptOutcomeEvent, Task > OnPlayPromptCompleted
Event raised when the bot gets the outcome of PlayPrompt action
Root namespace for the Microsoft Bot Builder SDK.
Func< WorkflowValidationOutcomeEvent, Task > OnWorkflowValidationFailed
Event raised when specified workflow fails to be validated by Bot platform