package main import ( "bufio" "encoding/binary" "fmt" "log" "net" "net/http" "os" "strconv" "strings" "text/template" "time" "github.com/spf13/viper" ) var items []item var inventar []invPos func checkErr(err error) { if err != nil { log.Fatal(err) } } var gActors = make(map[int]string) var isInTrade int var myTrade [16]tradeStr var hisTrade [16]tradeStr // read items list and create the array func populateItems() { file, err := os.Open("item_info.txt") if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { a := strings.Split(string(scanner.Text()), "|") id, _ := strconv.Atoi(strings.ReplaceAll(a[0], " ", "")) text_id := strings.TrimSpace(a[1]) item_price, _ := strconv.ParseFloat(strings.TrimSpace(a[2]), 32) n := item{Id: id, Text: text_id, Price: item_price} items = append(items, n) } if err := scanner.Err(); err != nil { log.Fatal(err) } } // Process incoming text messages from game func processPmMessage(c net.Conn, user string, mesaj string) { // fmt.Printf("PM MESSAGE command: |%s|\n", mesaj) msg := strings.Split(mesaj, " ")[1] switch msg { case "#ver", "ver": sendMessage(c, user, "GoEl bot v1 - written by mihaim") case "#inv", "inv": sendMessage(c, user, "Bot inventory:") for i := 0; i < 36; i++ { if inventar[i].Quantity != 0 { for _, it := range items { if inventar[i].Id == it.Id { sendMessage(c, user, "Item: "+strconv.Itoa(inventar[i].Quantity)+" "+it.Text+" sell price: "+fmt.Sprintf("%.2f", it.Price)) } } } } case "#set": if len(strings.Split(mesaj, " ")) != 4 { sendMessage(c, user, "#set ") break } itemId, _ := strconv.ParseInt(strings.Split(mesaj, " ")[2], 0, 0) newPrice, _ := strconv.ParseFloat(strings.Split(mesaj, " ")[3], 64) items[itemId].Price = newPrice a1 := fmt.Sprintf("Set price for %s to %.2f", items[itemId].Text, items[itemId].Price) sendMessage(c, user, a1) case "#get", "get": if len(strings.Split(mesaj, " ")) != 3 { sendMessage(c, user, "#get ") break } itemId, _ := strconv.ParseInt(strings.Split(mesaj, " ")[2], 0, 0) a1 := fmt.Sprintf("Price for %s is %.2f", items[itemId].Text, items[itemId].Price) sendMessage(c, user, a1) case "#buy", "buy": if len(strings.Split(mesaj, " ")) < 3 { sendMessage(c, user, "usage: #buy ") break } if isInTrade == 0 { sendMessage(c, user, "You need to trade with me before buying something") break } q, _ := strconv.ParseInt(strings.Split(mesaj, " ")[2], 0, 0) search_for := strings.Join(strings.Split(mesaj, " ")[3:], " ") // search in inventory , add to trade window i := 0 for i = 0; i < 36; i++ { if inventar[i].Quantity != 0 && strings.Contains(strings.ToLower(items[inventar[i].Id].Text), strings.ToLower(search_for)) { putItemOnTrade(c, int(q), i) sendMessage(c, user, fmt.Sprintf("Added to trade window %d %s", int(q), items[inventar[i].Id].Text)) break } } case "#help", "help": sendMessage(c, user, "GoEl trade bot v1 - written by mihaim") sendMessage(c, user, "Available commands: #buy, #inv, #ver, #help") case "#acc", "acc": acceptTrade(c) //sendMessage(c, user, "Accepting trade") case "#dbg", "dbg": for i := 0; i < 16; i++ { if hisTrade[i].Quantity != 0 { sendMessage(c, user, fmt.Sprintf("His_Trade: %s %d\n", items[hisTrade[i].Id].Text, hisTrade[i].Quantity)) } } for i := 0; i < 16; i++ { if myTrade[i].Quantity != 0 { sendMessage(c, user, fmt.Sprintf("My_Trade: %s %d\n", items[myTrade[i].Id].Text, myTrade[i].Quantity)) } } default: fmt.Printf("command: |%s|", strings.Split(mesaj, " ")[1]) switch user { case "mihaim": sendMessage(c, user, "Sar'na conashu' mihai") case "radu": sendMessage(c, user, "Sa traiasca dom' shef. Dulapu raporteaza ca nu's probleme") case "domino": sendMessage(c, user, "Traiasca dom' sherif. Dulapu la raport . Niem problem. All good") default: sendMessage(c, user, "Unknown command but hello to you too . And use #help for more informations") } } } func processBuffer(c net.Conn, buffer []byte, length int) { switch buffer[0] { // received inventory case HERE_YOUR_INVENTORY: for i := 0; i < int(buffer[3]); i++ { quantity := binary.LittleEndian.Uint32(buffer[i*10+3+3 : i*10+3+3+4]) itemId := int(buffer[i*10+3+3+3+3]) + 256*int(buffer[i*10+3+3+3+4]) pos := int(buffer[i*10+3+7]) inventar[pos].Id = itemId inventar[pos].Quantity = int(quantity) inventar[pos].Pos = pos } // Ping from server case PING_REQUEST: a := int(buffer[1]) + 256*int(buffer[2]) fmt.Printf("->Command: %d %d PING_REQUEST\n", buffer[0], a) sendPingReply(c, buffer) // text message received case RAW_TEXT: // trade request if strings.Split(string(buffer[5:]), " ")[1] == "wants" { name := strings.Split(string(buffer[5:]), " ")[0] //sendMessage(c, name, "Wanna trade? "+fmt.Sprint(is_in_trade)) // if we have the actor ... send trade // Oare ce dracu se intimpla daca nu exista actorul ... :D will see for key, value := range gActors { // sendMessage(c, name, "Wanna trade? |"+value+"|") if strings.Split(value, " ")[0] == name { sendTrade(c, key) isInTrade = 1 break } } } // normal PM message if string(buffer[5:14]) == "[PM from " { msg := buffer[14 : length-1] dela := strings.Split(string(msg), ":") pm := string(strings.Join(dela[1:], ":")) processPmMessage(c, dela[0], pm) break } if strings.Split(string(buffer[5:]), " ")[1] == "aborted" { isInTrade = 0 resetTrade() } fmt.Printf("Alt mesaj |%s|\n", buffer[5:]) // add new actor case ADD_NEW_ACTOR: actorName := make([]byte, 30) actorId := int(buffer[3]) + 256*int(buffer[4]) for i := 0; i < 30; i++ { if buffer[i+20] != 0 { actorName[i] = buffer[i+20] } else { break } } act := strings.TrimRight(string(actorName), "\t \n\x00") gActors[actorId] = act fmt.Printf("NE Actor: Id: %d Name: |%s|\n", actorId, act) // new minute case NEW_MINUTE: break case GET_TRADE_OBJECT: fmt.Printf("GET_TRADE_OBJECT ") img := int(buffer[3]) + 256*int(buffer[4]) qua := int(buffer[5]) + 256*int(buffer[6]) + 65535*(int(buffer[7])+256*int(buffer[8])) pos := int(buffer[7+3]) tipe := int(buffer[3+6]) us := int(buffer[11]) id := int(buffer[12]) + 256*int(buffer[13]) if us == 0 { fmt.Printf("Noi am pus %d %s \n", qua, items[id].Text) myTrade[pos].Id = id myTrade[pos].ImageId = img myTrade[pos].Quantity += qua myTrade[pos].Type = tipe } else { fmt.Printf("El a pus %d %s \n", qua, items[id].Text) hisTrade[pos].Id = id hisTrade[pos].ImageId = img hisTrade[pos].Quantity += qua hisTrade[pos].Type = tipe } for i := 0; i < length-3; i++ { fmt.Printf("%2x ", buffer[i+3]) } fmt.Printf("\n") case REMOVE_TRADE_OBJECT: fmt.Printf("REMOVE_TRADE_OBJECT ") qua := int(buffer[3]) + 256*int(buffer[4]) + 65535*(int(buffer[5])+256*int(buffer[6])) pos := int(buffer[3+4]) us := int(buffer[3+5]) if us == 0 { fmt.Printf("Noi am retras %d %s \n", qua, items[myTrade[pos].Id].Text) myTrade[pos].Quantity -= qua } else { fmt.Printf("El a retras %d %s \n", qua, items[hisTrade[pos].Id].Text) hisTrade[pos].Quantity -= qua } for i := 0; i < length-3; i++ { fmt.Printf("%2x ", buffer[i+3]) } fmt.Printf("\n") case GET_TRADE_ACCEPT: // fmt.Printf("GET_TRADE ACCEPT %02x %02x %02x %02x\n", buffer[0], buffer[1], buffer[2], buffer[3]) if buffer[3] == 0 { break } trade_value := 0.0 for i := 0; i < 16; i++ { trade_value += float64(myTrade[i].Quantity) * float64(items[myTrade[i].Id].Price) } fmt.Printf("Total trade value %d\n", int(trade_value)) for i := 0; i < 16; i++ { //fmt.Printf("GET_TRADE_ACCEPT: index %d valoare = %d %d %d \n", i, his_trade[i].Id, his_trade[i].Quantity, int(trade_value)) if hisTrade[i].Id == MONEY && int(hisTrade[i].Quantity) == int(trade_value) { //fmt.Printf("GET_TRADE_ACCEPT: valoare = %d %d \n", his_trade[i].Quantity, int(trade_value)) acceptTrade(c) break } } //fmt.Printf("GET_TRADE_ACCEPT: valoare = %d \n", int(trade_value)) case GET_TRADE_REJECT: fmt.Printf("GET_TRADE REJECT\n") case GET_TRADE_EXIT: fmt.Printf("GET_TRADE EXIT\n") // get partial stat case SEND_PARTIAL_STAT: // fmt.Printf("get_partial_stat from server") break // add new enhanced actor case ADD_NEW_ENHANCED_ACTOR: actorName := make([]byte, 30) actorId := int(buffer[3]) + 256*int(buffer[4]) for i := 0; i < 30; i++ { if buffer[i+31] != 0 { actorName[i] = buffer[i+31] } else { break } } act := strings.TrimRight(string(actorName), "\t \n\x00") gActors[actorId] = act fmt.Printf("Actor: Id: %d Name: |%s|\n", actorId, act) // remove actor case REMOVE_ACTOR: actorId := int(buffer[3]) + 256*int(buffer[4]) delete(gActors, actorId) fmt.Printf("Remove Actor Id: %d \n", actorId) // add actor command ( do nothing with this ) case ADD_ACTOR_COMMAND: break // everything else ( commands not implemented yet) default: a := int(buffer[1]) + 256*int(buffer[2]) fmt.Printf("->Command: %d %d\n", buffer[0], a) fmt.Printf("->: %s \n", buffer[3:]) } } func main() { isInTrade = 0 resetTrade() viper.SetConfigName("config") viper.AddConfigPath(".") viper.AutomaticEnv() viper.SetConfigType("yml") var configuration configurations populateItems() // Start adding web interface and handlers ( for now listen on 8080 . move it to config.yml in the end) http.HandleFunc("/hello", webHello) http.HandleFunc("/", webSlash) http.HandleFunc("/inv", webShowInventory) http.HandleFunc("/save", saveItemsAndPrices) http.HandleFunc("/test", func(w http.ResponseWriter, r *http.Request) { tmpl := template.Must(template.ParseFiles("templates/test.tmpl")) data := webPageData{ PageTitle: "Items list and prices", Ite: items, } tmpl.Execute(w, data) }) go func() { fmt.Println("listen on 8082") log.Fatal(http.ListenAndServe(":8082", nil)) }() if err := viper.ReadInConfig(); err != nil { fmt.Printf("Error reading config file, %s", err) } if err := viper.Unmarshal(&configuration); err != nil { fmt.Printf("Unable to decode into struct, %s", err) } c, err := net.Dial("tcp", configuration.Server.Ip+":"+fmt.Sprint(configuration.Server.Port)) if err != nil { fmt.Println(err) return } // initial inventory is empty for i := 0; i < 36; i++ { var v1 = new(invPos) v1.Id = 0 v1.Quantity = 0 v1.Pos = 0 inventar = append(inventar, *v1) } sendLogin(c, configuration.Credential.User, configuration.Credential.Password) sec := time.Now().Unix() // main loop for communicating with el server // One thread to rule them all :P ( and keep us connected) go func() { for { if time.Now().Unix()-sec > 30 { sendHeartBeat(c) sec = time.Now().Unix() fmt.Printf(time.Now().Format("2006-01-02 15:04:05") + "Avoid lagging out. Sending heartbeat pack\n") } time.Sleep(2 * time.Second) } }() for { reply := make([]byte, 4096) length, err := c.Read(reply) checkErr(err) //fmt.Printf("Received command: %d and length %d \n", reply[0], length) //fmt.Printf("Hex dump: %s\n", hex.Dump(reply)) indexul := 0 for ok := true; ok; ok = (indexul+int(reply[indexul+1])+256*int(reply[indexul+2]) < length) { packLen := int(reply[indexul+1]) + 256*int(reply[indexul+2]) if packLen >= 2 { process := reply[indexul : indexul+packLen+2] // fmt.Printf("->Command imbinata: %d %s\n", process[0], process[3:]) processBuffer(c, process, len(process)) } indexul = indexul + int(reply[indexul+1]) + 256*int(reply[indexul+2]) + 2 } } } func webHello(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "hello\n") // testing modify variable from web isInTrade++ } func webSlash(w http.ResponseWriter, req *http.Request) { tmpl := template.Must(template.ParseFiles("templates/home.tmpl")) data := webPageData{ PageTitle: "Home page", Ite: nil, } tmpl.Execute(w, data) } func webShowInventory(w http.ResponseWriter, req *http.Request) { var inv_full []item // Create structure to be rendered in webpage for i := 0; i < 36; i++ { if inventar[i].Quantity != 0 { for _, it := range items { if inventar[i].Id == it.Id { n := item{Id: it.Id, Text: it.Text, Price: it.Price, Quantity: inventar[i].Quantity} inv_full = append(inv_full, n) } } } } tmpl := template.Must(template.ParseFiles("templates/inv.tmpl")) tmpl.Execute(w, inv_full) } func saveItemsAndPrices(w http.ResponseWriter, req *http.Request) { // a test // items[0].Price++ file, err := os.OpenFile("item_info.txt", os.O_CREATE|os.O_WRONLY, 0644) if err != nil { log.Fatal(err) } defer file.Close() datawriter := bufio.NewWriter(file) for _, data := range items { _, _ = datawriter.WriteString(fmt.Sprintf("%d | %s | %.2f\n", data.Id, data.Text, data.Price)) } datawriter.Flush() } func resetTrade() { fmt.Printf("Reset Trade\n") for i := 0; i < 16; i++ { myTrade[i].Id = 0 myTrade[i].ImageId = 0 myTrade[i].Quantity = 0 myTrade[i].Type = 0 hisTrade[i].Id = 0 hisTrade[i].ImageId = 0 hisTrade[i].Quantity = 0 hisTrade[i].Type = 0 } }