├── .vscode └── launch.json ├── LICENSE ├── README.md ├── followbot └── followbot.go ├── go.mod ├── go.sum ├── main.go ├── results ├── accounts.txt ├── tokens.txt └── userpass.txt └── shared ├── config.go ├── structs.go └── utils.go /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch Program", 6 | "type": "go", 7 | "request": "launch", 8 | "mode": "auto", 9 | "program": "${workspaceFolder}/main.go", 10 | "console": "externalTerminal", 11 | "cwd": "${workspaceFolder}" 12 | }, 13 | { 14 | "name": "Launch FollowBot", 15 | "type": "go", 16 | "request": "launch", 17 | "mode": "auto", 18 | "program": "${workspaceFolder}/followbot/followbot.go", 19 | "console": "externalTerminal", 20 | "cwd": "${workspaceFolder}" 21 | } 22 | ] 23 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Gui 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Not working since 20 Jan 2025 ❌ 3 |

4 | 5 |

6 | 1secMail service stopped working this month. Currently searching for a reliable alternative temporary email provider. 7 |

8 | 9 | 10 |

11 | 🟣 Twitch Accounts 🟣 12 |

13 | 14 | 15 |

16 | A Golang bot for creating Twitch accounts 17 |

18 | 19 | Twitch Accounts provides a script for creating Twitch accounts and verify them using temporary email addresses. This 20 | project can be useful for various purposes, such as testing or automation 21 | 22 |

23 | 🚀 Recent Enhancements 🚀 24 |

25 | 26 |

27 | 🤖 Follow bot functionality has been added! 🎉 28 |

29 | 30 |
31 | 32 | **⭐ If you found this project helpful, illuminate it with your support by dropping a brilliant star! 🌟** 33 | 34 | ## :fire: Features 35 | 36 | ✔ Create accounts on TwitchTV 37 | ✔ Verify account email 38 | ✔ Captcha Validation 39 | ✔ Proxy Support 40 | ✔ Follow bot 41 | ✔ Simple and easy-to-use script 42 | 43 | ## ⚠️ Warning 44 | 45 | ❌ The follow-bot feature is currently **not working**. (since 18 Nov 2024) 46 | 47 | --- 48 | 49 | ## ⚙️・How to setup Twitch Accounts? 50 | ```sh-session 51 | - Basic setup 52 | > Clone this repository 53 | > Create an account on https://salamoonder.com/ then add some credits and set up Captcha Api Key on your config.go file 54 | > If you want to use proxy, then setup it in config.go file as well, otherwise just let the default value or "" - also, make sure that your proxy service isn't blocking access to Twitch. 55 | Personally I recommend using ProxyLogic provider: https://dash.proxylogic.org 56 | 57 | - How to run 58 | > Make sure you have Golang installed on your machine, then run the following command in the project root: 'go run main.go' (without the single quotes) 59 | > To run the follow-bot feature, use the following command: 'go run followbot/followbot.go' (without the single quotes) 60 | ``` 61 | 62 | ## 🎉・Next Steps/Enhancements 63 | 64 | - ~~Follow bot~~ Done! 65 | - ~~Code cleanup~~ Done! 66 | - ~~Proxy configuration~~ Done! 67 | 68 | ## 📄・License 69 | 70 | This project is licensed under the GPL General Public License v3.0 License - see the [LICENSE.md](./LICENSE) file for details 71 | ```js 72 | ・Educational purpose only and all your consequences caused by you actions is your responsibility 73 | ・Selling this Free gen is forbidden 74 | ・If you make a copy of this/or fork it, it must be open-source and have credits linking to this repo 75 | ``` 76 | 77 | ## ⭐・Contributing 78 | Contributions are welcome! If you have any ideas, suggestions, or improvements, feel free to open an issue or create a pull request. 79 | 80 | ## ❗・Notice 81 | Remember, automations are against Twitch rules, do not abuse this project. I've created this tool out of genuine interest and released it for wider use. Let's keep it positive and avoid any misuse to maintain a healthy environment on Twitch. 82 | 83 | ## 💭・ChangeLog 84 | ```diff 85 | v0.0.6 ⋮ 04 jan 2025 86 | + Fixed email verification problem 87 | + Change email verification provider from mailnator to 1secMail 88 | - Removed support for gmail domains 89 | 90 | 91 | v0.0.5 ⋮ 18 nov 2024 92 | + Fixed 'kasada taking too long' error 93 | + Updated bot account creation flow to accommodate Twitch adjustments 94 | - Follow-bot not working 95 | 96 | v0.0.4 ⋮ 13 may 2024 97 | + Added follow-bot 98 | 99 | v0.0.3 ⋮ 11 may 2024 100 | + Added proxy support to all requests 101 | 102 | v0.0.2 ⋮ 09 may 2024 103 | + Code cleanup 104 | + Enhanced error handling 105 | + Improved status logging 106 | 107 | v0.0.1 ⋮ 07 may 2024 108 | + Added main script (creating accounts + email verification (gmail)) 109 | ``` 110 | --- 111 | 112 |

113 | All the registered accounts information is going to be stored at results/accounts.txt 114 |

