package main import ( "bufio" "encoding/json" "fmt" "io" "log" "net/http" "os" "os/exec" "path/filepath" "sync" "time" ) type Message struct { Message string `json:"message"` From string `json:"from"` To string `json:"to"` } const linux = "python3" func streamResponse(w http.ResponseWriter, messages <-chan string) { w.Header().Set("Content-Type", "text/plain; charset=utf-8") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") flusher, ok := w.(http.Flusher) if !ok { http.Error(w, "Streaming unsupported!", http.StatusInternalServerError) return } for msg := range messages { fmt.Fprintf(w, "%s\n", msg) flusher.Flush() } } func downloadPackages(w http.ResponseWriter, fromLanguage, toLanguage string) { messages := make(chan string) defer close(messages) go streamResponse(w, messages) cmd := exec.Command(linux, "translator/download.py", fromLanguage, toLanguage) output, err := cmd.StdoutPipe() if err != nil { messages <- fmt.Sprintf("Error starting Download: %s", err.Error()) return } err = cmd.Start() if err != nil { messages <- fmt.Sprintf("Error starting Download: %s", err.Error()) return } messages <- "Download started..." ticker := time.NewTicker(2 * time.Second) go func() { for range ticker.C { messages <- ". . ." } }() scanner := bufio.NewScanner(output) firstOutputReceived := false for scanner.Scan() { if !firstOutputReceived { ticker.Stop() firstOutputReceived = true } messages <- scanner.Text() } err = cmd.Wait() if err != nil { messages <- fmt.Sprintf("Error waiting for Download: %s", err.Error()) return } } func executeTranslator(w http.ResponseWriter, message, fromLanguage, toLanguage string) { messages := make(chan string) defer close(messages) go streamResponse(w, messages) cmd := exec.Command(linux, "translator/translate.py", message, fromLanguage, toLanguage) output, err := cmd.StdoutPipe() if err != nil { messages <- fmt.Sprintf("Error starting Translation: %s", err.Error()) return } err = cmd.Start() if err != nil { messages <- fmt.Sprintf("Error starting Translation: %s", err.Error()) return } scanner := bufio.NewScanner(output) for scanner.Scan() { // Directly send the translated text as a message messages <- scanner.Text() } err = cmd.Wait() if err != nil { messages <- fmt.Sprintf("Error waiting for Translation: %s", err.Error()) return } } func CheckLanguagesInstalled(fromCode, toCode string) (bool, error) { homeDir, err := os.UserHomeDir() if err != nil { return false, fmt.Errorf("unable to determine the user home directory: %w", err) } targetDir := filepath.Join(homeDir, ".local", "share", "argos-translate", "packages", fromCode+"_"+toCode) _, err = os.Stat(targetDir) if err != nil { if os.IsNotExist(err) { return false, nil } return false, fmt.Errorf("error checking for language directory: %w", err) } return true, nil } func handleRequest(w http.ResponseWriter, r *http.Request) { if r.Header.Get("Content-Type") != "application/json" { http.Error(w, "Request content type must be application/json", http.StatusBadRequest) return } body, err := io.ReadAll(io.NopCloser(io.Reader(r.Body))) if err != nil { http.Error(w, "Error reading request body: "+err.Error(), http.StatusInternalServerError) return } defer r.Body.Close() var msg Message err = json.Unmarshal(body, &msg) if err != nil { http.Error(w, "Error unmarshalling JSON: "+err.Error(), http.StatusBadRequest) return } if len(msg.From) > 2 || len(msg.To) > 2 { http.Error(w, "From and To fields should not be longer than 2 letters.", http.StatusBadRequest) return } installed, err := CheckLanguagesInstalled(msg.From, msg.To) if err != nil { if err.Error() == "file not found" { http.Error(w, "file not found "+err.Error(), http.StatusInternalServerError) } else { fmt.Println("An error occurred:", err) } return } var wg sync.WaitGroup wg.Add(1) go func() { defer wg.Done() if installed { executeTranslator(w, msg.Message, msg.From, msg.To) } else { downloadPackages(w, msg.From, msg.To) } }() wg.Wait() } func main() { http.HandleFunc("/", handleRequest) port := "53184" address := fmt.Sprintf("0.0.0.0:%s", port) fmt.Printf("Listening on %s...\n", address) err := http.ListenAndServe(address, nil) if err != nil { log.Fatalf("Failed to start server: %v", err) } }