淺談 Exchange 退信通知處理自動化
| | 1 | |
用 Exchange 或任何郵件伺服器寄信,當信件因種種原因無法送達,Outlook 會收到一封來自 Mail Delivery Subsystem <MAILER-DAEMON> 的退信通知,NDR 格式及內容沒有統一規範,依伺服器版本而異,但都會包含有問題的 Email 地址及無法送達原因,
這封退信通知有個專業術語叫 NDR Non-Delivery Report,Exchange 的 NDR 類似這樣:
傳遞至下列收件者或群組失敗:
user1@gmail.com
因為權限或安全性問題,所以無法傳遞您的郵件。郵件可能遭到仲裁者拒絕,郵件地址可能只接受某些寄件者的電子郵件,或者有其他限制阻止傳遞郵件。
user2@hotmail.com
收件者的信箱已滿,目前無法接受郵件。請稍後再試著重新傳送郵件,或直接與收件者連絡。
user3@yahoo.com.tw
收件者的信箱有問題。請嘗試重新傳送您的郵件。如果問題持續發生,請與您的電子郵件系統管理員連絡。
下列組織已拒絕您的郵件: gmail-smtp-in.l.google.com
系統管理員的診斷資訊:
產生的伺服器: smtp.example.com
... 下方附帶一堆偵錯資訊 ...
基本上,NDR 有 Email 地址及退信原因可供我們判斷該地址是錯誤、失效還是信箱爆了,再決定處置方式。要如何從一堆 NDR 中提取關鍵資訊,讓整理工作更有效率?這篇來分享我的做法。
假設你手上累積了一堆 NDR,第一步可先用 Outlook 篩選寄件者 MAILER-DAEMON 將它們搬到特定資料夾方便用程式批次處理。
註:NDR 在 Exchange 是一種特別型別(REPORT.IPM.Note.NDR),非一般標準郵件,雖然能用郵件規則設定寄件者條件搬檔,但郵件規在收到 NDR 時不會生效。 參考)
我寫了一段 PowerShell Script 可從指定資料夾擷取每一封 NDR 中的 Email 及退信原因,彙整成一份 CSV:(註:程式會用到 .NET Framework API 操作 Outlook COM+,需以 PowerShell 5.1 執行,不支援 pwsh 7+ 跑)
$ErrorActionPreference = 'STOP'
$outlook = [System.Runtime.InteropServices.Marshal]::GetActiveObject('Outlook.Application')
$ns = $outlook.GetNamespace('MAPI')
# $inbox = $ns.GetDefaultFolder([Microsoft.Office.Interop.Outlook.OlDefaultFolders]::olFolderInbox)
$mailbox = $ns.Folders | Where-Object { $_.Name -eq 'amdin@example.com' }
# $ndrFolder = $mailbox.Folders | Where-Object { $_.Name -eq '刪除的郵件' }
$ndrFolder = $mailbox.Folders | Where-Object { $_.Name -eq 'NDR資料夾' }
$header = '傳遞至下列收件者或群組失敗:'
$headerLen = $header.Length
$reportTime = $null
$list = @()
$ndrFolder.Items | ForEach-Object {
$item = $_
if ($item -is [Microsoft.Office.Interop.Outlook.ReportItem]) {
$reportTime = " " + $item.CreationTime.ToString('yyyy-MM-dd HH:mm:ss')
$body = $item.Body
$end = $body.IndexOf('系統管理員的診斷資訊:')
if ($end -gt 0) {
$body = $body.Substring($headerLen, $end - $headerLen)
}
$lines = $body.Replace("`r", '').Split("`n")
$email = ''
$lines | ForEach-Object {
$line = $_.Trim()
if ($email) {
$list += [PSCustomObject]@{
Email = $email
Time = $reportTime
Reason = $line
}
$email = ''
}
else {
$match = [regex]::Match($line, '^(?<m>[\w-\.]+@([\w-]+\.)+[\w-]{2,4})')
if ($match.Success) {
$email = $match.Groups['m'].Value
}
}
}
}
}
$tmpCsvPath = [IO.Path]::GetTempFileName() + ".csv"
$list | Sort-Object Email, Time | ConvertTo-Csv -NoTypeInformation | Out-File $tmpCsvPath -Encoding utf8
Start-Process excel.exe $tmpCsvPath
程式原理不難,先找到信箱,找出放 NDR 的資料夾,逐一檢查資料夾裡的項目是否為 Microsoft.Office.Interop.Outlook.ReportItem,若是則取出內容進行解析,依據 Exchage NDR 格式擷取出 Email 及無法寄達原因,最後匯出 CSV 呼叫 Excel 開啟。
產生的 CSV 如下,同一 Email 的多次退信記錄列在一起,方便快速 Email 是一時信箱滿了還是地址有錯,亦或為亂填的髒資料,再決定要如何處置。
就醬,用不到 50 行 PowerShell 可省下大量的人工處理時間。
雖然要自己寫程式,與這幾年興起的 Low Code/No Code/RPA 主流背道而馳(延伸閱讀:關於 RPA (機器人流程自動化),我說的其實是...),但這仍是熱愛 Coding 的我最偏好的作業自動化實踐方式。
最後,順手整理常見退信原因以備不時之需:
- 因為權限或安全性問題,所以無法傳遞您的郵件。郵件可能遭到仲裁者拒絕,郵件地址可能只接受某些寄件者的電子郵件,或者有其他限制阻止傳遞郵件。
- 收件者的信箱已滿,目前無法接受郵件。請稍後再試著重新傳送郵件,或直接與收件者連絡。
- 收件者的信箱有問題。請嘗試重新傳送此郵件。如果問題持續發生,請與您的電子郵件系統管理員連絡。
- 收件者的信箱有問題。請嘗試重新傳送您的郵件。如果問題持續發生,請與您的電子郵件系統管理員連絡。
- 收件者的電子郵件地址不正確。請檢查電子郵件地址,然後嘗試重新傳送郵件。如果問題持續發生,請連絡電子郵件系統管理員。
- 伺服器嘗試傳遞此郵件但不成功,已停止嘗試。收件者的郵件伺服器可能暫時離線或暫時無法接收郵件。如果問題持續發生,請連絡您的電子郵件系統管理員。
- 找不到您輸入的電子郵件地址。請檢查收件者的電子郵件地址,然後嘗試重新傳送郵件。如果問題持續發生,請連絡您的電子郵件系統管理員。
- 無法傳遞您的郵件。請稍後再嘗試重新傳送。如果問題持續發生,請連絡電子郵件系統管理員。
- 傳遞此郵件時發生通訊失敗。請稍後再嘗試重新傳送郵件。如果問題持續發生,請連絡電子郵件系統管理員。
- 傳遞此郵件至此電子郵件地址時發生通訊失敗。請稍後再嘗試重新傳送郵件。如果問題持續發生,請連絡電子郵件系統管理員。
This blog discusses using PowerShell to automate processing Non-Delivery Reports (NDR) in Outlook, extracting email addresses and failure reasons into a CSV for efficient handling.
Comments
# by 阿吉
不少公司的 Exchange 會關閉 MAPI, POP3 等協定。 之前那這 code 繞 AI 幫忙轉成 pwsh 7+ 可以用的,結果卡在協定上。