namespace AIStudio.Tools.ChatArchive; /// /// Finds the files AI Studio keeps inside a chat directory itself, such as transcripts. /// /// /// Documents the user attaches are never copied into a chat directory: they stay where the /// user put them and the chat only stores their path. Everything below a chat directory was /// therefore written by AI Studio, which makes the directory contents a reliable definition /// of "the app's own files" — minus the two files the chat storage owns itself. /// public static class ChatArchiveChatFiles { /// /// Collects the app-owned files of one chat. /// /// The directory of the chat. /// The absolute paths of the files, keyed by their chat-relative path. public static Dictionary Collect(string chatDirectory) { var files = new Dictionary(PathTools.COMPARER); if (!Directory.Exists(chatDirectory)) return files; foreach (var filePath in Directory.EnumerateFiles(chatDirectory, "*", SearchOption.AllDirectories)) { // Skip the temporary files the chat storage writes while saving a chat: var fileName = Path.GetFileName(filePath); if (fileName.StartsWith('.') && fileName.EndsWith(".tmp", StringComparison.OrdinalIgnoreCase)) continue; var chatRelativePath = Path.GetRelativePath(chatDirectory, filePath).Replace(Path.DirectorySeparatorChar, ChatArchiveFormat.ENTRY_SEPARATOR); // The chat itself is always exported and is not one of its own attachments: if (chatRelativePath.Equals(ChatArchiveFormat.THREAD_FILE_NAME, StringComparison.OrdinalIgnoreCase) || chatRelativePath.Equals(ChatArchiveFormat.NAME_FILE_NAME, StringComparison.OrdinalIgnoreCase)) continue; files[chatRelativePath] = filePath; } return files; } /// /// Determines whether at least one of the given chats owns a file. /// /// /// This stops at the first file found, so that asking the question stays cheap even for /// a large chat storage. /// /// The directories of the chats to check. /// Cancels the search. /// Whether any app-owned file exists. public static bool HasAny(IEnumerable chatDirectories, CancellationToken token) { foreach (var chatDirectory in chatDirectories) { token.ThrowIfCancellationRequested(); if (Collect(chatDirectory).Count > 0) return true; } return false; } }