handlers.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  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. // TODO Delete from workers, miners, payouts
  109. }
  110. LogInfo.Println("User was deleted:", user)
  111. return "Done!", nil
  112. }
  113. func (c *Connection) GetActualDataFromDatabase(chatId int64) string {
  114. errMsgToUser := "An exception occurred on sending messages while executing /actual command"
  115. var wallet string
  116. result := c.GormDb.Model(&User{}).
  117. Where("chat_id = ?", chatId).
  118. Select("wallet").
  119. Scan(&wallet)
  120. if result.Error != nil {
  121. LogError.Println(result.Error)
  122. return errMsgToUser
  123. }
  124. if wallet == "" {
  125. return "Set wallet at first! Use /setwallet"
  126. }
  127. var lastMinerRecord Miner
  128. result = c.GormDb.
  129. Where(&Miner{Wallet: wallet}).
  130. Order("time desc").
  131. Limit(1).
  132. Find(&lastMinerRecord)
  133. if result.Error != nil {
  134. LogError.Println(result.Error)
  135. return "No miner data for now!"
  136. }
  137. var lastTime int64
  138. result = c.GormDb.Model(&Worker{}).
  139. Where("wallet = ?", wallet).
  140. Select("MAX(time)").
  141. Scan(&lastTime)
  142. if result.Error != nil {
  143. LogError.Println(result.Error)
  144. return errMsgToUser
  145. }
  146. var lastWorkerRecord []Worker
  147. result = c.GormDb.
  148. Where(&Worker{Wallet: wallet, Time: lastTime}).
  149. Find(&lastWorkerRecord)
  150. if result.Error != nil {
  151. LogError.Println(result.Error)
  152. return errMsgToUser
  153. }
  154. if len(lastWorkerRecord) == 0 {
  155. return "No workers data for now!"
  156. }
  157. msgText := fmt.Sprintf("Miner %s stats:\n", lastMinerRecord.Wallet)
  158. msgText += fmt.Sprintf("Updated: %s\n", time.Unix(lastMinerRecord.Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  159. msgText += fmt.Sprintf("Reported Hashrate: %.3f MH/s\n", math.Round(float64(lastMinerRecord.ReportedHashrate)/1000)/1000)
  160. msgText += fmt.Sprintf("Current Hashrate: %.3f MH/s\n", math.Round(float64(lastMinerRecord.CurrentHashrate)/1000)/1000)
  161. msgText += fmt.Sprintf("Valid Shares: %d\n", lastMinerRecord.ValidShares)
  162. msgText += fmt.Sprintf("Stale Shares: %d\n", lastMinerRecord.StaleShares)
  163. msgText += fmt.Sprintf("Workers: %d\n", lastMinerRecord.Workers)
  164. msgText += fmt.Sprintf("Unpaid Balance: %.5f ETH\n", float64(lastMinerRecord.Unpaid)/math.Pow10(18)) //TODO ETH = AppSettings.currency
  165. msg := tgbotapi.NewMessage(chatId, msgText)
  166. myBotClient.Send(msg)
  167. LogInfo.Printf("Replied with: %s", strings.ReplaceAll(msg.Text, "\n", "\\n"))
  168. for i := range lastWorkerRecord {
  169. // Per worker message
  170. time.Sleep(1 * time.Second)
  171. // Anti spam filter
  172. if i != 0 && i%20 == 0 { //TODO SOMETIHNG BETTER THAN USUAL TIMER
  173. time.Sleep(30 * time.Second)
  174. }
  175. msgText := fmt.Sprintf("Worker %s stats:\n", lastWorkerRecord[i].Worker)
  176. msgText += fmt.Sprintf("Updated: %s\n", time.Unix(lastWorkerRecord[i].Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  177. msgText += fmt.Sprintf("Reported Hashrate: %.3f MH/s\n", math.Round(float64(lastWorkerRecord[i].ReportedHashrate)/1000)/1000)
  178. msgText += fmt.Sprintf("Current Hashrate: %.3f MH/s\n", math.Round(float64(lastWorkerRecord[i].CurrentHashrate)/1000)/1000)
  179. msgText += fmt.Sprintf("Valid Shares: %d\n", lastWorkerRecord[i].ValidShares)
  180. msgText += fmt.Sprintf("Stale Shares: %d\n", lastWorkerRecord[i].StaleShares)
  181. msgText += fmt.Sprintf("Unpaid Balance: %.5f ETH\n", float64(lastWorkerRecord[i].WorkerUnpaid)/math.Pow10(18)) //TODO ETH = AppSettings.currency
  182. msg := tgbotapi.NewMessage(chatId, msgText)
  183. myBotClient.Send(msg)
  184. LogInfo.Printf("Replied with: %s", strings.ReplaceAll(msg.Text, "\n", "\\n"))
  185. }
  186. return "nice"
  187. }
  188. func (c *Connection) GetLastPayout(chatId int64) string {
  189. errMsgToUser := "An exception occurred on sending messages while executing /actual command"
  190. var wallet string
  191. result := c.GormDb.Model(&User{}).
  192. Where(User{ChatId: chatId}).
  193. Select("wallet").
  194. Scan(&wallet)
  195. if result.Error != nil {
  196. LogError.Println(result.Error)
  197. return errMsgToUser
  198. }
  199. if wallet == "" {
  200. return "Set wallet at first! Use /setwallet"
  201. }
  202. var payoutTime int64
  203. result = c.GormDb.Model(&Payout{}).
  204. Where(&Payout{Wallet: wallet}).
  205. Order("time desc").
  206. Limit(1).
  207. Select("time").
  208. Scan(&payoutTime)
  209. if result.Error != nil {
  210. LogInfo.Printf("No payouts data for %s! Time is set to 0!", wallet)
  211. LogError.Println(result.Error)
  212. payoutTime = 0
  213. }
  214. // NOTE Pointless if previous 'if' statement occurs
  215. // Must go from the start to line 235
  216. var dbPayoutRecords []Payout
  217. result = c.GormDb.
  218. Where(&Payout{Wallet: wallet, Time: payoutTime}).
  219. Find(&dbPayoutRecords)
  220. //Line 560 of Program.cs
  221. if len(dbPayoutRecords) == 0 {
  222. url := fmt.Sprintf("%s/miner/%s/payouts", appSettings.ApiUrl, wallet)
  223. var payouts JsonPayouts
  224. err := UnmasrshalFromUrl(url, &payouts)
  225. if err != nil {
  226. LogError.Println(err)
  227. return fmt.Sprintf("No payout data for %s", wallet)
  228. }
  229. lastPayout := payouts.Data[0]
  230. message := fmt.Sprintf("Payout date: %s\n", time.Unix(lastPayout.PaidOn, 0).Format("Monday, January 2, 2006 3:04 PM"))
  231. message += fmt.Sprintf("Amount: %.5f %s\n", float64(lastPayout.Amount)/math.Pow10(18), appSettings.Currency)
  232. message += "Data source: Ethermine API \n"
  233. return message
  234. }
  235. if result.Error != nil {
  236. LogError.Println(result.Error)
  237. return errMsgToUser
  238. }
  239. //Line 545 of Program.cs
  240. message := fmt.Sprintf("Payout date: %s\n", time.Unix(dbPayoutRecords[0].Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  241. message += fmt.Sprintf("Amount: %.5f ETH\n", float64(dbPayoutRecords[0].Amount)/math.Pow10(18))
  242. for _, payoutRecord := range dbPayoutRecords {
  243. message += fmt.Sprintf("Worker %s paid: %.5f %s\n", //TODO ETH = AppSettings.currency
  244. payoutRecord.Worker,
  245. float64(payoutRecord.Amount)/math.Pow10(18),
  246. appSettings.Currency)
  247. }
  248. message += "Data source: Bot database \n"
  249. return message
  250. }
  251. func GetActualRate() string {
  252. url := fmt.Sprintf("%s/networkStats", appSettings.ApiUrl)
  253. var networkStats JsonNetworkStats
  254. err := UnmasrshalFromUrl(url, &networkStats)
  255. if err != nil {
  256. return fmt.Sprintf("Error with getting data from %s", appSettings.ApiUrl)
  257. }
  258. data := networkStats.Data
  259. actualRate := fmt.Sprintf("ETH: %.2f\nBTC: %.2f", data.Usd, float64(data.Usd)/float64(data.Btc))
  260. return actualRate
  261. }
  262. func GetActualData(miner string) string {
  263. url := fmt.Sprintf("%s/miner/%s/currentStats", appSettings.ApiUrl, miner)
  264. var currentStats JsonCurrentStats
  265. err := UnmasrshalFromUrl(url, &currentStats)
  266. if err != nil {
  267. // As in original...
  268. return "An exception occurred while executing this command."
  269. // return fmt.Sprintf("No data for specified wallet\n")
  270. }
  271. data := currentStats.Data
  272. actualData := fmt.Sprintf("Stats of miner\n%s\n", miner)
  273. actualData += fmt.Sprintf("Updated: %s\n", time.Unix(data.Time, 0).Format("Monday, January 2, 2006 3:04 PM"))
  274. actualData += fmt.Sprintf("Reported Hashrate: %.3f MH/s\n", math.Round(float64(data.ReportedHashrate)/1000)/1000)
  275. actualData += fmt.Sprintf("Current Hashrate: %.3f MH/s\n", math.Round(data.CurrentHashrate/1000)/1000)
  276. actualData += fmt.Sprintf("Valid Shares: %d\n", data.ValidShares)
  277. actualData += fmt.Sprintf("Stale Shares: %d\n", data.StaleShares)
  278. actualData += fmt.Sprintf("Workers: %d\n", data.ActiveWorkers)
  279. actualData += fmt.Sprintf("Unpaid Balance: %.5f ETH\n", float64(data.Unpaid)/math.Pow10(18))
  280. return actualData
  281. }
  282. func GetHelp() (help string) {
  283. // TODO separate help variable
  284. help = "This is bot tracks your miner stats and calculates unpaid per worker.\n"
  285. help += "How to start:\n"
  286. help += "1) \"/start\" - it will add you to database\n"
  287. help += "2) \"/setwallet <wallet>\" - it will set wallet for tracking, bot will start to add info about your miner and workers to database\n"
  288. help += "3) \"/actual\" - it will send you up to date data about your worker\n"
  289. help += "4) \"/lastpayout\" - it will send you last payout data with calculated worker unpaid for that period if avaliable\n"
  290. help += "5) \"/stop\" - it will clear all data about you from database\n"
  291. help += "Additional commands:\n"
  292. help += "\"/rate\" - get actual ETH and BTC rate from ethermine\n"
  293. help += "\"/actual <wallet>\" - get up to date data about any wallet without unpaid per worker\n"
  294. return help
  295. }