handlers.go 12 KB

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