/// <summary> /// Method responsible for processing the data sent with POST request to callback URL /// </summary> /// <param name="content">The content of request</param> /// <returns>Returns the response that should be sent to the sender of POST request</returns> public async Task <ResponseResult> ProcessCallbackAsync(string content) { ConversationResult conversationResult; if (content == null) { return(new ResponseResult(ResponseType.BadRequest)); } try { conversationResult = RealTimeMediaSerializer.DeserializeFromJson <ConversationResult>(content); if (conversationResult == null) { Trace.TraceWarning($"Could not deserialize the callback.. returning badrequest"); return(new ResponseResult(ResponseType.BadRequest)); } conversationResult.Validate(); } catch (Exception ex) { Trace.TraceWarning($"Exception in conversationResult validate {ex}"); return(new ResponseResult(ResponseType.BadRequest)); } RealTimeMediaCallService service; if (!_activeCalls.TryGetValue(conversationResult.Id, out service)) { Trace.TraceWarning($"CallId {conversationResult.Id} not found"); return(new ResponseResult(ResponseType.NotFound)); } return(new ResponseResult(ResponseType.Accepted, await service.ProcessConversationResult(conversationResult).ConfigureAwait(false))); }
/// <summary> /// Invokes handlers for callback on the bot /// </summary> /// <param name="conversationResult">ConversationResult that has the details of the callback</param> /// <returns></returns> internal async Task <string> ProcessConversationResult(ConversationResult conversationResult) { conversationResult.Validate(); var newWorkflowResult = await PassActionResultToHandler(conversationResult).ConfigureAwait(false); if (newWorkflowResult == null) { throw new BotCallingServiceException($"[{CallLegId}]: No workflow returned for AnswerAppHostedMediaOutcome"); } bool expectEmptyActions = false; if (conversationResult.OperationOutcome.Type == RealTimeMediaValidOutcomes.AnswerAppHostedMediaOutcome && conversationResult.OperationOutcome.Outcome == Outcome.Success) { Uri link; if (conversationResult.Links.TryGetValue("subscriptions", out link)) { _subscriptionLink = link; Trace.TraceInformation($"RealTimeMediaCallService [{CallLegId}]: Caching subscription link {link}"); } if (conversationResult.Links.TryGetValue("call", out link)) { _callLink = link; Trace.TraceInformation($"RealTimeMediaCallService [{CallLegId}]: Caching call link {link}"); } expectEmptyActions = true; Trace.TraceInformation($"RealTimeMediaCallService [{CallLegId}]: Disposing call expiry timer"); _timer.Dispose(); } newWorkflowResult.Validate(expectEmptyActions); return(RealTimeMediaSerializer.SerializeToJson(newWorkflowResult)); }
/// <summary> /// Configuration settings like Auth, Routes for OWIN /// </summary> /// <param name="app"></param> public void Configuration(IAppBuilder app) { HttpConfiguration httpConfig = new HttpConfiguration(); httpConfig.MapHttpAttributeRoutes(); httpConfig.MessageHandlers.Add(new LoggingMessageHandler(isIncomingMessageHandler: true, logContext: LogContext.FrontEnd)); httpConfig.Services.Add(typeof(IExceptionLogger), new ExceptionLogger()); httpConfig.Formatters.JsonFormatter.SerializerSettings = RealTimeMediaSerializer.GetSerializerSettings(); httpConfig.EnsureInitialized(); app.UseWebApi(httpConfig); }
/// <summary> /// Method responsible for processing the data sent with POST request to incoming call URL /// </summary> /// <param name="content">The content of request</param> /// <param name="skypeChainId">X-Microsoft-Skype-Chain-Id header value used to associate calls across different services</param> /// <returns>Returns the response that should be sent to the sender of POST request</returns> public async Task <ResponseResult> ProcessIncomingCallAsync(string content, string skypeChainId) { if (content == null) { return(new ResponseResult(ResponseType.BadRequest)); } Conversation conversation; try { conversation = RealTimeMediaSerializer.DeserializeFromJson <Conversation>(content); if (conversation == null) { Trace.TraceWarning($"Could not deserialize the incoming request.. returning badrequest"); return(new ResponseResult(ResponseType.BadRequest)); } conversation.Validate(); } catch (Exception ex) { Trace.TraceWarning($"Exception in conversation validate {ex}"); return(new ResponseResult(ResponseType.BadRequest)); } RealTimeMediaCallService service = new RealTimeMediaCallService(conversation.Id, skypeChainId, _makeBot, _settings); var workflow = await service.HandleIncomingCall(conversation).ConfigureAwait(false); if (workflow == null) { throw new BotCallingServiceException("Incoming call not handled. No workflow produced for incoming call."); } workflow.Validate(); RealTimeMediaCallService prevService; if (_activeCalls.TryRemove(conversation.Id, out prevService)) { Trace.TraceWarning($"Another call with the same Id {conversation.Id} exists. ending the old call"); await prevService.LocalCleanup().ConfigureAwait(false); } _activeCalls[conversation.Id] = service; var serializedResponse = RealTimeMediaSerializer.SerializeToJson(workflow); return(new ResponseResult(ResponseType.Accepted, serializedResponse)); }
/// <summary> /// Subscribe to a video or video based screen sharing channel /// </summary> /// <param name="videoSubscription"></param> /// <returns></returns> public async Task Subscribe(VideoSubscription videoSubscription) { if (_subscriptionLink == null) { throw new InvalidOperationException($"[{CallLegId}]: No subscription link was present in the AnswerAppHostedMediaOutcome"); } videoSubscription.Validate(); HttpContent content = new StringContent(RealTimeMediaSerializer.SerializeToJson(videoSubscription), Encoding.UTF8, JSONConstants.ContentType); //Subscribe try { Trace.TraceInformation( $"RealTimeMediaCallService [{CallLegId}]: Sending subscribe request for " + $"user: {videoSubscription.ParticipantIdentity}" + $"subscriptionLink: {_subscriptionLink}"); //TODO: add retries & logging using (var request = new HttpRequestMessage(HttpMethod.Put, _subscriptionLink) { Content = content }) { request.Headers.Add("X-Microsoft-Skype-Chain-ID", CorrelationId); request.Headers.Add("X-Microsoft-Skype-Message-ID", Guid.NewGuid().ToString()); var client = GetHttpClient(); var response = await client.SendAsync(request).ConfigureAwait(false); response.EnsureSuccessStatusCode(); Trace.TraceInformation($"RealTimeMediaCallService [{CallLegId}]: Response to subscribe: {response}"); } } catch (Exception exception) { Trace.TraceError($"RealTimeMediaCallService [{CallLegId}]: Received error while sending request to subscribe participant. Message: {exception}"); throw; } }
/// <summary> /// Method responsible for processing the data sent with POST request to notification URL /// </summary> /// <param name="content">The content of request</param> /// <returns>Returns the response that should be sent to the sender of POST request</returns> public async Task <ResponseResult> ProcessNotificationAsync(string content) { NotificationBase notification; if (content == null) { return(new ResponseResult(ResponseType.BadRequest)); } try { Trace.TraceWarning($"Received notification {content}"); notification = RealTimeMediaSerializer.DeserializeFromJson <NotificationBase>(content); if (notification == null) { Trace.TraceWarning($"Could not deserialize the notification.. returning badrequest"); return(new ResponseResult(ResponseType.BadRequest)); } notification.Validate(); } catch (Exception ex) { Trace.TraceWarning($"Exception in notification validate {ex}"); return(new ResponseResult(ResponseType.BadRequest)); } RealTimeMediaCallService service; if (!_activeCalls.TryGetValue(notification.Id, out service)) { return(new ResponseResult(ResponseType.NotFound, $"Call {notification.Id} not found")); } await service.ProcessNotificationResult(notification).ConfigureAwait(false); return(new ResponseResult(ResponseType.Accepted)); }