diff --git a/src/OpenClaw.Shared/NotificationCategorizer.cs b/src/OpenClaw.Shared/NotificationCategorizer.cs index 8559d69..e832638 100644 --- a/src/OpenClaw.Shared/NotificationCategorizer.cs +++ b/src/OpenClaw.Shared/NotificationCategorizer.cs @@ -87,29 +87,37 @@ public class NotificationCategorizer /// public static (string title, string type) ClassifyByKeywords(string text) { - var lower = text.ToLowerInvariant(); - if (lower.Contains("blood sugar") || lower.Contains("glucose") || - lower.Contains("cgm") || lower.Contains("mg/dl")) + // Use OrdinalIgnoreCase overloads to avoid allocating a lowercased copy of `text`. + if (text.Contains("blood sugar", StringComparison.OrdinalIgnoreCase) || + text.Contains("glucose", StringComparison.OrdinalIgnoreCase) || + text.Contains("cgm", StringComparison.OrdinalIgnoreCase) || + text.Contains("mg/dl", StringComparison.OrdinalIgnoreCase)) return ("🩸 Blood Sugar Alert", "health"); - if (lower.Contains("urgent") || lower.Contains("critical") || - lower.Contains("emergency")) + if (text.Contains("urgent", StringComparison.OrdinalIgnoreCase) || + text.Contains("critical", StringComparison.OrdinalIgnoreCase) || + text.Contains("emergency", StringComparison.OrdinalIgnoreCase)) return ("🚨 Urgent Alert", "urgent"); - if (lower.Contains("reminder")) + if (text.Contains("reminder", StringComparison.OrdinalIgnoreCase)) return ("⏰ Reminder", "reminder"); - if (lower.Contains("stock") || lower.Contains("in stock") || - lower.Contains("available now")) + if (text.Contains("stock", StringComparison.OrdinalIgnoreCase) || + text.Contains("in stock", StringComparison.OrdinalIgnoreCase) || + text.Contains("available now", StringComparison.OrdinalIgnoreCase)) return ("📦 Stock Alert", "stock"); - if (lower.Contains("email") || lower.Contains("inbox") || - lower.Contains("gmail")) + if (text.Contains("email", StringComparison.OrdinalIgnoreCase) || + text.Contains("inbox", StringComparison.OrdinalIgnoreCase) || + text.Contains("gmail", StringComparison.OrdinalIgnoreCase)) return ("📧 Email", "email"); - if (lower.Contains("calendar") || lower.Contains("meeting") || - lower.Contains("event")) + if (text.Contains("calendar", StringComparison.OrdinalIgnoreCase) || + text.Contains("meeting", StringComparison.OrdinalIgnoreCase) || + text.Contains("event", StringComparison.OrdinalIgnoreCase)) return ("📅 Calendar", "calendar"); - if (lower.Contains("error") || lower.Contains("failed") || - lower.Contains("exception")) + if (text.Contains("error", StringComparison.OrdinalIgnoreCase) || + text.Contains("failed", StringComparison.OrdinalIgnoreCase) || + text.Contains("exception", StringComparison.OrdinalIgnoreCase)) return ("⚠️ Error", "error"); - if (lower.Contains("build") || lower.Contains("ci ") || - lower.Contains("deploy")) + if (text.Contains("build", StringComparison.OrdinalIgnoreCase) || + text.Contains("ci ", StringComparison.OrdinalIgnoreCase) || + text.Contains("deploy", StringComparison.OrdinalIgnoreCase)) return ("🔨 Build", "build"); return ("🤖 OpenClaw", "info"); } diff --git a/src/OpenClaw.Shared/OpenClawGatewayClient.cs b/src/OpenClaw.Shared/OpenClawGatewayClient.cs index 0e21836..e044e90 100644 --- a/src/OpenClaw.Shared/OpenClawGatewayClient.cs +++ b/src/OpenClaw.Shared/OpenClawGatewayClient.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Frozen; using System.Collections.Generic; using System.Text.Json; using System.Threading; @@ -1569,23 +1570,25 @@ private static (string title, string type) ClassifyNotification(string text) // --- Utility --- - private static ActivityKind ClassifyTool(string toolName) - { - return toolName.ToLowerInvariant() switch - { - "exec" => ActivityKind.Exec, - "read" => ActivityKind.Read, - "write" => ActivityKind.Write, - "edit" => ActivityKind.Edit, - "web_search" => ActivityKind.Search, - "web_fetch" => ActivityKind.Search, - "browser" => ActivityKind.Browser, - "message" => ActivityKind.Message, - "tts" => ActivityKind.Tool, - "image" => ActivityKind.Tool, - _ => ActivityKind.Tool - }; - } + // FrozenDictionary gives O(1) case-insensitive lookup without allocating a + // lowercased copy of toolName on every call. + private static readonly FrozenDictionary s_toolKindMap = + new Dictionary(StringComparer.OrdinalIgnoreCase) + { + ["exec"] = ActivityKind.Exec, + ["read"] = ActivityKind.Read, + ["write"] = ActivityKind.Write, + ["edit"] = ActivityKind.Edit, + ["web_search"] = ActivityKind.Search, + ["web_fetch"] = ActivityKind.Search, + ["browser"] = ActivityKind.Browser, + ["message"] = ActivityKind.Message, + ["tts"] = ActivityKind.Tool, + ["image"] = ActivityKind.Tool, + }.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase); + + private static ActivityKind ClassifyTool(string toolName) => + s_toolKindMap.TryGetValue(toolName, out var kind) ? kind : ActivityKind.Tool; private static string ShortenPath(string path) {