diff --git a/EmailSearch/EmailSearchTools.cs b/EmailSearch/EmailSearchTools.cs index 3cd23be..29dca91 100644 --- a/EmailSearch/EmailSearchTools.cs +++ b/EmailSearch/EmailSearchTools.cs @@ -5,6 +5,7 @@ using System.Text; using OutlookApp = NetOffice.OutlookApi.Application; using NetOffice.OutlookApi; using NetOffice.OutlookApi.Enums; +using EmailSearch.SpamDetection; [McpServerToolType] public class EmailSearchTools @@ -75,6 +76,41 @@ public class EmailSearchTools } } + [McpServerTool, Description("Move an email to the Junk folder by subject and date. Use after SearchEmails to identify the email to move.")] + public static string MoveToJunk( + [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("Outlook folder to search for the email: Inbox, SentMail, Drafts, All, or any custom folder name (default Inbox)")] string folder = "Inbox") + { + try + { + if (!TryParseDate(date, out var targetDate)) + return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy"; + + using var outlookApp = new OutlookApp(); + var ns = outlookApp.GetNamespace("MAPI"); + var foldersToSearch = GetFoldersToSearch(ns, folder); + var junkFolder = ns.GetDefaultFolder(OlDefaultFolders.olFolderJunk); + + foreach (var mailFolder in foldersToSearch) + { + var mail = FindEmail(mailFolder, subject, targetDate); + if (mail != null) + { + var emailSubject = mail.Subject; + mail.Move(junkFolder); + return $"Successfully moved email '{emailSubject}' to Junk folder."; + } + } + + return "Email not found with the specified subject and date."; + } + catch (System.Exception ex) + { + return $"Error moving email to junk: {ex.Message}"; + } + } + [McpServerTool, Description("Read the full body of a specific email by subject and date. Use after SearchEmails to get complete email content.")] public static string ReadEmail( [Description("Exact or partial subject line to match")] string subject, @@ -105,6 +141,198 @@ public class EmailSearchTools } } + [McpServerTool, Description("Analyze a specific email for spam indicators. Returns spam score (0.0-1.0), spam likelihood, and detected red flags.")] + public static string AnalyzeSpam( + [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("Outlook folder: Inbox, SentMail, Drafts, DeletedItems, Junk, All, or any custom folder name (default All)")] string folder = "All") + { + try + { + if (!TryParseDate(date, out var targetDate)) + return $"Invalid date format '{date}'. Supported formats: yyyy-MM-dd, MM/dd/yyyy, dd/MM/yyyy"; + + using var outlookApp = new OutlookApp(); + var ns = outlookApp.GetNamespace("MAPI"); + var foldersToSearch = GetFoldersToSearch(ns, folder); + + foreach (var mailFolder in foldersToSearch) + { + var mail = FindEmail(mailFolder, subject, targetDate); + if (mail != null) + { + var detector = new SpamDetector(); + var result = detector.Analyze(mail); + return FormatSpamAnalysis(mail, result); + } + } + + return "Email not found with the specified subject and date."; + } + catch (System.Exception ex) + { + return $"Error analyzing email: {ex.Message}"; + } + } + + [McpServerTool, Description("Scan recent emails for spam and return a summary with spam scores. Useful for identifying potential spam in your inbox.")] + public static string ScanForSpam( + [Description("Number of days back to scan (default 7)")] int daysBack = 7, + [Description("Maximum number of emails to scan (default 50)")] int maxEmails = 50, + [Description("Minimum spam score to include in results (0.0-1.0, default 0.3)")] double minScore = 0.3, + [Description("Outlook folder to scan: Inbox, SentMail, Drafts, All, or custom folder name (default Inbox)")] string folder = "Inbox") + { + try + { + using var outlookApp = new OutlookApp(); + var ns = outlookApp.GetNamespace("MAPI"); + var foldersToSearch = GetFoldersToSearch(ns, folder); + var cutoffDate = DateTime.Now.AddDays(-daysBack); + var detector = new SpamDetector(); + + var results = new List<(EmailResult email, SpamAnalysisResult spam)>(); + + foreach (var mailFolder in foldersToSearch) + { + try + { + var items = mailFolder.Items; + items.Sort("[ReceivedTime]", true); + + var filter = $"[ReceivedTime] >= '{cutoffDate:MM/dd/yyyy}'"; + var filteredItems = items.Restrict(filter); + + foreach (var item in filteredItems) + { + if (results.Count >= maxEmails) + break; + + if (item is MailItem mail) + { + var spamResult = detector.Analyze(mail); + if (spamResult.FinalScore >= minScore) + { + var emailResult = EmailResult.FromMailItem(mail, mailFolder.Name); + results.Add((emailResult, spamResult)); + } + } + } + } + catch { } + + if (results.Count >= maxEmails) + break; + } + + if (results.Count == 0) + return $"No emails with spam score >= {minScore:P0} found in the last {daysBack} days."; + + // Sort by spam score descending + results = results.OrderByDescending(r => r.spam.FinalScore).ToList(); + + return FormatSpamScanResults(results, daysBack, minScore); + } + catch (System.Exception ex) + { + return $"Error scanning for spam: {ex.Message}"; + } + } + + private static string FormatSpamAnalysis(MailItem mail, SpamAnalysisResult result) + { + var output = new StringBuilder(); + output.AppendLine("=== SPAM ANALYSIS REPORT ==="); + output.AppendLine(); + output.AppendLine($"Subject: {mail.Subject}"); + output.AppendLine($"From: {mail.SenderName} <{mail.SenderEmailAddress}>"); + output.AppendLine($"Date: {mail.ReceivedTime:yyyy-MM-dd HH:mm}"); + output.AppendLine(); + output.AppendLine("--- SPAM SCORE ---"); + output.AppendLine($"Score: {result.FinalScore:P0}"); + output.AppendLine($"Likelihood: {result.SpamLikelihood}"); + output.AppendLine($"Predicted Spam: {(result.PredictedSpam ? "YES" : "No")}"); + output.AppendLine(); + + if (result.RedFlags.Count > 0) + { + output.AppendLine("--- RED FLAGS DETECTED ---"); + foreach (var flag in result.RedFlags) + { + output.AppendLine($" - {flag}"); + } + output.AppendLine(); + } + + if (result.Features != null) + { + output.AppendLine("--- SENDER ANALYSIS ---"); + output.AppendLine($"Display Name: {result.Features.DisplayName}"); + output.AppendLine($"Email Address: {result.Features.FromAddress}"); + output.AppendLine($"Domain: {result.Features.FromDomain}"); + output.AppendLine($"Free Email Provider: {(result.Features.FreeMailboxDomain ? "Yes" : "No")}"); + output.AppendLine($"Known/Trusted Domain: {(!result.Features.UnknownDomain ? "Yes" : "No")}"); + output.AppendLine($"Blocklisted: {(result.Features.IsBlocklisted ? "YES" : "No")}"); + output.AppendLine(); + + output.AppendLine("--- AUTHENTICATION ---"); + output.AppendLine($"SPF Failed: {(result.Features.SpfFail ? "YES" : "No")}"); + output.AppendLine($"DKIM Failed: {(result.Features.DkimFail ? "YES" : "No")}"); + output.AppendLine($"DMARC Failed: {(result.Features.DmarcFail ? "YES" : "No")}"); + output.AppendLine($"Reply-To Mismatch: {(result.Features.ReplyToDomainMismatch ? "YES" : "No")}"); + output.AppendLine(); + + output.AppendLine("--- CONTENT ANALYSIS ---"); + output.AppendLine($"URLs Found: {result.Features.UrlCount}"); + output.AppendLine($"Uses URL Shortener: {(result.Features.UsesShortener ? "YES" : "No")}"); + output.AppendLine($"IP-based URL: {(result.Features.HasIpLink ? "YES" : "No")}"); + output.AppendLine($"Suspicious TLD: {(result.Features.SuspiciousTld ? "YES" : "No")}"); + output.AppendLine($"Has Attachments: {(result.Features.HasAttachment ? "Yes" : "No")}"); + if (result.Features.HasAttachment) + output.AppendLine($"Attachment Risk: {result.Features.AttachmentRiskScore:P0}"); + output.AppendLine($"Keyword Bait: {(result.Features.KeywordBait ? "YES" : "No")}"); + output.AppendLine($"Has Tracking Pixel: {(result.Features.HasTrackingPixel ? "Yes" : "No")}"); + } + + return output.ToString(); + } + + private static string FormatSpamScanResults(List<(EmailResult email, SpamAnalysisResult spam)> results, int daysBack, double minScore) + { + var output = new StringBuilder(); + output.AppendLine($"=== SPAM SCAN RESULTS ==="); + output.AppendLine($"Scanned last {daysBack} days, showing {results.Count} email(s) with spam score >= {minScore:P0}"); + output.AppendLine(); + + foreach (var (email, spam) in results) + { + var scoreBar = new string('#', (int)(spam.FinalScore * 10)); + var emptyBar = new string('-', 10 - scoreBar.Length); + + output.AppendLine($"[{scoreBar}{emptyBar}] {spam.FinalScore:P0} - {spam.SpamLikelihood}"); + output.AppendLine($" Subject: {email.Subject}"); + output.AppendLine($" From: {email.Sender}"); + output.AppendLine($" Date: {email.ReceivedDate:yyyy-MM-dd HH:mm}"); + output.AppendLine($" Folder: {email.Folder}"); + + if (spam.RedFlags.Count > 0) + { + var topFlags = spam.RedFlags.Take(3); + output.AppendLine($" Flags: {string.Join("; ", topFlags)}"); + } + output.AppendLine(); + } + + var highSpam = results.Count(r => r.spam.FinalScore >= 0.7); + var mediumSpam = results.Count(r => r.spam.FinalScore >= 0.5 && r.spam.FinalScore < 0.7); + + output.AppendLine("--- SUMMARY ---"); + output.AppendLine($"High likelihood spam (>=70%): {highSpam}"); + output.AppendLine($"Medium likelihood spam (50-69%): {mediumSpam}"); + output.AppendLine($"Lower likelihood ({minScore:P0}-49%): {results.Count - highSpam - mediumSpam}"); + + return output.ToString(); + } + private static List GetFoldersToSearch(_NameSpace ns, string folder) { var folders = new List();