├── .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 |
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 |27 | 🤖 Follow bot functionality has been added! 🎉 28 |
29 | 30 |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 | --------------------------------------------------------------------------------