115 | 116 | 117 | ## Author 118 | Authored by: gui-fkb [Github](https://github.com/gui-fkb) 119 | 120 | ## Credits 121 | This project design was based on 'twitch-account-creator' NodeJS repo by masterking32 [Github](https://github.com/masterking32). Since the project wasn't being maintained and the email verification feature proposed in the original repository had stopped working, I decided to add some of my own twist to it. 122 | 123 | -------------------------------------------------------------------------------- /followbot/followbot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "io/ioutil" 11 | "log" 12 | "net/http" 13 | "net/url" 14 | "os" 15 | "strings" 16 | "sync" 17 | "time" 18 | "twitch-accounts/shared" 19 | ) 20 | 21 | var friendRequestsSent = 0 22 | var tokensFile string = "./results/tokens.txt" 23 | 24 | func main() { 25 | fmt.Println("twitch-accounts by gui-fkb - https://github.com/gui-fkb") 26 | 27 | if shared.Config.CapSolverKey == "your_captcha_key" { 28 | log.Fatal("It looks like your captcha solver API token isn't configured yet. Change it in the shared.Config.go file and run again.") 29 | } 30 | 31 | var username string 32 | fmt.Println("Enter the username you want to follow: ") 33 | //username = "guirerume_" // Take this opportunity to follow me :) 34 | fmt.Scanln(&username) 35 | 36 | userId, err := getUserId(username) 37 | if err != nil { 38 | log.Fatal(err) 39 | } 40 | 41 | if userId == "" { 42 | log.Fatal("User not found. Program exited.") 43 | } else { 44 | fmt.Println("User found with ID: " + userId) 45 | } 46 | 47 | tokens := getTokenList() 48 | 49 | var wg sync.WaitGroup 50 | sem := make(chan bool, 5) // Limit to 5 concurrent goroutines 51 | 52 | for _, oauthToken := range tokens { 53 | wg.Add(1) 54 | go func(oauthToken string) { 55 | oauthToken = strings.TrimSpace(oauthToken) 56 | sem <- true // Will block if there is already 5 goroutines running 57 | defer func() { 58 | <-sem // Release the slot 59 | wg.Done() 60 | }() 61 | 62 | fmt.Println("Using token: " + oauthToken) 63 | followTwitchUser(userId, oauthToken) 64 | }(oauthToken) 65 | } 66 | 67 | wg.Wait() 68 | close(sem) 69 | 70 | fmt.Println("All follow requests sent.") 71 | } 72 | 73 | func getTokenList() []string { 74 | file, err := os.Open(tokensFile) 75 | if err != nil { 76 | fmt.Println(err) 77 | return make([]string, 0) 78 | } 79 | defer file.Close() 80 | 81 | var tokens []string 82 | scanner := bufio.NewScanner(file) 83 | for scanner.Scan() { 84 | tokens = append(tokens, scanner.Text()) 85 | } 86 | 87 | if err := scanner.Err(); err != nil { 88 | fmt.Println(err) 89 | return make([]string, 0) 90 | } 91 | 92 | return tokens 93 | } 94 | 95 | func followTwitchUser(userid string, oauth string) { 96 | fmt.Println("Getting twitch cookies.") 97 | cookies, err := getTwitchCookies() 98 | if err != nil { 99 | fmt.Println(err, "\n account creation exited") 100 | return 101 | } 102 | 103 | for i := 0; i < 3; i++ { 104 | fmt.Println("Following attempt", i+1, " of ", 3) 105 | 106 | fmt.Println("Getting kasada code") 107 | taskResponse, err := kasadaResolver() 108 | if err != nil { 109 | fmt.Println(err, "\n account creation exited") 110 | return 111 | } 112 | 113 | clientSessionId := shared.GenerateRandomID(16) 114 | xDeviceId := cookies["unique_id"] 115 | clientVersion := "3040e141-5964-4d72-b67d-e73c1cf355b5" 116 | clientRequestId := shared.GenerateRandomID(32) 117 | 118 | fmt.Println("Getting public integrity token...") 119 | publicIntegrityData, err := publicIntegrityGetToken(xDeviceId, clientRequestId, clientSessionId, clientVersion, taskResponse.Solution["x-kpsdk-ct"], taskResponse.Solution["x-kpsdk-cd"], oauth, taskResponse.Solution["user-agent"]) 120 | if err != nil { 121 | fmt.Println(err, "\n error getting public integrity token - account creation exited") 122 | continue 123 | } 124 | 125 | jsonResp := startFollowRequest(userid, oauth, xDeviceId, clientVersion, clientSessionId, publicIntegrityData.Token, taskResponse.Solution["user-agent"]) 126 | if strings.Contains(jsonResp, "displayName") { 127 | friendRequestsSent++ 128 | fmt.Println("Follow request sent succesfully. ", " - friend requests sent in total:", friendRequestsSent) 129 | break 130 | } 131 | } 132 | } 133 | 134 | func startFollowRequest(userId string, oauth string, XDeviceId string, ClientVersion string, ClientSessionId string, ClientIntegrity string, UserAgent string) string { 135 | query := []shared.TwitchOperationQuery{ 136 | { 137 | OperationName: "FollowButton_FollowUser", 138 | Variables: map[string]interface{}{ 139 | "input": map[string]interface{}{ 140 | "disableNotifications": false, 141 | "targetID": userId, 142 | }, 143 | }, 144 | Extensions: map[string]interface{}{ 145 | "persistedQuery": map[string]interface{}{ 146 | "version": 1, 147 | "sha256Hash": "800e7346bdf7e5278a3c1d3f21b2b56e2639928f86815677a7126b093b2fdd08", 148 | }, 149 | }, 150 | }, 151 | } 152 | 153 | queryBytes, _ := json.Marshal(query) 154 | 155 | req, _ := http.NewRequest("POST", "https://gql.twitch.tv/gql#origin=twilight", bytes.NewBuffer(queryBytes)) 156 | req.Header.Set("User-Agent", UserAgent) 157 | req.Header.Set("Accept", "application/json") 158 | req.Header.Set("Accept-Language", "en-US") 159 | req.Header.Set("Accept-Encoding", "identity") 160 | req.Header.Set("Referer", "https://www.twitch.tv/") 161 | req.Header.Set("Client-Id", shared.Config.TwitchClientID) 162 | req.Header.Set("X-Device-Id", XDeviceId) 163 | req.Header.Set("Client-Version", ClientVersion) 164 | req.Header.Set("Client-Session", ClientSessionId) 165 | req.Header.Set("Authorization", "OAuth "+oauth) 166 | req.Header.Set("Client-Integrity", ClientIntegrity) 167 | req.Header.Set("Content-Type", "text/plain;charset=UTF-8") 168 | req.Header.Set("Origin", "https://www.twitch.tv") 169 | req.Header.Set("DNT", "1") 170 | req.Header.Set("Connection", "keep-alive") 171 | req.Header.Set("Sec-Fetch-Dest", "empty") 172 | req.Header.Set("Sec-Fetch-Mode", "cors") 173 | req.Header.Set("Sec-Fetch-Site", "same-site") 174 | 175 | client := &http.Client{} 176 | resp, err := client.Do(req) 177 | if err != nil { 178 | fmt.Println(err) 179 | return "" 180 | } 181 | defer resp.Body.Close() 182 | 183 | body, _ := ioutil.ReadAll(resp.Body) 184 | 185 | return string(body) 186 | } 187 | 188 | func getUserId(username string) (string, error) { 189 | query := shared.TwitchOperationQuery{ 190 | OperationName: "ChannelShell", 191 | Variables: map[string]interface{}{"login": username}, 192 | Extensions: map[string]interface{}{ 193 | "persistedQuery": map[string]interface{}{ 194 | "version": 1, 195 | "sha256Hash": "580ab410bcd0c1ad194224957ae2241e5d252b2c5173d8e0cce9d32d5bb14efe", 196 | }, 197 | }, 198 | } 199 | 200 | queryBytes, _ := json.Marshal(query) 201 | req, _ := http.NewRequest("POST", "https://gql.twitch.tv/gql", bytes.NewBuffer(queryBytes)) 202 | req.Header.Set("Client-ID", shared.Config.TwitchClientID) 203 | 204 | client := &http.Client{} 205 | resp, err := client.Do(req) 206 | if err != nil { 207 | return "", err 208 | } 209 | defer resp.Body.Close() 210 | 211 | body, _ := ioutil.ReadAll(resp.Body) 212 | 213 | var result map[string]interface{} 214 | json.Unmarshal(body, &result) 215 | 216 | data := result["data"].(map[string]interface{}) 217 | userOrError := data["userOrError"].(map[string]interface{}) 218 | 219 | if userOrError != nil && userOrError["id"] != nil { 220 | return userOrError["id"].(string), nil 221 | } else { 222 | return "", nil 223 | } 224 | } 225 | 226 | func kasadaResolver() (*shared.ResultTaskResponse, error) { 227 | taskResponse, err := createKasadaTask() 228 | if err != nil { 229 | return nil, err 230 | } 231 | 232 | maxAttemps := 12 233 | for i := 0; i < maxAttemps; i++ { 234 | time.Sleep(time.Millisecond * 400) 235 | 236 | taskResult, err := getTaskResult(taskResponse.TaskId) 237 | if err != nil { 238 | return nil, err 239 | } 240 | 241 | if taskResult.Status == "ready" { 242 | return taskResult, nil 243 | } 244 | } 245 | 246 | return nil, errors.New("kasada task took too long to resolve") 247 | } 248 | 249 | func createKasadaTask() (*shared.CreateTaskResponse, error) { 250 | // There is not the need to use proxy here, because the kasada task is not being blocked by the server 251 | 252 | requestBody := shared.CreateKasadaTask{ 253 | ApiKey: shared.Config.CapSolverKey, 254 | Task: shared.Task{ 255 | Type: "KasadaCaptchaSolver", 256 | Pjs: "https://k.twitchcdn.net/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/p.js", 257 | CdOnly: false, 258 | }, 259 | } 260 | 261 | jsonBody, err := json.Marshal(requestBody) 262 | if err != nil { 263 | return nil, err 264 | } 265 | 266 | resp, err := http.Post("https://salamoonder.com/api/createTask", "application/json", bytes.NewBuffer(jsonBody)) 267 | if err != nil { 268 | return nil, err 269 | } 270 | defer resp.Body.Close() 271 | 272 | body, err := io.ReadAll(resp.Body) 273 | if err != nil { 274 | return nil, err 275 | } 276 | 277 | taskResp := &shared.CreateTaskResponse{} 278 | 279 | err = json.Unmarshal(body, taskResp) 280 | if err != nil { 281 | return nil, err 282 | } 283 | 284 | return taskResp, nil 285 | } 286 | 287 | func getTaskResult(taskId string) (*shared.ResultTaskResponse, error) { 288 | // There is not the need to use proxy here, because the kasada task is not being blocked by the server 289 | task := shared.GetTaskResult{TaskId: taskId} 290 | 291 | jsonBody, err := json.Marshal(task) 292 | if err != nil { 293 | return nil, err 294 | } 295 | 296 | resp, err := http.Post("https://salamoonder.com/api/getTaskResult", "application/json", bytes.NewBuffer(jsonBody)) 297 | if err != nil { 298 | return nil, err 299 | } 300 | 301 | defer resp.Body.Close() 302 | 303 | body, err := io.ReadAll(resp.Body) 304 | if err != nil { 305 | return nil, err 306 | } 307 | 308 | taskResponse := &shared.ResultTaskResponse{} 309 | 310 | err = json.Unmarshal(body, &taskResponse) 311 | if err != nil { 312 | return nil, err 313 | } 314 | 315 | return taskResponse, nil 316 | } 317 | 318 | func publicIntegrityGetToken(XDeviceId, ClientRequestId, ClientSessionId, ClientVersion, kpsdkct, kpsdkcd, accesstoken, current_useragent string) (publicIntegrity *shared.PublicIntegrityData, err error) { 319 | requestBody := []byte("{}") 320 | 321 | headers := map[string]string{ 322 | "User-Agent": current_useragent, 323 | "Accept": "application/json", 324 | "Accept-Language": "en-US", 325 | "Accept-Encoding": "identity", 326 | "Authorization": "OAuth " + accesstoken, 327 | "Referer": "https://www.twitch.tv/", 328 | "Client-Id": shared.Config.TwitchClientID, 329 | "X-Device-Id": XDeviceId, 330 | "Client-Request-Id": ClientRequestId, 331 | "Client-Session-Id": ClientSessionId, 332 | "Client-Version": ClientVersion, 333 | "x-kpsdk-ct": kpsdkct, 334 | "x-kpsdk-cd": kpsdkcd, 335 | "Origin": "https://www.twitch.tv", 336 | "DNT": "1", 337 | "Connection": "keep-alive", 338 | "Sec-Fetch-Dest": "empty", 339 | "Sec-Fetch-Mode": "cors", 340 | "Sec-Fetch-Site": "same-site", 341 | "Content-Length": "0", 342 | } 343 | 344 | req, err := http.NewRequest("POST", "https://gql.twitch.tv/integrity", bytes.NewBuffer(requestBody)) 345 | if err != nil { 346 | return nil, err 347 | } 348 | 349 | for key, value := range headers { 350 | req.Header.Set(key, value) 351 | } 352 | 353 | client := &http.Client{} 354 | var proxyURL *url.URL 355 | 356 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 357 | // Warning, if you are not using proxy, the requests can be blocked 358 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 359 | } else { 360 | var err error 361 | proxyURL, err = url.Parse(shared.Config.Proxy) 362 | if err != nil { 363 | return nil, err 364 | } 365 | 366 | client.Transport = &http.Transport{ 367 | Proxy: http.ProxyURL(proxyURL), 368 | } 369 | } 370 | 371 | resp, err := client.Do(req) 372 | if err != nil { 373 | return nil, err 374 | } 375 | defer resp.Body.Close() 376 | 377 | body, err := io.ReadAll(resp.Body) 378 | if err != nil { 379 | return nil, err 380 | } 381 | 382 | if resp.StatusCode != http.StatusOK { 383 | return nil, fmt.Errorf("unexpected response status code: %d", resp.StatusCode) 384 | } 385 | 386 | var cookiesReturn string 387 | 388 | for _, cookieData := range resp.Header["Set-Cookie"] { 389 | p1 := strings.Split(cookieData, ";")[0] 390 | cookiesReturn += cookiesReturn + p1 + "; " 391 | } 392 | 393 | tokenReturn := shared.Token{} 394 | err = json.Unmarshal(body, &tokenReturn) 395 | if err != nil { 396 | return nil, err 397 | } 398 | 399 | publicIntegrityData := &shared.PublicIntegrityData{ 400 | Cookies: cookiesReturn, 401 | Token: tokenReturn.Token, 402 | } 403 | 404 | return publicIntegrityData, nil 405 | } 406 | 407 | func getTwitchCookies() (map[string]string, error) { 408 | cookiesMap := make(map[string]string) 409 | httpClient := &http.Client{} 410 | var proxyURL *url.URL 411 | 412 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 413 | // Warning, if you are not using proxy, the requests can be blocked 414 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 415 | } else { 416 | var err error 417 | proxyURL, err = url.Parse(shared.Config.Proxy) 418 | if err != nil { 419 | return nil, err 420 | } 421 | 422 | httpClient.Transport = &http.Transport{ 423 | Proxy: http.ProxyURL(proxyURL), 424 | } 425 | } 426 | 427 | req, err := http.NewRequest("GET", "https://twitch.tv", nil) 428 | if err != nil { 429 | return nil, err 430 | } 431 | 432 | req.Header.Set("User-Agent", "current_useragent") 433 | req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") 434 | req.Header.Set("Accept-Language", "en-US,en;q=0.5") 435 | req.Header.Set("Accept-Encoding", "gzip, deflate, br") 436 | req.Header.Set("DNT", "1") 437 | req.Header.Set("Connection", "keep-alive") 438 | req.Header.Set("Upgrade-Insecure-Requests", "1") 439 | req.Header.Set("Sec-Fetch-Dest", "document") 440 | req.Header.Set("Sec-Fetch-Mode", "navigate") 441 | req.Header.Set("Sec-Fetch-Site", "none") 442 | req.Header.Set("Sec-Fetch-User", "?1") 443 | 444 | resp, err := httpClient.Do(req) 445 | if err != nil { 446 | return nil, err 447 | } 448 | defer resp.Body.Close() 449 | 450 | for _, cookieData := range resp.Header["Set-Cookie"] { 451 | cookie := strings.Split(cookieData, ";")[0] 452 | cookiesMap[strings.Split(cookie, "=")[0]] = strings.Split(cookie, "=")[1] 453 | } 454 | 455 | return cookiesMap, nil 456 | } 457 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module twitch-accounts 2 | 3 | go 1.22.2 4 | 5 | require ( 6 | github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e 7 | github.com/ox-y/GoGmailnator v0.0.0-20221007105726-efc90fa60848 8 | github.com/sethvargo/go-password v0.3.0 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e h1:XmA6L9IPRdUr28a+SK/oMchGgQy159wvzXA5tJ7l+40= 2 | github.com/goombaio/namegenerator v0.0.0-20181006234301-989e774b106e/go.mod h1:AFIo+02s+12CEg8Gzz9kzhCbmbq6JcKNrhHffCGA9z4= 3 | github.com/ox-y/GoGmailnator v0.0.0-20221007105726-efc90fa60848 h1:I5CTueWHirX0aZTX3qY49U2uuwwf/OFv30/MI49rDKQ= 4 | github.com/ox-y/GoGmailnator v0.0.0-20221007105726-efc90fa60848/go.mod h1:B/IZdt9W/ZLOQ8YeB7CB55J0trhJzyhWMYfxu6ABrTs= 5 | github.com/sethvargo/go-password v0.3.0 h1:OLFHZ91Z7NiNP3dnaPxLxCDXlb6TBuxFzMvv6bu+Ptw= 6 | github.com/sethvargo/go-password v0.3.0/go.mod h1:p6we8DZ0eyYXof9pon7Cqrw98N4KTaYiadDml1dUEEw= 7 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "io" 9 | "log" 10 | "math/rand" 11 | "net/http" 12 | "net/url" 13 | "os" 14 | "path/filepath" 15 | "strconv" 16 | "strings" 17 | "sync" 18 | "time" 19 | 20 | "twitch-accounts/shared" 21 | 22 | "github.com/goombaio/namegenerator" 23 | "github.com/ox-y/GoGmailnator" 24 | "github.com/sethvargo/go-password/password" 25 | ) 26 | 27 | var outputFile string = "./results/accounts.txt" 28 | var userPassFile string = "./results/userpass.txt" 29 | var tokensFile string = "./results/tokens.txt" 30 | 31 | func main() { 32 | fmt.Println("twitch-accounts by gui-fkb - https://github.com/gui-fkb") 33 | //shared.FastEmailTest() // Uncomment this line if you want to test the trash email in a fast way, dont forget to enable breakpoints inside the function 34 | 35 | if shared.Config.CapSolverKey == "your_captcha_key" { 36 | log.Fatal("It looks like your captcha solver API token isn't configured yet. Change it in the shared.Config.go file and run again.") 37 | } 38 | 39 | fmt.Println("Starting account creation...") 40 | fmt.Println("How many accounts do you want to create?") 41 | var quantity int 42 | _, err := fmt.Scanln(&quantity) 43 | if err != nil { 44 | fmt.Println("Invalid input. Please enter a number.") 45 | return 46 | } 47 | 48 | var wg sync.WaitGroup 49 | sem := make(chan bool, 4) // Limit to 4 concurrent goroutines 50 | 51 | for i := 0; i < quantity; i++ { 52 | wg.Add(1) 53 | go func(i int) { 54 | sem <- true // Will block if there is already 4 goroutines running 55 | defer func() { 56 | <-sem // Release the slot 57 | wg.Done() 58 | }() 59 | 60 | fmt.Printf("Creating account %d of %d\n", i+1, quantity) 61 | createNewAccount() 62 | }(i) 63 | } 64 | 65 | wg.Wait() 66 | close(sem) 67 | 68 | fmt.Println("Finished accounts creation.") 69 | } 70 | 71 | func createNewAccount() { 72 | randomUsername := getRandomUsername() + "_" + shared.GenerateRandomID(3) 73 | 74 | randomEmail := getEmail(randomUsername) 75 | 76 | registerPostData := generateRandomRegisterData(randomUsername, randomEmail) 77 | 78 | fmt.Println("Getting twitch cookies.") 79 | cookies, err := getTwitchCookies() 80 | if err != nil { 81 | fmt.Println(err, "\n account creation exited") 82 | return 83 | } 84 | 85 | fmt.Println("Getting kasada code") 86 | taskResponse, err := kasadaResolver() 87 | if err != nil { 88 | fmt.Println(err, "\n account creation exited") 89 | return 90 | } 91 | 92 | fmt.Println("Getting local integrity token") 93 | err = getIntegrityOption(taskResponse) 94 | if err != nil { 95 | fmt.Println(err, "\n account creation exited") 96 | return 97 | } 98 | 99 | integrityData, err := integrityGetToken(taskResponse, cookies) 100 | fmt.Printf("IntegrityToken: %v", integrityData.Token[:48]+"+"+strconv.FormatInt(int64(len(integrityData.Token)-48), 10)+"... \n") 101 | if err != nil { 102 | fmt.Println(err, "\n unable to register token - account creation exited") 103 | return 104 | } 105 | 106 | fmt.Println("Creating account...") 107 | registerPostData.IntegrityToken = integrityData.Token 108 | registerPostData.IsPasswordGuide = "nist" 109 | 110 | _, err = registerFinal(cookies, registerPostData, taskResponse.Solution["user-agent"]) 111 | if err != nil { 112 | var errorResponse shared.ErrorResponse 113 | err := json.Unmarshal([]byte(err.Error()), &errorResponse) 114 | if err != nil { 115 | fmt.Println("error parsing error response", err) 116 | return 117 | } 118 | 119 | if errorResponse.ErrorCode != 2026 { // If error = 2026 then it's all right to proceed 120 | fmt.Printf("Parsed error response: %+v\n", errorResponse) 121 | return 122 | } 123 | } 124 | 125 | fmt.Println("Waiting email verification ...") 126 | verifyCode, err := getVerificationCode(randomUsername) 127 | if err != nil { 128 | fmt.Println(err, "\n error getting verification code - account creation exited") 129 | return 130 | } 131 | 132 | maxRetries := 4 133 | var registerData *shared.AccountRegisterResponse 134 | 135 | for i := 0; i < maxRetries; i++ { 136 | fmt.Println("Registering account attempt ", i+1, " of ", maxRetries) 137 | 138 | fmt.Println("Getting kasada code") 139 | taskResponse, err = kasadaResolver() 140 | if err != nil { 141 | fmt.Println(err, "\n account creation exited") 142 | continue 143 | } 144 | 145 | fmt.Println("Getting local integrity token") 146 | err = getIntegrityOption(taskResponse) 147 | if err != nil { 148 | fmt.Println(err, "\n account creation exited") 149 | continue 150 | } 151 | 152 | integrityData, err = integrityGetToken(taskResponse, cookies) 153 | if err != nil { 154 | fmt.Println(err, "\n unable to register token - account creation exited") 155 | continue 156 | } 157 | fmt.Printf("IntegrityToken: %v", integrityData.Token[:48]+"+"+strconv.FormatInt(int64(len(integrityData.Token)-48), 10)+"... \n") 158 | 159 | registerPostData.IntegrityToken = integrityData.Token 160 | registerPostData.EmailVerificationCode = &verifyCode 161 | 162 | registerData, err = registerFinal(cookies, registerPostData, taskResponse.Solution["user-agent"]) 163 | if err != nil { 164 | var errorResponse shared.ErrorResponse 165 | err := json.Unmarshal([]byte(err.Error()), &errorResponse) 166 | if err != nil { 167 | fmt.Println("error parsing error response", err) 168 | return 169 | } 170 | 171 | if errorResponse.ErrorCode != 2013 { // ErrorCode 2013 means the email is already registered for too many accounts, so it need to give it up 172 | fmt.Println("Error response:", errorResponse.Error) 173 | break 174 | } 175 | } 176 | // If we reach here, it means the operation was successful 177 | break 178 | } 179 | 180 | if err != nil { 181 | fmt.Println("Failed to create account after", maxRetries, "attempts") 182 | return 183 | } 184 | 185 | fmt.Println("Account created!") 186 | fmt.Println("UserID:", registerData.UserId, "AccessToken:", registerData.AccessToken) 187 | 188 | err = saveAccountData(registerPostData, registerData.UserId, registerData.AccessToken) 189 | if err != nil { 190 | fmt.Println(err, "\n error saving account data - account creation exited") 191 | return 192 | } 193 | 194 | fmt.Println("Account verified and saved!") 195 | fmt.Println("Account is ready!") 196 | } 197 | 198 | func getRandomUsername() string { 199 | nameGenerator := namegenerator.NewNameGenerator(time.Now().UTC().UnixNano()) 200 | 201 | name := strings.Replace(nameGenerator.Generate(), "-", "", -1) 202 | return name 203 | } 204 | 205 | func getEmail(username string) string { // This function is not being used right now, but it can be useful in the future 206 | return fmt.Sprintf("%s@%s", username, shared.Config.EmailDomain) 207 | } 208 | 209 | func generateRandomRegisterData(uname string, email string) shared.RandomRegisterData { 210 | return shared.RandomRegisterData{ 211 | Username: uname, 212 | Password: getRandomPassword(), 213 | Birthday: generateRandomBirthday(), 214 | Email: email, 215 | ClientID: shared.Config.TwitchClientID, 216 | IntegrityToken: "", 217 | } 218 | } 219 | 220 | func getRandomPassword() string { 221 | res, err := password.Generate(32, 1, 1, false, false) 222 | if err != nil { 223 | log.Fatal(err) 224 | } 225 | 226 | return res 227 | } 228 | 229 | func generateRandomBirthday() shared.Birthday { 230 | return shared.Birthday{ 231 | Day: rand.Intn(25) + 1, 232 | Month: rand.Intn(12) + 1, 233 | Year: rand.Intn(30) + 1970, 234 | IsOver18: true, 235 | } 236 | } 237 | 238 | func getTwitchCookies() (map[string]string, error) { 239 | cookiesMap := make(map[string]string) 240 | httpClient := &http.Client{} 241 | var proxyURL *url.URL 242 | 243 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 244 | // Warning, if you are not using proxy, the requests can be blocked 245 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 246 | } else { 247 | var err error 248 | proxyURL, err = url.Parse(shared.Config.Proxy) 249 | if err != nil { 250 | return nil, err 251 | } 252 | 253 | httpClient.Transport = &http.Transport{ 254 | Proxy: http.ProxyURL(proxyURL), 255 | } 256 | } 257 | 258 | req, err := http.NewRequest("GET", "https://twitch.tv", nil) 259 | if err != nil { 260 | return nil, err 261 | } 262 | 263 | req.Header.Set("User-Agent", "current_useragent") 264 | req.Header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8") 265 | req.Header.Set("Accept-Language", "en-US,en;q=0.5") 266 | req.Header.Set("Accept-Encoding", "gzip, deflate, br") 267 | req.Header.Set("DNT", "1") 268 | req.Header.Set("Connection", "keep-alive") 269 | req.Header.Set("Upgrade-Insecure-Requests", "1") 270 | req.Header.Set("Sec-Fetch-Dest", "document") 271 | req.Header.Set("Sec-Fetch-Mode", "navigate") 272 | req.Header.Set("Sec-Fetch-Site", "none") 273 | req.Header.Set("Sec-Fetch-User", "?1") 274 | 275 | resp, err := httpClient.Do(req) 276 | if err != nil { 277 | return nil, err 278 | } 279 | defer resp.Body.Close() 280 | 281 | for _, cookieData := range resp.Header["Set-Cookie"] { 282 | cookie := strings.Split(cookieData, ";")[0] 283 | cookiesMap[strings.Split(cookie, "=")[0]] = strings.Split(cookie, "=")[1] 284 | } 285 | 286 | return cookiesMap, nil 287 | } 288 | 289 | func kasadaResolver() (*shared.ResultTaskResponse, error) { 290 | taskResponse, err := createKasadaTask() 291 | if err != nil { 292 | return nil, err 293 | } 294 | 295 | if taskResponse.ErrorCode > 0 { 296 | return nil, errors.New(taskResponse.ErrorDescription) 297 | } 298 | 299 | maxAttemps := 100 300 | for i := 0; i < maxAttemps; i++ { 301 | time.Sleep(time.Millisecond * 50) // Pooling every 50ms 302 | 303 | taskResult, err := getTaskResult(taskResponse.TaskId) 304 | if err != nil { 305 | return nil, err 306 | } 307 | 308 | if taskResult.Status == "ready" { 309 | return taskResult, nil 310 | } 311 | } 312 | 313 | return nil, errors.New("kasada task took too long to resolve") 314 | } 315 | 316 | func createKasadaTask() (*shared.CreateTaskResponse, error) { 317 | // There is not the need to use proxy here, because the kasada task is not being blocked by the server 318 | 319 | requestBody := shared.CreateKasadaTask{ 320 | ApiKey: shared.Config.CapSolverKey, 321 | Task: shared.Task{ 322 | Type: "KasadaCaptchaSolver", 323 | Pjs: "https://k.twitchcdn.net/149e9513-01fa-4fb0-aad4-566afd725d1b/2d206a39-8ed7-437e-a3be-862e0f06eea3/p.js", 324 | CdOnly: false, 325 | }, 326 | } 327 | 328 | jsonBody, err := json.Marshal(requestBody) 329 | if err != nil { 330 | return nil, err 331 | } 332 | 333 | resp, err := http.Post("https://salamoonder.com/api/createTask", "application/json", bytes.NewBuffer(jsonBody)) 334 | if err != nil { 335 | return nil, err 336 | } 337 | defer resp.Body.Close() 338 | 339 | body, err := io.ReadAll(resp.Body) 340 | if err != nil { 341 | return nil, err 342 | } 343 | 344 | taskResp := &shared.CreateTaskResponse{} 345 | 346 | err = json.Unmarshal(body, taskResp) 347 | if err != nil { 348 | return nil, err 349 | } 350 | 351 | return taskResp, nil 352 | } 353 | 354 | func getTaskResult(taskId string) (*shared.ResultTaskResponse, error) { 355 | // There is not the need to use proxy here, because the kasada task is not being blocked by the server 356 | task := shared.GetTaskResult{ 357 | ApiKey: shared.Config.CapSolverKey, 358 | TaskId: taskId, 359 | } 360 | 361 | jsonBody, err := json.Marshal(task) 362 | if err != nil { 363 | return nil, err 364 | } 365 | 366 | resp, err := http.Post("https://salamoonder.com/api/getTaskResult", "application/json", bytes.NewBuffer(jsonBody)) 367 | if err != nil { 368 | return nil, err 369 | } 370 | 371 | defer resp.Body.Close() 372 | 373 | body, err := io.ReadAll(resp.Body) 374 | if err != nil { 375 | return nil, err 376 | } 377 | 378 | taskResponse := &shared.ResultTaskResponse{} 379 | 380 | err = json.Unmarshal(body, &taskResponse) 381 | if err != nil { 382 | return nil, err 383 | } 384 | 385 | return taskResponse, nil 386 | } 387 | 388 | func getIntegrityOption(taskResponse *shared.ResultTaskResponse) error { 389 | client := &http.Client{} 390 | var proxyURL *url.URL 391 | 392 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 393 | // Warning, if you are not using proxy, the requests can be blocked 394 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 395 | } else { 396 | var err error 397 | proxyURL, err = url.Parse(shared.Config.Proxy) 398 | if err != nil { 399 | return err 400 | } 401 | 402 | client.Transport = &http.Transport{ 403 | Proxy: http.ProxyURL(proxyURL), 404 | } 405 | } 406 | 407 | req, err := http.NewRequest("OPTIONS", "https://passport.twitch.tv/integrity", nil) 408 | if err != nil { 409 | return err 410 | } 411 | 412 | req.Header.Set("User-Agent", taskResponse.Solution["user-agent"]) 413 | req.Header.Set("Accept", "*/*") 414 | req.Header.Set("Accept-Language", "en-US,en;q=0.5") 415 | req.Header.Set("Accept-Encoding", "gzip, deflate, br") 416 | req.Header.Set("Access-Control-Request-Method", "POST") 417 | req.Header.Set("Access-Control-Request-Headers", "x-kpsdk-cd,x-kpsdk-ct") 418 | req.Header.Set("Referer", "https://www.twitch.tv/") 419 | req.Header.Set("Origin", "https://www.twitch.tv") 420 | req.Header.Set("DNT", "1") 421 | req.Header.Set("Connection", "keep-alive") 422 | req.Header.Set("Sec-Fetch-Dest", "empty") 423 | req.Header.Set("Sec-Fetch-Mode", "cors") 424 | req.Header.Set("Sec-Fetch-Site", "same-site") 425 | 426 | resp, err := client.Do(req) 427 | if err != nil { 428 | return err 429 | } 430 | 431 | defer resp.Body.Close() 432 | 433 | return nil 434 | } 435 | 436 | func integrityGetToken(taskResponse *shared.ResultTaskResponse, cookies map[string]string) (*shared.Token, error) { 437 | client := &http.Client{} 438 | 439 | var proxyURL *url.URL 440 | 441 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 442 | // Warning, if you are not using proxy, the requests can be blocked 443 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 444 | } else { 445 | var err error 446 | proxyURL, err = url.Parse(shared.Config.Proxy) 447 | if err != nil { 448 | return nil, err 449 | } 450 | 451 | client.Transport = &http.Transport{ 452 | Proxy: http.ProxyURL(proxyURL), 453 | } 454 | } 455 | 456 | req, err := http.NewRequest("POST", "https://passport.twitch.tv/integrity", nil) 457 | if err != nil { 458 | return nil, err 459 | } 460 | 461 | req.Header.Set("User-Agent", taskResponse.Solution["user-agent"]) 462 | req.Header.Set("Accept", "*/*") 463 | req.Header.Set("Accept-Language", "en-US,en;q=0.5") 464 | req.Header.Set("Accept-Encoding", "gzip, deflate, br") 465 | req.Header.Set("Referer", "https://www.twitch.tv/") 466 | req.Header.Set("x-kpsdk-ct", taskResponse.Solution["x-kpsdk-ct"]) 467 | req.Header.Set("x-kpsdk-cd", taskResponse.Solution["x-kpsdk-cd"]) 468 | req.Header.Set("Origin", "https://www.twitch.tv") 469 | req.Header.Set("DNT", "1") 470 | req.Header.Set("Connection", "keep-alive") 471 | req.Header.Set("Sec-Fetch-Dest", "empty") 472 | req.Header.Set("Sec-Fetch-Mode", "cors") 473 | req.Header.Set("Sec-Fetch-Site", "same-site") 474 | req.Header.Set("Content-Length", "0") 475 | 476 | resp, err := client.Do(req) 477 | if err != nil { 478 | return nil, err 479 | } 480 | defer resp.Body.Close() 481 | 482 | for _, cookieData := range resp.Header["Set-Cookie"] { 483 | cookie := strings.Split(cookieData, ";")[0] 484 | cookies[strings.Split(cookie, "=")[0]] = strings.Split(cookie, "=")[1] 485 | } 486 | 487 | body, err := io.ReadAll(resp.Body) 488 | if err != nil { 489 | return nil, err 490 | } 491 | 492 | token := &shared.Token{} 493 | err = json.Unmarshal(body, token) 494 | if err != nil { 495 | return nil, err 496 | } 497 | 498 | return token, nil 499 | } 500 | 501 | func registerFinal(cookies map[string]string, postParams shared.RandomRegisterData, userAgent string) (*shared.AccountRegisterResponse, error) { 502 | var cookiesString string 503 | for key, value := range cookies { 504 | cookiesString += key + "=" + value + "; " 505 | } 506 | 507 | client := &http.Client{} 508 | var proxyURL *url.URL 509 | 510 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 511 | // Warning, if you are not using proxy, the requests can be blocked 512 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 513 | } else { 514 | var err error 515 | proxyURL, err = url.Parse(shared.Config.Proxy) 516 | if err != nil { 517 | return nil, err 518 | } 519 | 520 | client.Transport = &http.Transport{ 521 | Proxy: http.ProxyURL(proxyURL), 522 | } 523 | } 524 | 525 | jsonBody, err := json.Marshal(postParams) 526 | if err != nil { 527 | return nil, err 528 | } 529 | 530 | req, err := http.NewRequest("POST", "https://passport.twitch.tv/protected_register", bytes.NewBuffer(jsonBody)) 531 | if err != nil { 532 | return nil, err 533 | } 534 | 535 | req.Header.Set("User-Agent", userAgent) 536 | req.Header.Set("Accept", "*/*") 537 | req.Header.Set("Accept-Language", "en-US,en;q=0.5") 538 | req.Header.Set("Accept-Encoding", "gzip, deflate, br") 539 | req.Header.Set("Referer", "https://www.twitch.tv/") 540 | req.Header.Set("Content-Type", "text/plain;charset=UTF-8") 541 | req.Header.Set("Origin", "https://www.twitch.tv") 542 | req.Header.Set("DNT", "1") 543 | req.Header.Set("Connection", "keep-alive") 544 | req.Header.Set("Cookie", cookiesString) 545 | req.Header.Set("Sec-Fetch-Dest", "empty") 546 | req.Header.Set("Sec-Fetch-Mode", "cors") 547 | req.Header.Set("Sec-Fetch-Site", "same-site") 548 | 549 | resp, err := client.Do(req) 550 | if err != nil { 551 | return nil, err 552 | } 553 | 554 | body, err := io.ReadAll(resp.Body) 555 | if err != nil { 556 | return nil, err 557 | } 558 | 559 | if resp.StatusCode == 200 { 560 | registerResponse := &shared.AccountRegisterResponse{} 561 | err = json.Unmarshal(body, registerResponse) 562 | if err != nil { 563 | return nil, err 564 | } 565 | 566 | return registerResponse, nil 567 | } else { 568 | return nil, errors.New(string(body)) 569 | } 570 | } 571 | 572 | func getTrashMailSession() (*shared.MailnatorData, error) { 573 | var sess GoGmailnator.Session 574 | 575 | var proxy *string 576 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 577 | // Warning, if you are not using proxy, the requests can be blocked 578 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 579 | proxy = nil 580 | } else { 581 | tempProxy := strings.Replace(strings.Replace(shared.Config.Proxy, "https://", "", -1), "http://", "", -1) // Remove https:// from the proxy, because the GoGmailnator package is hardcoded to use http 582 | proxy = &tempProxy 583 | } 584 | 585 | // session will expire after a few hours 586 | err := sess.Init(proxy) 587 | if err != nil { 588 | return nil, err 589 | } 590 | 591 | // calling sess.GenerateEmailAddress or sess.RetrieveMail with a dead session will cause an error 592 | isAlive, err := sess.IsAlive() 593 | if err != nil { 594 | return nil, err 595 | } 596 | 597 | if isAlive { 598 | fmt.Println("Session is alive.") 599 | } else { 600 | fmt.Println("Session is dead.") 601 | return nil, fmt.Errorf("session is not alive") 602 | } 603 | 604 | emailAddress, err := sess.GenerateEmailAddress() 605 | if err != nil { 606 | return nil, err 607 | } 608 | 609 | fmt.Println("Email address is " + emailAddress + ".") 610 | 611 | mailData := &shared.MailnatorData{ 612 | Session: sess, 613 | Email: emailAddress, 614 | } 615 | 616 | return mailData, nil 617 | } 618 | 619 | func getAllMails(username string) ([]map[string]interface{}, error) { 620 | resp, err := http.Get(fmt.Sprintf("https://www.1secmail.com/api/v1/?action=getMessages&login=%s&domain=%s", username, shared.Config.EmailDomain)) 621 | if err != nil { 622 | return nil, err 623 | } 624 | defer resp.Body.Close() 625 | 626 | body, err := io.ReadAll(resp.Body) 627 | if err != nil { 628 | return nil, err 629 | } 630 | 631 | var outputArray []map[string]interface{} 632 | err = json.Unmarshal(body, &outputArray) 633 | if err != nil { 634 | return nil, err 635 | } 636 | 637 | return outputArray, nil 638 | } 639 | 640 | func getVerificationCode(username string) (string, error) { 641 | for { 642 | lastMail, err := getAllMails(username) 643 | if err != nil { 644 | return "", err 645 | } 646 | 647 | if len(lastMail) > 0 { 648 | subject := lastMail[0]["subject"].(string) 649 | verifyCode := strings.Split(subject, "–")[0] 650 | verifyCode = strings.ReplaceAll(verifyCode, " ", "") 651 | return verifyCode, nil 652 | } 653 | 654 | time.Sleep(1 * time.Second) 655 | } 656 | } 657 | 658 | func publicIntegrityGetToken(XDeviceId, ClientRequestId, ClientSessionId, ClientVersion, kpsdkct, kpsdkcd, accesstoken, current_useragent string) (publicIntegrity *shared.PublicIntegrityData, err error) { 659 | requestBody := []byte("{}") 660 | 661 | headers := map[string]string{ 662 | "User-Agent": current_useragent, 663 | "Accept": "application/json", 664 | "Accept-Language": "en-US", 665 | "Accept-Encoding": "identity", 666 | "Authorization": "OAuth " + accesstoken, 667 | "Referer": "https://www.twitch.tv/", 668 | "Client-Id": shared.Config.TwitchClientID, 669 | "X-Device-Id": XDeviceId, 670 | "Client-Request-Id": ClientRequestId, 671 | "Client-Session-Id": ClientSessionId, 672 | "Client-Version": ClientVersion, 673 | "x-kpsdk-ct": kpsdkct, 674 | "x-kpsdk-cd": kpsdkcd, 675 | "Origin": "https://www.twitch.tv", 676 | "DNT": "1", 677 | "Connection": "keep-alive", 678 | "Sec-Fetch-Dest": "empty", 679 | "Sec-Fetch-Mode": "cors", 680 | "Sec-Fetch-Site": "same-site", 681 | "Content-Length": "0", 682 | } 683 | 684 | req, err := http.NewRequest("POST", "https://gql.twitch.tv/integrity", bytes.NewBuffer(requestBody)) 685 | if err != nil { 686 | return nil, err 687 | } 688 | 689 | for key, value := range headers { 690 | req.Header.Set(key, value) 691 | } 692 | 693 | client := &http.Client{} 694 | var proxyURL *url.URL 695 | 696 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 697 | // Warning, if you are not using proxy, the requests can be blocked 698 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 699 | } else { 700 | var err error 701 | proxyURL, err = url.Parse(shared.Config.Proxy) 702 | if err != nil { 703 | return nil, err 704 | } 705 | 706 | client.Transport = &http.Transport{ 707 | Proxy: http.ProxyURL(proxyURL), 708 | } 709 | } 710 | 711 | resp, err := client.Do(req) 712 | if err != nil { 713 | return nil, err 714 | } 715 | defer resp.Body.Close() 716 | 717 | body, err := io.ReadAll(resp.Body) 718 | if err != nil { 719 | return nil, err 720 | } 721 | 722 | if resp.StatusCode != http.StatusOK { 723 | return nil, fmt.Errorf("unexpected response status code: %d", resp.StatusCode) 724 | } 725 | 726 | var cookiesReturn string 727 | 728 | for _, cookieData := range resp.Header["Set-Cookie"] { 729 | p1 := strings.Split(cookieData, ";")[0] 730 | cookiesReturn += cookiesReturn + p1 + "; " 731 | } 732 | 733 | tokenReturn := shared.Token{} 734 | err = json.Unmarshal(body, &tokenReturn) 735 | if err != nil { 736 | return nil, err 737 | } 738 | 739 | publicIntegrityData := &shared.PublicIntegrityData{ 740 | Cookies: cookiesReturn, 741 | Token: tokenReturn.Token, 742 | } 743 | 744 | return publicIntegrityData, nil 745 | } 746 | 747 | func verifyEmail(XDeviceId, ClientVersion, ClientSessionId, accessToken, ClientIntegrity, code, userId, email, current_useragent string) (*shared.VerificationCodeResponse, error) { 748 | query := `{"operationName":"ValidateVerificationCode","variables":{"input":{"code":"` + code + `","key":"` + userId + `","address":"` + email + `"}},"extensions":{"persistedQuery":{"version":1,"sha256Hash":"05eba55c37ee4eff4dae260850dd6703d99cfde8b8ec99bc97a67e584ae9ec31"}}}` 749 | 750 | requestBody := bytes.NewBufferString(query) 751 | 752 | headers := map[string]string{ 753 | "User-Agent": current_useragent, 754 | "Accept": "application/json", 755 | "Accept-Language": "en-US", 756 | "Accept-Encoding": "identity", 757 | "Referer": "https://www.twitch.tv/", 758 | "Client-Id": shared.Config.TwitchClientID, 759 | "X-Device-Id": XDeviceId, 760 | "Client-Version": ClientVersion, 761 | "Client-Session": ClientSessionId, 762 | "Authorization": "OAuth " + accessToken, 763 | "Client-Integrity": ClientIntegrity, 764 | "Content-Type": "text/plain;charset=UTF-8", 765 | "Origin": "https://www.twitch.tv", 766 | "DNT": "1", 767 | "Connection": "keep-alive", 768 | "Sec-Fetch-Dest": "empty", 769 | "Sec-Fetch-Mode": "cors", 770 | "Sec-Fetch-Site": "same-site", 771 | } 772 | 773 | req, err := http.NewRequest("POST", "https://gql.twitch.tv/gql#origin=twilight", requestBody) 774 | if err != nil { 775 | return nil, fmt.Errorf("error creating request: %v", err) 776 | } 777 | 778 | for key, value := range headers { 779 | req.Header.Set(key, value) 780 | } 781 | 782 | client := &http.Client{} 783 | var proxyURL *url.URL 784 | 785 | if shared.Config.Proxy == "your_proxy" || shared.Config.Proxy == "" { 786 | // Warning, if you are not using proxy, the requests can be blocked 787 | fmt.Println("!! There is no proxy configuration found. The requests are going to be handled without any proxy !!") 788 | } else { 789 | var err error 790 | proxyURL, err = url.Parse(shared.Config.Proxy) 791 | if err != nil { 792 | return nil, err 793 | } 794 | 795 | client.Transport = &http.Transport{ 796 | Proxy: http.ProxyURL(proxyURL), 797 | } 798 | } 799 | 800 | resp, err := client.Do(req) 801 | if err != nil { 802 | return nil, err 803 | } 804 | defer resp.Body.Close() 805 | 806 | body, err := io.ReadAll(resp.Body) 807 | if err != nil { 808 | return nil, err 809 | } 810 | 811 | if resp.StatusCode != http.StatusOK { 812 | return nil, fmt.Errorf("unexpected response status code: %d", resp.StatusCode) 813 | } 814 | 815 | verificationResponse := &shared.VerificationCodeResponse{} 816 | if err := json.Unmarshal(body, &verificationResponse); err != nil { 817 | return nil, err 818 | } 819 | 820 | return verificationResponse, nil 821 | } 822 | 823 | func saveAccountData(r shared.RandomRegisterData, userId string, accesToken string) error { 824 | dir := filepath.Dir(outputFile) 825 | if _, err := os.Stat(dir); os.IsNotExist(err) { 826 | if err := os.MkdirAll(dir, 0755); err != nil { 827 | return err 828 | } 829 | } 830 | 831 | // Check if the file exists 832 | if _, err := os.Stat(outputFile); os.IsNotExist(err) { 833 | // If the file doesn't exist, create an empty file 834 | if err := os.WriteFile(outputFile, []byte(""), 0644); err != nil { 835 | return err 836 | } 837 | } 838 | dataAll := r.Username + " " + r.Password + " " + r.Email + " " + userId + " " + accesToken + " \n" 839 | file, err := os.OpenFile(outputFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 840 | if err != nil { 841 | return err 842 | } 843 | defer file.Close() 844 | // Write data to the file 845 | if _, err := file.Write([]byte(dataAll)); err != nil { 846 | return err 847 | } 848 | 849 | // 2. Save only the username and password 850 | dir = filepath.Dir(userPassFile) 851 | if _, err := os.Stat(dir); os.IsNotExist(err) { 852 | if err := os.MkdirAll(dir, 0755); err != nil { 853 | return err 854 | } 855 | } 856 | 857 | if _, err := os.Stat(userPassFile); os.IsNotExist(err) { 858 | if err := os.WriteFile(userPassFile, []byte(""), 0644); err != nil { 859 | return err 860 | } 861 | } 862 | userPass := r.Username + " " + r.Password + " \n" 863 | file, err = os.OpenFile(userPassFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 864 | if err != nil { 865 | return err 866 | } 867 | defer file.Close() 868 | if _, err := file.Write([]byte(userPass)); err != nil { 869 | return err 870 | } 871 | 872 | // 3. Save only the tokens (oauth acces token) 873 | dir = filepath.Dir(tokensFile) 874 | if _, err := os.Stat(dir); os.IsNotExist(err) { 875 | if err := os.MkdirAll(dir, 0755); err != nil { 876 | return err 877 | } 878 | } 879 | 880 | if _, err := os.Stat(tokensFile); os.IsNotExist(err) { 881 | if err := os.WriteFile(tokensFile, []byte(""), 0644); err != nil { 882 | return err 883 | } 884 | } 885 | token := accesToken + " \n" 886 | file, err = os.OpenFile(tokensFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) 887 | if err != nil { 888 | return err 889 | } 890 | defer file.Close() 891 | if _, err := file.Write([]byte(token)); err != nil { 892 | return err 893 | } 894 | 895 | return nil 896 | } 897 | -------------------------------------------------------------------------------- /results/accounts.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gui-fkb/twitch-accounts/d572d205a407fbbf6c96e765b5cad27a4a833846/results/accounts.txt -------------------------------------------------------------------------------- /results/tokens.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gui-fkb/twitch-accounts/d572d205a407fbbf6c96e765b5cad27a4a833846/results/tokens.txt -------------------------------------------------------------------------------- /results/userpass.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gui-fkb/twitch-accounts/d572d205a407fbbf6c96e765b5cad27a4a833846/results/userpass.txt -------------------------------------------------------------------------------- /shared/config.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | type Configuration struct { 4 | CapSolverKey string 5 | Proxy string 6 | UserAgent string 7 | EmailDomain string 8 | TwitchClientID string 9 | } 10 | 11 | // Config variable stores your configuration 12 | var Config = Configuration{ 13 | CapSolverKey: "your_api_key", 14 | Proxy: "your_proxy", 15 | UserAgent: "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:109.0) Gecko/20100101 Firefox/109.0", 16 | TwitchClientID: "kimne78kx3ncx6brgo4mv6wki5h1ko", 17 | EmailDomain: "rteet.com", 18 | } 19 | -------------------------------------------------------------------------------- /shared/structs.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import "github.com/ox-y/GoGmailnator" 4 | 5 | type RandomRegisterData struct { 6 | Username string `json:"username"` 7 | Password string `json:"password"` 8 | Birthday Birthday `json:"birthday"` 9 | Email string `json:"email"` 10 | EmailVerificationCode *string `json:"email_verification_code"` 11 | ClientID string `json:"client_id"` 12 | IntegrityToken string `json:"integrity_token"` 13 | IsPasswordGuide string `json:"is_password_guide"` 14 | } 15 | 16 | type Birthday struct { 17 | Day int `json:"day"` 18 | Month int `json:"month"` 19 | Year int `json:"year"` 20 | IsOver18 bool `json:"is_over_18"` 21 | } 22 | 23 | type CreateKasadaTask struct { 24 | ApiKey string `json:"api_key"` 25 | Task Task `json:"task"` 26 | } 27 | type GetTaskResult struct { 28 | ApiKey string `json:"api_key"` 29 | TaskId string `json:"taskId"` 30 | } 31 | 32 | type Task struct { 33 | Type string `json:"type"` 34 | Pjs string `json:"pjs"` 35 | CdOnly bool `json:"cdOnly"` 36 | } 37 | 38 | type CreateTaskResponse struct { 39 | ErrorCode int `json:"error_code"` 40 | ErrorDescription string `json:"error_description"` 41 | TaskId string `json:"taskId"` 42 | } 43 | 44 | type ResultTaskResponse struct { 45 | ErrorId int `json:"errorId"` 46 | Solution map[string]string `json:"solution"` 47 | Status string `json:"status"` 48 | } 49 | 50 | type IntegrityInfo struct { 51 | Token string 52 | Cookies map[string]string 53 | } 54 | 55 | type Token struct { 56 | Token string `json:"token"` 57 | } 58 | 59 | type MailnatorData struct { 60 | Session GoGmailnator.Session 61 | Email string 62 | } 63 | 64 | type AccountRegisterResponse struct { 65 | AccessToken string `json:"access_token"` 66 | RedirectPath string `json:"redirect_path"` 67 | UserId string `json:"userID"` 68 | } 69 | 70 | type PublicIntegrityData struct { 71 | Token string 72 | Cookies string 73 | } 74 | 75 | type Error struct { 76 | Code interface{} `json:"code"` 77 | Typename string `json:"__typename"` 78 | } 79 | 80 | type Request struct { 81 | Status string `json:"status"` 82 | Typename string `json:"__typename"` 83 | } 84 | 85 | type ValidateVerificationCode struct { 86 | Error Error `json:"error"` 87 | Request Request `json:"request"` 88 | Typename string `json:"__typename"` 89 | } 90 | 91 | type Extensions struct { 92 | DurationMilliseconds int `json:"durationMilliseconds"` 93 | OperationName string `json:"operationName"` 94 | RequestID string `json:"requestID"` 95 | } 96 | 97 | type Data struct { 98 | ValidateVerificationCode ValidateVerificationCode `json:"validateVerificationCode"` 99 | } 100 | 101 | type VerificationCodeResponse struct { 102 | Data Data `json:"data"` 103 | Extensions Extensions `json:"extensions"` 104 | } 105 | 106 | // Follow Bot 107 | 108 | type TwitchOperationQuery struct { // This is a struct for Twitch generics requests. Various requests will use this struct. 109 | OperationName string `json:"operationName"` 110 | Variables map[string]interface{} `json:"variables"` 111 | Extensions map[string]interface{} `json:"extensions"` 112 | } 113 | 114 | type ErrorResponse struct { 115 | Breached bool `json:"breached"` 116 | Error string `json:"error"` 117 | Errors []string `json:"errors"` 118 | ErrorCode int `json:"error_code"` 119 | } 120 | -------------------------------------------------------------------------------- /shared/utils.go: -------------------------------------------------------------------------------- 1 | package shared 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "os" 7 | "os/exec" 8 | "runtime" 9 | 10 | "github.com/ox-y/GoGmailnator" 11 | ) 12 | 13 | func FastEmailTest() { 14 | var sess GoGmailnator.Session 15 | 16 | err := sess.Init(nil) 17 | if err != nil { 18 | panic(err) 19 | } 20 | 21 | isAlive, err := sess.IsAlive() 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | if isAlive { 27 | fmt.Println("Session is alive.") 28 | } else { 29 | fmt.Println("Session is dead.") 30 | return 31 | } 32 | 33 | emailAddress, err := sess.GenerateEmailAddress() 34 | if err != nil { 35 | panic(err) 36 | } 37 | 38 | fmt.Println("Email address is " + emailAddress + ".") 39 | 40 | emails, err := sess.RetrieveMail(emailAddress) 41 | if err != nil { 42 | panic(err) 43 | } 44 | 45 | for _, email := range emails { 46 | fmt.Printf("From: %s, Subject: %s, Time: %s\n", email.From, email.Subject, email.Time) 47 | } 48 | } 49 | 50 | func ClearScreen() { 51 | // Clear the screen using platform-specific commands 52 | 53 | switch runtime.GOOS { 54 | case "linux", "darwin": 55 | // For Linux and macOS 56 | cmd := exec.Command("clear") 57 | cmd.Stdout = os.Stdout 58 | cmd.Run() 59 | case "windows": 60 | // For Windows 61 | cmd := exec.Command("cmd", "/c", "cls") 62 | cmd.Stdout = os.Stdout 63 | cmd.Run() 64 | default: 65 | // Unsupported platform 66 | fmt.Println("Unsupported platform") 67 | } 68 | } 69 | 70 | func GenerateRandomID(length int) string { 71 | const charset = "abcdefghijklmnopqrstuvwxyz0123456789" 72 | 73 | bytes := make([]byte, length) 74 | for i := range bytes { 75 | index := rand.Intn(len(charset)) 76 | bytes[i] = charset[index] 77 | } 78 | return string(bytes) 79 | } 80 | --------------------------------------------------------------------------------