Public Access
171 lines
5.5 KiB
C#
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);
|
|
}
|