2023-01-01 01:23:52 +00:00
# GoSmartKeyboard Client
2023-01-27 00:51:31 +00:00
When the GoSmartKeyboard client is started, it does the following:
2023-01-01 01:23:52 +00:00
2023-01-27 00:51:31 +00:00
1. Load the connection URL from the first CLI argument.
2. Load the auth token from the environment variable `KEYBOARD_AUTH` or stdin if it does not exist.
2023-01-01 21:25:32 +00:00
3. Connect to the server.
4 Send the auth token to the server.
5. If the server responds with "authenticated", we start reading keys from stdin and sending them to the server until EOF.
2023-03-13 18:24:02 +00:00
5.1 If the environment variable `KEYBOARD_FIFO` is set, we read from the path specified there instead as a named pipe.
5.2 Regardless, each message is sent to the server with the prefix `@{payload delimiter}` .
2023-01-01 01:23:52 +00:00
2023-01-22 00:55:58 +00:00
``` go
2023-01-02 07:24:35 +00:00
--- handle client command
2023-01-27 00:51:31 +00:00
if len(os.Args) > 1 {
2023-03-03 03:20:34 +00:00
@{get client fifo input file from environment}
2023-01-22 03:28:56 +00:00
@{setup client}
2023-03-03 03:20:34 +00:00
if clientFifoInputFileEnvExists {
2023-01-22 03:28:56 +00:00
@{start client with fifo}
os.Exit(0)
}
@{start client with stdin}
2023-01-02 07:24:35 +00:00
os.Exit(0)
}
---
2023-01-22 00:55:58 +00:00
```
2023-01-02 07:24:35 +00:00
2023-01-01 21:25:32 +00:00
## Connecting
The base64 authentication token is loaded from the environment variable `KEYBOARD_AUTH` , if it does not exist we read it from stdin (base64 encoded), ended with a newline.
2023-01-01 01:23:52 +00:00
``` go
2023-01-22 03:28:56 +00:00
--- setup client
2023-01-01 01:23:52 +00:00
2023-03-13 18:24:02 +00:00
@{handle version command}
2023-01-01 21:25:32 +00:00
@{load connection URL from second CLI argument}
@{get authTokenInput from environment}
2023-01-23 01:23:52 +00:00
@{add xdotool if non qwerty function}
2023-01-01 21:25:32 +00:00
if !authTokenInputExists {
fmt.Print("Enter authentication token: ")
_, err := fmt.Scanln(& authTokenInput)
if err != nil {
log.Fatal(err)
}
}
client, _, err := websocket.DefaultDialer.Dial(connectionURL, nil)
if err != nil {
log.Fatal("dial:", err)
}
defer client.Close()
err = client.WriteMessage(websocket.TextMessage, []byte(authTokenInput))
if err != nil {
log.Fatal("write:", err)
}
_, authResponse, err := client.ReadMessage()
if err != nil {
log.Fatal("read:", err)
}
2023-01-02 07:24:35 +00:00
if string(authResponse) == "authenticated" {
fmt.Println("authenticated")
} else {
2023-01-01 21:25:32 +00:00
log.Fatal("authentication failed")
}
---
--- load connection URL from second CLI argument --- noWeave
2023-01-27 23:22:02 +00:00
if len(os.Args) < 2 {
2023-01-01 21:25:32 +00:00
log.Fatal("missing connection URL")
}
2023-01-01 01:23:52 +00:00
2023-01-27 23:22:02 +00:00
connectionURL := os.Args[1]
2023-01-01 21:25:32 +00:00
if !strings.HasPrefix(connectionURL, "ws://") & & !strings.HasPrefix(connectionURL, "wss://") {
log.Fatal("connection URL must start with ws:// or wss://")
}
2023-01-01 01:23:52 +00:00
---
2023-01-01 21:25:32 +00:00
```
2023-01-22 03:28:56 +00:00
## Sending keys from a named pipe
2023-03-13 18:24:02 +00:00
We create a fifo and then continuously read from it in a loop.
2023-01-22 03:28:56 +00:00
2023-03-13 18:24:02 +00:00
``` go
--- start client with fifo
2023-01-23 01:23:52 +00:00
2023-03-03 03:20:34 +00:00
syscall.Mkfifo(clientFifoInputFile, syscall.S_IFIFO|0666)
2023-03-13 18:24:02 +00:00
inputString := ""
2023-01-22 03:28:56 +00:00
for {
2023-03-13 18:24:02 +00:00
readData, err := ioutil.ReadFile(clientFifoInputFile)
2023-01-22 03:28:56 +00:00
if err != nil {
log.Fatal(err)
}
2023-03-13 18:24:02 +00:00
inputString = addXDoToolIfNonQWERTY(string(readData))
input := []byte("@{payload delimiter}" + inputString)
2023-01-22 03:28:56 +00:00
if len(input) > 0 {
err = client.WriteMessage(websocket.TextMessage, input)
if err != nil {
log.Fatal("write:", err)
}
}
}
---
```
## Sending keys from stdin
2023-01-01 21:25:32 +00:00
2023-01-27 23:22:02 +00:00
We read keys from stdin and send them to the server until we get EOF.
We specify xdotool if the key is not a QWERTY key or if KEYBOARD_ALWAYS_XDOTOOL is set to true.
2023-01-01 21:25:32 +00:00
``` go
2023-01-22 03:28:56 +00:00
--- start client with stdin
2023-01-01 21:25:32 +00:00
2023-01-22 00:55:58 +00:00
reader := bufio.NewReader(os.Stdin)
2023-01-01 21:25:32 +00:00
for {
var key string
2023-01-22 00:55:58 +00:00
2023-01-05 07:10:57 +00:00
rune, _, err := reader.ReadRune() //:= fmt.Scan(& key)
2023-01-23 01:23:52 +00:00
key = addXDoToolIfNonQWERTY(string(rune))
2023-01-05 07:10:57 +00:00
2023-01-01 21:25:32 +00:00
if err != nil {
if err == io.EOF {
break
}
log.Fatal(err)
}
2023-03-13 18:24:02 +00:00
err = client.WriteMessage(websocket.TextMessage, []byte("@{payload delimiter}" + key))
2023-01-01 21:25:32 +00:00
if err != nil {
log.Fatal("write:", err)
}
}
---
2023-01-01 01:23:52 +00:00
2023-01-23 01:23:52 +00:00
```
# Handling unicode outside of the ASCII set
``` go
--- add xdotool if non qwerty function --- noWeave
addXDoToolIfNonQWERTY := func(message string)(string) {
2023-01-27 23:22:02 +00:00
2023-03-13 18:24:02 +00:00
if strings.HasPrefix(message, "@{use xdotool cmd}") {
2023-01-23 01:23:52 +00:00
return message
}
for _, char := range message {
if char < 32 | | char > 126 {
if char != 8 & & char != 9 & & char != 10 {
2023-03-13 18:24:02 +00:00
return "@{use xdotool cmd}" + message
2023-01-23 01:23:52 +00:00
}
}
}
return message
}
---
2023-01-27 00:51:31 +00:00
```
``` go
--- /client/main-client.go
package main
import (
"strings"
"io/ioutil"
2023-03-03 03:20:34 +00:00
"syscall"
2023-01-27 00:51:31 +00:00
"io"
"bufio"
"log"
"fmt"
"os"
@{gorilla/websocket import string}
)
func main(){@{handle client command}}
---
```