handlers.go 12 KB

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