// Darkest Isle push-to-talk helper (Windows). // // Browsers can't see key presses while the game has focus, so this tiny program watches ONE key or mouse button that // you choose and tells the Darkest Isle voice page (and nothing else) whether it is held down. It does not record, // store or send any other keys, and it only listens on this computer (127.0.0.1); nothing goes over the internet. // // first run: asks you to press the key or mouse button you want (saved in %APPDATA%\DarkestIsleVoice\key.txt) // pick another key: run it with -rebind // then: keep it open while you play, and choose "Push-to-talk (helper app)" on the voice page // // Build: GOOS=windows GOARCH=amd64 go build -ldflags "-s -w" -o DarkestIslePTT.exe package main import ( "flag" "fmt" "net" "net/http" "os" "path/filepath" "strconv" "strings" "sync" "syscall" "time" ) const ( port = "47391" origin = "https://darkestisle.com" ) var ( user32 = syscall.NewLazyDLL("user32.dll") getAsyncKeyState = user32.NewProc("GetAsyncKeyState") ) func down(vk int) bool { r, _, _ := getAsyncKeyState.Call(uintptr(vk)) return r&0x8000 != 0 } var names = map[int]string{0x01: "left mouse", 0x02: "right mouse", 0x04: "middle mouse", 0x05: "mouse button 4", 0x06: "mouse button 5", 0x08: "Backspace", 0x09: "Tab", 0x0D: "Enter", 0x10: "Shift", 0x11: "Ctrl", 0x12: "Alt", 0x14: "Caps Lock", 0x20: "Space", 0xA0: "Left Shift", 0xA1: "Right Shift", 0xA2: "Left Ctrl", 0xA3: "Right Ctrl", 0xA4: "Left Alt", 0xA5: "Right Alt", 0xC0: "` (backtick)"} func keyName(vk int) string { if n, ok := names[vk]; ok { return n } if (vk >= 0x30 && vk <= 0x39) || (vk >= 0x41 && vk <= 0x5A) { return string(rune(vk)) } if vk >= 0x70 && vk <= 0x87 { return fmt.Sprintf("F%d", vk-0x6F) } return fmt.Sprintf("key 0x%02X", vk) } func configPath() string { dir := os.Getenv("APPDATA") if dir == "" { dir = "." } return filepath.Join(dir, "DarkestIsleVoice", "key.txt") } func pickKey() int { fmt.Println("Press the key or mouse button you want to hold to talk (not the left mouse button)...") time.Sleep(400 * time.Millisecond) held := map[int]bool{} for vk := 1; vk < 0xFF; vk++ { held[vk] = down(vk) } for { for vk := 2; vk < 0xFF; vk++ { if vk == 0x10 || vk == 0x11 || vk == 0x12 { // generic Shift/Ctrl/Alt: use the left/right versions instead continue } d := down(vk) if d && !held[vk] { return vk } held[vk] = d } time.Sleep(10 * time.Millisecond) } } type hub struct { mu sync.Mutex clients map[chan string]bool state string } func (h *hub) set(s string) { h.mu.Lock() defer h.mu.Unlock() if s == h.state { return } h.state = s for c := range h.clients { select { case c <- s: default: } } } func main() { rebind := flag.Bool("rebind", false, "choose a different push-to-talk key") flag.Parse() fmt.Println("Darkest Isle push-to-talk helper") fmt.Println("--------------------------------") vk := 0 if b, err := os.ReadFile(configPath()); err == nil && !*rebind { vk, _ = strconv.Atoi(strings.TrimSpace(string(b))) } if vk <= 1 || vk >= 0xFF { vk = pickKey() _ = os.MkdirAll(filepath.Dir(configPath()), 0o755) _ = os.WriteFile(configPath(), []byte(strconv.Itoa(vk)), 0o644) } fmt.Printf("Push-to-talk key: %s (run with -rebind to change it)\n", keyName(vk)) h := &hub{clients: map[chan string]bool{}, state: "up"} http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) { o := r.Header.Get("Origin") if o != origin { http.Error(w, "only the Darkest Isle voice page may use this helper", http.StatusForbidden) return } w.Header().Set("Access-Control-Allow-Origin", origin) w.Header().Set("Access-Control-Allow-Private-Network", "true") if r.Method == http.MethodOptions { w.Header().Set("Access-Control-Allow-Methods", "GET") w.WriteHeader(http.StatusNoContent) return } w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-store") fl, ok := w.(http.Flusher) if !ok { return } c := make(chan string, 8) h.mu.Lock() h.clients[c] = true cur := h.state h.mu.Unlock() defer func() { h.mu.Lock(); delete(h.clients, c); h.mu.Unlock() }() fmt.Fprintf(w, "event: key\ndata: %s\n\n", keyName(vk)) fmt.Fprintf(w, "data: %s\n\n", cur) fl.Flush() fmt.Println("Voice page connected.") ping := time.NewTicker(15 * time.Second) defer ping.Stop() for { select { case s := <-c: fmt.Fprintf(w, "data: %s\n\n", s) fl.Flush() case <-ping.C: fmt.Fprint(w, ": ping\n\n") fl.Flush() case <-r.Context().Done(): fmt.Println("Voice page disconnected.") return } } }) ln, err := net.Listen("tcp", "127.0.0.1:"+port) if err != nil { fmt.Println("Couldn't start (is the helper already running?):", err) time.Sleep(10 * time.Second) return } go func() { for { if down(vk) { h.set("down") } else { h.set("up") } time.Sleep(10 * time.Millisecond) } }() fmt.Println("Ready. Keep this window open while you play; choose \"Push-to-talk (helper app)\" on the voice page.") _ = http.Serve(ln, nil) }