├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── configs └── query_list.conf ├── go.mod ├── go.sum ├── main.go ├── requests └── requests.go ├── types └── types.go └── utils └── utils.go /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | ./bin/ 3 | ./bin/* 4 | .idea 5 | .idea/* 6 | ./.idea/ 7 | ./.idea/* 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 DrunkLeen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PLATFORMS := windows linux darwin 2 | ARCHS := amd64 arm64 3 | BINARY := blumbot 4 | 5 | .PHONY: build run test clean 6 | 7 | # Build for all platforms and architectures 8 | build: 9 | @mkdir -p ./bin 10 | @for GOOS in $(PLATFORMS); do \ 11 | for GOARCH in $(ARCHS); do \ 12 | output_name=./bin/$(BINARY)-$$GOOS-$$GOARCH; \ 13 | if [ $$GOOS = "windows" ]; then \ 14 | output_name=$$output_name.exe; \ 15 | fi; \ 16 | echo "Building for $$GOOS/$$GOARCH..."; \ 17 | GOOS=$$GOOS GOARCH=$$GOARCH go build -o $$output_name .; \ 18 | done \ 19 | done 20 | 21 | # Run the built binary (only works for the current OS/ARCH) 22 | run: build 23 | @./bin/$(BINARY) 24 | 25 | test: 26 | @go test -v ./... 27 | 28 | clean: 29 | @rm -rf ./bin 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Blum Bot 2 | --- 3 | 4 | ### Overview 5 | This bot is designed to automate various tasks in a Telegram mini-app BlumBot. It handles everything from playing drop games, claiming referral balances, starting missions, and claiming mission balances once they are completed. 6 | 7 | ### Features 8 | - Automated Task Execution: Automatically handles all in-game tasks. 9 | - Drop Game Participation: Plays the drop games to earn rewards. 10 | - Referral Balance Claiming: Claims balances from referrals. 11 | - Mission Management: Starts missions and claims the mission balance once completed. 12 | 13 | ### Getting Started 14 | #### Prerequisites 15 | Before you begin, ensure you have met the following 16 | requirements: 17 | - You have a Telegram account 18 | - You have to download the executable of build it from source 19 | 20 | How to get query? Follow this steps: 21 | 1. open Telegram Desktop and login. 22 | 2. go to `Settings` 23 | 3. click on `Advanced` 24 | 4. click on `Experimental settings` 25 | 5. turn on `Enable webview inspecting` 26 | 6. go your BlumBot page and click on `Launch Blum` 27 | 7. right click on the blum page and select `Inspect Element` 28 | 8. click on `Application` tab 29 | 9. in the left bar use the dropdown next to `session storage` and click on `telegram.blum.codes` 30 | 10. select `telegram_initparam` 31 | 11. right click on `tgwebappdata` and select `copy value` 32 | 12. create a new directory in the same directory as executable file and name it `configs` 33 | 13. create a new blank file with the name `query_list.conf` 34 | 14. paste your query IDs 35 | 15. run the bot and enjoy! 36 | 37 | 38 | ### Disclaimer 39 | 40 | This bot is intended for educational purposes only. Use it at your own risk. The author is not responsible for any misuse or damage caused by using this bot. 41 | 42 | ### Contributing 43 | 44 | Contributions are welcome! Please follow these steps to contribute: 45 | 46 | 1. Fork the repository. 47 | 2. Create a new branch (git checkout -b feature/your-feature). 48 | 3. Make your changes and commit them (git commit -m 'Add some feature'). 49 | 4. Push to the branch (git push origin feature/your-feature). 50 | Create a pull request. 51 | 52 | ### License 53 | 54 | This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details. 55 | -------------------------------------------------------------------------------- /configs/query_list.conf: -------------------------------------------------------------------------------- 1 | query 1 2 | query 2 3 | query 3 4 | query ... n 5 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/drunkleen/blum-bot 2 | 3 | go 1.22.4 4 | 5 | require ( 6 | github.com/fatih/color v1.17.0 // indirect 7 | github.com/mattn/go-colorable v0.1.13 // indirect 8 | github.com/mattn/go-isatty v0.0.20 // indirect 9 | golang.org/x/sys v0.21.0 // indirect 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= 2 | github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 3 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 4 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 5 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 6 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 7 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 8 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 9 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 10 | golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= 11 | golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 12 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "math/rand" 7 | "strconv" 8 | "time" 9 | 10 | "github.com/drunkleen/blum-bot/requests" 11 | "github.com/drunkleen/blum-bot/utils" 12 | 13 | "github.com/fatih/color" 14 | ) 15 | 16 | var ( 17 | startTime time.Time 18 | checkedTasksMap map[string]bool = map[string]bool{} 19 | tokenMap map[string]string = map[string]string{} 20 | checkTaskEnable bool 21 | claimRefEnable bool 22 | 23 | initStage bool = true 24 | 25 | printText string = "" 26 | 27 | bold = color.New(color.Bold).SprintFunc() 28 | 29 | red = color.New(color.FgRed).SprintFunc() 30 | cyan = color.New(color.FgCyan).SprintFunc() 31 | green = color.New(color.FgGreen).SprintFunc() 32 | yellow = color.New(color.FgYellow).SprintFunc() 33 | ) 34 | 35 | func init() { 36 | startTime = time.Now() 37 | } 38 | 39 | func main() { 40 | utils.ClearScreen() 41 | utils.PrintLogo() 42 | checkTaskEnable, claimRefEnable = utils.ParseArgs() 43 | fmt.Printf("Task: %v\n", checkTaskEnable) 44 | fmt.Printf("Reff: %v\n", claimRefEnable) 45 | 46 | queryList, err := utils.ParseQueries() 47 | if err != nil { 48 | log.Fatalf(red("QueryList Error: %v\n"), err) 49 | } 50 | 51 | mainLoop(queryList) 52 | } 53 | 54 | func mainLoop(queryList []string) { 55 | utils.ClearScreen() 56 | 57 | nowTime := time.Now() 58 | nextTrigger := time.Now() 59 | if nextTrigger.Before(nowTime) { 60 | nextTrigger = nextTrigger.Add(2 * time.Hour) 61 | } 62 | 63 | for { // start infinite loop 64 | now := time.Now() 65 | 66 | for _, queryID := range queryList { // start query loop 67 | // get Token if not exists 68 | val, exists := tokenMap[queryID] 69 | if !exists || val == "" { 70 | newToken, err := requests.GetNewToken(queryID) 71 | if err != nil { 72 | log.Printf(red("Error: %v\n"), err) 73 | } 74 | tokenMap[queryID] = newToken 75 | } 76 | token := tokenMap[queryID] 77 | 78 | // fetching user info 79 | userInfo, err := requests.GetUserInfo(token, queryID) 80 | if err != nil { 81 | if err.Error() == "token is invalid" { 82 | newToken, err := requests.GetNewToken(queryID) 83 | if err != nil { 84 | log.Printf(red("Error: %v\n"), err) 85 | } 86 | tokenMap[queryID] = newToken 87 | } else { 88 | log.Printf(red("Error: %v\n"), err) 89 | } 90 | } 91 | username := userInfo["username"] 92 | printText += fmt.Sprintf("---\n"+"["+bold(cyan("User"))+"] "+"%s\n", bold(green(username))) 93 | if initStage { 94 | fmt.Printf("---\nUser: %s\n", bold(username)) 95 | } 96 | 97 | // fetching balance info 98 | balanceInfo, err := requests.GetUserBalance(token) 99 | if err != nil { 100 | log.Printf(red("Error: %v\n"), err) 101 | } 102 | 103 | printText += fmt.Sprintf("["+bold(cyan("Balance"))+"] "+"%v\n", balanceInfo.AvailableBalance) 104 | if initStage { 105 | fmt.Printf("[Balance] %v\n", bold(cyan(balanceInfo.AvailableBalance))) 106 | } 107 | 108 | nextFarmingTime, _ := utils.TimeLeft(balanceInfo.Farming.EndTime) 109 | 110 | if balanceInfo.Farming.Balance != "57.6" { 111 | printText += fmt.Sprintf("["+bold(cyan("Farming"))+"] "+"next claim %v remaining", nextFarmingTime) 112 | printText += fmt.Sprintf(" | Earned: %v\n", balanceInfo.Farming.Balance) 113 | } else { 114 | ok, err := requests.ClaimFarm(token) 115 | if err != nil { 116 | log.Printf(red("Error: %v\n"), err) 117 | } 118 | if ok { 119 | printText += fmt.Sprintf("[" + bold(cyan("Farming")) + "] " + "claimed successfully!\n") 120 | } else { 121 | printText += fmt.Sprintf("[" + bold(cyan("Farming")) + "] " + "Failed to claim farm!\n") 122 | } 123 | 124 | ok, err = requests.StartFarm(token) 125 | if err != nil { 126 | log.Printf(red("Error: %v\n"), err) 127 | } 128 | if ok { 129 | printText += fmt.Sprintf("[" + bold(cyan("Farming")) + "] " + "started farming successfully!\n") 130 | } else { 131 | printText += fmt.Sprintf("[" + bold(cyan("Farming")) + "] " + "Failed to start farming!\n") 132 | } 133 | } 134 | 135 | // Check Daily Rewards 136 | dailyRewardResponse, err := requests.CheckDailyReward(token) 137 | if err != nil { 138 | log.Printf(red("Error: %v\n"), err) 139 | } 140 | 141 | switch dailyRewardResponse["message"] { 142 | case "same day": 143 | printText += fmt.Sprintf("[" + bold(cyan("Daily Reward")) + "] " + " already claimed today\n") 144 | case "OK": 145 | printText += fmt.Sprintf("[" + bold(cyan("Daily Reward")) + "] " + " successfully claimed!\n") 146 | default: 147 | printText += fmt.Sprintf("[" + bold(cyan("Daily Reward")) + "] " + " Failed to check daily reward!\n") 148 | } 149 | 150 | if checkTaskEnable { 151 | checked, exists := checkedTasksMap[queryID] 152 | if !exists || !checked { 153 | fmt.Println("Checking tasks...") 154 | requests.CheckTasks(token) 155 | checkedTasksMap[queryID] = true 156 | } 157 | } 158 | 159 | if claimRefEnable { 160 | friendsBalance, err := requests.CheckBalanceFriend(token) 161 | 162 | if friendsBalance.AmountForClaim != "0" || friendsBalance.CanClaim { 163 | 164 | if err != nil { 165 | log.Printf(red("[Referrals Balance] Failed to get friend's balance: %v\n"), err) 166 | } 167 | 168 | printText += fmt.Sprintf("[" + bold(cyan("Referrals")) + "] ") 169 | printText += fmt.Sprintf(yellow("amount: %v"), friendsBalance.AmountForClaim) 170 | printText += fmt.Sprintf(yellow(" | Claimable: %v"), friendsBalance.CanClaim) 171 | 172 | var claimTime int64 173 | if friendsBalance.CanClaimAt != "" { 174 | claimTime, err = strconv.ParseInt(friendsBalance.CanClaimAt, 10, 64) 175 | if err != nil { 176 | log.Printf(red("[Referrals] Failed to parse claim time: %v\n"), err) 177 | } 178 | remainingClaimTime, err := utils.TimeLeft(claimTime) 179 | if err != nil { 180 | log.Printf(red("[Referrals] Failed to calculate remaining claim time: %v\n"), err) 181 | } 182 | printText += fmt.Sprintf(yellow(" | %v remaining\n"), remainingClaimTime) 183 | } else { 184 | printText += "\n" 185 | } 186 | 187 | if friendsBalance.CanClaim { 188 | ok, err := requests.ClaimBalanceFriend(token) 189 | if err != nil { 190 | log.Printf(red("[Referrals] Failed to claim friend's balance: %v\n"), err) 191 | } 192 | if ok { 193 | printText += fmt.Sprintf(bold(cyan("[Referrals]")) + " successfully claimed!\n") 194 | } 195 | } 196 | 197 | } 198 | } 199 | 200 | printText += fmt.Sprintf("[%v] %v tickets\n", bold(cyan("Play Passes")), balanceInfo.PlayPasses) 201 | 202 | for balanceInfo.PlayPasses > 0 { // game loop 203 | gameResponse, err := requests.PlayGame(token) 204 | if err != nil { 205 | fmt.Printf("Error: %v\n", err) 206 | } else { 207 | fmt.Printf("Game played successfully: %v\n", gameResponse) 208 | } 209 | 210 | if gameId, ok := gameResponse["gameId"].(string); ok { 211 | requests.ClaimGame(token, gameId, queryID, func() int { 212 | return rand.Intn(1999-1301) + 1301 213 | }()) 214 | } 215 | balanceInfo.PlayPasses-- 216 | 217 | } // end game loop 218 | 219 | } // end queryList loop 220 | 221 | if now.After(nextTrigger) || now.Equal(nextTrigger) { 222 | for i := 0; i < 3; i++ { 223 | for _, token := range tokenMap { 224 | fmt.Println("\nClaiming daily reward") 225 | _, err := requests.GetDailyRewards(token) 226 | if err != nil { 227 | fmt.Printf(red("Error: %v\n"), err) 228 | } 229 | } 230 | } 231 | nextTrigger = nextTrigger.Add(2 * time.Hour) 232 | } 233 | 234 | if initStage { 235 | initStage = !initStage 236 | } else { 237 | time.Sleep(time.Duration(rand.Intn(3600-900)+512) * time.Second) 238 | } 239 | h, m, _ := now.Clock() 240 | utils.ClearScreen() 241 | utils.PrintLogo() 242 | fmt.Printf( 243 | "-------- Time: %d:%d --------\n%v\n------------ Up Time: %v ------------", 244 | h, m, printText, yellow(utils.FormatUpTime(time.Since(startTime))), 245 | ) 246 | printText = "" 247 | 248 | } // end infinite loop 249 | 250 | } 251 | -------------------------------------------------------------------------------- /requests/requests.go: -------------------------------------------------------------------------------- 1 | package requests 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "fmt" 7 | "io" 8 | "log" 9 | "net" 10 | "net/http" 11 | "time" 12 | 13 | "github.com/drunkleen/blum-bot/types" 14 | ) 15 | 16 | var ( 17 | getHeaders = map[string]string{ 18 | "accept": "application/json, text/plain, */*", 19 | "accept-language": "en-US,en;q=0.9", 20 | "origin": "https://telegram.blum.codes", 21 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0", 22 | } 23 | ) 24 | 25 | func GetNewToken(queryID string) (string, error) { 26 | url := "https://user-domain.blum.codes/api/v1/auth/provider/PROVIDER_TELEGRAM_MINI_APP" 27 | 28 | headers := map[string]string{ 29 | "accept": "application/json, text/plain, */*", 30 | "accept-language": "en-US,en;q=0.9", 31 | "content-type": "application/json", 32 | "origin": "https://telegram.blum.codes", 33 | "priority": "u=1, i", 34 | "referer": "https://telegram.blum.codes/", 35 | } 36 | 37 | // Define the data to be sent in the POST request 38 | data := types.RequestBody{Query: queryID} 39 | jsonData, err := json.Marshal(data) 40 | if err != nil { 41 | return "", fmt.Errorf("failed to marshal JSON: %w", err) 42 | } 43 | 44 | // Try to get the token up to 3 Times 45 | for attempt := 1; attempt <= 3; attempt++ { 46 | fmt.Printf("Getting token (attempt %d)...\n", attempt) 47 | 48 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) 49 | if err != nil { 50 | return "", fmt.Errorf("failed to create request: %w", err) 51 | } 52 | 53 | for key, val := range headers { 54 | req.Header.Set(key, val) 55 | } 56 | 57 | client := &http.Client{Timeout: 10 * time.Second} 58 | resp, err := client.Do(req) 59 | if err != nil { 60 | return "", fmt.Errorf("failed to send request: %w", err) 61 | } 62 | defer resp.Body.Close() 63 | 64 | if resp.StatusCode == http.StatusOK { 65 | fmt.Println("Token successfully created.") 66 | 67 | body, err := io.ReadAll(resp.Body) 68 | if err != nil { 69 | return "", fmt.Errorf("failed to read response body: %w", err) 70 | } 71 | 72 | var response types.ResponseBody 73 | if err := json.Unmarshal(body, &response); err != nil { 74 | return "", fmt.Errorf("failed to unmarshal JSON: %w", err) 75 | } 76 | 77 | return response.Token.Refresh, nil 78 | } else { 79 | body, _ := io.ReadAll(resp.Body) 80 | fmt.Printf("Failed to get token, attempt %d: %s\n", attempt, body) 81 | } 82 | 83 | } 84 | 85 | // If all attempts fail 86 | fmt.Println("Failed to get token after 3 attempts.") 87 | return "", nil 88 | } 89 | 90 | func GetUserInfo(token string, queryID string) (map[string]any, error) { 91 | url := "https://user-domain.blum.codes/api/v1/user/me" 92 | headers := getHeaders 93 | headers["Authorization"] = "Bearer " + token 94 | 95 | req, err := http.NewRequest("GET", url, nil) 96 | if err != nil { 97 | return nil, fmt.Errorf("failed to create request: %w", err) 98 | } 99 | 100 | for key, val := range headers { 101 | req.Header.Set(key, val) 102 | } 103 | client := &http.Client{Timeout: 10 * time.Second} 104 | 105 | resp, err := client.Do(req) 106 | if err != nil { 107 | return nil, fmt.Errorf("failed to send request: %w", err) 108 | } 109 | defer resp.Body.Close() 110 | 111 | body, err := io.ReadAll(resp.Body) 112 | if err != nil { 113 | return nil, fmt.Errorf("failed to read response body: %w", err) 114 | } 115 | 116 | if resp.StatusCode == http.StatusOK { 117 | var result map[string]any 118 | if err := json.Unmarshal(body, &result); err != nil { 119 | return nil, fmt.Errorf("failed to unmarshal JSON: %w", err) 120 | } 121 | 122 | return result, nil 123 | } 124 | 125 | var tokenResponse types.TokenResponseBody 126 | if err := json.Unmarshal(body, &tokenResponse); err != nil { 127 | return nil, fmt.Errorf("failed to unmarshal JSON: %w", err) 128 | } 129 | 130 | if tokenResponse.Message == "Token is invalid" { 131 | return nil, fmt.Errorf("token is invalid") 132 | } else { 133 | fmt.Println("Failed to get user information.") 134 | return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) 135 | } 136 | 137 | } 138 | 139 | func GetUserBalance(token string) (types.UserBalance, error) { 140 | url := "https://game-domain.blum.codes/api/v1/user/balance" 141 | headers := getHeaders 142 | headers["Authorization"] = "Bearer " + token 143 | 144 | req, err := http.NewRequest("GET", url, nil) 145 | if err != nil { 146 | return types.UserBalance{}, fmt.Errorf("failed to send request: %w", err) 147 | } 148 | 149 | for key, val := range headers { 150 | req.Header.Set(key, val) 151 | } 152 | 153 | client := &http.Client{Timeout: 10 * time.Second} 154 | resp, err := client.Do(req) 155 | if err != nil { 156 | return types.UserBalance{}, fmt.Errorf("failed to send request: %w", err) 157 | } 158 | defer func(Body io.ReadCloser) { 159 | err := Body.Close() 160 | if err != nil { 161 | fmt.Printf("Error closing response body: %v\n", err) 162 | } 163 | }(resp.Body) 164 | 165 | if resp.StatusCode == http.StatusOK { 166 | body, err := io.ReadAll(resp.Body) 167 | if err != nil { 168 | return types.UserBalance{}, fmt.Errorf("failed to read response body: %w", err) 169 | } 170 | var result types.UserBalance 171 | if err := json.Unmarshal(body, &result); err != nil { 172 | return types.UserBalance{}, fmt.Errorf("failed to unmarshal JSON: %w", err) 173 | } 174 | return result, nil 175 | 176 | } 177 | 178 | return types.UserBalance{}, fmt.Errorf("failed to get balance") 179 | } 180 | 181 | func CheckDailyReward(token string) (map[string]any, error) { 182 | url := "https://game-domain.blum.codes/api/v1/daily-reward?offset=-420" 183 | headers := getHeaders 184 | headers["Authorization"] = "Bearer " + token 185 | headers["content-length"] = "0" 186 | headers["sec-ch-ua"] = `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"` 187 | headers["sec-ch-ua-mobile"] = "?0" 188 | headers["sec-ch-ua-platform"] = `Windows"` 189 | headers["sec-fetch-dest"] = "empty" 190 | headers["sec-fetch-mode"] = "cors" 191 | headers["sec-fetch-site"] = "same-site" 192 | 193 | req, err := http.NewRequest("POST", url, nil) 194 | if err != nil { 195 | return nil, fmt.Errorf("failed to create request: %w", err) 196 | } 197 | 198 | for key, val := range headers { 199 | req.Header.Set(key, val) 200 | } 201 | 202 | client := &http.Client{Timeout: 5 * time.Second} 203 | resp, err := client.Do(req) 204 | if err != nil { 205 | if err, ok := err.(net.Error); ok && err.Timeout() { 206 | fmt.Println("Failed to claim daily: Timeout") 207 | return nil, err 208 | } 209 | return nil, fmt.Errorf("failed to send request: %w", err) 210 | } 211 | defer resp.Body.Close() 212 | 213 | body, err := io.ReadAll(resp.Body) 214 | if err != nil { 215 | return nil, fmt.Errorf("failed to read response body: %w", err) 216 | } 217 | 218 | if resp.StatusCode == 400 { 219 | var result map[string]any 220 | if err := json.Unmarshal(body, &result); err != nil { 221 | if string(body) == "OK" { 222 | return map[string]any{"message": "OK"}, nil 223 | } 224 | return nil, nil 225 | } 226 | return result, nil 227 | } 228 | 229 | var result map[string]any 230 | if err := json.Unmarshal(body, &result); err != nil { 231 | fmt.Printf("Json Error: %s\n", string(body)) 232 | return nil, nil 233 | } 234 | 235 | return result, nil 236 | } 237 | 238 | func CheckTasks(token string) { 239 | url := "https://game-domain.blum.codes/api/v1/tasks" 240 | 241 | // Assuming getHeaders is a function, invoke it. 242 | headers := getHeaders 243 | headers["Authorization"] = "Bearer " + token 244 | headers["content-length"] = "0" 245 | headers["priority"] = "u=1, i" 246 | headers["sec-ch-ua"] = `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"` 247 | headers["sec-ch-ua-mobile"] = "?0" 248 | headers["sec-ch-ua-platform"] = `"Windows"` 249 | headers["sec-fetch-dest"] = "empty" 250 | headers["sec-fetch-mode"] = "cors" 251 | headers["sec-fetch-site"] = "same-site" 252 | 253 | // Create a new HTTP GET request 254 | req, err := http.NewRequest("GET", url, nil) 255 | if err != nil { 256 | log.Printf("failed to create request: %v", err) 257 | return 258 | } 259 | 260 | // Set headers for the request 261 | for key, val := range headers { 262 | req.Header.Set(key, val) 263 | } 264 | 265 | // Send the request 266 | client := &http.Client{Timeout: 5 * time.Second} 267 | resp, err := client.Do(req) 268 | if err != nil { 269 | log.Printf("failed to send request: %v", err) 270 | return 271 | } 272 | defer resp.Body.Close() 273 | 274 | body, err := io.ReadAll(resp.Body) 275 | if err != nil { 276 | log.Printf("Failed to read response body: %v\n", err) 277 | return 278 | } 279 | 280 | if resp.StatusCode == http.StatusOK { 281 | var tasks []map[string]any 282 | if err := json.Unmarshal(body, &tasks); err != nil { 283 | log.Printf("Failed to unmarshal JSON: %v\n", err) 284 | return 285 | } 286 | for _, task := range tasks { 287 | taskTitle, ok := task["title"].(string) 288 | if !ok { 289 | continue 290 | } 291 | taskID, ok := task["id"].(string) 292 | if !ok { 293 | continue 294 | } 295 | tasksList, ok := task["tasks"].([]interface{}) 296 | if !ok { 297 | continue 298 | } 299 | for _, t := range tasksList { 300 | taskMap, ok := t.(map[string]interface{}) 301 | if !ok { 302 | continue 303 | } 304 | taskStatus, ok := taskMap["status"].(string) 305 | if !ok { 306 | continue 307 | } 308 | if taskStatus == "NOT_STARTED" { 309 | fmt.Printf("Starting Task: %s\n", taskTitle) 310 | startTask(token, taskID, taskTitle) 311 | claimTask(token, taskID, taskTitle) 312 | } else { 313 | fmt.Printf("[Task %s | Reward: %v] %s\n", taskStatus, task["reward"], taskTitle) 314 | } 315 | } 316 | } 317 | } 318 | } 319 | 320 | func startTask(token, taskID, taskTitle string) { 321 | url := "https://game-domain.blum.codes/api/v1/tasks/" + taskID + "/start" 322 | headers := getHeaders 323 | headers["Authorization"] = "Bearer " + token 324 | headers["content-length"] = "0" 325 | headers["priority"] = "u=1, i" 326 | headers["sec-ch-ua"] = `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"` 327 | headers["sec-ch-ua-mobile"] = "?0" 328 | headers["sec-ch-ua-platform"] = `"Windows"` 329 | headers["sec-fetch-dest"] = "empty" 330 | headers["sec-fetch-mode"] = "cors" 331 | headers["sec-fetch-site"] = "same-site" 332 | 333 | req, err := http.NewRequest("POST", url, nil) 334 | if err != nil { 335 | log.Printf("failed to send request: %v\n", err) 336 | } 337 | 338 | for key, val := range headers { 339 | req.Header.Set(key, val) 340 | } 341 | 342 | client := &http.Client{Timeout: 5 * time.Second} 343 | resp, err := client.Do(req) 344 | if err != nil { 345 | log.Printf("failed to send request: %v\n", err) 346 | } 347 | defer resp.Body.Close() 348 | 349 | if resp.StatusCode == http.StatusOK { 350 | fmt.Printf("[Task Started] %s\n", taskTitle) 351 | } else { 352 | fmt.Printf("[Staring Task Failed] %s\n", taskTitle) 353 | } 354 | 355 | } 356 | 357 | func claimTask(token, taskID, taskTitle string) { 358 | url := "https://game-domain.blum.codes/api/v1/tasks/" + taskID + "/claim" 359 | headers := getHeaders 360 | headers["Authorization"] = "Bearer " + token 361 | headers["content-length"] = "0" 362 | headers["priority"] = "u=1, i" 363 | headers["sec-ch-ua"] = `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"` 364 | headers["sec-ch-ua-mobile"] = "?0" 365 | headers["sec-ch-ua-platform"] = `"Windows"` 366 | headers["sec-fetch-dest"] = "empty" 367 | headers["sec-fetch-mode"] = "cors" 368 | headers["sec-fetch-site"] = "same-site" 369 | 370 | req, err := http.NewRequest("POST", url, nil) 371 | if err != nil { 372 | log.Printf("failed to send request: %v\n", err) 373 | } 374 | 375 | for key, val := range headers { 376 | req.Header.Set(key, val) 377 | } 378 | 379 | client := &http.Client{Timeout: 5 * time.Second} 380 | resp, err := client.Do(req) 381 | if err != nil { 382 | log.Printf("failed to send request: %v\n", err) 383 | } 384 | defer resp.Body.Close() 385 | 386 | if resp.StatusCode == http.StatusOK { 387 | fmt.Printf("[Task Claimed] %s\n", taskTitle) 388 | } else { 389 | fmt.Printf("[Claim Task Failed] %s\n", taskTitle) 390 | } 391 | 392 | } 393 | 394 | func PlayGame(token string) (map[string]any, error) { 395 | url := "https://game-domain.blum.codes/api/v1/game/play" 396 | headers := getHeaders 397 | headers["Authorization"] = "Bearer " + token 398 | headers["content-length"] = "0" 399 | 400 | req, err := http.NewRequest("POST", url, nil) 401 | if err != nil { 402 | return nil, fmt.Errorf("failed to create request: %w", err) 403 | } 404 | 405 | for key, val := range headers { 406 | req.Header.Set(key, val) 407 | } 408 | 409 | client := &http.Client{Timeout: 10 * time.Second} 410 | resp, err := client.Do(req) 411 | if err != nil { 412 | return nil, fmt.Errorf("failed to play the game due to connection problems: %w", err) 413 | } 414 | defer resp.Body.Close() 415 | 416 | var result map[string]any 417 | if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { 418 | return nil, fmt.Errorf("failed to decode response: %w", err) 419 | } 420 | 421 | return result, nil 422 | 423 | } 424 | 425 | func claimGameRequest(token, gameID string, points int) (*http.Response, error) { 426 | url := "https://game-domain.blum.codes/api/v1/game/claim" 427 | headers := map[string]string{ 428 | "accept": "application/json, text/plain, */*", 429 | "accept-language": "en-US,en;q=0.9", 430 | "authorization": "Bearer " + token, 431 | "content-type": "application/json", 432 | "origin": "https://telegram.blum.codes", 433 | "priority": "u=1, i", 434 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0", 435 | } 436 | 437 | payload := types.GameClaimRequest{ 438 | GameID: gameID, 439 | Points: points, 440 | } 441 | 442 | jsonData, err := json.Marshal(payload) 443 | if err != nil { 444 | return nil, fmt.Errorf("failed to marshal JSON: %w", err) 445 | } 446 | 447 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) 448 | if err != nil { 449 | return nil, fmt.Errorf("failed to create request: %w", err) 450 | } 451 | 452 | for key, value := range headers { 453 | req.Header.Set(key, value) 454 | } 455 | 456 | client := &http.Client{ 457 | Timeout: 10 * time.Second, 458 | } 459 | resp, err := client.Do(req) 460 | if err != nil { 461 | return nil, fmt.Errorf("failed to claim game rewards due to connection problems: %w", err) 462 | } 463 | 464 | return resp, nil 465 | 466 | } 467 | 468 | func ClaimGame(token, gameID, queryID string, points int) { 469 | for { 470 | resp, err := claimGameRequest(token, gameID, points) 471 | if err != nil { 472 | fmt.Printf("Failed to claim game, try again: %v\n", err) 473 | return 474 | } 475 | 476 | defer resp.Body.Close() 477 | body, err := io.ReadAll(resp.Body) 478 | if err != nil { 479 | fmt.Printf("Failed to read response: %v\n", err) 480 | return 481 | } 482 | 483 | bodyString := string(body) 484 | 485 | if bodyString == `{"message":"game session not finished"}` { 486 | fmt.Println("Playing drop game...") 487 | time.Sleep(1 * time.Second) 488 | continue 489 | } else if bodyString == `{"message":"game session not found"}` { 490 | fmt.Println("The game is over") 491 | break 492 | } else { 493 | var response map[string]interface{} 494 | if err := json.Unmarshal(body, &response); err == nil { 495 | if response["message"] == "Token is invalid" { 496 | fmt.Println("Invalid token, get new token...") 497 | token, _ = GetNewToken(queryID) 498 | continue 499 | } 500 | } 501 | fmt.Printf("Game finished: %s\n", bodyString) 502 | break 503 | } 504 | } 505 | } 506 | 507 | func CheckBalanceFriend(token string) (types.FriendsBalance, error) { 508 | url := "https://gateway.blum.codes/v1/friends/balance" 509 | headers := map[string]string{ 510 | "Authorization": "Bearer " + token, 511 | "accept": "application/json, text/plain, */*", 512 | "accept-language": "en-US,en;q=0.9", 513 | "origin": "https://telegram.blum.codes", 514 | "priority": "u=1, i", 515 | "sec-ch-ua": `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"`, 516 | "sec-ch-ua-mobile": "?0", 517 | "sec-ch-ua-platform": `"Windows"`, 518 | "sec-fetch-dest": "empty", 519 | "sec-fetch-mode": "cors", 520 | "sec-fetch-site": "same-site", 521 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0", 522 | } 523 | 524 | client := &http.Client{Timeout: 10 * time.Second} 525 | req, err := http.NewRequest("GET", url, nil) 526 | if err != nil { 527 | return types.FriendsBalance{}, fmt.Errorf("failed to create request: %w", err) 528 | } 529 | 530 | for key, value := range headers { 531 | req.Header.Set(key, value) 532 | } 533 | 534 | resp, err := client.Do(req) 535 | if err != nil { 536 | return types.FriendsBalance{}, fmt.Errorf("failed to get friend's balance: %w", err) 537 | } 538 | defer resp.Body.Close() 539 | 540 | if resp.StatusCode != http.StatusOK { 541 | return types.FriendsBalance{}, fmt.Errorf("unexpected status code: %d", resp.StatusCode) 542 | } 543 | 544 | var friendsBalance types.FriendsBalance 545 | if err := json.NewDecoder(resp.Body).Decode(&friendsBalance); err != nil { 546 | return types.FriendsBalance{}, fmt.Errorf("failed to decode response: %w", err) 547 | } 548 | 549 | return friendsBalance, nil 550 | } 551 | 552 | func ClaimBalanceFriend(token string) (bool, error) { 553 | url := "https://gateway.blum.codes/v1/friends/claim" 554 | headers := map[string]string{ 555 | "Authorization": "Bearer " + token, 556 | "accept": "application/json, text/plain, */*", 557 | "accept-language": "en-US,en;q=0.9", 558 | "content-length": "0", 559 | "origin": "https://telegram.blum.codes", 560 | "priority": "u=1, i", 561 | "sec-ch-ua": `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"`, 562 | "sec-ch-ua-mobile": "?0", 563 | "sec-ch-ua-platform": `"Windows"`, 564 | "sec-fetch-dest": "empty", 565 | "sec-fetch-mode": "cors", 566 | "sec-fetch-site": "same-site", 567 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0", 568 | } 569 | 570 | req, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte{})) 571 | if err != nil { 572 | return false, fmt.Errorf("failed to create request: %w", err) 573 | } 574 | 575 | for key, value := range headers { 576 | req.Header.Set(key, value) 577 | } 578 | 579 | client := &http.Client{Timeout: 10 * time.Second} 580 | resp, err := client.Do(req) 581 | if err != nil { 582 | return false, fmt.Errorf("failed to claim friend's balance: %w", err) 583 | } 584 | defer resp.Body.Close() 585 | 586 | if resp.StatusCode != http.StatusOK { 587 | return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode) 588 | } 589 | 590 | //var claimFriendBalance map[string]any 591 | //if err := json.NewDecoder(resp.Body).Decode(&claimFriendBalance); err != nil { 592 | // return false, fmt.Errorf("failed to decode response: %w", err) 593 | //} 594 | 595 | return true, nil 596 | } 597 | 598 | func ClaimFarm(token string) (bool, error) { 599 | url := "https://game-domain.blum.codes/api/v1/farming/claim" 600 | headers := getHeaders 601 | headers["Authorization"] = "Bearer " + token 602 | 603 | req, err := http.NewRequest("POST", url, nil) 604 | if err != nil { 605 | return false, fmt.Errorf("failed to create request: %w", err) 606 | } 607 | 608 | for key, value := range headers { 609 | req.Header.Set(key, value) 610 | } 611 | 612 | client := &http.Client{Timeout: 10 * time.Second} 613 | resp, err := client.Do(req) 614 | if err != nil { 615 | return false, fmt.Errorf("failed to claim farm: %w", err) 616 | } 617 | defer resp.Body.Close() 618 | 619 | if resp.StatusCode != http.StatusOK { 620 | return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode) 621 | } 622 | 623 | return true, nil 624 | } 625 | 626 | func StartFarm(token string) (bool, error) { 627 | url := "https://game-domain.blum.codes/api/v1/farming/start" 628 | headers := getHeaders 629 | headers["Authorization"] = "Bearer " + token 630 | 631 | req, err := http.NewRequest("POST", url, nil) 632 | if err != nil { 633 | return false, fmt.Errorf("failed to create request: %w", err) 634 | } 635 | 636 | for key, value := range headers { 637 | req.Header.Set(key, value) 638 | } 639 | 640 | client := &http.Client{Timeout: 10 * time.Second} 641 | resp, err := client.Do(req) 642 | if err != nil { 643 | return false, fmt.Errorf("failed to start farm: %w", err) 644 | } 645 | defer resp.Body.Close() 646 | 647 | if resp.StatusCode != http.StatusOK { 648 | return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode) 649 | } 650 | 651 | return true, nil 652 | } 653 | 654 | func GetDailyRewards(token string) (bool, error) { 655 | url := "https://game-domain.blum.codes/api/v1/daily-reward" 656 | headers := map[string]string{ 657 | "accept": "application/json, text/plain, */*", 658 | "accept-language": "en-US,en;q=0.9", 659 | "origin": "https://telegram.blum.codes", 660 | "priority": "u=1, i", 661 | "sec-ch-ua": `"Microsoft Edge";v="125", "Chromium";v="125", "Not.A/Brand";v="24", "Microsoft Edge WebView2";v="125"`, 662 | "sec-ch-ua-mobile": "?0", 663 | "sec-ch-ua-platform": `"Windows"`, 664 | "sec-fetch-dest": "empty", 665 | "sec-fetch-mode": "cors", 666 | "sec-fetch-site": "same-site", 667 | "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36 Edg/125.0.0.0", 668 | } 669 | headers["Authorization"] = "Bearer " + token 670 | 671 | req, err := http.NewRequest("GET", url, nil) 672 | if err != nil { 673 | return false, fmt.Errorf("failed to create request: %w", err) 674 | } 675 | for key, value := range headers { 676 | req.Header.Set(key, value) 677 | } 678 | client := &http.Client{Timeout: 10 * time.Second} 679 | resp, err := client.Do(req) 680 | if err != nil { 681 | return false, fmt.Errorf("failed to make the request: %w", err) 682 | } 683 | defer resp.Body.Close() 684 | 685 | if resp.StatusCode == http.StatusOK { 686 | 687 | req, err := http.NewRequest("POST", url, nil) 688 | if err != nil { 689 | return false, fmt.Errorf("failed to create request: %w", err) 690 | } 691 | for key, value := range headers { 692 | req.Header.Set(key, value) 693 | } 694 | client := &http.Client{Timeout: 10 * time.Second} 695 | resp, err := client.Do(req) 696 | if err != nil { 697 | return false, fmt.Errorf("failed to claim reward: %w", err) 698 | } 699 | defer resp.Body.Close() 700 | 701 | if resp.StatusCode == http.StatusOK { 702 | return true, nil 703 | } 704 | 705 | } 706 | return false, nil 707 | 708 | } 709 | -------------------------------------------------------------------------------- /types/types.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | type UserBalance struct { 4 | AvailableBalance string `json:"availableBalance"` 5 | PlayPasses int `json:"playPasses"` 6 | Timestamp int64 `json:"timestamp"` 7 | Farming struct { 8 | StartTime int64 `json:"startTime"` 9 | EndTime int64 `json:"endTime"` 10 | EarningsRate string `json:"earningsRate"` 11 | Balance string `json:"balance"` 12 | } `json:"farming"` 13 | } 14 | 15 | // FriendsBalance represents the response from the '/v1/friends/balance' endpoint. 16 | type FriendsBalance struct { 17 | LimitInvitation string `json:"limitInvitation"` 18 | UsedInvitation string `json:"usedInvitation"` 19 | AmountForClaim string `json:"amountForClaim"` 20 | ReferralToken string `json:"referralToken"` 21 | PercentFromFriends int `json:"percentFromFriends"` 22 | PercentFromFriendsOfFriends float64 `json:"percentFromFriendsOfFriends"` 23 | CanClaim bool `json:"canClaim"` 24 | CanClaimAt string `json:"canClaimAt"` 25 | } 26 | 27 | type RequestBody struct { 28 | Query string `json:"query"` 29 | } 30 | 31 | type ResponseBody struct { 32 | Token struct { 33 | Refresh string `json:"refresh"` 34 | } `json:"token"` 35 | } 36 | 37 | type TokenResponseBody struct { 38 | Message string `json:"message"` 39 | ResponseBody 40 | } 41 | 42 | type GameClaimRequest struct { 43 | GameID string `json:"gameId"` 44 | Points int `json:"points"` 45 | } 46 | -------------------------------------------------------------------------------- /utils/utils.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "os" 9 | "os/exec" 10 | "runtime" 11 | "strings" 12 | "time" 13 | 14 | "github.com/fatih/color" 15 | ) 16 | 17 | var ( 18 | yellow = color.New(color.FgYellow).SprintFunc() 19 | ) 20 | 21 | func ParseArgs() (bool, bool) { 22 | tasks := flag.String("task", "", "Check and Claim Task (Y/n)") 23 | reffs := flag.String("reff", "", "Do you want to claim referrals? (Y/n)") 24 | flag.Parse() 25 | 26 | var tasksEnable bool 27 | var reffsEnable bool 28 | 29 | if *tasks == "" { 30 | fmt.Print("Do you want to check and claim tasks? (Y/n): ") 31 | var taskInput string 32 | fmt.Scanln(&taskInput) 33 | taskInput = strings.TrimSpace(strings.ToLower(taskInput)) 34 | 35 | switch taskInput { 36 | case "y": 37 | tasksEnable = true 38 | case "n": 39 | tasksEnable = false 40 | default: 41 | tasksEnable = true 42 | } 43 | } 44 | 45 | if *reffs == "" { 46 | fmt.Print("Do you want to claim Referrals? (Y/n): ") 47 | var reffInput string 48 | fmt.Scanln(&reffInput) 49 | reffInput = strings.TrimSpace(strings.ToLower(reffInput)) 50 | 51 | switch reffInput { 52 | case "y": 53 | reffsEnable = true 54 | case "no": 55 | reffsEnable = false 56 | default: 57 | reffsEnable = true 58 | } 59 | } 60 | 61 | return tasksEnable, reffsEnable 62 | } 63 | 64 | func PrintLogo() { 65 | 66 | fmt.Printf(yellow(" ____ _ _ _ __ __ ____ ___ _____ \n| __ )| | | | | | \\/ | __ ) / _ \\_ _|\n| _ \\| | | | | | |\\/| | _ \\| | | || | \n| |_) | |__| |_| | | | | |_) | |_| || | \n|____/|_____\\___/|_| |_|____/ \\___/ |_| \n")) 67 | } 68 | 69 | func ClearScreen() { 70 | var cmd *exec.Cmd 71 | switch runtime.GOOS { 72 | case "linux", "darwin": 73 | cmd = exec.Command("clear") 74 | case "windows": 75 | cmd = exec.Command("cmd", "/c", "cls") 76 | default: 77 | return 78 | } 79 | cmd.Stdout = os.Stdout 80 | cmd.Run() 81 | } 82 | 83 | func loadListFile(fileName string) ([]string, error) { 84 | file, err := os.Open(fileName) 85 | if err != nil { 86 | return nil, err 87 | } 88 | defer file.Close() 89 | 90 | var parseList []string 91 | scanner := bufio.NewScanner(file) 92 | for scanner.Scan() { 93 | if scanner.Text() != "" { 94 | parseList = append(parseList, strings.TrimSpace(scanner.Text())) 95 | } 96 | } 97 | 98 | if err := scanner.Err(); err != nil { 99 | return nil, err 100 | } 101 | 102 | if len(parseList) < 1 { 103 | return nil, errors.New(fmt.Sprintf("\"%v\" is empty!", fileName)) 104 | } 105 | 106 | return parseList, nil 107 | 108 | } 109 | 110 | func ParseQueries() ([]string, error) { 111 | return loadListFile("./configs/query_list.conf") 112 | } 113 | 114 | func FormatUpTime(d time.Duration) string { 115 | totalSeconds := int(d.Seconds()) 116 | 117 | hours := totalSeconds / 3600 118 | minutes := (totalSeconds % 3600) / 60 119 | // seconds := totalSeconds % 60 120 | 121 | return fmt.Sprintf("%dh%dm", hours, minutes) 122 | } 123 | 124 | func TimeLeft(futureTimestamp int64) (string, error) { 125 | 126 | seconds := futureTimestamp / 1000 127 | nanoseconds := (futureTimestamp % 1000) * 1e6 128 | 129 | t := time.Unix(seconds, nanoseconds) 130 | 131 | currentTime := time.Now() 132 | 133 | duration := t.Sub(currentTime) 134 | 135 | if duration < 0 { 136 | return "00:00:00", nil 137 | } 138 | 139 | hours := int(duration.Hours()) 140 | minutes := int(duration.Minutes()) % 60 141 | seconds = int64(duration.Seconds()) % 60 142 | 143 | return fmt.Sprintf("%02d:%02d:%02d", hours, minutes, seconds), nil 144 | } 145 | --------------------------------------------------------------------------------