Преглед изворни кода

structure rework, implemented cascade deleting

TooManySugar пре 3 година
родитељ
комит
65b4e3797e
15 измењених фајлова са 448 додато и 377 уклоњено
  1. 4 1
      .gitignore
  2. 11 11
      README.md
  3. 0 0
      api_structs.go
  4. 0 45
      code/unmasrshal_url.go
  5. 0 318
      code/update_data.go
  6. 0 0
      connection.go
  7. 1 1
      go.mod
  8. 18 1
      handlers.go
  9. 0 0
      logger.go
  10. 0 0
      main.go
  11. 0 0
      message_handler.go
  12. 0 0
      models.go
  13. 0 0
      settings.go
  14. 87 0
      unmasrshal_url.go
  15. 327 0
      update_data.go

+ 4 - 1
.gitignore

@@ -1,8 +1,11 @@
 # .log files
 *.log
 
+# exe`s 
+*.exe 
+
 # .json files
 config.json
 
 # Visual Studio Code directory
-.vscode/
+.vscode/

+ 11 - 11
README.md

@@ -11,14 +11,14 @@
     - [x] JsonWorker.cs - in api_structs.go
 - [x] Logger.cs
 - [ ] Program.cs 
-	- [ ] Main - main.go Fully ported needs testing
-	- [ ] BotOnMessage - Listener in main.go
-	- [x] GetActualRate - in main.go
-	- [x] GetActualData - in main.go
-	- [ ] GetActualDataFromDatabase
-	- [ ] AddUser
-	- [ ] SetWallet
-    - [ ] DeleteUser - see TODO
-	- [x] SendHelp - in handlers.go
-    - [ ] GetLastPayout
-- [ ] UpdateData.cs - update_data.go Fully ported needs testing
+	- [x] Main - Fully ported needs testing
+	- [x] BotOnMessage
+	- [x] GetActualRate
+	- [x] GetActualData
+	- [x] GetActualDataFromDatabase
+	- [x] AddUser
+	- [x] SetWallet
+    - [x] DeleteUser
+	- [x] SendHelp
+    - [x] GetLastPayout
+- [x] UpdateData.cs - Fully ported needs testing

+ 0 - 0
code/api_structs.go → api_structs.go


+ 0 - 45
code/unmasrshal_url.go

@@ -1,45 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"io"
-	"log"
-	"net/http"
-	"strings"
-)
-
-func UnmasrshalFromUrl(url string, v interface{}) error {
-	// Usage:
-	// <url> - string with web url to raw json data
-	// <JsonStruct> - struct type that describes json data at <url>
-	//
-	//    -- code --
-	//    var parseTo <JsonStruct>
-	//    err := UnmasrshalFromUrl(<url>, &parseTo)
-	//    -- code --
-
-	webClient := &http.Client{}
-	// Get response from url
-	webResp, err := webClient.Get(url)
-	if err != nil {
-		log.Println(err)
-		return err
-	}
-	// Close response body
-	defer webResp.Body.Close()
-	// Convert from Reader to []byte
-	jsonData, err := io.ReadAll(webResp.Body)
-	if err != nil {
-		log.Println(err)
-		return err
-	}
-	// Convert []byte to string
-	jsonDataStr := fmt.Sprintf("%s", jsonData)
-	jsonDataStr = strings.ReplaceAll(jsonDataStr, "null", "0")
-	// Covenrt string ot []byte
-	jsonData = []byte(jsonDataStr)
-
-	// LogDebug.Println(v)
-	return json.Unmarshal([]byte(jsonData), &v)
-}

+ 0 - 318
code/update_data.go

