Browse Source

update_data code optimization, separate api related stuff to it's own module

TooManySugar 3 years ago
parent
commit
219c657c61
9 changed files with 409 additions and 385 deletions
  1. 55 0
      connection.go
  2. 8 1
      ethermineapi/api_structs.go
  3. 3 0
      ethermineapi/go.mod
  4. 110 0
      ethermineapi/unmasrshal_url.go
  5. 3 0
      go.mod
  6. 8 6
      handlers.go
  7. 2 0
      main.go
  8. 0 107
      unmasrshal_url.go
  9. 220 271
      update_data.go

+ 55 - 0
connection.go

@@ -40,3 +40,58 @@ func (c *Connection) Init() {
 
 	c.GormDb = gormDb
 }
+
+
+func (c *Connection) LatestWorkerRecord(worker string) (lastWorkerRecord Worker, err error) {
+	result := c.GormDb.
+		Where("worker = ?", worker).
+		Order("time desc").
+		Limit(1).
+		Find(&lastWorkerRecord)
+	
+	return lastWorkerRecord, result.Error
+} 
+
+func (c *Connection) LatestMinerRecord(wallet string) (lastMinerRecord Miner, err error) {
+	result := c.GormDb.
+		Where("wallet = ?", wallet).
+		Order("time desc").
+		Limit(1).
+		Find(&lastMinerRecord)
+	
+	return lastMinerRecord, result.Error
+}
+
+func (c *Connection) ChatsWithWallet(wallet string) (ChatIds []int64, err error) {
+	result := c.GormDb.Model(&User{}).
+			Where("wallet = ?", wallet).
+			Select("chat_id").
+			Scan(&ChatIds)
+	return ChatIds, result.Error
+}
+
+func (c *Connection) IsWorkerRowExists(worker string, time int64) (exists bool, err error) {
+	var rowsCount int64
+	result := c.GormDb.Model(&Worker{}).
+		Where("worker = ? and time = ?", worker, time).
+		Count(&rowsCount)
+	if result.Error != nil {
+		return false , result.Error
+	}
+
+	if rowsCount != 0 {
+		return true, nil
+	}
+
+	return false, nil
+}
+
+// 	List of unique, not empty wallets in user table
+func (c *Connection) UniqueWallets() (wallets []string, err error) {
+	result := c.GormDb.Model(&User{}).
+		Not("wallet = ?", "").
+		Select("DISTINCT(wallet)").
+		Scan(&wallets)
+
+	return wallets, result.Error
+}

+ 8 - 1
api_structs.go → ethermineapi/api_structs.go

@@ -1,4 +1,6 @@
-package main
+package ethermineapi
+
+import "encoding/json"
 
 type JsonCurrentStats struct {
 	Status string
@@ -57,3 +59,8 @@ type JsonWorker struct {
 		AverageHashrate  float64
 	}
 }
+
+type baseResponce struct {
+	Status 	string
+	Data 	json.RawMessage
+}

+ 3 - 0
ethermineapi/go.mod

@@ -0,0 +1,3 @@
+module ethermineapi
+
+go 1.17

+ 110 - 0
ethermineapi/unmasrshal_url.go

