commit ff0811bf90f27d7aabd794e21a27c0e85d8ef75b Author: David Majdandžić Date: Fri Jul 19 12:50:57 2024 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cf204c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +cache.db +main.log diff --git a/cache.go b/cache.go new file mode 100644 index 0000000..1bbcb37 --- /dev/null +++ b/cache.go @@ -0,0 +1,137 @@ +package main + +import ( + "database/sql" + "fmt" + "log" + "time" + + _ "github.com/mattn/go-sqlite3" +) + +// region DB +type DB struct { + path string + readConn *sql.DB + writeConn *sql.DB +} + +func (db *DB) Open() error { + if db.path == "" { + return fmt.Errorf("database path not set") + } + + writeConn, err := sql.Open("sqlite3", db.path+"?_journal=WAL&_synchronous=NORMAL") + if err != nil { + Error.Printf("%++v", err) + return err + } + writeConn.SetMaxOpenConns(1) + writeConn.SetConnMaxIdleTime(30 * time.Second) + writeConn.SetConnMaxLifetime(30 * time.Second) + db.writeConn = writeConn + + readConn, err := sql.Open("sqlite3", db.path+"?mode=ro&_journal=WAL&_synchronous=NORMAL&_mode=ro") + if err != nil { + Error.Printf("%++v", err) + return err + } + readConn.SetMaxOpenConns(4) + readConn.SetConnMaxIdleTime(30 * time.Second) + readConn.SetConnMaxLifetime(30 * time.Second) + db.readConn = readConn + + return nil +} + +func (db *DB) Close() error { + err := db.writeConn.Close() + if err != nil { + return err + } + + err = db.readConn.Close() + if err != nil { + return err + } + + return nil +} + +type DBCache interface { + Get(key string) (any, error) + Set(key string, value any) error + Tidy() error + Init() error +} + +// region Cache +type Cache struct { + db *DB + hits int + misses int +} +type CacheEntry struct { + Key string + Value string + Date time.Time +} + +func (cache *Cache) Get(key string) (CacheEntry, error) { + ret := CacheEntry{} + var rkey, rvalue string + var date time.Time + err := cache.db.readConn.QueryRow("SELECT key, value, date FROM cache WHERE key = ?", key).Scan(&rkey, &rvalue, &date) + if err != nil { + log.Printf("Cache miss for %s: %v", key, err) + cache.misses++ + return ret, err + } + + ret.Key = rkey + ret.Value = rvalue + ret.Date = date + + cache.hits++ + log.Printf("Cache hit for %s", key) + return ret, nil +} + +func (cache *Cache) Set(key string, value string) error { + _, err := cache.db.writeConn.Exec("INSERT INTO cache (key, value) VALUES (?, ?)", key, value) + if err != nil { + return err + } + + return nil +} + +func (cache *Cache) Init() error { + _, err := cache.db.writeConn.Exec("CREATE TABLE IF NOT EXISTS cache (key TEXT, value TEXT, date DATE DEFAULT CURRENT_TIMESTAMP)") + if err != nil { + return err + } + + _, err = cache.db.writeConn.Exec("CREATE INDEX IF NOT EXISTS idx_cache_key ON cache (key)") + if err != nil { + return err + } + + return nil +} + +func (cache *Cache) Tidy() error { + _, err := cache.db.writeConn.Exec("DELETE FROM cache WHERE date < datetime('now', '-1 day')") + if err != nil { + return err + } + + return nil +} + +func (cache *Cache) Rate() float32 { + return float32(cache.hits) / float32(cache.hits+cache.misses) +} +func (cache *Cache) RatePerc() string { + return fmt.Sprintf("%.2f%%", cache.Rate()*100) +} diff --git a/colors.go b/colors.go new file mode 100644 index 0000000..ab53052 --- /dev/null +++ b/colors.go @@ -0,0 +1,76 @@ +package main + +const ( + // Reset + Reset = "\033[0m" // Text Reset + + // Regular Colors + Black = "\033[0;30m" // Black + Red = "\033[0;31m" // Red + Green = "\033[0;32m" // Green + Yellow = "\033[0;33m" // Yellow + Blue = "\033[0;34m" // Blue + Purple = "\033[0;35m" // Purple + Cyan = "\033[0;36m" // Cyan + White = "\033[0;37m" // White + + // Bold + BBlack = "\033[1;30m" // Black + BRed = "\033[1;31m" // Red + BGreen = "\033[1;32m" // Green + BYellow = "\033[1;33m" // Yellow + BBlue = "\033[1;34m" // Blue + BPurple = "\033[1;35m" // Purple + BCyan = "\033[1;36m" // Cyan + BWhite = "\033[1;37m" // White + + // Underline + UBlack = "\033[4;30m" // Black + URed = "\033[4;31m" // Red + UGreen = "\033[4;32m" // Green + UYellow = "\033[4;33m" // Yellow + UBlue = "\033[4;34m" // Blue + UPurple = "\033[4;35m" // Purple + UCyan = "\033[4;36m" // Cyan + UWhite = "\033[4;37m" // White + + // Background + On_Black = "\033[40m" // Black + On_Red = "\033[41m" // Red + On_Green = "\033[42m" // Green + On_Yellow = "\033[43m" // Yellow + On_Blue = "\033[44m" // Blue + On_Purple = "\033[45m" // Purple + On_Cyan = "\033[46m" // Cyan + On_White = "\033[47m" // White + + // High Intensty + IBlack = "\033[0;90m" // Black + IRed = "\033[0;91m" // Red + IGreen = "\033[0;92m" // Green + IYellow = "\033[0;93m" // Yellow + IBlue = "\033[0;94m" // Blue + IPurple = "\033[0;95m" // Purple + ICyan = "\033[0;96m" // Cyan + IWhite = "\033[0;97m" // White + + // Bold High Intensty + BIBlack = "\033[1;90m" // Black + BIRed = "\033[1;91m" // Red + BIGreen = "\033[1;92m" // Green + BIYellow = "\033[1;93m" // Yellow + BIBlue = "\033[1;94m" // Blue + BIPurple = "\033[1;95m" // Purple + BICyan = "\033[1;96m" // Cyan + BIWhite = "\033[1;97m" // White + + // High Intensty backgrounds + On_IBlack = "\033[0;100m" // Black + On_IRed = "\033[0;101m" // Red + On_IGreen = "\033[0;102m" // Green + On_IYellow = "\033[0;103m" // Yellow + On_IBlue = "\033[0;104m" // Blue + On_IPurple = "\033[10;95m" // Purple + On_ICyan = "\033[0;106m" // Cyan + On_IWhite = "\033[0;107m" // White +) \ No newline at end of file diff --git a/entity.go b/entity.go new file mode 100644 index 0000000..dd3ba47 --- /dev/null +++ b/entity.go @@ -0,0 +1,19 @@ +package main + +type ( + Config struct { + Active bool `json:"Active"` + StackMult int `json:"StackMult"` + List []Entry `json:"List"` + } + Entry struct { + Name string `json:"name"` + Id string `json:"_id"` + Props EntryProps `json:"_props"` + } + EntryProps struct { + StackSize int `json:"StackMaxSize"` + } +) + +type Locale map[string]string \ No newline at end of file diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0ae3eb7 --- /dev/null +++ b/go.mod @@ -0,0 +1,15 @@ +module main + +go 1.22.4 + +require ( + github.com/mattn/go-sqlite3 v1.14.22 + golang.design/x/clipboard v0.7.0 +) + +require ( + golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 // indirect + golang.org/x/image v0.6.0 // indirect + golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c // indirect + golang.org/x/sys v0.5.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..9a2b491 --- /dev/null +++ b/go.sum @@ -0,0 +1,51 @@ +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= +github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.design/x/clipboard v0.7.0 h1:4Je8M/ys9AJumVnl8m+rZnIvstSnYj1fvzqYrU3TXvo= +golang.design/x/clipboard v0.7.0/go.mod h1:PQIvqYO9GP29yINEfsEn5zSQKAz3UgXmZKzDA6dnq2E= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56 h1:estk1glOnSVeJ9tdEZZc5mAMDZk5lNJNyJ6DvrBkTEU= +golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.6.0 h1:bR8b5okrPI3g/gyZakLZHeWxAR8Dn5CyxXv1hLH5g/4= +golang.org/x/image v0.6.0/go.mod h1:MXLdDR43H7cDJq5GEGXEVeeNhPgi+YYEQ2pC1byI1x0= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c h1:Gk61ECugwEHL6IiyyNLXNzmu8XslmRP2dS0xjIYhbb4= +golang.org/x/mobile v0.0.0-20230301163155-e0f57694e12c/go.mod h1:aAjjkJNdrh3PMckS4B10TGS2nag27cbKR1y2BpUxsiY= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/main.go b/main.go new file mode 100644 index 0000000..48d42da --- /dev/null +++ b/main.go @@ -0,0 +1,148 @@ +package main + +import ( + "context" + "encoding/json" + "fmt" + "io" + "log" + "os" + "regexp" + + "golang.design/x/clipboard" +) + +var Error *log.Logger +var Warning *log.Logger + +const CONFIG_FILE = `../config/barter.json` +const LOCALE_FILE = `C:\Games\Escape from Tarkov\Escape from Tarkov\SPT_Data\Server\database\locales\global\en.json` + +func init() { + log.SetFlags(log.Lmicroseconds | log.Lshortfile) + logFile, err := os.Create("main.log") + if err != nil { + log.Printf("Error creating log file: %v", err) + os.Exit(1) + } + logger := io.MultiWriter(os.Stdout, logFile) + log.SetOutput(logger) + + Error = log.New(io.MultiWriter(logFile, os.Stderr, os.Stdout), + fmt.Sprintf("%sERROR:%s ", "\033[0;101m", "\033[0m"), + log.Lmicroseconds|log.Lshortfile) + Warning = log.New(io.MultiWriter(logFile, os.Stdout), + fmt.Sprintf("%sWARNING:%s ", "\033[0;103m", "\033[0m"), + log.Lmicroseconds|log.Lshortfile) +} + +var dataRegex = regexp.MustCompile(`Tpl: (.+?)\n`) + +func main() { + file, err := os.Open(LOCALE_FILE) + if err != nil { + Error.Printf("Error opening items file: %v", err) + os.Exit(1) + } + + var locales Locale + err = json.NewDecoder(file).Decode(&locales) + if err != nil { + Error.Printf("Error decoding locale file: %v", err) + os.Exit(1) + } + + db := &DB{ + path:"cache.db", + } + db.Open() + cache := Cache{ + db: db, + } + cache.Init() + cache.Tidy() + + ch := clipboard.Watch(context.TODO(), clipboard.FmtText) + for data := range ch { + go func(data []byte) { + stringData := string(data) + // log.Println(stringData) + matches := dataRegex.FindStringSubmatch(stringData) + if len(matches) < 1 { + Warning.Printf("No matches found in: %s", stringData) + return + } + tpl := matches[1] + config := readConfig() + + log.Printf("Creating new entry: %s", tpl) + newEntry := Entry{ + Name: "", + Id: tpl, + Props: EntryProps{ + StackSize: 100, + }, + } + + name, ok := locales[tpl+" Name"] + if ok { + log.Printf("Name found for %s as %s", tpl, name) + newEntry.Name = name + } else { + Warning.Printf("Name not found for %s", tpl) + } + + val, err := cache.Get(tpl) + if err == nil { + log.Printf("%s%s already registered on %v!%s", On_IGreen, val.Value, val.Date, Reset) + } + cache.Set(tpl, name) + + for _, entry := range config.List { + if entry.Id == tpl { + Warning.Printf("Entry already exists: %s", tpl) + return + } + } + + config.List = append(config.List, newEntry) + writeConfig(config) + log.Printf("Config saved for: %s", tpl) + }(data) + } +} + +func readConfig() *Config { + config := &Config{} + + file, err := os.Open(CONFIG_FILE) + if err != nil { + Error.Printf("Error opening config file: %v", err) + return config + } + defer file.Close() + + err = json.NewDecoder(file).Decode(&config) + if err != nil { + Error.Printf("Error decoding config file: %v", err) + return config + } + return config +} + +func writeConfig(config *Config) { + file, err := os.Create(CONFIG_FILE) + if err != nil { + Error.Printf("Error creating config file: %v", err) + return + } + defer file.Close() + + encoder := json.NewEncoder(file) + encoder.SetIndent("", "\t") + err = encoder.Encode(config) + if err != nil { + Error.Printf("Error encoding config file: %v", err) + return + } +}