public async Task <IActionResult> PostAsync([FromQuery] string code, [FromBody] JObject requestContext) { if (code != _config.GetValue <string>("WebhookKey")) { return(Unauthorized()); } try { // Get a reference to the post/postcomment that has just been created. The requestContext holds a RemoteExecutionContext // object, so extract out the PrimaryEntityName and PrimaryEntityId properties var postReference = new EntityReference(requestContext.Value <string>("PrimaryEntityName"), new Guid(requestContext.Value <string>("PrimaryEntityId"))); using (var org = new CdsServiceClient(new Uri("https://" + DomainName), _config.GetValue <string>("MicrosoftAppId"), _config.GetValue <string>("MicrosoftAppPassword"), true, null)) { var post = org.Retrieve(postReference.LogicalName, postReference.Id, new ColumnSet(true)); var postComment = post; if (postReference.LogicalName == "postcomment") { // This is a comment to an existing post, go and retrieve the original post post = org.Retrieve("post", postComment.GetAttributeValue <EntityReference>("postid").Id, new ColumnSet(true)); } // Get the entity the post is on var entityRef = post.GetAttributeValue <EntityReference>("regardingobjectid"); var entity = org.Retrieve(entityRef.LogicalName, entityRef.Id, new ColumnSet(true)); post = GetFullPostText(org, entityRef, post, ref postComment); // Get the users to notify var entityRelationships = new Dictionary <Guid, Link>(); var usersToNotify = new HashSet <EntityReference>(); if (postComment != post) { entityRelationships.Add(postComment.Id, new Link { From = post.ToEntityReference(), Description = "Contains Comment" }); entityRelationships.Add(post.Id, new Link { From = postComment.ToEntityReference(), Description = "Is Comment On" }); } entityRelationships.Add(entity.Id, new Link { From = post.ToEntityReference(), Description = "Is Posted On" }); if (entity.LogicalName == "systemuser") { usersToNotify.Add(entity.ToEntityReference()); } GetInterestedUsers(post, postComment, entity, org, usersToNotify, entityRelationships); ExpandTeamsToUsers(usersToNotify, org, entityRelationships); AddUserFollows(usersToNotify, org, entityRelationships); // Remove the user who added the post usersToNotify.Remove(postComment.GetAttributeValue <EntityReference>("createdby")); // Remove any possible null value usersToNotify.Remove(null); if (usersToNotify.Any()) { string avatarUrl = null; // Connect to storage account so we can look up the details we need to send the message to each user var connectionString = _config.GetConnectionString("Storage"); var storageAccount = CloudStorageAccount.Parse(connectionString); var tableClient = storageAccount.CreateCloudTableClient(); var table = tableClient.GetTableReference("users"); foreach (var userRef in usersToNotify) { var user = org.Retrieve(userRef.LogicalName, userRef.Id, new ColumnSet("domainname")); var username = user.GetAttributeValue <string>("domainname"); var userTeamsDetails = (User)table.Execute(TableOperation.Retrieve <User>(username, "")).Result; if (userTeamsDetails == null) { continue; } MicrosoftAppCredentials.TrustServiceUrl(userTeamsDetails.ServiceUrl); var client = new ConnectorClient(new Uri(userTeamsDetails.ServiceUrl), _config.GetValue <string>("MicrosoftAppId"), _config.GetValue <string>("MicrosoftAppPassword")); if (avatarUrl == null) { var sender = org.Retrieve("systemuser", postComment.GetAttributeValue <EntityReference>("createdby").Id, new ColumnSet("domainname")); avatarUrl = await GetAvatarUrlAsync(userTeamsDetails.TenantId, sender.GetAttributeValue <string>("domainname"), postComment.GetAttributeValue <EntityReference>("createdby").Name); } // Create or get existing chat conversation with user var parameters = new ConversationParameters { Bot = new ChannelAccount("28:" + _config.GetValue <string>("MicrosoftAppId")), Members = new[] { new ChannelAccount(userTeamsDetails.UserId) }, ChannelData = new TeamsChannelData { Tenant = new TenantInfo(userTeamsDetails.TenantId), }, }; var response = await client.Conversations.CreateConversationAsync(parameters); // Construct the message to post to conversation var model = new PostNotify { Regarding = entityRef, Post = post, Comment = postComment == post ? null : postComment, Links = GetChain(postComment.Id, userRef.Id, entityRelationships) }; EnsureChainDetails(org, model.Links); var newActivity = new Activity { //Text = model.ToString(), Type = ActivityTypes.Message, Conversation = new ConversationAccount { Id = response.Id }, Attachments = new List <Attachment> { ToAdaptiveCard(model, avatarUrl) } }; // Post the message to chat conversation with user await client.Conversations.SendToConversationAsync(response.Id, new Activity { Type = ActivityTypes.Typing }); await Task.Delay(TimeSpan.FromSeconds(2)); await client.Conversations.SendToConversationAsync(response.Id, newActivity); // Update the user to record the details of which post should be replied to if the user sends a message back var updatedUser = new User(username) { LastDomainName = DomainName, LastPostId = post.Id, ETag = userTeamsDetails.ETag }; var update = TableOperation.Merge(updatedUser); try { await table.ExecuteAsync(update); } catch (StorageException ex) { if (ex.RequestInformation.HttpStatusCode != 412) { throw; } } } } } return(Ok()); } catch (Exception ex) { return(Problem(ex.ToString(), statusCode: 500)); } }