Files
Wino-Mail/Wino.Core/Integration/Processors/OutlookChangeProcessor.cs
T

214 lines
8.8 KiB
C#
Raw Normal View History

2024-06-02 21:35:03 +02:00
using System;
using System.Collections.Generic;
using System.Linq;
2024-06-02 21:35:03 +02:00
using System.Threading.Tasks;
using Microsoft.Graph.Models;
using Serilog;
using Wino.Core.Domain.Entities.Calendar;
using Wino.Core.Domain.Entities.Shared;
using Wino.Core.Domain.Enums;
using Wino.Core.Domain.Interfaces;
using Wino.Core.Extensions;
using Wino.Services;
2026-01-01 15:02:40 +01:00
using Reminder = Wino.Core.Domain.Entities.Calendar.Reminder;
2025-02-16 11:54:23 +01:00
namespace Wino.Core.Integration.Processors;
public class OutlookChangeProcessor(IDatabaseService databaseService,
IFolderService folderService,
ICalendarService calendarService,
IMailService mailService,
IAccountService accountService,
IMimeFileService mimeFileService) : DefaultChangeProcessor(databaseService, folderService, mailService, calendarService, accountService, mimeFileService)
, IOutlookChangeProcessor
{
2024-06-02 21:35:03 +02:00
2025-02-16 11:54:23 +01:00
public Task<string> ResetAccountDeltaTokenAsync(Guid accountId)
=> AccountService.UpdateSyncIdentifierRawAsync(accountId, string.Empty);
2025-02-16 11:54:23 +01:00
public async Task<string> ResetFolderDeltaTokenAsync(Guid folderId)
{
var folder = await FolderService.GetFolderAsync(folderId);
2025-02-16 11:54:23 +01:00
folder.DeltaToken = null;
2025-02-16 11:54:23 +01:00
await FolderService.UpdateFolderAsync(folder);
2025-02-16 11:54:23 +01:00
return string.Empty;
}
2025-02-16 11:54:23 +01:00
public Task UpdateFolderDeltaSynchronizationIdentifierAsync(Guid folderId, string synchronizationIdentifier)
=> Connection.ExecuteAsync("UPDATE MailItemFolder SET DeltaToken = ? WHERE Id = ?", synchronizationIdentifier, folderId);
2025-02-16 11:54:23 +01:00
public async Task ManageCalendarEventAsync(Event calendarEvent, AccountCalendar assignedCalendar, MailAccount organizerAccount)
{
// We parse the occurrences based on the parent event.
// There is literally no point to store them because
// type=Exception events are the exceptional childs of recurrency parent event.
2025-02-16 11:54:23 +01:00
if (calendarEvent.Type == EventType.Occurrence) return;
2025-02-16 11:54:23 +01:00
var savingItem = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.Id);
2025-02-16 11:54:23 +01:00
Guid savingItemId = Guid.Empty;
bool isNewItem = savingItem == null;
2025-02-16 11:54:23 +01:00
if (savingItem != null)
savingItemId = savingItem.Id;
else
{
savingItemId = Guid.NewGuid();
savingItem = new CalendarItem() { Id = savingItemId };
}
2025-02-16 11:54:23 +01:00
DateTimeOffset eventStartDateTimeOffset = OutlookIntegratorExtensions.GetDateTimeOffsetFromDateTimeTimeZone(calendarEvent.Start);
DateTimeOffset eventEndDateTimeOffset = OutlookIntegratorExtensions.GetDateTimeOffsetFromDateTimeTimeZone(calendarEvent.End);
2025-02-16 11:54:23 +01:00
var durationInSeconds = (eventEndDateTimeOffset - eventStartDateTimeOffset).TotalSeconds;
// Store dates as UTC in the database
2025-02-16 11:54:23 +01:00
savingItem.RemoteEventId = calendarEvent.Id;
savingItem.StartDate = eventStartDateTimeOffset.UtcDateTime;
2025-02-16 11:54:23 +01:00
savingItem.DurationInSeconds = durationInSeconds;
2025-12-26 20:46:48 +01:00
// Store the timezone information from the event
// This preserves the original timezone from Outlook, allowing proper reconstruction later
// If no timezone is provided, null will indicate UTC
2025-12-26 20:46:48 +01:00
savingItem.StartTimeZone = calendarEvent.Start?.TimeZone;
savingItem.EndTimeZone = calendarEvent.End?.TimeZone;
2025-02-16 11:54:23 +01:00
savingItem.Title = calendarEvent.Subject;
savingItem.Description = calendarEvent.Body?.Content;
savingItem.Location = calendarEvent.Location?.DisplayName;
2025-02-16 11:54:23 +01:00
if (calendarEvent.Type == EventType.Exception && !string.IsNullOrEmpty(calendarEvent.SeriesMasterId))
{
// This is a recurring event exception.
// We need to find the parent event and set it as recurring event id.
2025-02-16 11:54:23 +01:00
var parentEvent = await CalendarService.GetCalendarItemAsync(assignedCalendar.Id, calendarEvent.SeriesMasterId);
2025-02-16 11:43:30 +01:00
2025-02-16 11:54:23 +01:00
if (parentEvent != null)
{
2025-02-16 11:54:23 +01:00
savingItem.RecurringCalendarItemId = parentEvent.Id;
}
else
{
2025-02-16 11:54:23 +01:00
Log.Warning($"Parent recurring event is missing for event. Skipping creation of {calendarEvent.Id}");
return;
}
2025-02-16 11:54:23 +01:00
}
// Convert the recurrence pattern to string for parent recurring events.
if (calendarEvent.Type == EventType.SeriesMaster && calendarEvent.Recurrence != null)
{
savingItem.Recurrence = OutlookIntegratorExtensions.ToRfc5545RecurrenceString(calendarEvent.Recurrence);
}
2025-02-16 11:54:23 +01:00
savingItem.HtmlLink = calendarEvent.WebLink;
savingItem.CalendarId = assignedCalendar.Id;
savingItem.OrganizerEmail = calendarEvent.Organizer?.EmailAddress?.Address;
savingItem.OrganizerDisplayName = calendarEvent.Organizer?.EmailAddress?.Name;
savingItem.IsHidden = false;
2025-12-26 20:46:48 +01:00
// Set timestamps
if (calendarEvent.CreatedDateTime.HasValue)
savingItem.CreatedAt = calendarEvent.CreatedDateTime.Value;
2025-12-26 20:46:48 +01:00
if (calendarEvent.LastModifiedDateTime.HasValue)
savingItem.UpdatedAt = calendarEvent.LastModifiedDateTime.Value;
// Set visibility
if (calendarEvent.Sensitivity != null)
{
savingItem.Visibility = calendarEvent.Sensitivity.Value switch
{
Sensitivity.Normal => CalendarItemVisibility.Public,
Sensitivity.Personal => CalendarItemVisibility.Private,
Sensitivity.Private => CalendarItemVisibility.Private,
Sensitivity.Confidential => CalendarItemVisibility.Confidential,
_ => CalendarItemVisibility.Public
};
}
else
{
savingItem.Visibility = CalendarItemVisibility.Public;
}
// Set IsLocked based on whether the user is the organizer
// Read-only events are those where the current user is not the organizer
savingItem.IsLocked = calendarEvent.IsOrganizer.HasValue && !calendarEvent.IsOrganizer.Value;
2025-02-16 11:54:23 +01:00
if (calendarEvent.ResponseStatus?.Response != null)
{
switch (calendarEvent.ResponseStatus.Response.Value)
{
2025-02-16 11:54:23 +01:00
case ResponseType.None:
case ResponseType.NotResponded:
savingItem.Status = CalendarItemStatus.NotResponded;
break;
case ResponseType.TentativelyAccepted:
savingItem.Status = CalendarItemStatus.Tentative;
break;
case ResponseType.Accepted:
case ResponseType.Organizer:
2026-01-03 19:33:36 +01:00
savingItem.Status = CalendarItemStatus.Accepted;
2025-02-16 11:54:23 +01:00
break;
case ResponseType.Declined:
savingItem.Status = CalendarItemStatus.Cancelled;
savingItem.IsHidden = true;
break;
default:
break;
}
}
2025-02-16 11:54:23 +01:00
else
{
2026-01-03 19:33:36 +01:00
savingItem.Status = CalendarItemStatus.Accepted;
2025-02-16 11:54:23 +01:00
}
// Prepare attendees list
List<CalendarEventAttendee> attendees = null;
2025-02-16 11:54:23 +01:00
if (calendarEvent.Attendees != null)
{
2026-01-01 10:07:56 +01:00
// Pass the organizer's email address to properly identify the organizer in the attendees list
string organizerEmail = calendarEvent.Organizer?.EmailAddress?.Address;
attendees = calendarEvent.Attendees.Select(a => a.CreateAttendee(savingItemId, organizerEmail)).ToList();
}
2026-01-01 15:02:40 +01:00
// Prepare reminders list from Outlook event
List<Reminder> reminders = null;
if (calendarEvent.IsReminderOn.GetValueOrDefault() && calendarEvent.ReminderMinutesBeforeStart.HasValue)
{
var reminderMinutes = calendarEvent.ReminderMinutesBeforeStart.Value;
var reminderDurationInSeconds = reminderMinutes * 60; // Convert minutes to seconds
reminders = new List<Reminder>
{
new Reminder
{
Id = Guid.NewGuid(),
CalendarItemId = savingItemId,
DurationInSeconds = reminderDurationInSeconds,
ReminderType = CalendarItemReminderType.Popup
}
};
}
// Use CalendarService to create or update the event
if (isNewItem)
{
// New item - use CreateNewCalendarItemAsync
await CalendarService.CreateNewCalendarItemAsync(savingItem, attendees).ConfigureAwait(false);
}
else
{
// Existing item - use UpdateCalendarItemAsync
await CalendarService.UpdateCalendarItemAsync(savingItem, attendees).ConfigureAwait(false);
2025-02-16 11:54:23 +01:00
}
2026-01-01 15:02:40 +01:00
// Save reminders separately
await CalendarService.SaveRemindersAsync(savingItemId, reminders).ConfigureAwait(false);
}
}