@@ -0,0 +1,110 @@
+package ethermineapi
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"net/http"
+	"strings"
+)
+
+// Unmarahals JSON from specified url to v 
+// Usage example:
+// <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 --
+func UnmasrshalFromUrl(url string, v interface{}) error {
+	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(pool, wallet string) (JsonCurrentStats, error) {
+	var currentStats JsonCurrentStats
+
+	url := fmt.Sprintf("%s/miner/%s/currentStats", pool, wallet)
+	dataExists, err := IsDataExists(url)
+	if err != nil {
+		return currentStats, err
+	}
+
+	if !dataExists {
+		return currentStats, fmt.Errorf("no data for miner %s", wallet)
+	}
+
+	return currentStats, UnmasrshalFromUrl(url, &currentStats)
+}
+
+func Workers(pool, wallet string) (JsonWorker, error) {
+	var workers JsonWorker
+
+	url := fmt.Sprintf("%s/miner/%s/workers", pool, wallet)
+	dataExists, err := IsDataExists(url)
+	if err != nil {
+		return workers, err
+	}
+
+	if !dataExists {
+		return workers, fmt.Errorf("no data for workers of %s", wallet)
+	}
+
+	return workers, UnmasrshalFromUrl(url, &workers)
+}
+
+func Payouts(pool, wallet string) (JsonPayouts, error) {
+    var payouts JsonPayouts
+
+	url := fmt.Sprintf("%s/miner/%s/payouts", pool, wallet)
+	// dataExists, err := IsDataExists(url) Not needed for payouts
+	// if err != nil {
+	// 	return payouts, err
+	// }
+	// 
+	// if !dataExists {
+	// 	return workers, fmt.Errorf("no data for workers of %s", wallet)
+	// }
+	err := UnmasrshalFromUrl(url, &payouts)
+	if err != nil {
+		return payouts, fmt.Errorf("couldn't get payouts from %s", url)
+	}
+
+	return payouts, err
+}
+
+func IsDataExists(url string) (bool, error) {
+	var base baseResponce
+	
+	err := UnmasrshalFromUrl(url, &base)
+	if err != nil {
+		return false, fmt.Errorf("couldn't get base response from %s", url)
+	}
+
+	if string(base.Data) == "\"NO DATA\"" {
+		return false, nil
+	}
+
+	return true, nil
+}

+ 3 - 0
go.mod

@@ -9,8 +9,11 @@ require (
 )
 
 require (
+	GoEthemineTelegramBot/ethermineapi v0.0.0-unpublished // indirect
 	github.com/go-sql-driver/mysql v1.6.0 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.2 // indirect
 	github.com/technoweenie/multipartstreamer v1.0.1 // indirect
 )
+
+replace GoEthemineTelegramBot/ethermineapi v0.0.0-unpublished => ./ethermineapi

+ 8 - 6
handlers.go

@@ -8,6 +8,8 @@ import (
 	"math"
 
 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
+
+	ethmineapi "GoEthemineTelegramBot/ethermineapi"
 )
 
 func (c *Connection) AddUser(reqChat *tgbotapi.Chat) (string, error) {
@@ -264,8 +266,8 @@ func (c *Connection) GetLastPayout(chatId int64) string {
 	//Line 560 of Program.cs
 	if len(dbPayoutRecords) == 0 {
 		url := fmt.Sprintf("%s/miner/%s/payouts", appSettings.ApiUrl, wallet)
-		var payouts JsonPayouts
-		err := UnmasrshalFromUrl(url, &payouts)
+		var payouts ethmineapi.JsonPayouts
+		err := ethmineapi.UnmasrshalFromUrl(url, &payouts)
 		if err != nil {
 			LogError.Println(err)
 			return fmt.Sprintf("No payout data for %s", wallet)
@@ -303,8 +305,8 @@ func (c *Connection) GetLastPayout(chatId int64) string {
 
 func GetActualRate() string {
 	url := fmt.Sprintf("%s/networkStats", appSettings.ApiUrl)
-	var networkStats JsonNetworkStats
-	err := UnmasrshalFromUrl(url, &networkStats)
+	var networkStats ethmineapi.JsonNetworkStats
+	err := ethmineapi.UnmasrshalFromUrl(url, &networkStats)
 	if err != nil {
 		return fmt.Sprintf("Error with getting data from %s", appSettings.ApiUrl)
 	}
@@ -315,8 +317,8 @@ func GetActualRate() string {
 
 func GetActualData(miner string) string {
 	url := fmt.Sprintf("%s/miner/%s/currentStats", appSettings.ApiUrl, miner)
-	var currentStats JsonCurrentStats
-	err := UnmasrshalFromUrl(url, &currentStats)
+	var currentStats ethmineapi.JsonCurrentStats
+	err := ethmineapi.UnmasrshalFromUrl(url, &currentStats)
 	if err != nil {
 		// As in original...
 		return "An exception occurred while executing this command."

+ 2 - 0
main.go

@@ -8,6 +8,8 @@ import (
 	"time"
 
 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
+
+	_ "GoEthemineTelegramBot/ethermineapi"
 )
 
 

+ 0 - 107
unmasrshal_url.go

@@ -1,107 +0,0 @@
-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 Workers(url string) (JsonWorker, error) {
-	var workers JsonWorker
-
-	urlToWorkers := url + "/workers"
-	dataExists, err := IsDataExists(urlToWorkers)
-	if err != nil {
-		return workers, err
-	}
-
-	if !dataExists {
-		return workers, fmt.Errorf("No data for workers %s", urlToWorkers)
-	}
-
-	return workers, UnmasrshalFromUrl(urlToWorkers, &workers)
-}
-
-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
-
-		case []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
-}

+ 220 - 271
update_data.go

@@ -3,23 +3,18 @@ package main
 import (
 	"fmt"
 	"math"
-	"strings"
 
 	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
+
+	ethmineapi "GoEthemineTelegramBot/ethermineapi"
 )
 
 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
+	walletList, err := c.UniqueWallets()
+	if err != nil {
+		LogError.Println(err); return
 	}
 
 	for _, wallet := range walletList {
@@ -29,302 +24,256 @@ func (c *Connection) UpdateData() {
 
 func (c *Connection) UpdateWallet(wallet string) {
 	LogInfo.Printf(">> Getting data for wallet %s\n", wallet)
+	LogInfo.Printf("From: %s/miner/%s/currentStats\n", appSettings.ApiUrl, wallet)
+	
+	// Get current stats for miner
+	currentStats, err := ethmineapi.MinerStats(appSettings.ApiUrl, wallet)
+	if err != nil {
+		LogWarn.Println(err); return
+	}
 
-	url := fmt.Sprintf("%s/miner/%s", appSettings.ApiUrl, wallet)
-	LogInfo.Print("From: " + url)
-
-	currentStats, err := MinerStats(url)
+	if currentStats.Status != "OK" {
+		LogError.Printf("Error getting response from pool for %s!\n", wallet); return
+	}
 
+	// Line 42 of UpdateData.cs
+	// Search for the latest record for miner
+	lastMinerRecord, err := c.LatestMinerRecord(wallet)
 	if err != nil {
-		if strings.Contains(err.Error(), "No data for miner") {
-			LogInfo.Println("No currentStats data for miner")
-		} else {
+		LogError.Println(err); return
+	}
+
+	// Exist previous record with the same time
+	if lastMinerRecord.Time == currentStats.Data.Time {
+		LogInfo.Printf("Row with wallet: %s and time: %d already exists!", wallet, currentStats.Data.Time)
+		return
+	}
+
+	LogInfo.Printf("Creating new record for miner %s, time %d\n", wallet, currentStats.Data.Time)
+	// Line 28 of UpdateData.cs
+	newMinerRecord := Miner {
+	    Wallet:           wallet,
+	    Time:             currentStats.Data.Time,
+	    ReportedHashrate: currentStats.Data.ReportedHashrate,
+	    CurrentHashrate:  currentStats.Data.CurrentHashrate,
+	    ValidShares:      currentStats.Data.ValidShares,
+	    StaleShares:      currentStats.Data.StaleShares,
+	    InvalidShares:    currentStats.Data.InvalidShares,
+	    Workers:          currentStats.Data.ActiveWorkers,
+	    Unpaid:           currentStats.Data.Unpaid,
+	}
+
+	result := c.GormDb.Create(&newMinerRecord)
+	if result.Error != nil {
+		LogError.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 {
+		walletChats, err := c.ChatsWithWallet(newMinerRecord.Wallet)
+		if err != nil {
 			LogError.Println(err)
+		} else {	
+			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)
+	LogInfo.Printf("From: %s/miner/%s", appSettings.ApiUrl, wallet)
+
+	currentWorker, err := ethmineapi.Workers(appSettings.ApiUrl, wallet)
+	if err != nil {
+		LogWarn.Println(err); return
+	}
+
+	if currentWorker.Status != "OK" {
 		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
+	// Unsure is it occurs
+	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 _, workerData := range currentWorker.Data {
+		LogInfo.Printf("Creating new worker record for %s, time %d\n", workerData.Worker, workerData.Time)
+
+		newWorkerRecord := Worker{
+		    Wallet:            newMinerRecord.Wallet,
+		    Time:              workerData.Time,
+		    Worker:            workerData.Worker,
+		    ReportedHashrate:  workerData.ReportedHashrate,
+		    CurrentHashrate:   workerData.CurrentHashrate,
+		    ValidShares:       workerData.ValidShares,
+		    StaleShares:       workerData.StaleShares,
+		    InvalidShares:     workerData.InvalidShares,
 		}
-		// 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
+
+		LogInfo.Printf("New worker record creating complete %s", newWorkerRecord.Worker)
+
+		//Search for latest record for worker
+		lastWorkerRecord, err := c.LatestWorkerRecord(workerData.Worker)
+		if err != nil {
+			LogError.Println(err); 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)
-				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
+		// Line 85 of UpdateData.cs
+		if lastWorkerRecord != (Worker{}) {
+			//check for payout
+			if newMinerRecord.Unpaid < lastMinerRecord.Unpaid {
+				// Adding new payout field to db
+				payouts, err := ethmineapi.Payouts(appSettings.ApiUrl, wallet)
+				if err != nil {
+					LogError.Println(err); continue
 				}
 
-				for _, chat := range walletChats {
-					LogInfo.Println("Sended 'Payout detected!' message to chat ", chat)
-					go myBotClient.Send(tgbotapi.NewMessage(chat, "Payout detected!"))
+				if (len(payouts.Data) == 0) {
+					LogWarn.Println("No actual payouts for wallet", wallet)
+					continue
 				}
-			}
 
-			// Line 64 of UpdateData.cs
-			LogInfo.Printf("Getting workers data for wallet %s\n", wallet)
-			url = fmt.Sprintf("%s/miner/%s", appSettings.ApiUrl, wallet)
+				lastPayout := payouts.Data[0]
 
-			LogInfo.Print("From: " + url)
+				LogInfo.Println("Last payout time =", lastPayout.PaidOn)
 
-			currentWorker, err := Workers(url)
-			if err != nil {
-				if strings.Contains(err.Error(), "No data for workers") {
-					LogInfo.Println("No workers data for miner")
-				} else {
-					LogError.Println(err)
+				newPayoutRecord := Payout{
+				    Wallet:  wallet,
+				    Time:    lastPayout.PaidOn,
+				    Amount:  lastPayout.Amount,
+				    Worker:  workerData.Worker,
 				}
-				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
+				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
 
-				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)
+				result = c.GormDb.Create(&newPayoutRecord)
+				if result.Error != nil {
+					LogError.Println(result.Error)
+					return
+				}
+				LogInfo.Println("Added newPayoutRecord, time =", newPayoutRecord.Time)
 
-					//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)
-					}
+				//removing old records
+				result = c.GormDb.
+					Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
+					Delete(Worker{})
+				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)
-						}
+				result = c.GormDb.
+					Where("wallet = ? AND time < ?", newPayoutRecord.Wallet, newPayoutRecord.Time).
+					Delete(Miner{})
+				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
-					}
+				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
+				}
 
-					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 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
 					}
-
-					if rowsCount != 0 {
-						LogInfo.Printf("Error adding new row: row with worker: %s, time: %d already exists!\n", newWorkerRecord.Worker, newWorkerRecord.Time)
-						return
+					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
 					}
-					result = c.GormDb.Create(&newWorkerRecord)
+					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 {
-						LogInfo.Printf("Added new row for worker %s\n", newWorkerRecord.Worker)
+						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 222 of UpdateData.cs
+		
+			// Line 196 of UpdateData.cs
 		} else {
-			LogInfo.Printf("Row with wallet: %s and time: %d already exists!", wallet, currentStats.Data.Time)
+			LogDebug.Println("Setting WorkerUnpaid to 0")
+			newWorkerRecord.WorkerUnpaid = 0
+		}
+
+
+		rowExists, err := c.IsWorkerRowExists(newWorkerRecord.Worker, newWorkerRecord.Time)
+		if err != nil {
+			LogError.Println(err); return
+		}
+
+		if rowExists {
+			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)
+			return
 		}
-	} else {
-		LogError.Printf("Error response from pool for %s!\n", wallet)
+		
+		LogInfo.Printf("Added new row for worker %s\n", newWorkerRecord.Worker)
 	}
 }