mirror of
https://github.com/Macrame-App/Macrame
synced 2025-12-29 07:19:26 +00:00
Redundant commit: only to switch branches. Can be removed.
This commit is contained in:
commit
59dd711ab3
102 changed files with 34954 additions and 0 deletions
99
be/app/api.go
Normal file
99
be/app/api.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"be/app/helper"
|
||||
"log"
|
||||
"mime"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ApiCORS(w http.ResponseWriter, r *http.Request) (http.ResponseWriter, *http.Request) {
|
||||
origin := r.Header.Get("Origin")
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:5173")
|
||||
|
||||
if strings.HasPrefix(r.Host, "192.168.") {
|
||||
log.Println("lan device")
|
||||
w.Header().Set("Access-Control-Allow-Origin", origin)
|
||||
}
|
||||
|
||||
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS")
|
||||
w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Accept, Accept-Language, Accept-Encoding")
|
||||
|
||||
return w, r
|
||||
}
|
||||
|
||||
func ApiGet(w http.ResponseWriter, r *http.Request) {
|
||||
file := "" // base directory
|
||||
|
||||
if r.URL.Path != "/" {
|
||||
file = "../public" + r.URL.Path // request
|
||||
}
|
||||
contentType := mime.TypeByExtension(filepath.Ext(file)) // get content type
|
||||
|
||||
if contentType != "" {
|
||||
w.Header().Set("Content-Type", contentType) // set content type header
|
||||
}
|
||||
|
||||
if contentType == "" {
|
||||
file = "../public/index.html" // default
|
||||
}
|
||||
|
||||
// log.Println("GET:", file)
|
||||
|
||||
http.ServeFile(w, r, file) // serve file
|
||||
}
|
||||
|
||||
func ApiPost(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
access, data := helper.EndpointAccess(w, r)
|
||||
|
||||
if !access {
|
||||
return
|
||||
}
|
||||
|
||||
log.Println("api post", data == "")
|
||||
if data != "" {
|
||||
ApiAuth(data, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
switch r.URL.Path {
|
||||
case "/macro/record":
|
||||
SaveMacro(w, r)
|
||||
case "/macro/list":
|
||||
ListMacros(w, r)
|
||||
case "/macro/delete":
|
||||
DeleteMacro(w, r)
|
||||
case "/macro/play":
|
||||
PlayMacro("", w, r)
|
||||
case "/device/list":
|
||||
DeviceList(w, r)
|
||||
case "/device/access/check":
|
||||
DeviceAccessCheck(w, r)
|
||||
case "/device/access/request":
|
||||
DeviceAccessRequest(w, r)
|
||||
case "/device/link/ping":
|
||||
PingLink(w, r)
|
||||
case "/device/link/start":
|
||||
StartLink(w, r)
|
||||
case "/device/link/poll":
|
||||
PollLink(w, r)
|
||||
case "/device/link/remove":
|
||||
RemoveLink("", w, r)
|
||||
case "/device/handshake":
|
||||
Handshake(w, r)
|
||||
}
|
||||
}
|
||||
|
||||
func ApiAuth(data string, w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("apiauth", data != "")
|
||||
switch r.URL.Path {
|
||||
case "/macro/play":
|
||||
PlayMacro(data, w, r)
|
||||
case "/device/link/remove":
|
||||
RemoveLink(data, w, r)
|
||||
}
|
||||
}
|
||||
245
be/app/device.go
Normal file
245
be/app/device.go
Normal file
|
|
@ -0,0 +1,245 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"be/app/helper"
|
||||
"be/app/structs"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
func DeviceList(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("device list")
|
||||
dir := "devices"
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
devices := make(map[string]map[string]interface{})
|
||||
|
||||
for _, file := range files {
|
||||
filePath := dir + "/" + file.Name()
|
||||
ext := filepath.Ext(filePath)
|
||||
device := strings.TrimSuffix(file.Name(), ext)
|
||||
|
||||
log.Println(device, ext)
|
||||
|
||||
if _, ok := devices[device]; !ok {
|
||||
devices[device] = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if ext == ".json" {
|
||||
devices[device]["settings"] = readDeviceSettings(filePath)
|
||||
}
|
||||
if ext == ".key" {
|
||||
devices[device]["key"] = true
|
||||
}
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"devices": devices,
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(result)
|
||||
}
|
||||
|
||||
func readDeviceSettings(filepath string) (settings structs.Settings) {
|
||||
data, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(data, &settings)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
log.Println(settings)
|
||||
return settings
|
||||
}
|
||||
|
||||
func DeviceAccessCheck(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("device access check")
|
||||
var req structs.Check
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
_, errSett := os.Stat("devices/" + req.Uuid + ".json")
|
||||
_, errKey := os.Stat("devices/" + req.Uuid + ".key")
|
||||
|
||||
if (errSett == nil) && (errKey == nil) {
|
||||
log.Println("authorized")
|
||||
json.NewEncoder(w).Encode("authorized")
|
||||
} else if (errSett == nil) && (errKey != nil) {
|
||||
log.Println("unauthorized")
|
||||
json.NewEncoder(w).Encode("unauthorized")
|
||||
} else {
|
||||
log.Println("unauthorized")
|
||||
json.NewEncoder(w).Encode("unlinked")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func DeviceAccessRequest(w http.ResponseWriter, r *http.Request) {
|
||||
var req structs.Request
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
deviceSettings := structs.Settings{Name: req.Name, Type: req.Type}
|
||||
|
||||
settingsJSON, err := json.Marshal(deviceSettings)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile("devices/"+req.Uuid+".json", settingsJSON, 0644)
|
||||
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode("unauthorized")
|
||||
}
|
||||
|
||||
func PingLink(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("ping link")
|
||||
var req structs.Check
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode(false)
|
||||
return
|
||||
}
|
||||
|
||||
key, keyErr := os.ReadFile("devices/" + req.Uuid + ".key")
|
||||
pin, pinErr := os.ReadFile("devices/" + req.Uuid + ".tmp")
|
||||
|
||||
encryptedKey, encErr := helper.EncryptAES(string(pin), string(key))
|
||||
|
||||
log.Println(encryptedKey, string(pin), string(key))
|
||||
|
||||
if keyErr == nil && pinErr == nil && encErr == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.Write([]byte(encryptedKey))
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(false)
|
||||
}
|
||||
|
||||
func StartLink(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("start link")
|
||||
var req structs.Check
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
pin := fmt.Sprintf("%04d", rand.Intn(10000))
|
||||
|
||||
deviceKey := helper.GenerateKey()
|
||||
|
||||
err = helper.SaveDeviceKey(req.Uuid, deviceKey)
|
||||
|
||||
if err == nil && helper.TempPinFile(req.Uuid, pin) {
|
||||
json.NewEncoder(w).Encode(pin)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func PollLink(w http.ResponseWriter, r *http.Request) {
|
||||
var req structs.Check
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode(false)
|
||||
return
|
||||
}
|
||||
|
||||
if helper.CheckPinFile(req.Uuid) {
|
||||
json.NewEncoder(w).Encode(true)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(false)
|
||||
}
|
||||
|
||||
func RemoveLink(data string, w http.ResponseWriter, r *http.Request) {
|
||||
req := &structs.Check{}
|
||||
_, err := helper.ParseRequest(req, data, r)
|
||||
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode(false)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.Remove("devices/" + req.Uuid + ".key")
|
||||
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode(false)
|
||||
return
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(true)
|
||||
}
|
||||
|
||||
func Handshake(w http.ResponseWriter, r *http.Request) {
|
||||
var req structs.Handshake
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
deviceKey, err := helper.GetKeyByUuid(req.Uuid)
|
||||
|
||||
if err != nil {
|
||||
json.NewEncoder(w).Encode(false)
|
||||
return
|
||||
}
|
||||
|
||||
decryptShake, _ := helper.DecryptAES(deviceKey, req.Shake)
|
||||
|
||||
if decryptShake == getDateStr() {
|
||||
os.Remove("devices/" + req.Uuid + ".tmp")
|
||||
|
||||
json.NewEncoder(w).Encode(true)
|
||||
return
|
||||
} else {
|
||||
os.Remove("devices/" + req.Uuid + ".key")
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(false)
|
||||
}
|
||||
|
||||
func getDateStr() string {
|
||||
date := time.Now()
|
||||
year, month, day := date.Date()
|
||||
formattedDate := fmt.Sprintf("%04d%02d%02d", year, month, day)
|
||||
return formattedDate
|
||||
}
|
||||
102
be/app/helper/api-helper.go
Normal file
102
be/app/helper/api-helper.go
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"be/app/structs"
|
||||
. "be/app/structs"
|
||||
)
|
||||
|
||||
func EndpointAccess(w http.ResponseWriter, r *http.Request) (bool, string) {
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if (isLocal(ip) && isEndpointAllowed("Local", r.URL.Path)) ||
|
||||
(isLanRemote(ip) && isEndpointAllowed("Remote", r.URL.Path)) {
|
||||
log.Println(r.URL.Path, "endpoint access: accessible")
|
||||
return true, ""
|
||||
} else if isLanRemote(ip) && isEndpointAllowed("Auth", r.URL.Path) {
|
||||
log.Println(r.URL.Path, "endpoint access: authorized")
|
||||
|
||||
data := decryptAuth(r)
|
||||
|
||||
return data != "", data
|
||||
}
|
||||
|
||||
log.Println(r.URL.Path, "endpoint access: not authorized or accessible")
|
||||
|
||||
return false, ""
|
||||
}
|
||||
|
||||
func isLocal(ip string) bool {
|
||||
return ip == "127.0.0.1" || ip == "::1"
|
||||
}
|
||||
|
||||
func isLanRemote(ip string) bool {
|
||||
return strings.HasPrefix(ip, "192.168.")
|
||||
}
|
||||
|
||||
func isEndpointAllowed(source string, endpoint string) bool {
|
||||
var endpoints, err = getAllowedEndpoints(source)
|
||||
if err != "" {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
if (endpoints != nil) && (len(endpoints) > 0) {
|
||||
for _, e := range endpoints {
|
||||
if e == endpoint {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func getAllowedEndpoints(source string) (endpoints []string, err string) {
|
||||
if source == "Local" {
|
||||
return Endpoints.Local, ""
|
||||
}
|
||||
if source == "Remote" {
|
||||
return Endpoints.Remote, ""
|
||||
}
|
||||
if source == "Auth" {
|
||||
return Endpoints.Auth, ""
|
||||
}
|
||||
|
||||
return []string{}, "No allowed endpoints"
|
||||
}
|
||||
|
||||
func decryptAuth(r *http.Request) string {
|
||||
var req structs.Authcall
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil || req.Uuid == "" || req.Data == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
deviceKey, errKey := GetKeyByUuid(req.Uuid)
|
||||
decryptData, errDec := DecryptAES(deviceKey, req.Data)
|
||||
|
||||
if errKey != nil && errDec != nil || decryptData == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return decryptData
|
||||
}
|
||||
|
||||
func ParseRequest(req interface{}, data string, r *http.Request) (d interface{}, err error) {
|
||||
if data != "" {
|
||||
dataBytes := []byte(data)
|
||||
return req, json.Unmarshal(dataBytes, &req)
|
||||
} else {
|
||||
return req, json.NewDecoder(r.Body).Decode(&req)
|
||||
}
|
||||
}
|
||||
23
be/app/helper/browser-helper.go
Normal file
23
be/app/helper/browser-helper.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
func OpenBrowser(url string) bool {
|
||||
var args []string
|
||||
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
args = []string{"open"}
|
||||
case "windows":
|
||||
args = []string{"cmd", "/c", "start"}
|
||||
default:
|
||||
args = []string{"xdg-open"}
|
||||
}
|
||||
|
||||
cmd := exec.Command(args[0], append(args[1:], url)...)
|
||||
|
||||
return cmd.Start() == nil
|
||||
}
|
||||
46
be/app/helper/device-helper.go
Normal file
46
be/app/helper/device-helper.go
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TempPinFile(Uuid string, pin string) bool {
|
||||
log.Println("temp pin file", Uuid, pin)
|
||||
err := os.WriteFile("devices/"+Uuid+".tmp", []byte(pin), 0644)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
time.AfterFunc(1*time.Minute, func() {
|
||||
log.Println("deleting", Uuid, pin)
|
||||
os.Remove("devices/" + Uuid + ".tmp")
|
||||
})
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func CheckPinFile(Uuid string) bool {
|
||||
_, err := os.Stat("devices/" + Uuid + ".tmp")
|
||||
return err == nil
|
||||
}
|
||||
|
||||
func SaveDeviceKey(Uuid string, key string) error {
|
||||
err := os.WriteFile("devices/"+Uuid+".key", []byte(key), 0644)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetKeyByUuid(Uuid string) (string, error) {
|
||||
data, err := os.ReadFile("devices/" + Uuid + ".key")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(data), nil
|
||||
}
|
||||
105
be/app/helper/encrypt-helper.go
Normal file
105
be/app/helper/encrypt-helper.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func EncryptAES(key string, plaintext string) (string, error) {
|
||||
origData := []byte(plaintext)
|
||||
|
||||
// Create AES cipher
|
||||
block, err := aes.NewCipher(keyToBytes(key))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
blockSize := block.BlockSize()
|
||||
|
||||
origData = PKCS5Padding(origData, blockSize)
|
||||
|
||||
iv := []byte(EnvGet("MCRM__IV"))
|
||||
blockMode := cipher.NewCBCEncrypter(block, iv)
|
||||
|
||||
crypted := make([]byte, len(origData))
|
||||
blockMode.CryptBlocks(crypted, origData)
|
||||
|
||||
cryptedString := base64.StdEncoding.EncodeToString(crypted)
|
||||
|
||||
return cryptedString, nil
|
||||
}
|
||||
|
||||
func DecryptAES(key string, cryptedText string) (string, error) {
|
||||
crypted, err := base64.StdEncoding.DecodeString(cryptedText)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(keyToBytes(key))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
iv := []byte(EnvGet("MCRM__IV"))
|
||||
blockMode := cipher.NewCBCDecrypter(block, iv)
|
||||
|
||||
origData := make([]byte, len(crypted))
|
||||
|
||||
blockMode.CryptBlocks(origData, crypted)
|
||||
origData, err = PKCS5UnPadding(origData)
|
||||
|
||||
if err != nil || len(origData) <= 3 {
|
||||
return "", errors.New("invalid key")
|
||||
}
|
||||
|
||||
origDataString := string(origData)
|
||||
|
||||
return origDataString, nil
|
||||
}
|
||||
|
||||
func generateRandomString(length int) string {
|
||||
b := make([]byte, length)
|
||||
_, err := rand.Read(b)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return base64.StdEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
func GenerateKey() string {
|
||||
return strings.Replace(generateRandomString(24), "=", "", -1)
|
||||
}
|
||||
|
||||
func keyToBytes(key string) []byte {
|
||||
// Convert key to bytes
|
||||
keyBytes := []byte(key)
|
||||
|
||||
// If key is 4 characters, append salt
|
||||
if len(key) == 4 {
|
||||
keyBytes = []byte(key + EnvGet("MCRM__SALT"))
|
||||
}
|
||||
|
||||
return keyBytes
|
||||
}
|
||||
|
||||
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
func PKCS5UnPadding(origData []byte) ([]byte, error) {
|
||||
length := len(origData)
|
||||
unpadding := int(origData[length-1])
|
||||
|
||||
if (unpadding >= length) || (unpadding == 0) {
|
||||
return nil, errors.New("unpadding error")
|
||||
}
|
||||
|
||||
return origData[:(length - unpadding)], nil
|
||||
}
|
||||
16
be/app/helper/env-helper.go
Normal file
16
be/app/helper/env-helper.go
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func EnvGet(key string) string {
|
||||
err := godotenv.Load("../.env")
|
||||
if err != nil {
|
||||
log.Fatal("Error loading .env file")
|
||||
}
|
||||
return os.Getenv("VITE_" + key)
|
||||
}
|
||||
64
be/app/helper/macro-helper.go
Normal file
64
be/app/helper/macro-helper.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"be/app/structs"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
func FormatMacroFileName(s string) string {
|
||||
// Remove invalid characters
|
||||
re := regexp.MustCompile(`[\/\?\*\>\<\:\\"\|\n]`)
|
||||
s = re.ReplaceAllString(s, "")
|
||||
|
||||
// Replace spaces with underscores
|
||||
s = strings.ReplaceAll(s, " ", "_")
|
||||
|
||||
// Remove special characters
|
||||
re = regexp.MustCompile(`[!@#$%^&\(\)\[\]\{\}\~]`)
|
||||
s = re.ReplaceAllString(s, "")
|
||||
|
||||
// Truncate the string
|
||||
if len(s) > 255 {
|
||||
s = s[:255]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func ReadMacroFile(filename string) (steps []structs.Step, err error) {
|
||||
log.Println(filename)
|
||||
|
||||
content, err := os.ReadFile(filename)
|
||||
|
||||
if err != nil {
|
||||
log.Fatal("Error when opening file: ", err)
|
||||
}
|
||||
|
||||
err = json.Unmarshal(content, &steps)
|
||||
|
||||
return steps, err
|
||||
}
|
||||
|
||||
func RunMacroSteps(steps []structs.Step) {
|
||||
for _, step := range steps {
|
||||
// log.Println(step)
|
||||
switch step.Type {
|
||||
case "key":
|
||||
robotgo.KeyToggle(step.Key, step.Direction)
|
||||
// log.Println("Toggling", step.Key, "to", step.Direction)
|
||||
case "delay":
|
||||
time.Sleep(time.Duration(step.Location) * time.Millisecond)
|
||||
// log.Println("Sleeping for", step.Value, "milliseconds")
|
||||
default:
|
||||
log.Println("Unknown step type:", step.Type)
|
||||
}
|
||||
}
|
||||
}
|
||||
88
be/app/macro.go
Normal file
88
be/app/macro.go
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"be/app/helper"
|
||||
"be/app/structs"
|
||||
)
|
||||
|
||||
func SaveMacro(w http.ResponseWriter, r *http.Request) {
|
||||
var newMacro structs.NewMacro
|
||||
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Println(string(body))
|
||||
|
||||
err = json.Unmarshal(body, &newMacro)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stepsJSON, err := json.Marshal(newMacro.Steps)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile("../macros/"+helper.FormatMacroFileName(newMacro.Name)+".json", stepsJSON, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func ListMacros(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("listing macros")
|
||||
dir := "../macros"
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
|
||||
var fileNames []string
|
||||
|
||||
for _, file := range files {
|
||||
filename := filepath.Base(file.Name())
|
||||
filename = strings.TrimSuffix(filename, filepath.Ext(filename))
|
||||
filename = strings.Replace(filename, "_", " ", -1)
|
||||
|
||||
fileNames = append(fileNames, filename)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(fileNames)
|
||||
}
|
||||
|
||||
func DeleteMacro(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
func PlayMacro(data string, w http.ResponseWriter, r *http.Request) {
|
||||
req := &structs.MacroRequest{}
|
||||
_, err := helper.ParseRequest(req, data, r)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
macro := req.Macro
|
||||
|
||||
var filename = helper.FormatMacroFileName(macro)
|
||||
var filepath = fmt.Sprintf("../macros/%s.json", filename)
|
||||
|
||||
macroFile, err := helper.ReadMacroFile(filepath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
helper.RunMacroSteps(macroFile)
|
||||
}
|
||||
37
be/app/structs/api-struct.go
Normal file
37
be/app/structs/api-struct.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package structs
|
||||
|
||||
type Allowed struct {
|
||||
Local []string
|
||||
Remote []string
|
||||
Auth []string
|
||||
}
|
||||
|
||||
var Endpoints = Allowed{
|
||||
Local: []string{
|
||||
"/macro/record",
|
||||
"/macro/list",
|
||||
"/macro/delete",
|
||||
"/macro/play",
|
||||
"/device/list",
|
||||
"/device/access/check",
|
||||
"/device/access/request",
|
||||
"/device/link/ping",
|
||||
"/device/link/start",
|
||||
"/device/link/poll",
|
||||
"/device/link/remove",
|
||||
"/device/handshake",
|
||||
},
|
||||
Remote: []string{
|
||||
"/macro/list",
|
||||
"/device/access/check",
|
||||
"/device/access/request",
|
||||
"/device/link/ping",
|
||||
"/device/link/end",
|
||||
"/device/handshake",
|
||||
"/device/auth",
|
||||
},
|
||||
Auth: []string{
|
||||
"/macro/play",
|
||||
"/device/link/remove",
|
||||
},
|
||||
}
|
||||
31
be/app/structs/device-struct.go
Normal file
31
be/app/structs/device-struct.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package structs
|
||||
|
||||
type Settings struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type RemoteWebhook struct {
|
||||
Event string `json:"event"`
|
||||
Data string `json:"data"`
|
||||
}
|
||||
|
||||
type Check struct {
|
||||
Uuid string `json:"uuid"`
|
||||
}
|
||||
|
||||
type Request struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type Handshake struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Shake string `json:"shake"`
|
||||
}
|
||||
|
||||
type Authcall struct {
|
||||
Uuid string `json:"uuid"`
|
||||
Data string `json:"d"`
|
||||
}
|
||||
19
be/app/structs/macro-struct.go
Normal file
19
be/app/structs/macro-struct.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package structs
|
||||
|
||||
type MacroRequest struct {
|
||||
Macro string `json:"macro"`
|
||||
}
|
||||
|
||||
type Step struct {
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Code string `json:"code"`
|
||||
Location int `json:"location"`
|
||||
Direction string `json:"direction"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
type NewMacro struct {
|
||||
Name string `json:"name"`
|
||||
Steps []Step `json:"steps"`
|
||||
}
|
||||
1
be/devices/a42e16a8-0e99-4bb9-a93f-363740c45b24.json
Normal file
1
be/devices/a42e16a8-0e99-4bb9-a93f-363740c45b24.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"name":"Unknown desktop","type":"desktop"}
|
||||
1
be/devices/a42e16a8-0e99-4bb9-a93f-363740c45b24.key
Normal file
1
be/devices/a42e16a8-0e99-4bb9-a93f-363740c45b24.key
Normal file
|
|
@ -0,0 +1 @@
|
|||
3JYxP8LOq1Y2fhpgEtpXVJ3v4s3qdML3
|
||||
1
be/devices/f70778be-99c1-4c5c-b1a2-36ef73d971a0.json
Normal file
1
be/devices/f70778be-99c1-4c5c-b1a2-36ef73d971a0.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"name":"Unknown mobile","type":"mobile"}
|
||||
1
be/devices/f70778be-99c1-4c5c-b1a2-36ef73d971a0.key
Normal file
1
be/devices/f70778be-99c1-4c5c-b1a2-36ef73d971a0.key
Normal file
|
|
@ -0,0 +1 @@
|
|||
4MqIbBoPsHizsWCyeqg6gd/wpQzfhc7e
|
||||
38
be/go.mod
Normal file
38
be/go.mod
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
module be
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require (
|
||||
github.com/go-vgo/robotgo v0.110.6
|
||||
github.com/joho/godotenv v1.5.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e // indirect
|
||||
github.com/ebitengine/purego v0.8.2 // indirect
|
||||
github.com/gen2brain/shm v0.1.1 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||
github.com/jezek/xgb v1.1.1 // indirect
|
||||
github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 // indirect
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e // indirect
|
||||
github.com/otiai10/gosseract v2.2.1+incompatible // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/robotn/xgb v0.10.0 // indirect
|
||||
github.com/robotn/xgbutil v0.10.0 // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 // indirect
|
||||
github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.14 // indirect
|
||||
github.com/tklauser/numcpus v0.9.0 // indirect
|
||||
github.com/vcaesar/gops v0.40.0 // indirect
|
||||
github.com/vcaesar/imgo v0.40.2 // indirect
|
||||
github.com/vcaesar/keycode v0.10.1 // indirect
|
||||
github.com/vcaesar/tt v0.20.1 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f // indirect
|
||||
golang.org/x/image v0.24.0 // indirect
|
||||
golang.org/x/net v0.38.0 // indirect
|
||||
golang.org/x/sys v0.31.0 // indirect
|
||||
)
|
||||
80
be/go.sum
Normal file
80
be/go.sum
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
|
||||
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e h1:L+XrFvD0vBIBm+Wf9sFN6aU395t7JROoai0qXZraA4U=
|
||||
github.com/dblohm7/wingoes v0.0.0-20240820181039-f2b84150679e/go.mod h1:SUxUaAK/0UG5lYyZR1L1nC4AaYYvSSYTWQSH3FPcxKU=
|
||||
github.com/ebitengine/purego v0.8.2 h1:jPPGWs2sZ1UgOSgD2bClL0MJIqu58nOmIcBuXr62z1I=
|
||||
github.com/ebitengine/purego v0.8.2/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
|
||||
github.com/gen2brain/shm v0.1.1 h1:1cTVA5qcsUFixnDHl14TmRoxgfWEEZlTezpUj1vm5uQ=
|
||||
github.com/gen2brain/shm v0.1.1/go.mod h1:UgIcVtvmOu+aCJpqJX7GOtiN7X2ct+TKLg4RTxwPIUA=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-vgo/robotgo v0.110.6 h1:1tOxlmTXYg6F3Xs8IT++331MxY2nZ+Q3B6eW312llbo=
|
||||
github.com/go-vgo/robotgo v0.110.6/go.mod h1:eBUjTHY1HYjzdi1+UWJUbxB+b9gE+l4Ei7vQU/9SnLw=
|
||||
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/jezek/xgb v1.1.1 h1:bE/r8ZZtSv7l9gk6nU0mYx51aXrvnyb44892TwSaqS4=
|
||||
github.com/jezek/xgb v1.1.1/go.mod h1:nrhwO0FX/enq75I7Y7G8iN1ubpSGZEiA3v9e9GyRFlk=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c h1:1IlzDla/ZATV/FsRn1ETf7ir91PHS2mrd4VMunEtd9k=
|
||||
github.com/kbinani/screenshot v0.0.0-20250118074034-a3924b7bbc8c/go.mod h1:Pmpz2BLf55auQZ67u3rvyI2vAQvNetkK/4zYUmpauZQ=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683 h1:7UMa6KCCMjZEMDtTVdcGu0B1GmmC7QJKiCCjyTAWQy0=
|
||||
github.com/lufia/plan9stats v0.0.0-20240909124753-873cd0166683/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e h1:H+t6A/QJMbhCSEH5rAuRxh+CtW96g0Or0Fxa9IKr4uc=
|
||||
github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/otiai10/gosseract v2.2.1+incompatible h1:Ry5ltVdpdp4LAa2bMjsSJH34XHVOV7XMi41HtzL8X2I=
|
||||
github.com/otiai10/gosseract v2.2.1+incompatible/go.mod h1:XrzWItCzCpFRZ35n3YtVTgq5bLAhFIkascoRo8G32QE=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
github.com/robotn/xgb v0.0.0-20190912153532-2cb92d044934/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ=
|
||||
github.com/robotn/xgb v0.10.0 h1:O3kFbIwtwZ3pgLbp1h5slCQ4OpY8BdwugJLrUe6GPIM=
|
||||
github.com/robotn/xgb v0.10.0/go.mod h1:SxQhJskUJ4rleVU44YvnrdvxQr0tKy5SRSigBrCgyyQ=
|
||||
github.com/robotn/xgbutil v0.10.0 h1:gvf7mGQqCWQ68aHRtCxgdewRk+/KAJui6l3MJQQRCKw=
|
||||
github.com/robotn/xgbutil v0.10.0/go.mod h1:svkDXUDQjUiWzLrA0OZgHc4lbOts3C+uRfP6/yjwYnU=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1 h1:QSWkTc+fu9LTAWfkZwZ6j8MSUk4A2LV7rbH0ZqmLjXs=
|
||||
github.com/shirou/gopsutil/v4 v4.25.1/go.mod h1:RoUCUpndaJFtT+2zsZzzmhvbfGoDCJ7nFXKJf8GqJbI=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35 h1:wAZbkTZkqDzWsqxPh2qkBd3KvFU7tcxV0BP0Rnhkxog=
|
||||
github.com/tailscale/win v0.0.0-20250213223159-5992cb43ca35/go.mod h1:aMd4yDHLjbOuYP6fMxj1d9ACDQlSWwYztcpybGHCQc8=
|
||||
github.com/tc-hib/winres v0.2.1 h1:YDE0FiP0VmtRaDn7+aaChp1KiF4owBiJa5l964l5ujA=
|
||||
github.com/tc-hib/winres v0.2.1/go.mod h1:C/JaNhH3KBvhNKVbvdlDWkbMDO9H4fKKDaN7/07SSuk=
|
||||
github.com/tklauser/go-sysconf v0.3.14 h1:g5vzr9iPFFz24v2KZXs/pvpvh8/V9Fw6vQK5ZZb78yU=
|
||||
github.com/tklauser/go-sysconf v0.3.14/go.mod h1:1ym4lWMLUOhuBOPGtRcJm7tEGX4SCYNEEEtghGG/8uY=
|
||||
github.com/tklauser/numcpus v0.9.0 h1:lmyCHtANi8aRUgkckBgoDk1nHCux3n2cgkJLXdQGPDo=
|
||||
github.com/tklauser/numcpus v0.9.0/go.mod h1:SN6Nq1O3VychhC1npsWostA+oW+VOQTxZrS604NSRyI=
|
||||
github.com/vcaesar/gops v0.40.0 h1:I+1RCGiV+LkZJUYNzAd373xs0uM2UyeFdZBmow8HfCM=
|
||||
github.com/vcaesar/gops v0.40.0/go.mod h1:3u/USW7JovqUK6i13VOD3qWfvXXd2TIIKE4PYIv4TOM=
|
||||
github.com/vcaesar/imgo v0.40.2 h1:5GWScRLdBCMtO1v2I1bs+ZmDLZFINxYSMZ+mtUw5qPM=
|
||||
github.com/vcaesar/imgo v0.40.2/go.mod h1:MVCl+FxHI2gTgmiHoi0n5xNCbYcfv9SVtdEOUC92+eo=
|
||||
github.com/vcaesar/keycode v0.10.1 h1:0DesGmMAPWpYTCYddOFiCMKCDKgNnwiQa2QXindVUHw=
|
||||
github.com/vcaesar/keycode v0.10.1/go.mod h1:JNlY7xbKsh+LAGfY2j4M3znVrGEm5W1R8s/Uv6BJcfQ=
|
||||
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f h1:oFMYAjX0867ZD2jcNiLBrI9BdpmEkvPyi5YrBGXbamg=
|
||||
golang.org/x/exp v0.0.0-20250215185904-eff6e970281f/go.mod h1:BHOTPb3L19zxehTsLoJXVaTktb06DFgmdW6Wb9s8jqk=
|
||||
golang.org/x/image v0.24.0 h1:AN7zRgVsbvmTfNyqIbbOraYL8mSwcKncEj8ofjgzcMQ=
|
||||
golang.org/x/image v0.24.0/go.mod h1:4b/ITuLfqYq1hqZcjofwctIhi7sZh2WaCjvsBNjjya8=
|
||||
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
|
||||
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
23
be/index.html
Normal file
23
be/index.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>404 - BALLS</title>
|
||||
<style>
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
background-color: #f0f0f0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Balls found</h1>
|
||||
<span>So not the content you're looking for.</span>
|
||||
</body>
|
||||
</html>
|
||||
162
be/macroOLD/macro.go
Normal file
162
be/macroOLD/macro.go
Normal file
|
|
@ -0,0 +1,162 @@
|
|||
package macro
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/go-vgo/robotgo"
|
||||
)
|
||||
|
||||
type Step struct {
|
||||
Type string `json:"type"`
|
||||
Key string `json:"key"`
|
||||
Code string `json:"code"`
|
||||
Location int `json:"location"`
|
||||
Direction string `json:"direction"`
|
||||
Value int `json:"value"`
|
||||
}
|
||||
|
||||
var newMacro struct {
|
||||
Name string `json:"name"`
|
||||
Steps []Step `json:"steps"`
|
||||
}
|
||||
|
||||
func Save(w http.ResponseWriter, r *http.Request) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
log.Println(string(body))
|
||||
|
||||
err = json.Unmarshal(body, &newMacro)
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
stepsJSON, err := json.Marshal(newMacro.Steps)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = os.WriteFile("../macros/"+makeValidFilename(newMacro.Name)+".json", stepsJSON, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func makeValidFilename(s string) string {
|
||||
// Remove invalid characters
|
||||
re := regexp.MustCompile(`[\/\?\*\>\<\:\\"\|\n]`)
|
||||
s = re.ReplaceAllString(s, "")
|
||||
|
||||
// Replace spaces with underscores
|
||||
s = strings.ReplaceAll(s, " ", "_")
|
||||
|
||||
// Remove special characters
|
||||
re = regexp.MustCompile(`[!@#$%^&\(\)\[\]\{\}\~]`)
|
||||
s = re.ReplaceAllString(s, "")
|
||||
|
||||
// Truncate the string
|
||||
if len(s) > 255 {
|
||||
s = s[:255]
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
func List(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("listing macros")
|
||||
dir := "../macros"
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var fileNames []string
|
||||
|
||||
for _, file := range files {
|
||||
filename := filepath.Base(file.Name())
|
||||
filename = strings.TrimSuffix(filename, filepath.Ext(filename))
|
||||
filename = strings.Replace(filename, "_", " ", -1)
|
||||
|
||||
fileNames = append(fileNames, filename)
|
||||
}
|
||||
|
||||
json.NewEncoder(w).Encode(fileNames)
|
||||
}
|
||||
|
||||
func Delete(w http.ResponseWriter, r *http.Request) {}
|
||||
|
||||
func Play(w http.ResponseWriter, r *http.Request) {
|
||||
type MacroRequest struct {
|
||||
Macro string `json:"macro"`
|
||||
}
|
||||
|
||||
var req MacroRequest
|
||||
|
||||
err := json.NewDecoder(r.Body).Decode(&req)
|
||||
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
macro := req.Macro
|
||||
|
||||
macroFile, err := readMacroFile(fmt.Sprintf("../macros/%s.json", makeValidFilename(macro)))
|
||||
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
playMacro(macroFile)
|
||||
// fmt.Println(macroFile)
|
||||
}
|
||||
|
||||
func readMacroFile(filename string) (steps []Step, err error) {
|
||||
|
||||
log.Println(filename)
|
||||
// Let's first read the `config.json` file
|
||||
content, err := os.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Fatal("Error when opening file: ", err)
|
||||
}
|
||||
|
||||
// Now let's unmarshall the data into `steps`
|
||||
err = json.Unmarshal(content, &steps)
|
||||
if err != nil {
|
||||
log.Fatal("Error during Unmarshal(): ", err)
|
||||
}
|
||||
|
||||
return steps, nil
|
||||
}
|
||||
|
||||
func playMacro(steps []Step) {
|
||||
for _, step := range steps {
|
||||
// log.Println(step)
|
||||
switch step.Type {
|
||||
case "key":
|
||||
robotgo.KeyToggle(step.Key, step.Direction)
|
||||
// log.Println("Toggling", step.Key, "to", step.Direction)
|
||||
case "delay":
|
||||
time.Sleep(time.Duration(step.Location) * time.Millisecond)
|
||||
// log.Println("Sleeping for", step.Value, "milliseconds")
|
||||
default:
|
||||
log.Println("Unknown step type:", step.Type)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
26
be/main.go
Normal file
26
be/main.go
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"be/app"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||
apiInit(w, r)
|
||||
})
|
||||
|
||||
log.Println(http.ListenAndServe(":6970", nil))
|
||||
}
|
||||
|
||||
func apiInit(w http.ResponseWriter, r *http.Request) {
|
||||
app.ApiCORS(w, r)
|
||||
|
||||
if r.Method == "GET" {
|
||||
app.ApiGet(w, r)
|
||||
} else if r.Method == "POST" {
|
||||
app.ApiPost(w, r)
|
||||
}
|
||||
}
|
||||
1
be/tmp/build-errors.log
Normal file
1
be/tmp/build-errors.log
Normal file
|
|
@ -0,0 +1 @@
|
|||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
||||
BIN
be/tmp/main.exe
Normal file
BIN
be/tmp/main.exe
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue