feat: add time parameter to MoveToJunk, ReadEmail, and AnalyzeSpam

When multiple emails share the same subject and date, the optional time
parameter disambiguates by finding the closest match to the specified
time (e.g., '13:46', '1:46 PM').

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 13:11:23 -05:00
parent 0bddb21f8c
commit 27fc3b65e9

View File

@@ -80,13 +80,16 @@ public class EmailSearchTools
public static string MoveToJunk( public static string MoveToJunk(
[Description("Exact or partial subject line to match")] string subject, [Description("Exact or partial subject line to match")] string subject,
[Description("Date of the email (supports: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy)")] string date, [Description("Date of the email (supports: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy)")] string date,
[Description("Outlook folder to search for the email: Inbox, SentMail, Drafts, All, or any custom folder name (default Inbox)")] string folder = "Inbox") [Description("Outlook folder to search for the email: Inbox, SentMail, Drafts, All, or any custom folder name (default Inbox)")] string folder = "Inbox",
[Description("Optional time to find the closest match when multiple emails share the same subject and date (e.g., '13:46', '1:46 PM')")] string? time = null)
{ {
try try
{ {
if (!TryParseDate(date, out var targetDate)) if (!TryParseDate(date, out var targetDate))
return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy"; return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy";
TimeSpan? targetTime = TryParseTime(time);
using var outlookApp = new OutlookApp(); using var outlookApp = new OutlookApp();
var ns = outlookApp.GetNamespace("MAPI"); var ns = outlookApp.GetNamespace("MAPI");
var foldersToSearch = GetFoldersToSearch(ns, folder); var foldersToSearch = GetFoldersToSearch(ns, folder);
@@ -94,7 +97,7 @@ public class EmailSearchTools
foreach (var mailFolder in foldersToSearch) foreach (var mailFolder in foldersToSearch)
{ {
var mail = FindEmail(mailFolder, subject, targetDate); var mail = FindEmail(mailFolder, subject, targetDate, targetTime);
if (mail != null) if (mail != null)
{ {
var emailSubject = mail.Subject; var emailSubject = mail.Subject;
@@ -115,20 +118,23 @@ public class EmailSearchTools
public static string ReadEmail( public static string ReadEmail(
[Description("Exact or partial subject line to match")] string subject, [Description("Exact or partial subject line to match")] string subject,
[Description("Date of the email (supports: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy)")] string date, [Description("Date of the email (supports: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy)")] string date,
[Description("Outlook folder: Inbox, SentMail, Drafts, DeletedItems, Junk, All, or any custom folder name (default All)")] string folder = "All") [Description("Outlook folder: Inbox, SentMail, Drafts, DeletedItems, Junk, All, or any custom folder name (default All)")] string folder = "All",
[Description("Optional time to find the closest match when multiple emails share the same subject and date (e.g., '13:46', '1:46 PM')")] string? time = null)
{ {
try try
{ {
if (!TryParseDate(date, out var targetDate)) if (!TryParseDate(date, out var targetDate))
return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy"; return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy";
TimeSpan? targetTime = TryParseTime(time);
using var outlookApp = new OutlookApp(); using var outlookApp = new OutlookApp();
var ns = outlookApp.GetNamespace("MAPI"); var ns = outlookApp.GetNamespace("MAPI");
var foldersToSearch = GetFoldersToSearch(ns, folder); var foldersToSearch = GetFoldersToSearch(ns, folder);
foreach (var mailFolder in foldersToSearch) foreach (var mailFolder in foldersToSearch)
{ {
var mail = FindEmail(mailFolder, subject, targetDate); var mail = FindEmail(mailFolder, subject, targetDate, targetTime);
if (mail != null) if (mail != null)
return FormatFullEmail(mail); return FormatFullEmail(mail);
} }
@@ -145,20 +151,23 @@ public class EmailSearchTools
public static string AnalyzeSpam( public static string AnalyzeSpam(
[Description("Exact or partial subject line to match")] string subject, [Description("Exact or partial subject line to match")] string subject,
[Description("Date of the email (supports: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy)")] string date, [Description("Date of the email (supports: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy)")] string date,
[Description("Outlook folder: Inbox, SentMail, Drafts, DeletedItems, Junk, All, or any custom folder name (default All)")] string folder = "All") [Description("Outlook folder: Inbox, SentMail, Drafts, DeletedItems, Junk, All, or any custom folder name (default All)")] string folder = "All",
[Description("Optional time to find the closest match when multiple emails share the same subject and date (e.g., '13:46', '1:46 PM')")] string? time = null)
{ {
try try
{ {
if (!TryParseDate(date, out var targetDate)) if (!TryParseDate(date, out var targetDate))
return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy"; return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy";
TimeSpan? targetTime = TryParseTime(time);
using var outlookApp = new OutlookApp(); using var outlookApp = new OutlookApp();
var ns = outlookApp.GetNamespace("MAPI"); var ns = outlookApp.GetNamespace("MAPI");
var foldersToSearch = GetFoldersToSearch(ns, folder); var foldersToSearch = GetFoldersToSearch(ns, folder);
foreach (var mailFolder in foldersToSearch) foreach (var mailFolder in foldersToSearch)
{ {
var mail = FindEmail(mailFolder, subject, targetDate); var mail = FindEmail(mailFolder, subject, targetDate, targetTime);
if (mail != null) if (mail != null)
{ {
var detector = new SpamDetector(); var detector = new SpamDetector();
@@ -436,7 +445,7 @@ public class EmailSearchTools
} }
} }
private static MailItem? FindEmail(MAPIFolder folder, string subject, DateTime targetDate) private static MailItem? FindEmail(MAPIFolder folder, string subject, DateTime targetDate, TimeSpan? targetTime = null)
{ {
var items = folder.Items; var items = folder.Items;
items.Sort("[ReceivedTime]", true); items.Sort("[ReceivedTime]", true);
@@ -445,6 +454,31 @@ public class EmailSearchTools
var filter = $"[ReceivedTime] >= '{targetDate:MM/dd/yyyy}' AND [ReceivedTime] < '{targetDate.AddDays(1):MM/dd/yyyy}'"; var filter = $"[ReceivedTime] >= '{targetDate:MM/dd/yyyy}' AND [ReceivedTime] < '{targetDate.AddDays(1):MM/dd/yyyy}'";
var filteredItems = items.Restrict(filter); var filteredItems = items.Restrict(filter);
if (targetTime.HasValue)
{
// Find the email closest to the specified time
var targetDateTime = targetDate.Date + targetTime.Value;
MailItem? bestMatch = null;
var bestDiff = TimeSpan.MaxValue;
foreach (var item in filteredItems)
{
if (item is MailItem mail &&
mail.Subject != null &&
mail.Subject.Contains(subject, StringComparison.OrdinalIgnoreCase))
{
var diff = (mail.ReceivedTime - targetDateTime).Duration();
if (diff < bestDiff)
{
bestDiff = diff;
bestMatch = mail;
}
}
}
return bestMatch;
}
foreach (var item in filteredItems) foreach (var item in filteredItems)
{ {
if (item is MailItem mail && if (item is MailItem mail &&
@@ -664,6 +698,17 @@ public class EmailSearchTools
out result); out result);
} }
private static TimeSpan? TryParseTime(string? timeString)
{
if (string.IsNullOrWhiteSpace(timeString))
return null;
if (DateTime.TryParse(timeString, CultureInfo.InvariantCulture, DateTimeStyles.NoCurrentDateDefault, out var parsed))
return parsed.TimeOfDay;
return null;
}
private static OlImportance? ParseImportance(string? importance) private static OlImportance? ParseImportance(string? importance)
{ {
if (string.IsNullOrEmpty(importance)) return null; if (string.IsNullOrEmpty(importance)) return null;