private static async Task DownloadChunk(RecordingMetadata metadata, CloudBlobContainer container, ILogger log) { var acsEndpoint = new Uri(Environment.GetEnvironmentVariable("AcsEndpoint", EnvironmentVariableTarget.Process)); var acsAccessKey = Environment.GetEnvironmentVariable("CUSTOMCONNSTR_AcsAccessKey", EnvironmentVariableTarget.Process); var downloadUrlForChunk = string.Format("recording/download/{0}?api-version=2021-04-15-preview1", metadata.ChunkDocumentId); var downloadRecordingUrl = new Uri(acsEndpoint, downloadUrlForChunk); using (var request = new HttpRequestMessage(HttpMethod.Get, downloadRecordingUrl)) { request.Content = null; // content required for POST methods AddHmacHeaders(request, CreateContentHash(string.Empty), acsAccessKey); using (var response = await client.SendAsync(request).ConfigureAwait(false)) { if (!response.IsSuccessStatusCode) { log.LogError($"Failed to download document. StatusCode: {response.StatusCode}"); return; } var recordingStream = await response.Content.ReadAsStreamAsync(); var recordingFile = string.Format("{0}.{1}", metadata.CallId, metadata.RecordingInfo.Format); var recordingBlob = container.GetBlockBlobReference(recordingFile); await recordingBlob.UploadFromStreamAsync(recordingStream).ConfigureAwait(false); log.LogInformation("Saved recording successfully"); } } }
private static async Task <Stream> DownloadChunk(RecordingMetadata metadata, ILogger log) { string downloadRecordingUrl = "https://{0}/recording/download/{1}?api-version=2021-04-15-preview1"; string acsEndPoint = Environment.GetEnvironmentVariable("AcsEndpoint", EnvironmentVariableTarget.Process); string acsAccessKey = Environment.GetEnvironmentVariable("CUSTOMCONNSTR_AcsAccessKey", EnvironmentVariableTarget.Process); var downloadUrlForChunk = String.Format(downloadRecordingUrl, acsEndPoint, metadata.ChunkDocumentId); var request = new HttpRequestMessage { Method = HttpMethod.Get, RequestUri = new Uri(downloadUrlForChunk), Content = null // content if required for POST methods }; // Hash the content of the request. var contentHashed = CreateContentHash(string.Empty); // Add HMAC headers. AddHmacHeaders(request, contentHashed, acsAccessKey, HttpMethod.Get); // Make a request to the ACS apis mentioned above var response = await client.SendAsync(request).ConfigureAwait(false); log.LogInformation("Download recording statusCode: {0}", response.StatusCode); if (!response.IsSuccessStatusCode) { log.LogError("Failed to download document. StatusCode: " + response.StatusCode); return(null); } var contentStream = await response.Content.ReadAsStreamAsync(); return(contentStream); }
public static async void Run( [EventGridTrigger] EventGridEvent eventGridEvent, ILogger log) { try { log.LogInformation("Received event : {0}", eventGridEvent.EventType); string json = JsonConvert.SerializeObject(eventGridEvent, Formatting.Indented); log.LogInformation(json); if (eventGridEvent.EventType == "Microsoft.Communication.RecordingFileStatusUpdated") { if (eventGridEvent.Data == null) { log.LogInformation("Received invalid event data"); return; } RecordingFileStatusUpdatedEventData eventData = ((JObject)(eventGridEvent.Data)).ToObject <RecordingFileStatusUpdatedEventData>(); foreach (var chunk in eventData.RecordingStorageInfo.RecordingChunks) { // Download metadata for chunk var metadataString = await GetDownloadMetadata(chunk.DocumentId, log).ConfigureAwait(false); if (metadataString == null) { log.LogInformation("Invalid metadata"); return; } RecordingMetadata metadata = JsonConvert.DeserializeObject <RecordingMetadata>(metadataString); var recordingStream = await DownloadChunk(metadata, log).ConfigureAwait(false); if (recordingStream == null) { log.LogInformation("Invalid recording stream"); return; } // Save to storage blob string connStr = Environment.GetEnvironmentVariable("CUSTOMCONNSTR_RecordingStorageBlob", EnvironmentVariableTarget.Process); CloudStorageAccount storageAccount = CloudStorageAccount.Parse(connStr); CloudBlobClient blobClient = storageAccount.CreateCloudBlobClient(); CloudBlobContainer container = blobClient.GetContainerReference("recordings"); string recordingMetadataFileName = String.Format("{0}_metadata.json", metadata.CallId); CloudBlockBlob metadataBlob = container.GetBlockBlobReference(recordingMetadataFileName); await metadataBlob.UploadTextAsync(metadataString).ConfigureAwait(false); string recordingFileName = String.Format("{0}.{1}", metadata.CallId, metadata.RecordingInfo.Format); CloudBlockBlob downloadBlob = container.GetBlockBlobReference(recordingFileName); await downloadBlob.UploadFromStreamAsync(recordingStream).ConfigureAwait(false); log.LogInformation("Saved recording successfully"); // Create a new record in Redis for this recording string lastIDKey = "LastID"; string lastIDValue = ""; string cacheConnection = Environment.GetEnvironmentVariable("CUSTOMCONNSTR_cacheConnection", EnvironmentVariableTarget.Process); if (string.IsNullOrEmpty(cacheConnection)) { log.LogInformation($"Missing Redis connection string '{cacheConnection}'"); } else { ConnectionMultiplexer cm = ConnectionMultiplexer.Connect(cacheConnection); IDatabase cache = cm.GetDatabase(); lastIDValue = cache.StringGet("LastID"); log.LogInformation($"{lastIDKey}={lastIDValue}"); var trans = cache.CreateTransaction(); var task = trans.StringIncrementAsync(lastIDKey); if (await trans.ExecuteAsync().ConfigureAwait(false)) { await task.ConfigureAwait(false); log.LogInformation($"transaction completed"); lastIDValue = cache.StringGet(lastIDKey); log.LogInformation($"After change: {lastIDKey}={lastIDValue}"); cache.StringSet(lastIDValue, recordingFileName); log.LogInformation($"Mapping '{lastIDValue}' --> '{recordingFileName}' created"); var message = $"Your call record is available here: https://arturlcallrecordingdownloader.azurewebsites.net/api/Get?id={lastIDValue}"; string phoneNumberTo = ""; foreach (var participant in metadata.Participants) { if (participant.ParticipantId.Contains("acs:")) { continue; // this is not a human } if (participant.ParticipantId.Length >= 12) { phoneNumberTo = participant.ParticipantId.Substring(participant.ParticipantId.Length - 12, 12); log.LogInformation($"Found target phone number: {phoneNumberTo}"); } } if (string.IsNullOrEmpty(phoneNumberTo)) { log.LogError("Cannot extract target phone number"); } else { var acsParams = GetACSConnectionParameters(log); string acsconnectionString = $"endpoint=https://{acsParams.AcsEndPoint}/;accesskey={acsParams.AcsAccessKey}"; string SMSSenderPhoneNumber = Environment.GetEnvironmentVariable("SMSSenderPhoneNumber", EnvironmentVariableTarget.Process); if (string.IsNullOrEmpty(SMSSenderPhoneNumber)) { log.LogError($"SMSSenderPhoneNumber application setting not set, cannot send SMS"); } else { var smsResponse = SendSMSAsync(acsconnectionString, SMSSenderPhoneNumber, phoneNumberTo, message); // TBD: parametrize phone number log.LogInformation($"Sent SMS to {phoneNumberTo}. Message: '{message}'"); string QAOperatorPhoneNumber = Environment.GetEnvironmentVariable("QAOperatorPhoneNumber", EnvironmentVariableTarget.Process); if (!string.IsNullOrEmpty(QAOperatorPhoneNumber)) { // Text a copy to QA operator for quality assurance purposes if (phoneNumberTo != QAOperatorPhoneNumber) { var smsResponse2 = SendSMSAsync(acsconnectionString, SMSSenderPhoneNumber, QAOperatorPhoneNumber, message); } } } } } else { log.LogError($"transaction failed"); } log.LogInformation($"Done"); } } } } catch (Exception ex) { log.LogError("Failed with {0}", ex.Message); log.LogError("Failed with {0}", ex.InnerException.Message); } }