@@ -1,318 +0,0 @@
-package main
-
-import (
-	"fmt"
-	"math"
-
-	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
-)
-
-func (c *Connection) UpdateData() {
-	LogInfo.Println("Updating data!")
-	// Getting list of unique wallets in user table
-	var walletList []string
-	result := c.GormDb.Model(&User{}).
-		Not("wallet = ?", "").
-		Select("DISTINCT(wallet)").
-		Scan(&walletList)
-
-	if result.Error != nil {
-		LogError.Println(result.Error)
-		return
-	}
-
-	for _, wallet := range walletList {
-		LogInfo.Printf("Getting data for wallet %s\n", wallet)
-
-		url := fmt.Sprintf("%s/miner/%s/currentStats", appSettings.ApiUrl, wallet)
-		LogInfo.Print("From: " + url)
-
-		var currentStats JsonCurrentStats
-		err := UnmasrshalFromUrl(url, &currentStats)
-
-		if err != nil {
-			LogInfo.Println("Error with getting currentStats data")
-			continue
-		}
-
-		if currentStats.Status == "OK" {
-			if currentStats.Data == (JsonCurrentStats{}).Data {
-				// if fmt.Sprintf("%T", currentStats.(map[string]interface{})["data"]) != "map[string]interface {}" {
-				LogInfo.Printf("data in response from %s for wallet %s not an interface!\n", appSettings.ApiUrl, wallet)
-				continue
-			}
-			// data := currentStats.(map[string]interface{})["data"].(map[string]interface{})
-			LogInfo.Printf("Creating new record for miner %s, time %d\n", wallet, currentStats.Data.Time)
-			// Line 28 of UpdateData.cs
-			// NOTE: No use for creating new record here
-			var newMinerRecord Miner
-			newMinerRecord.Wallet = wallet
-			newMinerRecord.Time = currentStats.Data.Time
-			newMinerRecord.ReportedHashrate = currentStats.Data.ReportedHashrate
-			newMinerRecord.CurrentHashrate = currentStats.Data.CurrentHashrate
-			newMinerRecord.ValidShares = currentStats.Data.ValidShares
-			newMinerRecord.StaleShares = currentStats.Data.StaleShares
-			newMinerRecord.InvalidShares = currentStats.Data.InvalidShares
-			newMinerRecord.Workers = currentStats.Data.ActiveWorkers
-			newMinerRecord.Unpaid = currentStats.Data.Unpaid
-			LogInfo.Printf("New miner record creating complete %s", newMinerRecord.Wallet)
-
-			// Line 42 of UpdateData.cs
-			// Search for latest record for miner
-			var lastMinerRecord Miner
-			result = c.GormDb.
-				Where("wallet = ?", newMinerRecord.Wallet).
-				Order("time desc").
-				Limit(1).
-				Find(&lastMinerRecord)
-			if result.Error != nil {
-				LogError.Println(result.Error)
-				continue
-			}
-
-			// No previous records for miner
-			// Or previous record from different time
-			if result.RowsAffected == 0 || lastMinerRecord.Time != newMinerRecord.Time {
-				result = c.GormDb.Create(&newMinerRecord)
-				if result.Error != nil {
-					LogInfo.Println(result.Error)
-					continue
-				}
-				LogInfo.Printf("Added new row for miner %s\n", newMinerRecord.Wallet)
-
-				// Line 54 of UpdateData.cs
-				if lastMinerRecord != (Miner{}) && newMinerRecord.Unpaid < lastMinerRecord.Unpaid {
-					var walletChats []int64
-					result := c.GormDb.Model(&User{}).
-						Where("wallet = ?", newMinerRecord.Wallet).
-						Select("chat_id").
-						Scan(&walletChats)
-					if result.Error != nil {
-						LogError.Println(result.Error)
-						continue
-					}
-
-					for _, chat := range walletChats {
-						LogInfo.Println("Sended 'Payout detected!' message to chat ", chat)
-						go myBotClient.Send(tgbotapi.NewMessage(chat, "Payout detected!"))
-					}
-				}
-
-				// Line 64 of UpdateData.cs
-				LogInfo.Printf("Getting workers data for wallet %s\n", wallet)
-				url = fmt.Sprintf("%s/miner/%s/workers", appSettings.ApiUrl, wallet)
-
-				LogInfo.Print("From: " + url)
-				var currentWorker JsonWorker
-				err := UnmasrshalFromUrl(url, &currentWorker)
-				if err != nil {
-					LogError.Println(err)
-					continue
-				}
-
-				if currentWorker.Status == "OK" {
-					if len(currentWorker.Data) == 0 {
-						LogInfo.Printf("data in response from %s for workers of wallet %s not an interface!\n", appSettings.ApiUrl, wallet)
-						continue
-					}
-
-					for i := range currentWorker.Data {
-						workerData := currentWorker.Data[i]
-						LogInfo.Printf("Creating new worker record for %s, time %d\n", workerData.Worker, workerData.Time)
-
-						var newWorkerRecord Worker
-						newWorkerRecord.Wallet = newMinerRecord.Wallet
-						newWorkerRecord.Time = workerData.Time
-						newWorkerRecord.Worker = workerData.Worker
-						newWorkerRecord.ReportedHashrate = workerData.ReportedHashrate
-						newWorkerRecord.CurrentHashrate = workerData.CurrentHashrate
-						newWorkerRecord.ValidShares = workerData.ValidShares
-						newWorkerRecord.StaleShares = workerData.StaleShares
-						newWorkerRecord.InvalidShares = workerData.InvalidShares
-
-						LogInfo.Printf("New worker record creating complete %s", newWorkerRecord.Worker)
-
-						//Search for latest record for worker
-						var lastWorkerRecord Worker
-						result = c.GormDb.
-							Where("worker = ?", workerData.Worker).
-							Order("time desc").
-							Limit(1).
-							Find(&lastWorkerRecord)
-						if result.Error != nil {
-							LogError.Println(result.Error)
-						}
-
-						// Line 85 of UpdateData.cs
-						if lastWorkerRecord != (Worker{}) {
-							// Search for latest miner record... again?
-							var lastMinerRecord1 Miner
-							result = c.GormDb.
-								Where("wallet = ?", newMinerRecord.Wallet).
-								Order("time desc").
-								Limit(1).
-								Find(&lastMinerRecord1)
-							if result.Error != nil {
-								LogError.Println(result.Error)
-							}
-
-							//Line 91 of UpdateData.cs
-							if lastMinerRecord1 != (Miner{}) {
-								//check for payout
-								if newMinerRecord.Unpaid < lastMinerRecord1.Unpaid {
-									// Adding new payout field to db
-									url := fmt.Sprintf("%s/miner/%s/payouts", appSettings.ApiUrl, wallet)
-									var payouts JsonPayouts
-									err := UnmasrshalFromUrl(url, &payouts)
-									if err != nil {
-										LogError.Println(err)
-										continue
-									}
-									lastPayout := payouts.Data[0]
-
-									LogInfo.Println("Last payout time =", lastPayout.PaidOn)
-
-									var newPayoutRecord Payout
-									newPayoutRecord.Wallet = wallet
-									newPayoutRecord.Time = lastPayout.PaidOn
-									newPayoutRecord.Amount = lastPayout.Amount
-									newPayoutRecord.Worker = workerData.Worker
-
-									workerAmount := float64(lastWorkerRecord.WorkerUnpaid+
-										(float64(lastPayout.Amount-lastMinerRecord.Unpaid))) *
-										(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
-									if math.IsNaN(workerAmount) || math.IsInf(workerAmount, 0) {
-										workerAmount = lastWorkerRecord.WorkerUnpaid
-									}
-									newPayoutRecord.WorkerAmount = workerAmount
-
-									workerUnpaid := float64(newMinerRecord.Unpaid) *
-										(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
-									LogDebug.Println("newMinerRecord.Unpaid:", newMinerRecord.Unpaid)
-									LogDebug.Println("lastMinerRecord.Unpaid:", lastMinerRecord.Unpaid)
-									LogDebug.Println("workerData.ReportedHashrate:", workerData.ReportedHashrate)
-									LogDebug.Println("newMinerRecord.ReportedHashrate:", newMinerRecord.ReportedHashrate)
-									LogDebug.Println("workerUnpaid:", workerUnpaid)
-									if math.IsNaN(workerUnpaid) || math.IsInf(workerUnpaid, 0) {
-										LogDebug.Println("workerUnpaid is NaN or Inf")
-										workerUnpaid = 0
-									}
-									newWorkerRecord.WorkerUnpaid = workerUnpaid
-
-									result = c.GormDb.Create(&newPayoutRecord)
-									if result.Error != nil {
-										LogError.Println(result.Error)
-										continue
-									}
-									LogInfo.Println("Add newPayoutRecord, time =", newPayoutRecord.Time)
-
-									//removing old records
-									result = c.GormDb.
-										Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
-										Delete(Worker{})
-									if result.Error != nil {
-										LogError.Println(result.Error)
-									}
-
-									result = c.GormDb.
-										Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
-										Delete(Miner{})
-									if result.Error != nil {
-										LogError.Println(result.Error)
-									}
-
-									result = c.GormDb.
-										Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
-										Delete(Payout{})
-									if result.Error != nil {
-										LogError.Println(result.Error)
-									}
-									//Line 135 of UpdateData.cs
-								} else {
-									//no check that last balance and prev balance are the same
-									//don't sure > or >=
-									//TODO rewrite this
-									var max int64
-									result := c.GormDb.Model(&Payout{}).
-										Where("wallet = ?", lastWorkerRecord.Wallet).
-										Order("time desc").
-										Limit(1).
-										Select("time").
-										Scan(&max)
-									if result.Error != nil {
-										LogWarn.Printf("No payouts data for %s! Time is set to 0!\n", lastWorkerRecord.Wallet)
-										LogError.Println(result.Error)
-										max = 0
-									}
-
-									if lastWorkerRecord.Time > max {
-										workerUnpaid := float64(lastWorkerRecord.WorkerUnpaid+
-											float64(newMinerRecord.Unpaid-lastMinerRecord.Unpaid)) *
-											(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
-										if math.IsNaN(workerUnpaid) || math.IsInf(workerUnpaid, 0) {
-											workerUnpaid = lastWorkerRecord.WorkerUnpaid
-										}
-										newWorkerRecord.WorkerUnpaid = workerUnpaid
-										// Line 169 of UpdateData.cs
-									} else {
-										workerUnpaid := float64((newMinerRecord.Unpaid - lastMinerRecord.Unpaid)) *
-											(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
-										if math.IsNaN(workerUnpaid) || math.IsInf(workerUnpaid, 0) {
-											workerUnpaid = lastWorkerRecord.WorkerUnpaid
-										}
-										newWorkerRecord.WorkerUnpaid = workerUnpaid
-
-										var targetedChats []int64
-										result = c.GormDb.
-											Where(&User{Wallet: newMinerRecord.Wallet}).
-											Select("DISTINCT(chat_id)").
-											Scan(&targetedChats)
-										if result.Error != nil {
-											LogError.Println(result.Error)
-										} else {
-											msg := fmt.Sprintf("Debug info: Your worker %s hasn't been zeroed on payout!", workerData.Worker)
-											for _, chatId := range targetedChats {
-												go myBotClient.Send(tgbotapi.NewMessage(int64(chatId), msg))
-											}
-											LogDebug.Printf("Worker %s on address %s hasn't been zeroed on payout!\n", workerData.Worker, wallet)
-										}
-									}
-								}
-							}
-							// Line 196 of UpdateData.cs
-						} else {
-							LogDebug.Println("Setting WorkerUnpaid to 0")
-							newWorkerRecord.WorkerUnpaid = 0
-						}
-
-						var rowsCount int64
-						result = c.GormDb.Model(&Worker{}).
-							Where("worker = ? and time = ?", newWorkerRecord.Worker, newWorkerRecord.Time).
-							Count(&rowsCount)
-						if result.Error != nil {
-							LogError.Println(result.Error)
-							continue
-						}
-
-						if rowsCount != 0 {
-							LogInfo.Printf("Error adding new row: row with worker: %s, time: %d already exists!\n", newWorkerRecord.Worker, newWorkerRecord.Time)
-							continue
-						}
-						result = c.GormDb.Create(&newWorkerRecord)
-
-						if result.Error != nil {
-							LogError.Println(result.Error)
-						} else {
-							LogInfo.Printf("Added new row for worker %s\n", newWorkerRecord.Worker)
-						}
-					}
-				}
-				// Line 222 of UpdateData.cs
-			} else {
-				LogInfo.Printf("Row with wallet: %s and time: %d already exists!", wallet, currentStats.Data.Time)
-			}
-		} else {
-			LogError.Printf("Error response from pool for %s!\n", wallet)
-		}
-	}
-}

+ 0 - 0
code/connection.go → connection.go


+ 1 - 1
go.mod

@@ -1,4 +1,4 @@
-module GoEthemineTelegramBot
+module gogs.veloe.link/toomanysugar/GoEthemineTelegramBot/code
 
 go 1.17
 

+ 18 - 1
code/handlers.go → handlers.go

@@ -116,7 +116,24 @@ func (c *Connection) DeleteUser(reqChat *tgbotapi.Chat) (string, error) {
 		if result.Error != nil {
 			return "", fmt.Errorf("failed deleting user: %w", result.Error)
 		}
-		// TODO Delete from workers, miners, payouts
+		
+		result = c.GormDb.Where("wallet = ?", user.Wallet).Delete(Miner{})
+		if result.Error != nil {
+			return "", fmt.Errorf("failed deleting miner: %w", result.Error)
+		}
+		LogInfo.Println("Miners deleted with wallet:", user.Wallet)
+
+		result = c.GormDb.Where("wallet = ?", user.Wallet).Delete(Worker{})
+		if result.Error != nil {
+			return "", fmt.Errorf("failed deleting workers: %w", result.Error)
+		}
+		LogInfo.Println("Workers deleted with wallet:", user.Wallet)
+
+		result = c.GormDb.Where("wallet = ?", user.Wallet).Delete(Payout{})
+		if result.Error != nil {
+			return "", fmt.Errorf("failed deleting payouts: %w", result.Error)
+		}
+		LogInfo.Println("Payouts deleted for wallet:", user.Wallet)
 	}
 
 	LogInfo.Println("User was deleted:", user)

+ 0 - 0
code/logger.go → logger.go


+ 0 - 0
code/main.go → main.go


+ 0 - 0
code/message_handler.go → message_handler.go


+ 0 - 0
code/models.go → models.go


+ 0 - 0
code/settings.go → settings.go


+ 87 - 0
unmasrshal_url.go

@@ -0,0 +1,87 @@
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"strings"
+)
+
+func UnmasrshalFromUrl(url string, v interface{}) error {
+	// Usage:
+	// <url> - string with web url to raw json data
+	// <JsonStruct> - struct type that describes json data at <url>
+	//
+	//    -- code --
+	//    var parseTo <JsonStruct>
+	//    err := UnmasrshalFromUrl(<url>, &parseTo)
+	//    -- code --
+
+	webClient := &http.Client{}
+	// Get response from url
+	webResp, err := webClient.Get(url)
+	if err != nil {
+		return err
+	}
+	// Close response body
+	defer webResp.Body.Close()
+	// Convert from Reader to []byte
+	jsonData, err := io.ReadAll(webResp.Body)
+	if err != nil {
+		return err
+	}
+	// Convert []byte to string
+	jsonDataStr := fmt.Sprintf("%s", jsonData)
+	jsonDataStr = strings.ReplaceAll(jsonDataStr, "null", "0")
+	// Covenrt string ot []byte
+	jsonData = []byte(jsonDataStr)
+
+	// LogDebug.Println(v)
+	return json.Unmarshal([]byte(jsonData), &v)
+}
+
+
+func MinerStats(url string) (JsonCurrentStats, error) {
+	var currentStats JsonCurrentStats
+
+	urlToStats := url + "/currentStats"
+	dataExists, err := IsDataExists(urlToStats)
+	if err != nil {
+		return currentStats, err
+	}
+
+	if !dataExists {
+		return currentStats, fmt.Errorf("No data for miner %s", urlToStats)
+	}
+
+	return currentStats, UnmasrshalFromUrl(urlToStats, &currentStats)
+}
+
+func IsDataExists(url string) (bool, error) {
+	var interfaceJson interface{}
+	
+	err := UnmasrshalFromUrl(url, &interfaceJson)
+	if err != nil {
+		return false, err
+	}
+
+	switch interfaceJson.(map[string]interface{})["data"].(type) {
+		case string: 
+			if interfaceJson.(map[string]interface{})["data"].(string) == "NO DATA" {
+				return false, nil
+			}
+			return false, fmt.Errorf("unexpected string value at interface %v", interfaceJson.(map[string]interface{})["data"].(string)) 
+			break
+
+		case map[string]interface {}:
+			return true, nil
+			break
+
+		default:
+			return false, fmt.Errorf("unexpected type of Data at interface - %T", interfaceJson.(map[string]interface{})["data"])
+			break
+	}
+
+	return false, nil
+}

+ 327 - 0
update_data.go

@@ -0,0 +1,327 @@
+package main
+
+import (
+	"fmt"
+	"math"
+	"strings"
+
+	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
+)
+
+func (c *Connection) UpdateData() {
+	LogInfo.Println("Updating data!")
+	// Getting list of unique wallets in user table
+	var walletList []string
+	result := c.GormDb.Model(&User{}).
+		Not("wallet = ?", "").
+		Select("DISTINCT(wallet)").
+		Scan(&walletList)
+
+	if result.Error != nil {
+		LogError.Println(result.Error)
+		return
+	}
+
+	for _, wallet := range walletList {
+		c.UpdateWallet(wallet)
+	}
+}
+
+func (c *Connection) UpdateWallet(wallet string) {
+	LogInfo.Printf(">> Getting data for wallet %s\n", wallet)
+
+	url := fmt.Sprintf("%s/miner/%s", appSettings.ApiUrl, wallet)
+	LogInfo.Print("From: " + url)
+
+	currentStats, err := MinerStats(url)
+
+	if err != nil {
+		if strings.Contains(err.Error(), "No data for miner") {
+			LogInfo.Println("No currentStats data for miner")
+		} else {
+			LogError.Println(err)
+		}
+		return
+	}
+
+	if currentStats.Status == "OK" {
+		if currentStats.Data == (JsonCurrentStats{}).Data {
+			// if fmt.Sprintf("%T", currentStats.(map[string]interface{})["data"]) != "map[string]interface {}" {
+			LogInfo.Printf("data in response from %s for wallet %s not an interface!\n", appSettings.ApiUrl, wallet)
+			return
+		}
+		// data := currentStats.(map[string]interface{})["data"].(map[string]interface{})
+		LogInfo.Printf("Creating new record for miner %s, time %d\n", wallet, currentStats.Data.Time)
+		// Line 28 of UpdateData.cs
+		// NOTE: No use for creating new record here
+		var newMinerRecord Miner
+		newMinerRecord.Wallet = wallet
+		newMinerRecord.Time = currentStats.Data.Time
+		newMinerRecord.ReportedHashrate = currentStats.Data.ReportedHashrate
+		newMinerRecord.CurrentHashrate = currentStats.Data.CurrentHashrate
+		newMinerRecord.ValidShares = currentStats.Data.ValidShares
+		newMinerRecord.StaleShares = currentStats.Data.StaleShares
+		newMinerRecord.InvalidShares = currentStats.Data.InvalidShares
+		newMinerRecord.Workers = currentStats.Data.ActiveWorkers
+		newMinerRecord.Unpaid = currentStats.Data.Unpaid
+		LogInfo.Printf("New miner record creating complete %s", newMinerRecord.Wallet)
+
+		// Line 42 of UpdateData.cs
+		// Search for latest record for miner
+		var lastMinerRecord Miner
+		result := c.GormDb.
+			Where("wallet = ?", newMinerRecord.Wallet).
+			Order("time desc").
+			Limit(1).
+			Find(&lastMinerRecord)
+		if result.Error != nil {
+			LogError.Println(result.Error)
+			return
+		}
+
+		// No previous records for miner
+		// Or previous record from different time
+		if result.RowsAffected == 0 || lastMinerRecord.Time != newMinerRecord.Time {
+			result = c.GormDb.Create(&newMinerRecord)
+			if result.Error != nil {
+				LogInfo.Println(result.Error)
+				return
+			}
+			LogInfo.Printf("Added new row for miner %s\n", newMinerRecord.Wallet)
+
+			// Line 54 of UpdateData.cs
+			if lastMinerRecord != (Miner{}) && newMinerRecord.Unpaid < lastMinerRecord.Unpaid {
+				var walletChats []int64
+				result := c.GormDb.Model(&User{}).
+					Where("wallet = ?", newMinerRecord.Wallet).
+					Select("chat_id").
+					Scan(&walletChats)
+				if result.Error != nil {
+					LogError.Println(result.Error)
+					return
+				}
+
+				for _, chat := range walletChats {
+					LogInfo.Println("Sended 'Payout detected!' message to chat ", chat)
+					go myBotClient.Send(tgbotapi.NewMessage(chat, "Payout detected!"))
+				}
+			}
+
+			// Line 64 of UpdateData.cs
+			LogInfo.Printf("Getting workers data for wallet %s\n", wallet)
+			url = fmt.Sprintf("%s/miner/%s/workers", appSettings.ApiUrl, wallet)
+
+			LogInfo.Print("From: " + url)
+			var currentWorker JsonWorker
+			// currentWorker := ethermine_api.WorkersStats("") url to miner
+			err := UnmasrshalFromUrl(url, &currentWorker)
+			if err != nil {
+				LogError.Println(err)
+				return
+			}
+
+			if currentWorker.Status == "OK" {
+				if len(currentWorker.Data) == 0 {
+					LogInfo.Printf("data in response from %s for workers of wallet %s not an interface!\n", appSettings.ApiUrl, wallet)
+					return
+				}
+
+				for i := range currentWorker.Data {
+					workerData := currentWorker.Data[i]
+					LogInfo.Printf("Creating new worker record for %s, time %d\n", workerData.Worker, workerData.Time)
+
+					var newWorkerRecord Worker
+					newWorkerRecord.Wallet = newMinerRecord.Wallet
+					newWorkerRecord.Time = workerData.Time
+					newWorkerRecord.Worker = workerData.Worker
+					newWorkerRecord.ReportedHashrate = workerData.ReportedHashrate
+					newWorkerRecord.CurrentHashrate = workerData.CurrentHashrate
+					newWorkerRecord.ValidShares = workerData.ValidShares
+					newWorkerRecord.StaleShares = workerData.StaleShares
+					newWorkerRecord.InvalidShares = workerData.InvalidShares
+
+					LogInfo.Printf("New worker record creating complete %s", newWorkerRecord.Worker)
+
+					//Search for latest record for worker
+					var lastWorkerRecord Worker
+					result = c.GormDb.
+						Where("worker = ?", workerData.Worker).
+						Order("time desc").
+						Limit(1).
+						Find(&lastWorkerRecord)
+					if result.Error != nil {
+						LogError.Println(result.Error)
+					}
+
+					// Line 85 of UpdateData.cs
+					if lastWorkerRecord != (Worker{}) {
+						// Search for latest miner record... again?
+						var lastMinerRecord1 Miner
+						result = c.GormDb.
+							Where("wallet = ?", newMinerRecord.Wallet).
+							Order("time desc").
+							Limit(1).
+							Find(&lastMinerRecord1)
+						if result.Error != nil {
+							LogError.Println(result.Error)
+						}
+
+						//Line 91 of UpdateData.cs
+						if lastMinerRecord1 != (Miner{}) {
+							//check for payout
+							if newMinerRecord.Unpaid < lastMinerRecord1.Unpaid {
+								// Adding new payout field to db
+								url := fmt.Sprintf("%s/miner/%s/payouts", appSettings.ApiUrl, wallet)
+								var payouts JsonPayouts
+								err := UnmasrshalFromUrl(url, &payouts)
+								if err != nil {
+									LogError.Println(err)
+									return
+								}
+								lastPayout := payouts.Data[0]
+
+								LogInfo.Println("Last payout time =", lastPayout.PaidOn)
+
+								var newPayoutRecord Payout
+								newPayoutRecord.Wallet = wallet
+								newPayoutRecord.Time = lastPayout.PaidOn
+								newPayoutRecord.Amount = lastPayout.Amount
+								newPayoutRecord.Worker = workerData.Worker
+
+								workerAmount := float64(lastWorkerRecord.WorkerUnpaid+
+									(float64(lastPayout.Amount-lastMinerRecord.Unpaid))) *
+									(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
+								if math.IsNaN(workerAmount) || math.IsInf(workerAmount, 0) {
+									workerAmount = lastWorkerRecord.WorkerUnpaid
+								}
+								newPayoutRecord.WorkerAmount = workerAmount
+
+								workerUnpaid := float64(newMinerRecord.Unpaid) *
+									(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
+								LogDebug.Println("newMinerRecord.Unpaid:", newMinerRecord.Unpaid)
+								LogDebug.Println("lastMinerRecord.Unpaid:", lastMinerRecord.Unpaid)
+								LogDebug.Println("workerData.ReportedHashrate:", workerData.ReportedHashrate)
+								LogDebug.Println("newMinerRecord.ReportedHashrate:", newMinerRecord.ReportedHashrate)
+								LogDebug.Println("workerUnpaid:", workerUnpaid)
+								if math.IsNaN(workerUnpaid) || math.IsInf(workerUnpaid, 0) {
+									LogDebug.Println("workerUnpaid is NaN or Inf")
+									workerUnpaid = 0
+								}
+								newWorkerRecord.WorkerUnpaid = workerUnpaid
+
+								result = c.GormDb.Create(&newPayoutRecord)
+								if result.Error != nil {
+									LogError.Println(result.Error)
+									return
+								}
+								LogInfo.Println("Add newPayoutRecord, time =", newPayoutRecord.Time)
+
+								//removing old records
+								result = c.GormDb.
+									Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
+									Delete(Worker{})
+								if result.Error != nil {
+									LogError.Println(result.Error)
+								}
+
+								result = c.GormDb.
+									Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
+									Delete(Miner{})
+								if result.Error != nil {
+									LogError.Println(result.Error)
+								}
+
+								result = c.GormDb.
+									Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
+									Delete(Payout{})
+								if result.Error != nil {
+									LogError.Println(result.Error)
+								}
+								//Line 135 of UpdateData.cs
+							} else {
+								//no check that last balance and prev balance are the same
+								//don't sure > or >=
+								//TODO rewrite this
+								var max int64
+								result := c.GormDb.Model(&Payout{}).
+									Where("wallet = ?", lastWorkerRecord.Wallet).
+									Order("time desc").
+									Limit(1).
+									Select("time").
+									Scan(&max)
+								if result.Error != nil {
+									LogWarn.Printf("No payouts data for %s! Time is set to 0!\n", lastWorkerRecord.Wallet)
+									LogError.Println(result.Error)
+									max = 0
+								}
+
+								if lastWorkerRecord.Time > max {
+									workerUnpaid := float64(lastWorkerRecord.WorkerUnpaid+
+										float64(newMinerRecord.Unpaid-lastMinerRecord.Unpaid)) *
+										(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
+									if math.IsNaN(workerUnpaid) || math.IsInf(workerUnpaid, 0) {
+										workerUnpaid = lastWorkerRecord.WorkerUnpaid
+									}
+									newWorkerRecord.WorkerUnpaid = workerUnpaid
+									// Line 169 of UpdateData.cs
+								} else {
+									workerUnpaid := float64((newMinerRecord.Unpaid - lastMinerRecord.Unpaid)) *
+										(float64(workerData.ReportedHashrate) / float64(newMinerRecord.ReportedHashrate))
+									if math.IsNaN(workerUnpaid) || math.IsInf(workerUnpaid, 0) {
+										workerUnpaid = lastWorkerRecord.WorkerUnpaid
+									}
+									newWorkerRecord.WorkerUnpaid = workerUnpaid
+
+									var targetedChats []int64
+									result = c.GormDb.
+										Where(&User{Wallet: newMinerRecord.Wallet}).
+										Select("DISTINCT(chat_id)").
+										Scan(&targetedChats)
+									if result.Error != nil {
+										LogError.Println(result.Error)
+									} else {
+										msg := fmt.Sprintf("Debug info: Your worker %s hasn't been zeroed on payout!", workerData.Worker)
+										for _, chatId := range targetedChats {
+											go myBotClient.Send(tgbotapi.NewMessage(int64(chatId), msg))
+										}
+										LogDebug.Printf("Worker %s on address %s hasn't been zeroed on payout!\n", workerData.Worker, wallet)
+									}
+								}
+							}
+						}
+						// Line 196 of UpdateData.cs
+					} else {
+						LogDebug.Println("Setting WorkerUnpaid to 0")
+						newWorkerRecord.WorkerUnpaid = 0
+					}
+
+					var rowsCount int64
+					result = c.GormDb.Model(&Worker{}).
+						Where("worker = ? and time = ?", newWorkerRecord.Worker, newWorkerRecord.Time).
+						Count(&rowsCount)
+					if result.Error != nil {
+						LogError.Println(result.Error)
+						return
+					}
+
+					if rowsCount != 0 {
+						LogInfo.Printf("Error adding new row: row with worker: %s, time: %d already exists!\n", newWorkerRecord.Worker, newWorkerRecord.Time)
+						return
+					}
+					result = c.GormDb.Create(&newWorkerRecord)
+
+					if result.Error != nil {
+						LogError.Println(result.Error)
+					} else {
+						LogInfo.Printf("Added new row for worker %s\n", newWorkerRecord.Worker)
+					}
+				}
+			}
+			// Line 222 of UpdateData.cs
+		} else {
+			LogInfo.Printf("Row with wallet: %s and time: %d already exists!", wallet, currentStats.Data.Time)
+		}
+	} else {
+		LogError.Printf("Error response from pool for %s!\n", wallet)
+	}
+}