commit 0db5f3f7a3802538470b680b376466043efb08ce Author: nemunaire Date: Fri Jun 22 00:50:15 2018 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ad5b9a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +kogia diff --git a/main.go b/main.go new file mode 100644 index 0000000..2e0384b --- /dev/null +++ b/main.go @@ -0,0 +1,72 @@ +package main + +import ( + "flag" + "fmt" + "io" + "log" + "net/http" + "os/exec" + "path" + + "gopkg.in/fsnotify.v1" +) + +var SpeakerPipe io.WriteCloser + +var DefaultLang string + +const presencePath = "/tmp/isPresent" + +func main() { + var bind = flag.String("bind", ":7030", "Bind port/socket") + flag.StringVar(&DefaultLang, "lang", "fr", "Default language to speak") + flag.Parse() + + go func() { + for { + cmd := exec.Command("espeak", "-z", "-m") + if in, err := cmd.StdinPipe(); err != nil { + log.Fatal("Unable to create pipe to espeak:", err) + } else { + SpeakerPipe = in + } + cmd.Run() + } + }() + + if watcher, err := fsnotify.NewWatcher(); err != nil { + log.Fatal(err) + } else { + if err := watcher.Add(path.Dir(presencePath)); err != nil { + log.Fatal("Unable to watch: ", path.Dir(presencePath), ": ", err) + } + + go func() { + defer watcher.Close() + for { + select { + case ev := <-watcher.Events: + if path.Base(ev.Name) == presencePath { + if ev.Op & fsnotify.Create == fsnotify.Create { + log.Println("Presence file created!") + } else if ev.Op & fsnotify.Remove == fsnotify.Remove { + log.Println("Presence file removed!") + } + } + case err := <-watcher.Errors: + log.Println("watcher error:", err) + } + } + }() + } + + // Register handlers + http.Handle("/notify", http.StripPrefix("/notify", NotifyHandler{})) + + // Serve pages + log.Println(fmt.Sprintf("Ready, listening on %s", *bind)) + if err := http.ListenAndServe(*bind, nil); err != nil { + log.Fatal("Unable to listen and serve: ", err) + } +} diff --git a/notify.go b/notify.go new file mode 100644 index 0000000..a52ec2e --- /dev/null +++ b/notify.go @@ -0,0 +1,76 @@ +package main + +import ( + "encoding/json" + "encoding/xml" + "fmt" + "io" + "net/http" + "os" +) + +type MessagePart struct { + XMLName xml.Name `xml:"s"` + Message string `json:"msg" xml:",chardata"` + Lang string `json:"lang" xml:"xml:lang,attr"` +} + +type Notify struct { + Content []MessagePart `json:"content"` + Alert string `json:"alert"` +} + +type NotifyHandler struct{} + +func (n NotifyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + + if r.Method != "POST" { + http.Error(w, "{\"errmsg\":\"Requête invalide.\"}", http.StatusBadRequest) + return + } else if r.ContentLength < 0 || r.ContentLength > 65536 { + http.Error(w, "{\"errmsg\":\"Requête trop longue ou de taille inconnue\"}", http.StatusRequestEntityTooLarge) + return + } + + var body []byte + if r.ContentLength > 0 { + tmp := make([]byte, 1024) + for { + n, err := r.Body.Read(tmp) + for j := 0; j < n; j++ { + body = append(body, tmp[j]) + } + if err != nil || n <= 0 { + break + } + } + } + + var notif Notify + + err := json.Unmarshal(body, ¬if) + if err != nil { + http.Error(w, fmt.Sprintf("{\"errmsg\":\"%s\"}", err), http.StatusBadRequest) + return + } + + if notif.Alert != "" { + fmt.Println(notif.Alert) + } + + for _, cnt := range notif.Content { + if cnt.Lang == "" { + cnt.Lang = DefaultLang + } + + output, err := xml.Marshal(cnt) + if err != nil { + fmt.Printf("error: %v\n", err) + } else { + os.Stdout.Write(output) + SpeakerPipe.Write(output) + } + } + io.WriteString(SpeakerPipe, "\r\n") +}