handlers.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. // Response creators
  2. package main
  3. import (
  4. "fmt"
  5. "strings"
  6. "time"
  7. "math"
  8. tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
  9. )
  10. func (c *Connection) AddUser(reqChat *tgbotapi.Chat) (string, error) {
  11. // Check if user already registered
  12. var userCount int64
  13. result := c.GormDb.Model(&User{}).
  14. Where("chat_id = ?", reqChat.ID).
  15. Count(&userCount)
  16. if result.Error != nil {
  17. return "", fmt.Errorf("failed creating user: %w", result.Error)
  18. }
  19. if userCount != 0 {
  20. return "Already registered", nil
  21. }
  22. // Adding new user to database
  23. newUser := User{ChatId: reqChat.ID}
  24. result = c.GormDb.Create(newUser)
  25. if result.Error != nil {
  26. return "", fmt.Errorf("failed creating user: %w", result.Error)
  27. }
  28. LogInfo.Println("User was created:", newUser)
  29. return "Added new user to database. Now you can connect your miner wallet using command /setwallet <address>", nil
  30. }
  31. func (c *Connection) SetWallet(reqMsg *tgbotapi.Message) (string, error) {
  32. // Check if user already registered
  33. var userCount int64
  34. result := c.GormDb.Model(&User{}).
  35. Where("chat_id = ?", reqMsg.Chat.ID).
  36. Count(&userCount)
  37. if result.Error != nil {
  38. return "", fmt.Errorf("failed wallet set: %w", result.Error)
  39. }
  40. if userCount == 0 {
  41. return "You are not registered! Type /start first!", nil
  42. }
  43. // *Strange* check for length of predifined eth wallet hash
  44. if len(strings.Split(reqMsg.Text, " ")[1]) > 2 {
  45. // Check if wallet hash typed with 0x
  46. // TODO Should be: starts with '0x' or in format 0x....
  47. if strings.Contains(strings.Split(reqMsg.Text, " ")[1], "0x") {
  48. // TODO replace inefficient branching
  49. result = c.GormDb.Model(&User{}).
  50. Where("chat_id = ?", reqMsg.Chat.ID).
  51. Update("wallet", strings.Split(reqMsg.Text, " ")[1])
  52. if result.Error != nil {
  53. return "", fmt.Errorf("failed querying user: %w", result.Error)
  54. }
  55. } else {
  56. result = c.GormDb.Model(&User{}).
  57. Where("chat_id = ?", reqMsg.Chat.ID).
  58. Update("wallet", "0x"+strings.Split(reqMsg.Text, " ")[1])
  59. if result.Error != nil {
  60. return "", fmt.Errorf("failed querying user: %w", result.Error)
  61. }
  62. }
  63. LogInfo.Printf("Updated wallet of user %d to %s\n", reqMsg.Chat.ID, strings.Split(reqMsg.Text, " ")[1])
  64. // TODO this is ▼▼ lame bro
  65. return fmt.Sprintf("0x%s set!", strings.Split(reqMsg.Text, " ")[1]), nil
  66. } else {
  67. return "Too short!", nil
  68. }
  69. }
  70. func (c *Connection) DeleteUser(reqChat *tgbotapi.Chat) (string, error) {
  71. // Check for user existance
  72. var userCount int64
  73. result := c.GormDb.Model(&User{}).
  74. Where("chat_id = ?", reqChat.ID).
  75. Count(&userCount)
  76. if result.Error != nil {
  77. return "", fmt.Errorf("failed creating user: %w", result.Error)
  78. }
  79. if userCount == 0 {
  80. return "Already deleted", nil
  81. }
  82. // Taking target for deletion
  83. var user User
  84. result = c.GormDb.Take(&user, "chat_id = ?", reqChat.ID)
  85. if result.Error != nil {
  86. return "", fmt.Errorf("failed querying user: %w", result.Error)
  87. }
  88. var walletHoldersCount int64
  89. result = c.GormDb.Model(&User{}).
  90. Where("wallet = ?", user.Wallet).
  91. Count(&walletHoldersCount)
  92. if result.Error != nil {
  93. return "", fmt.Errorf("failed querying user: %w", result.Error)
  94. }
  95. // TODO Remove code repition
  96. if walletHoldersCount > 1 {
  97. // Wallet in multiple chats
  98. result = c.GormDb.Where("chat_id = ?", reqChat.ID).Delete(User{})
  99. if result.Error != nil {
  100. return "", fmt.Errorf("failed deleting user: %w", result.Error)
  101. }
  102. } else {
  103. // Wallet in single chat
  104. result = c.GormDb.Where("chat_id = ?", reqChat.ID).Delete(User{})
  105. if result.Error != nil {
  106. return "", fmt.Errorf("failed deleting user: %w", result.Error)
  107. }
  108. result = c.GormDb.Where("wallet = ?", user.Wallet).Delete(Miner{})
  109. if result.Error != nil {
  110. return "", fmt.Errorf("failed deleting miner: %w", result.Error)
  111. }
  112. LogInfo.Println("Miners deleted with wallet:", user.Wallet)
  113. result = c.GormDb.Where("wallet = ?", user.Wallet).Delete(Worker{})
  114. if result.Error != nil {
  115. return "", fmt.Errorf("failed deleting workers: %w", result.Error)
  116. }
  117. LogInfo.Println("Workers deleted with wallet:", user.Wallet)
  118. result = c.GormDb.Where("wallet = ?", user.Wallet).Delete(Payout{})
  119. if result.Error != nil {
  120. return "", fmt.Errorf("failed deleting payouts: %w", result.Error)
  121. }
  122. LogInfo.Println("Payouts deleted for wallet:", user.Wallet)
  123. }
  124. LogInfo.Println("User was deleted:", user)
  125. return "Done!", nil
  126. }
  127. func (c *Connection) GetActualDataFromDatabase(chatId int64) string {
  128. errMsgToUser := "An exception occurred on sending messages while executing /actual command"
  129. var wallet string
  130. result := c.GormDb.Model(&User{}).
  131. Where("chat_id = ?", chatId).
  132. Select("wallet").
  133. Scan(&wallet)
  134. if result.Error != nil {
  135. LogError.Println(result.Error)
  136. return errMsgToUser
  137. }
  138. if wallet == "" {
  139. return "Set wallet at first! Use /setwallet"
  140. }
  141. var lastMinerRecord Miner
  142. result = c.GormDb.
  143. Where(&Miner{Wallet: wallet}).
  144. Order("time desc").
  145. Limit(1).
  146. Find(&lastMinerRecord)
  147. if result.Error != nil {
  148. LogError.Println(result.Error)
  149. return "No miner data for now!"
  150. }
  151. var lastTime int64
  152. result = c.GormDb.Model(&Worker{}).
  153. Where("wallet = ?", wallet).
  154. Select("MAX(time)").
  155. Scan(&lastTime)
  156. if result.Error != nil {
  157. LogError.Println(result.Error)
  158. return errMsgToUser
  159. }
  160. var lastWorkerRecord []Worker
  161. result = c.GormDb.
  162. Where(&Worker{Wallet: wallet, Time: lastTime}).
  163. Find(&lastWorkerRecord)
  164. if result.Error != nil {
  165. LogError.Println(result.Error)
  166. return errMsgToUser
  167. }
  168. if len(lastWorkerRecord) == 0 {
  169. return "No workers data for now!"
  170. }
  171. msgText := fmt.Sprintf("Miner %s stats:\n", lastMinerRecord.Wallet)
  172. msgText += fmt.Sprintf("Updated: %s\n", time.Unix(lastMinerRecord.Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  173. msgText += fmt.Sprintf("Reported Hashrate: %.3f MH/s\n", math.Round(float64(lastMinerRecord.ReportedHashrate)/1000)/1000)
  174. msgText += fmt.Sprintf("Current Hashrate: %.3f MH/s\n", math.Round(float64(lastMinerRecord.CurrentHashrate)/1000)/1000)
  175. msgText += fmt.Sprintf("Valid Shares: %d\n", lastMinerRecord.ValidShares)
  176. msgText += fmt.Sprintf("Stale Shares: %d\n", lastMinerRecord.StaleShares)
  177. msgText += fmt.Sprintf("Workers: %d\n", lastMinerRecord.Workers)
  178. msgText += fmt.Sprintf("Unpaid Balance: %.5f ETH\n", float64(lastMinerRecord.Unpaid)/math.Pow10(18)) //TODO ETH = AppSettings.currency
  179. msg := tgbotapi.NewMessage(chatId, msgText)
  180. myBotClient.Send(msg)
  181. LogInfo.Printf("Replied with: %s", strings.ReplaceAll(msg.Text, "\n", "\\n"))
  182. for i := range lastWorkerRecord {
  183. // Per worker message
  184. time.Sleep(1 * time.Second)
  185. // Anti spam filter
  186. if i != 0 && i%20 == 0 { //TODO SOMETIHNG BETTER THAN USUAL TIMER
  187. time.Sleep(30 * time.Second)
  188. }
  189. msgText := fmt.Sprintf("Worker %s stats:\n", lastWorkerRecord[i].Worker)
  190. msgText += fmt.Sprintf("Updated: %s\n", time.Unix(lastWorkerRecord[i].Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  191. msgText += fmt.Sprintf("Reported Hashrate: %.3f MH/s\n", math.Round(float64(lastWorkerRecord[i].ReportedHashrate)/1000)/1000)
  192. msgText += fmt.Sprintf("Current Hashrate: %.3f MH/s\n", math.Round(float64(lastWorkerRecord[i].CurrentHashrate)/1000)/1000)
  193. msgText += fmt.Sprintf("Valid Shares: %d\n", lastWorkerRecord[i].ValidShares)
  194. msgText += fmt.Sprintf("Stale Shares: %d\n", lastWorkerRecord[i].StaleShares)
  195. msgText += fmt.Sprintf("Unpaid Balance: %.5f ETH\n", float64(lastWorkerRecord[i].WorkerUnpaid)/math.Pow10(18)) //TODO ETH = AppSettings.currency
  196. msg := tgbotapi.NewMessage(chatId, msgText)
  197. myBotClient.Send(msg)
  198. LogInfo.Printf("Replied with: %s", strings.ReplaceAll(msg.Text, "\n", "\\n"))
  199. }
  200. return "nice"
  201. }
  202. func (c *Connection) GetLastPayout(chatId int64) string {
  203. errMsgToUser := "An exception occurred on sending messages while executing /actual command"
  204. var wallet string
  205. result := c.GormDb.Model(&User{}).
  206. Where(User{ChatId: chatId}).
  207. Select("wallet").
  208. Scan(&wallet)
  209. if result.Error != nil {
  210. LogError.Println(result.Error)
  211. return errMsgToUser
  212. }
  213. if wallet == "" {
  214. return "Set wallet at first! Use /setwallet"
  215. }
  216. var payoutTime int64
  217. result = c.GormDb.Model(&Payout{}).
  218. Where(&Payout{Wallet: wallet}).
  219. Order("time desc").
  220. Limit(1).
  221. Select("time").
  222. Scan(&payoutTime)
  223. if result.Error != nil {
  224. LogInfo.Printf("No payouts data for %s! Time is set to 0!", wallet)
  225. LogError.Println(result.Error)
  226. payoutTime = 0
  227. }
  228. // NOTE Pointless if previous 'if' statement occurs
  229. // Must go from the start to line 235
  230. var dbPayoutRecords []Payout
  231. result = c.GormDb.
  232. Where(&Payout{Wallet: wallet, Time: payoutTime}).
  233. Find(&dbPayoutRecords)
  234. //Line 560 of Program.cs
  235. if len(dbPayoutRecords) == 0 {
  236. url := fmt.Sprintf("%s/miner/%s/payouts", appSettings.ApiUrl, wallet)
  237. var payouts JsonPayouts
  238. err := UnmasrshalFromUrl(url, &payouts)
  239. if err != nil {
  240. LogError.Println(err)
  241. return fmt.Sprintf("No payout data for %s", wallet)
  242. }
  243. lastPayout := payouts.Data[0]
  244. message := fmt.Sprintf("Payout date: %s\n", time.Unix(lastPayout.PaidOn, 0).Format("Monday, January 2, 2006 3:04 PM"))
  245. message += fmt.Sprintf("Amount: %.5f %s\n", float64(lastPayout.Amount)/math.Pow10(18), appSettings.Currency)
  246. message += "Data source: Ethermine API \n"
  247. return message
  248. }
  249. if result.Error != nil {
  250. LogError.Println(result.Error)
  251. return errMsgToUser
  252. }
  253. //Line 545 of Program.cs
  254. message := fmt.Sprintf("Payout date: %s\n", time.Unix(dbPayoutRecords[0].Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  255. message += fmt.Sprintf("Amount: %.5f ETH\n", float64(dbPayoutRecords[0].Amount)/math.Pow10(18))
  256. for _, payoutRecord := range dbPayoutRecords {
  257. message += fmt.Sprintf("Worker %s paid: %.5f %s\n", //TODO ETH = AppSettings.currency
  258. payoutRecord.Worker,
  259. float64(payoutRecord.Amount)/math.Pow10(18),
  260. appSettings.Currency)
  261. }
  262. message += "Data source: Bot database \n"
  263. return message
  264. }
  265. func GetActualRate() string {
  266. url := fmt.Sprintf("%s/networkStats", appSettings.ApiUrl)
  267. var networkStats JsonNetworkStats
  268. err := UnmasrshalFromUrl(url, &networkStats)
  269. if err != nil {
  270. return fmt.Sprintf("Error with getting data from %s", appSettings.ApiUrl)
  271. }
  272. data := networkStats.Data
  273. actualRate := fmt.Sprintf("ETH: %.2f\nBTC: %.2f", data.Usd, float64(data.Usd)/float64(data.Btc))
  274. return actualRate
  275. }
  276. func GetActualData(miner string) string {
  277. url := fmt.Sprintf("%s/miner/%s/currentStats", appSettings.ApiUrl, miner)
  278. var currentStats JsonCurrentStats
  279. err := UnmasrshalFromUrl(url, &currentStats)
  280. if err != nil {
  281. // As in original...
  282. return "An exception occurred while executing this command."
  283. // return fmt.Sprintf("No data for specified wallet\n")
  284. }
  285. data := currentStats.Data
  286. actualData := fmt.Sprintf("Stats of miner\n%s\n", miner)
  287. actualData += fmt.Sprintf("Updated: %s\n", time.Unix(data.Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  288. actualData += fmt.Sprintf("Reported Hashrate: %.3f MH/s\n", math.Round(float64(data.ReportedHashrate)/1000)/1000)
  289. actualData += fmt.Sprintf("Current Hashrate: %.3f MH/s\n", math.Round(data.CurrentHashrate/1000)/1000)
  290. actualData += fmt.Sprintf("Valid Shares: %d\n", data.ValidShares)
  291. actualData += fmt.Sprintf("Stale Shares: %d\n", data.StaleShares)
  292. actualData += fmt.Sprintf("Workers: %d\n", data.ActiveWorkers)
  293. actualData += fmt.Sprintf("Unpaid Balance: %.5f ETH\n", float64(data.Unpaid)/math.Pow10(18))
  294. return actualData
  295. }
  296. func GetHelp() (help string) {
  297. // TODO separate help variable
  298. help = "This is bot tracks your miner stats and calculates unpaid per worker.\n"
  299. help += "How to start:\n"
  300. help += "1) \"/start\" - it will add you to database\n"
  301. help += "2) \"/setwallet <wallet>\" - it will set wallet for tracking, bot will start to add info about your miner and workers to database\n"
  302. help += "3) \"/actual\" - it will send you up to date data about your worker\n"
  303. help += "4) \"/lastpayout\" - it will send you last payout data with calculated worker unpaid for that period if avaliable\n"
  304. help += "5) \"/stop\" - it will clear all data about you from database\n"
  305. help += "Additional commands:\n"
  306. help += "\"/rate\" - get actual ETH and BTC rate from ethermine\n"
  307. help += "\"/actual <wallet>\" - get up to date data about any wallet without unpaid per worker\n"
  308. return help
  309. }