Last active
August 31, 2023 15:29
-
-
Save tommck/7bf09e95da369ecd488ed4f0f7c15a88 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
$emailMessageSubject = "OutlookSync Meetings"; | |
$syncRetryIntervalInSeconds = 10; | |
Function Send-MeetingsEmail() { | |
param ( | |
$StartDate = (Get-Date -Hour 0 -Minute 00 -Second 00), | |
$EndDate = (Get-Date -Hour 0 -Minute 00 -Second 00).AddDays(14), | |
[Parameter(Mandatory = $true)] | |
[string] | |
$ExcludedCategory, | |
[switch] | |
$SkipFocusTime, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$Email, | |
[switch] | |
$IncludeBody, | |
[switch] | |
$DryRun | |
) | |
$ErrorActionPreference = 'Stop' | |
Add-Type -assembly "Microsoft.Office.Interop.Outlook" | Out-Null | |
$outlook = New-Object -ComObject outlook.application | |
Send-MeetingsEmailInternal ` | |
-outlook $outlook ` | |
-StartDate $StartDate ` | |
-EndDate $EndDate ` | |
-ExcludedCategory $ExcludedCategory ` | |
-SkipFocusTime $SkipFocusTime ` | |
-Email $Email ` | |
-IncludeBody $IncludeBody ` | |
-DryRun $DryRun | |
} | |
Function Import-Meetings() { | |
param( | |
[string] | |
[Parameter(Mandatory = $true)] | |
$FileName, | |
[string] | |
[Parameter(Mandatory = $true)] | |
$Recipient, | |
[int] | |
$ReminderMinutes = 5, | |
[switch] | |
$AddReminders, | |
[string] | |
[Parameter(Mandatory = $true)] | |
$Category, | |
# Make meetings private | |
[switch] | |
$Private, | |
# Ability to delete all meetings in date range with the same category | |
[switch] | |
$DeleteExistingInCategory, | |
[switch] | |
$DryRun | |
) | |
$ErrorActionPreference = 'Stop' | |
Add-Type -assembly "Microsoft.Office.Interop.Outlook" | Out-Null | |
$ol = New-Object -ComObject Outlook.Application | |
Import-MeetingsInternal ` | |
-Category $Category ` | |
-outlook $ol ` | |
-FileName $FileName ` | |
-Recipient $Recipient ` | |
-ReminderMinutes $ReminderMinutes ` | |
-AddReminders $AddReminders ` | |
-Private $Private ` | |
-DeleteExistingInCategory $DeleteExistingInCategory ` | |
-DryRun $DryRun | |
} | |
Function Get-Meetings() { | |
param ( | |
[Parameter(Mandatory = $true)] | |
$Outlook, # TODO: Type? | |
[datetime] | |
$StartDate, | |
[datetime] | |
$EndDate, | |
[string] | |
$ExcludedCategory, | |
[boolean] | |
$SkipFocusTime | |
) | |
$ErrorActionPreference = 'Stop' | |
$namespace = $Outlook.GetNameSpace("MAPI") | |
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type] | |
Write-Host "Gathering From $($StartDate.ToShortDateString()) To $($EndDate.ToShortDateString())..." | |
$folder = $namespace.getDefaultFolder($olFolders::olFolderCalendar) | |
$filter = "[Start] >= '" ` | |
+ $StartDate.ToString("g") ` | |
+ "' AND [End] <= '"` | |
+ $EndDate.ToString("g") + "'"; | |
if ($SkipFocusTime) { | |
$filter += " AND [Subject] <> 'Focus time'" | |
} | |
$calItems = $folder.Items; | |
$calItems.IncludeRecurrences = $true; | |
$calItems.Sort("[Start]") | |
$filteredRecurring = $calItems.restrict($filter) | |
$recurringToUse = $filteredRecurring | |
if ($ExcludedCategory) { | |
$recurringToUse = $recurringToUse ` | |
| Where-Object { -not $_.Categories.Contains($ExcludedCategory) } | |
} | |
$recurringToUse | |
} | |
Function Send-MeetingsEmailInternal() { | |
param ( | |
$outlook, | |
[Parameter(Mandatory = $true)] | |
[datetime] | |
$StartDate, | |
[Parameter(Mandatory = $true)] | |
[datetime] | |
$EndDate, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$ExcludedCategory, | |
[boolean] | |
$SkipFocusTime, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$Email, | |
[boolean] | |
$IncludeBody | |
) | |
$array = Get-Meetings $outlook $StartDate $EndDate $ExcludedCategory $SkipFocusTime | |
$items = @(); | |
foreach ($appt in $array) { | |
$items += @{ | |
Subject = $appt.Subject | |
Start = $appt.Start.ToString() | |
End = $appt.End.ToString() | |
IsRecurring = $false | |
ResponseStatus = $appt.ResponseStatus | |
Body = if ($IncludeBody) { $appt.Body } else { $null } | |
} | |
} | |
$json = @{ | |
StartDate = $StartDate.ToString() | |
EndDate = $EndDate.ToString() | |
Meetings = $items | |
} | ConvertTo-Json | |
$outFile = New-Item -Path "meetings.json" -ItemType File -Force | |
$json | Out-File $outFile; | |
$newMail = $outlook.CreateItem('olMailItem'); | |
$newMail.To = $Email; | |
$newMail.Subject = $emailMessageSubject; | |
$newMail.Body = "Save the attachment to sync things"; | |
$attachmentTypes = "Microsoft.Office.Interop.Outlook.OlAttachmentType" -as [type] | |
$newMail.Attachments.Add($outFile.FullName, $attachmentTypes.olByValue) | |
$newMail.Send(); | |
} | |
Function Import-MeetingsInternal() { | |
param( | |
$outlook, | |
[string] | |
[Parameter(Mandatory = $true)] | |
$FileName, | |
[string] | |
[Parameter(Mandatory = $true)] | |
$Recipient, | |
[int] | |
$ReminderMinutes = 5, | |
[boolean] | |
$AddReminders, | |
[string] | |
[Parameter(Mandatory = $true)] | |
$Category, | |
# Make meetings private | |
[boolean] | |
$Private, | |
# Ability to delete all meetings in date range with the same category | |
[boolean] | |
$DeleteExistingInCategory, | |
[boolean] | |
$DryRun | |
) | |
$contents = (Get-Content $FileName | Out-String | ConvertFrom-Json) | |
$start = [DateTime]::Parse($contents.StartDate) | |
$end = [DateTime]::Parse($contents.EndDate).AddDays(1) | |
# get meetings for date range | |
$existingMeetings = Get-Meetings -Outlook $outlook -StartDate $start -EndDate $end | |
if ($DeleteExistingInCategory) { | |
$catMeetings = $existingMeetings | ` | |
Where-Object { $_.Categories -contains $Category }; | |
# TODO: Add "Are You Sure?" | |
if ($catMeetings) { | |
Write-Host "CAT MEETINGS:" | |
foreach ($m in $catMeetings) { | |
Write-Host -ForegroundColor Yellow "Deleting Meeting '$($m.Subject)'" | |
if (!$DryRun) { | |
try { | |
$m.Delete(); | |
} | |
catch { | |
Write-Error "Error Deleting Item:", $_ | |
} | |
} | |
} | |
} | |
} | |
Write-Host "Creating New Appointments" | |
# TODO: filter recurring - need to use "Pattern" stuff for those | |
$nonRecurring = $contents.Meetings | Where-Object { $_.IsRecurring -eq $false } | |
foreach ($appt in $nonRecurring) { | |
# FIRST - Check for the existing meeting by Subject + Date | |
$existing = $existingMeetings | ` | |
Where-Object { $_.Subject -eq $appt.Subject -and [DateTime]::Parse($_.Start).Date -eq [DateTime]::Parse($appt.Start.ToString()).Date } | ` | |
Select-Object -First 1 | |
if (!$existing) { | |
$sub = $appt.Subject | |
Write-Host "Importing Meeting '$sub'", $(If ($Private) { "as Private" } else { "" }) | |
if (!$DryRun) { | |
$meeting = $outlook.CreateItem('olAppointmentItem') | |
$meeting.Subject = $sub | |
$meeting.Categories = $Category | |
$meeting.Body = if ($appt.Body) { $appt.Body } else { 'Placeholder' } | |
$meeting.Location = '' | |
$meeting.ReminderSet = $AddReminders | |
$meeting.Importance = 1 | |
$meeting.MeetingStatus = [Microsoft.Office.Interop.Outlook.OlMeetingStatus]::olMeeting | |
$meeting.Recipients.Add($Recipient) | |
$meeting.ReminderMinutesBeforeStart = $ReminderMinutes | |
$meeting.Start = [DateTime]::Parse($appt.Start.ToString()) | |
$meeting.End = [DateTime]::Parse($appt.End) | |
$meeting.ResponseStatus = $appt.ResponseStatus -as [Microsoft.Office.Interop.outlook.OlResponseStatus] | |
if ($Private) { | |
$meeting.Sensitivity = [Microsoft.Office.Interop.Outlook.OlSensitivity]::olPrivate; | |
} | |
$meeting.Send() | |
} | |
} | |
} | |
Write-Host "Finished Importing" | |
} | |
function Sync-OutlookMeetings() { | |
[CmdletBinding()] | |
param ( | |
[Parameter(Mandatory = $true)] | |
[string] | |
$ExternalCategory, | |
[switch] | |
$SkipFocusTime, | |
[Parameter(Mandatory = $true)] | |
[string] | |
$ExternalEmail, | |
[string] | |
[Parameter(Mandatory = $true)] | |
$InternalEmail, | |
[switch] | |
$IncludeBody, | |
[int] | |
$ReminderMinutes = 5, | |
[switch] | |
$AddReminders, | |
# Make meetings private | |
[switch] | |
$Private, | |
# Ability to delete all meetings in date range with the same category | |
[switch] | |
$DeleteExistingInCategory, | |
[switch] | |
$DryRun | |
) | |
$ErrorActionPreference = 'Stop' | |
# $CommandName = $PSCmdlet.MyInvocation.InvocationName; | |
# $ParameterList = (Get-Command -Name $CommandName).Parameters; | |
# Write-Host -ForegroundColor Yellow "CHECKING PARAMS" | |
# foreach ($Parameter in $ParameterList) { | |
# Get-Variable -Name $Parameter.Values.Name -ErrorAction SilentlyContinue; | |
# } | |
$StartDate = (Get-Date -Hour 0 -Minute 00 -Second 00) | |
$EndDate = $StartDate.AddDays(14) | |
# get outlook instance | |
Add-Type -assembly "Microsoft.Office.Interop.Outlook" | Out-Null | |
$outlook = New-Object -ComObject outlook.application | |
# send outlook meetings to external recipient | |
Send-MeetingsEmailInternal ` | |
-outlook $outlook ` | |
-StartDate $StartDate ` | |
-EndDate $EndDate ` | |
-ExcludedCategory $ExternalCategory ` | |
-SkipFocusTime $SkipFocusTime ` | |
-Email $ExternalEmail ` | |
-IncludeBody $IncludeBody | |
# watch the outlook inbox for a return email from the external system | |
$email = Get-SyncEmailMessage($outlook) | |
# save attachment as temp file (it's 1-based) | |
$attachment = $email.Attachments[1] | |
# $email.Attachments | Where-Object {$_.FileName -eq $attachmentFileName} | |
Write-Host "Processing attachment $($attachment.FileName)" | |
$tempFile = Join-Path "$ENV:Temp" "meetingsToSync.json" | |
$attachment.SaveAsFile($tempFile); | |
# import those meetings. | |
Import-MeetingsInternal ` | |
-Category $ExternalCategory ` | |
-outlook $outlook ` | |
-FileName $tempFile ` | |
-Recipient $InternalEmail ` | |
-ReminderMinutes $ReminderMinutes ` | |
-AddReminders $AddReminders ` | |
-Private $Private ` | |
-DeleteExistingInCategory $DeleteExistingInCategory ` | |
-DryRun $DryRun | |
# Cleanup | |
$email.UnRead = $false | |
$email.Delete() | |
Remove-item $tempFile | |
} | |
Function Get-SyncEmailMessage() { | |
param( | |
$outlook | |
) | |
# TODO: verify sender, etc. | |
$namespace = $outlook.GetNameSpace("MAPI") | |
$inbox = $namespace.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox) | |
$email = $null | |
do { | |
Write-Host -ForegroundColor Cyan "Checking Inbox for new message" | |
$email = $inbox.Items | Where-Object { $_.Subject -like $emailMessageSubject } | Select-Object -First 1 | |
if (-not $email) { | |
Write-Host -ForegroundColor Cyan "Sleeping for $syncRetryIntervalInSeconds seconds..." | |
Start-Sleep -Seconds $syncRetryIntervalInSeconds | |
} | |
# TODO: Bail out after X retries. | |
} | |
until ($email) | |
} | |
Export-ModuleMember -Function Send-MeetingsEmail, Import-Meetings, Sync-OutlookMeetings |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment