Add "DONE:" prefix to completed file names. Recursively scan all directories.
authorSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 22 Jun 2025 19:45:00 +0000 (22:45 +0300)
committerSvjatoslav Agejenko <svjatoslav@svjatoslav.eu>
Sun, 22 Jun 2025 19:45:00 +0000 (22:45 +0300)
src/main/java/eu/svjatoslav/alyverkko_cli/commands/mail_correspondant/MailCorrespondentCommand.java

index e336743..422d2d5 100644 (file)
@@ -17,8 +17,7 @@ import java.util.*;
 import static eu.svjatoslav.alyverkko_cli.Main.configuration;
 import static eu.svjatoslav.commons.file.IOHelper.getFileContentsAsString;
 import static eu.svjatoslav.commons.file.IOHelper.saveToFile;
-import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
-import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
+import static java.nio.file.StandardWatchEventKinds.*;
 
 
 /**
@@ -147,21 +146,16 @@ public class MailCorrespondentCommand implements Command {
         directoryWatcher.close();
     }
 
-    /**
-     * Performs an initial scan of existing files in the mail directory,
-     * adding those that need processing to the task queue.
-     *
-     * @throws IOException if file scanning fails.
-     */
     private void initialMailScanAndReply() throws IOException {
-        File[] files = mailDir.listFiles();
-        if (files == null) {
-            return;
-        }
-
-        for (File file : files) {
-           considerFileForQueuing(file.toPath());
-        }
+        Files.walk(mailDir.toPath())
+                .filter(path -> Files.isRegularFile(path))
+                .forEach(path -> {
+                    try {
+                        considerFileForQueuing(path);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    }
+                });
     }
 
     /**
@@ -212,37 +206,10 @@ public class MailCorrespondentCommand implements Command {
         }
     }
 
-    /**
-     * Processes a file if it has the "TOCOMPUTE:" marker, running an AI
-     * query and appending the result to the file. Otherwise logs that
-     * it's being ignored.
-     *
-     * @param file the file to possibly process.
-     * @throws IOException          if reading/writing the file fails.
-     * @throws InterruptedException if the AI query is interrupted.
-     */
-    private void processMailIfNeeded(File file) throws IOException, InterruptedException {
-        if (!isMailProcessingNeeded(file)) {
-            System.out.println("Ignoring file: " + file.getName() + " (does not need processing for now)");
-            return;
-        }
-
-        System.out.println("\nReplying to mail: " + file.getName());
-
-        MailQuery mailQuery = buildMailQueryFromFile(file);
-
-        // Run the query using the AI model while measuring the time taken
-        AiTask aiTask = new AiTask(mailQuery);
-        String aiGeneratedResponse = aiTask.runAiQuery();
-
-        saveAiResponseToFile(file, mailQuery, aiGeneratedResponse);
-    }
-
     private static void saveAiResponseToFile(File file, MailQuery mailQuery, String aiResponse) throws IOException {
-        // Build new content
         StringBuilder resultFileContent = new StringBuilder();
 
-        // The First line should be "TOCOMPUTE:" with settings that were used to process this file
+        // The First line should be "DONE:" with settings used to process this file
         resultFileContent.append(getDoneLine(mailQuery));
 
         // Ensure the user prompt block is labeled if it isn't already
@@ -259,10 +226,13 @@ public class MailCorrespondentCommand implements Command {
                 .append(aiResponse)
                 .append("\n");
 
-        // Write the combined result back to the same file
-        saveToFile(file, resultFileContent.toString());
-    }
+        File newFile = new File(file.getParentFile(), "DONE: " + file.getName());
+        saveToFile(newFile, resultFileContent.toString());
 
+        if (!file.delete()) {
+            System.err.println("Failed to delete original file: " + file.getAbsolutePath());
+        }
+    }
 
     /**
      * Processes a task by reading the file, building the MailQuery,
@@ -409,35 +379,55 @@ public class MailCorrespondentCommand implements Command {
      * @throws IOException if file processing fails.
      */
     private void processDetectedFilesystemEvents(WatchKey key) throws IOException {
+
+        Path dir = (Path) key.watchable();   // ★ new line
+
         for (WatchEvent<?> event : key.pollEvents()) {
+
             WatchEvent.Kind<?> kind = event.kind();
+            if (kind != ENTRY_CREATE && kind != ENTRY_MODIFY) continue;
 
-            // Skip OVERFLOW events
-            if (kind == StandardWatchEventKinds.OVERFLOW) {
-                continue;
-            }
+            @SuppressWarnings("unchecked")
+            WatchEvent<Path> ev = (WatchEvent<Path>) event;
+            Path filename = ev.context();
 
-            // The filename for the event
-            Path filename = ((WatchEvent<Path>) event).context();
-            System.out.println("Event: " + kind + " for file: " + filename);
+            Path fullPath = dir.resolve(filename);
+            System.out.printf("Event: %s – %s%n", kind.name(), fullPath);
 
-            // Add to task queue if needed
-            if (kind == ENTRY_CREATE || kind == ENTRY_MODIFY) {
-                Path filePath = mailDir.toPath().resolve(filename);
-                considerFileForQueuing(filePath);
+            // If the entry is a *new directory* we need to start watching it, too:
+            if (kind == ENTRY_CREATE && Files.isDirectory(fullPath)) {
+                registerAllSubdirectories(fullPath);
+            }
+
+            // And finally queue the file if it’s a regular, non-hidden file:
+            if (Files.isRegularFile(fullPath) && !Files.isHidden(fullPath)) {
+                considerFileForQueuing(fullPath);
             }
         }
     }
 
-    /**
-     * Registers the mail directory with a WatchService for ENTRY_CREATE
-     * and ENTRY_MODIFY events.
-     *
-     * @throws IOException if registration fails.
-     */
+
     private void initializeFileWatcher() throws IOException {
         this.directoryWatcher = FileSystems.getDefault().newWatchService();
-        Paths.get(mailDir.getAbsolutePath()).register(directoryWatcher, ENTRY_CREATE, ENTRY_MODIFY);
+        registerAllSubdirectories(mailDir.toPath());
+    }
+
+    private void registerAllSubdirectories(Path path) {
+        try {
+            System.out.println("Registering directory for watch service: " + path);
+            path.register(directoryWatcher, ENTRY_CREATE, ENTRY_MODIFY);
+            if (Files.isDirectory(path)) {
+                try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
+                    for (Path entry : stream) {
+                        if (Files.isDirectory(entry)) {
+                            registerAllSubdirectories(entry);
+                        }
+                    }
+                }
+            }
+        } catch (IOException e) {
+            System.err.println("Failed to register directory: " + path + " - " + e.getMessage());
+        }
     }
 
     /**