Files
meeting-assistant/MeetingAssistant/MeetingNotes/OutlookClassicMeetingMetadataProvider.Windows.cs
T
codex aecef30627
PR and Push Build/Test / build-and-test (push) Successful in 9m19s
Archive completed meeting assistant changes
2026-06-26 13:15:47 +02:00

171 lines
5.5 KiB
C#

using System.Diagnostics;
using MeetingAssistant.Calendar;
namespace MeetingAssistant.MeetingNotes;
public sealed class OutlookClassicMeetingMetadataProvider : IMeetingMetadataProvider, IMeetingMetadataDiagnosticProvider
{
private readonly ILogger<OutlookClassicMeetingMetadataProvider> logger;
public OutlookClassicMeetingMetadataProvider(ILogger<OutlookClassicMeetingMetadataProvider> logger)
{
this.logger = logger;
}
public Task<MeetingMetadata?> GetCurrentMeetingAsync(
DateTimeOffset startedAt,
CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
try
{
return OutlookClassicCom.RunStaAsync(
() => GetCurrentMeeting(startedAt),
cancellationToken,
"Meeting Assistant Outlook COM Lookup");
}
catch (Exception exception) when (exception is not OperationCanceledException)
{
logger.LogDebug(exception, "Outlook Classic meeting metadata was not available");
return Task.FromResult<MeetingMetadata?>(null);
}
}
public async Task<MeetingMetadataDiagnosticResult> DiagnoseCurrentMeetingAsync(
DateTimeOffset startedAt,
TimeSpan timeout,
CancellationToken cancellationToken)
{
var stopwatch = Stopwatch.StartNew();
try
{
var lookup = OutlookClassicCom.RunStaAsync(
() => GetCurrentMeeting(startedAt),
cancellationToken,
"Meeting Assistant Outlook COM Lookup");
var metadata = timeout > TimeSpan.Zero
? await lookup.WaitAsync(timeout, cancellationToken)
: await lookup.WaitAsync(cancellationToken);
stopwatch.Stop();
return new MeetingMetadataDiagnosticResult(
nameof(OutlookClassicMeetingMetadataProvider),
startedAt,
metadata is not null,
false,
stopwatch.ElapsedMilliseconds,
metadata,
metadata is null ? "No matching Teams appointment was found." : null);
}
catch (TimeoutException exception)
{
stopwatch.Stop();
return new MeetingMetadataDiagnosticResult(
nameof(OutlookClassicMeetingMetadataProvider),
startedAt,
false,
true,
stopwatch.ElapsedMilliseconds,
null,
exception.Message);
}
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
{
throw;
}
catch (Exception exception)
{
stopwatch.Stop();
return new MeetingMetadataDiagnosticResult(
nameof(OutlookClassicMeetingMetadataProvider),
startedAt,
false,
false,
stopwatch.ElapsedMilliseconds,
null,
exception.Message);
}
}
private MeetingMetadata? GetCurrentMeeting(DateTimeOffset startedAt)
{
using var calendar = OutlookClassicCom.OpenDefaultCalendar(logger, "metadata lookup");
if (calendar is null)
{
return null;
}
var startedLocal = startedAt.LocalDateTime;
var windowStart = startedLocal.AddHours(-4);
var windowEnd = startedLocal.AddMinutes(5);
var candidates = new List<OutlookAppointmentCandidate>();
foreach (var item in OutlookClassicCom.EnumerateItems(calendar.Items))
{
if (item is null)
{
continue;
}
var keepAppointment = false;
try
{
if (!OutlookClassicCom.IsAppointment(item))
{
continue;
}
var start = Convert.ToDateTime(OutlookClassicCom.GetProperty(item, "Start"));
if (start > windowEnd)
{
break;
}
var end = Convert.ToDateTime(OutlookClassicCom.GetProperty(item, "End"));
if (end < windowStart ||
OutlookClassicCom.IsCanceledAppointment(item) ||
!OutlookClassicAppointmentMetadata.IsTeamsAppointment(item))
{
continue;
}
candidates.Add(new OutlookAppointmentCandidate(item, start, end));
keepAppointment = true;
}
finally
{
if (!keepAppointment)
{
OutlookClassicCom.ReleaseComObject(item);
}
}
}
try
{
var selected = OutlookMeetingCandidateSelector.Select(
candidates,
startedLocal,
candidate => candidate.Start,
candidate => candidate.End);
if (selected is null)
{
return null;
}
return OutlookClassicAppointmentMetadata.CreateMetadata(selected.Appointment, selected.End);
}
finally
{
foreach (var candidate in candidates)
{
OutlookClassicCom.ReleaseComObject(candidate.Appointment);
}
}
}
private sealed record OutlookAppointmentCandidate(
object Appointment,
DateTime Start,
DateTime End);
}