private async Task SendEventMessageAsync(EventMessage eventMessage) { // Send the message try { await eventMessage.SendAsync(this.connectorClientFactory); this.logProvider.LogInfo($"Message {eventMessage.Id} sent successfully"); } catch (Exception ex) { this.logProvider.LogError($"Failed to send message {eventMessage.Id}", ex, new Dictionary <string, string> { { "EventId", eventMessage.EventId }, { "OccurrenceId", eventMessage.OccurrenceId }, { "ConversationId", eventMessage.Activity.Conversation?.Id }, { "LastAttemptTime", DateTimeOffset.UtcNow.ToString() }, { "LastAttemptStatusCode", eventMessage.MessageSendResult?.StatusCode.ToString() }, { "ResponseBody", eventMessage.MessageSendResult?.ResponseBody }, }); throw; } finally { // Record the result of the send await this.eventDataProvider.UpdateEventMessageAsync(eventMessage); } }
// Send the list of events as a carousel card private async Task SendMultipleEventNotificationsAsync(string teamId, List <EventNotificationData> notifications) { var firstNotification = notifications.First(); // Build the attachments and entities notifications.Sort((left, right) => StringComparer.InvariantCulture.Compare(left.User.DisplayName, right.User.DisplayName)); var attachments = notifications .Select(x => x.GetCard()) .ToList(); var entities = notifications .Select(x => x.GetMention()) .Distinct(new MentionEqualityComparer()) .AsEnumerable <Entity>() .ToList(); // Build the text message string allButLastEvent = string.Join( ", ", notifications.Take(notifications.Count - 1).Select(x => x.GetMessage())); var message = string.Format(Strings.MultipleEventsMessage, allButLastEvent, notifications[notifications.Count - 1].GetMessage()); // Create the activity var activity = new Activity(ActivityTypes.Message) { Conversation = new ConversationAccount { Id = teamId }, AttachmentLayout = AttachmentLayoutTypes.Carousel, Text = message, Summary = Strings.MultipleEventsSummary, Attachments = attachments, Entities = entities, ServiceUrl = firstNotification.User.ServiceUrl, }; // Add new entry to EventMessages collection for message type "preview" to track the status of message sent var eventMessage = new EventMessage { EventId = null, // This is a collection of events, so there is no single event or occurrence ID OccurrenceId = null, Activity = activity, TenantId = firstNotification.User.TenantId, MessageType = MessageType.Event, ExpireAt = firstNotification.Occurrence.GetLastAllowableTimeToSendNotification(), }; eventMessage = await this.eventDataProvider.AddEventMessageAsync(eventMessage); // Send the message try { await eventMessage.SendAsync(this.connectorClientFactory); this.logProvider.LogInfo($"Event notifications sent to {teamId} for events {string.Join(",", notifications.Select(x => x.Event.Id))}"); } catch (Exception ex) { this.logProvider.LogError($"Failed to send notifications to team {teamId}", ex, new Dictionary <string, string> { { "EventId", eventMessage.EventId }, { "OccurrenceId", eventMessage.OccurrenceId }, { "TeamId", teamId }, { "LastAttemptTime", DateTimeOffset.UtcNow.ToString() }, { "LastAttemptStatusCode", eventMessage.MessageSendResult?.StatusCode.ToString() }, { "ResponseBody", eventMessage.MessageSendResult?.ResponseBody }, }); throw; } finally { // Record the result of the send await this.eventDataProvider.UpdateEventMessageAsync(eventMessage); } }
// Send the event individually private async Task SendSingleEventNotificationAsync(string teamId, EventNotificationData notification) { // Ideally this should send a single message with text and card, but because of message splitting, // such a message is split into two (text-only and card-only). As a workaround, we start a reply chain // with the text message, then send the card as a reply to this post. // Create the activity var activity = new Activity(ActivityTypes.Message) { Conversation = new ConversationAccount { Id = teamId }, AttachmentLayout = AttachmentLayoutTypes.Carousel, Text = notification.GetMessage(), Summary = Strings.MultipleEventsSummary, Attachments = new List <Attachment> { notification.GetCard() }, Entities = new List <Entity> { notification.GetMention() }, ServiceUrl = notification.User.ServiceUrl, }; // Add new entry to EventMessages collection for message type "preview" to track the status of message sent var eventMessage = new EventMessage { EventId = notification.Event.Id, OccurrenceId = notification.Occurrence.Id, Activity = activity, TenantId = notification.User.TenantId, MessageType = MessageType.Event, ExpireAt = notification.Occurrence.GetLastAllowableTimeToSendNotification(), }; eventMessage = await this.eventDataProvider.AddEventMessageAsync(eventMessage); // Send the message try { await eventMessage.SendAsync(this.connectorClientFactory); this.logProvider.LogInfo($"Event notifications sent to {teamId} for event {notification.Event.Id}"); } catch (Exception ex) { this.logProvider.LogError($"Failed to send notifications to team {teamId}", ex, new Dictionary <string, string> { { "EventId", eventMessage.EventId }, { "OccurrenceId", eventMessage.OccurrenceId }, { "TeamId", teamId }, { "LastAttemptTime", DateTimeOffset.UtcNow.ToString() }, { "LastAttemptStatusCode", eventMessage.MessageSendResult?.StatusCode.ToString() }, { "ResponseBody", eventMessage.MessageSendResult?.ResponseBody }, }); throw; } finally { // Record the result of the send await this.eventDataProvider.UpdateEventMessageAsync(eventMessage); } }
// Send a messsage to the owner of the given event, reminding them that their event is coming up private async Task SendEventReminderAsync(CelebrationEvent celebrationEvent, DateTimeOffset currentDateTimeOffset) { this.logProvider.LogInfo($"Sending reminder for event {celebrationEvent.Id} (owner={celebrationEvent.OwnerAadObjectId}, date={celebrationEvent.Date.ToShortDateString()})"); // Determine the next occurrence of the event var deliveryTimeZone = TimeZoneInfo.FindSystemTimeZoneById(celebrationEvent.TimeZoneId); var currentTimeInDeliveryTimeZone = TimeZoneInfo.ConvertTimeFromUtc(currentDateTimeOffset.UtcDateTime, deliveryTimeZone); var upcomingEventDateTime = Common.GetNextOccurrenceAfterDateTime(celebrationEvent.Date.Add(this.timeToPostPreview), currentTimeInDeliveryTimeZone); var upcomingEventDateTimeInUTC = TimeZoneInfo.ConvertTimeToUtc(upcomingEventDateTime, deliveryTimeZone); // Do not send reminder if the next occurrence is not in the window var timeUntilNextOccurrence = upcomingEventDateTimeInUTC - currentDateTimeOffset; if ((timeUntilNextOccurrence.TotalMinutes < 0) || (upcomingEventDateTimeInUTC - currentDateTimeOffset) > TimeSpan.FromDays(this.daysInAdvanceToSendEventPreview)) { this.logProvider.LogInfo($"Next occurrence of event {celebrationEvent.Id} is not in the next {this.daysInAdvanceToSendEventPreview} days"); return; } // Add an entry to Occurrence collection for the event, so we know that we processed it var eventOccurrence = new EventOccurrence { EventId = celebrationEvent.Id, OwnerAadObjectId = celebrationEvent.OwnerAadObjectId, DateTime = upcomingEventDateTimeInUTC, }; eventOccurrence = await this.eventDataProvider.AddEventOccurrenceAsync(eventOccurrence); // Do not send reminder if we are within the period specified in the configuration if ((upcomingEventDateTimeInUTC - currentDateTimeOffset) < this.minimumTimeToProcessEvent) { this.logProvider.LogInfo($"Not sending reminder for event {celebrationEvent.Id} which is due in less than {(int)this.minimumTimeToProcessEvent.TotalHours} hours"); return; } // Get event owner information var user = await this.userManagementHelper.GetUserByAadObjectIdAsync(celebrationEvent.OwnerAadObjectId); await this.EnsureConversationWithUserAsync(user); // Send reminder of event to the owner var previewCard = CelebrationCard.GetPreviewCard(celebrationEvent, eventOccurrence.Id, user.DisplayName); var message = string.Format(Strings.EventPreviewMessageText, user.DisplayName); var activity = new Activity(ActivityTypes.Message) { Conversation = new ConversationAccount { Id = user.ConversationId }, Recipient = new ChannelAccount { Id = user.TeamsId }, Text = message, Summary = message, Attachments = new List <Attachment> { previewCard.ToAttachment() }, ServiceUrl = user.ServiceUrl, }; // Add new entry to EventMessages collection for message type "preview" to track the status of message sent var eventMessage = new EventMessage { EventId = celebrationEvent.Id, OccurrenceId = eventOccurrence.Id, Activity = activity, TenantId = user.TenantId, MessageType = MessageType.Preview, ExpireAt = upcomingEventDateTimeInUTC.AddHours(24), }; eventMessage = await this.eventDataProvider.AddEventMessageAsync(eventMessage); // Send the message try { await eventMessage.SendAsync(this.connectorClientFactory); this.logProvider.LogInfo($"Reminder message sent to the owner of event {celebrationEvent.Id}"); } catch (Exception ex) { this.logProvider.LogError($"Failed to send reminder for event {celebrationEvent.Id}", ex, new Dictionary <string, string> { { "EventId", eventMessage.EventId }, { "OccurrenceId", eventMessage.OccurrenceId }, { "ConversationId", user.ConversationId }, { "OccurrenceDateTime", eventOccurrence.DateTime.ToString() }, { "LastAttemptTime", DateTimeOffset.UtcNow.ToString() }, { "LastAttemptStatusCode", eventMessage.MessageSendResult?.StatusCode.ToString() }, { "ResponseBody", eventMessage.MessageSendResult?.ResponseBody }, }); throw; } finally { // Record the result of the send await this.eventDataProvider.UpdateEventMessageAsync(eventMessage); } }