diff --git a/config.yml b/config.yml index 4b30f54..7a01b24 100644 --- a/config.yml +++ b/config.yml @@ -13,12 +13,12 @@ direct_message_settings: friend_before_DM: false online_tokens: false receive_messages: false - skip_failed: true + skip_failed: false block_after_dm: false close_dm_after_message: false proxy_settings: - proxy_from_file: true + proxy_from_file: true proxy_for_captcha: true use_proxy_for_gateway: false proxy_protocol: "http" @@ -31,10 +31,11 @@ scraper_settings: captcha_settings: captcha_api_key: "" - captcha_api: "capmonster.cloud" + captcha_api: "2captcha.com" + max_captcha_wait: 60 + max_captcha_retry: 3 other_settings: - max_attempt_invite_rejoin: 3 disable_keep_alives: false suspicion_avoidance: diff --git a/main.go b/main.go index 9f22584..7000c80 100644 --- a/main.go +++ b/main.go @@ -34,7 +34,7 @@ import ( ) func main() { - version := "1.8.8" + version := "1.8.9" CaptchaServices = []string{"capmonster.cloud", "anti-captcha.com", "2captcha.com", "rucaptcha.com", "deathbycaptcha.com", "anycaptcha.com", "azcaptcha.com", "solvecaptcha.com"} rand.Seed(time.Now().UTC().UnixNano()) color.Blue(logo + " v" + version + "\n") @@ -403,14 +403,14 @@ func Options() { break } else { - if instances[i].Rejoin >= maxattempts { + if instances[i].Retry >= maxattempts { color.Red("[%v] Stopping token %v [Max server rejoin attempts]", time.Now().Format("15:04:05"), instances[i].Token) break } err := instances[i].Invite(invite) if err != nil { color.Red("[%v] Error while joining server: %v", time.Now().Format("15:04:05"), err) - instances[i].Rejoin++ + instances[i].Retry++ continue } } @@ -624,6 +624,13 @@ func Options() { failed = append(failed, member) color.Red("[%v] Token %v is being rate limited. Sleeping for 10 seconds", time.Now().Format("15:04:05"), instances[i].Token) time.Sleep(10 * time.Second) + } else if resp.StatusCode == 400 && strings.Contains(string(body), "captcha") { + color.Red("[%v] Token %v Captcha was attempted to solve but appeared again", time.Now().Format("15:04:05"), instances[i].Token) + instances[i].Retry++ + if instances[i].Retry >= cfg.CaptchaSettings.MaxCaptcha { + color.Red("[%v] Stopping token %v max captcha solves reached", time.Now().Format("15:04:05"), instances[i].Token) + break + } } else { failedCount++ failed = append(failed, member) diff --git a/utilities/captcha.go b/utilities/captcha.go index b7bcfeb..8132f48 100644 --- a/utilities/captcha.go +++ b/utilities/captcha.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "net/http" + "net/url" "strconv" "strings" "time" @@ -17,10 +18,12 @@ func (in *Instance) SolveCaptcha(sitekey string, cookie string, rqData string, r switch true { case Contains([]string{"capmonster.cloud", "anti-captcha.com", "anycaptcha.com"}, in.Config.CaptchaSettings.CaptchaAPI): return in.SolveCaptchaCapmonster(sitekey, cookie, rqData) - case Contains([]string{"rucaptcha.com", "azcaptcha.com", "solvecaptcha.com", "2captcha.com"}, in.Config.CaptchaSettings.CaptchaAPI): + case Contains([]string{"rucaptcha.com", "azcaptcha.com", "solvecaptcha.com"}, in.Config.CaptchaSettings.CaptchaAPI): return in.SolveCaptchaRucaptcha(sitekey, rqData, rqToken) case in.Config.CaptchaSettings.CaptchaAPI == "deathbycaptcha.com": return in.SolveCaptchaDeathByCaptcha(sitekey) + case in.Config.CaptchaSettings.CaptchaAPI == "2captcha.com": + return in.twoCaptcha(sitekey, rqData) default: return "", fmt.Errorf("unsupported captcha api: %s", in.Config.CaptchaSettings.CaptchaAPI) } @@ -37,7 +40,7 @@ func (in *Instance) SolveCaptchaCapmonster(sitekey string, cookies string, rqdat WebsiteURL: "https://discord.com/channels/@me", WebsiteKey: sitekey, Cookies: cookies, - UserAgent: Useragent, + UserAgent: UserAgent, Data: rqdata, }, } @@ -69,7 +72,7 @@ func (in *Instance) SolveCaptchaCapmonster(sitekey string, cookies string, rqdat Type: "HCaptchaTask", WebsiteURL: "https://discord.com/channels/@me", WebsiteKey: sitekey, - UserAgent: Useragent, + UserAgent: UserAgent, ProxyType: in.Config.ProxySettings.ProxyProtocol, ProxyAddress: address, ProxyPort: port, @@ -391,3 +394,106 @@ func (in *Instance) SolveCaptchaRucaptcha(sitekey string, rqData string, rqToken } return "", fmt.Errorf("max retries exceeded") } + +type twoCaptchaSubmitResponse struct { + Status int `json:"status"` + Request string `json:"request"` +} + +func (in *Instance) twoCaptcha(sitekey, rqdata string) (string, error) { + var solvedKey string + inEndpoint := "https://2captcha.com/in.php" + inURL, err := url.Parse(inEndpoint) + if err != nil { + return solvedKey, fmt.Errorf("error while parsing url %v", err) + } + q := inURL.Query() + if in.Config.CaptchaSettings.ClientKey == "" { + return solvedKey, fmt.Errorf("client key is empty") + } + q.Set("key", in.Config.CaptchaSettings.ClientKey) + q.Set("method", "hcaptcha") + q.Set("sitekey", sitekey) + // Page URL same as referer in headers + q.Set("pageurl", "https://discord.com/channels/@me") + q.Set("userAgent", UserAgent) + q.Set("json", "1") + q.Set("soft_id", "3359") + if rqdata != "" { + q.Set("data", rqdata) + } + if in.Config.ProxySettings.ProxyForCaptcha { + q.Set("proxy", in.Proxy) + q.Set("proxytype", in.Config.ProxySettings.ProxyProtocol) + } + inURL.RawQuery = q.Encode() + inEndpoint = inURL.String() + req, err := http.NewRequest(http.MethodGet, inEndpoint, nil) + if err != nil { + return solvedKey, fmt.Errorf("error creating request [%v]", err) + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return solvedKey, fmt.Errorf("error sending request [%v]", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return solvedKey, fmt.Errorf("error reading response [%v]", err) + } + var inResponse twoCaptchaSubmitResponse + err = json.Unmarshal(body, &inResponse) + if err != nil { + return solvedKey, fmt.Errorf("error unmarshalling response [%v]", err) + } + if inResponse.Status != 1 { + return solvedKey, fmt.Errorf("error %v", inResponse.Request) + } + outEndpoint := "https://2captcha.com/res.php" + outURL, err := url.Parse(outEndpoint) + if err != nil { + return solvedKey, fmt.Errorf("error while parsing url %v", err) + } + q = outURL.Query() + q.Set("key", in.Config.CaptchaSettings.ClientKey) + q.Set("action", "get") + q.Set("id", inResponse.Request) + q.Set("json", "1") + outURL.RawQuery = q.Encode() + outEndpoint = outURL.String() + req, err = http.NewRequest(http.MethodGet, outEndpoint, nil) + if err != nil { + return solvedKey, fmt.Errorf("error creating request [%v]", err) + } + time.Sleep(10 * time.Second) + now := time.Now() + for { + if time.Since(now) > time.Duration(in.Config.CaptchaSettings.Timeout)*time.Second { + return solvedKey, fmt.Errorf("captcha response from 2captcha timedout") + } + resp, err := http.DefaultClient.Do(req) + if err != nil { + return solvedKey, fmt.Errorf("error sending request [%v]", err) + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return solvedKey, fmt.Errorf("error reading response [%v]", err) + } + var outResponse twoCaptchaSubmitResponse + err = json.Unmarshal(body, &outResponse) + if err != nil { + return solvedKey, fmt.Errorf("error unmarshalling response [%v]", err) + } + if outResponse.Request == "CAPCHA_NOT_READY" { + time.Sleep(5 * time.Second) + continue + } else if strings.Contains(string(body), "ERROR") { + return solvedKey, fmt.Errorf("error %v", outResponse.Request) + } else { + solvedKey = outResponse.Request + break + } + } + return solvedKey, nil +} diff --git a/utilities/direct_messages.go b/utilities/direct_messages.go index 36f86d5..8b96411 100644 --- a/utilities/direct_messages.go +++ b/utilities/direct_messages.go @@ -223,7 +223,14 @@ func (in *Instance) OpenChannel(recepientUID string) (string, error) { return channelSnowflake.ID, nil } +type captchaDetected struct { + CaptchaKey []string `json:"captcha_key"` + Sitekey string `json:"captcha_sitekey"` + Service string `json:"captcha_service"` + RqData string `json:"captcha_rqdata"` + RqToken string `json:"captcha_rqtoken"` +} // Inputs the Channel snowflake and sends them the message; outputs the response code for error handling. func (in *Instance) SendMessage(channelSnowflake string, memberid string) (http.Response, error) { // Sending a random message incase there are multiple. @@ -240,7 +247,6 @@ func (in *Instance) SendMessage(channelSnowflake string, memberid string) (http. "tts": false, "nonce": Snowflake(), }) - if err != nil { return http.Response{}, fmt.Errorf("error while marshalling message %v %v ", index, err) } @@ -267,20 +273,43 @@ func (in *Instance) SendMessage(channelSnowflake string, memberid string) (http. return http.Response{}, fmt.Errorf("error while getting send message response %v", err) } if res.StatusCode == 400 { - msgid, err := in.greet(channelSnowflake, cookie, fingerprint) + body, err := ioutil.ReadAll(res.Body) + if err != nil { + return http.Response{}, fmt.Errorf("error while reading body %v", err) + } + if strings.Contains(string(body), "captcha") { + color.Yellow("[%v] Captcha detected %v Solving", time.Now().Format("15:04:05"), in.Token) + } + if in.Config.CaptchaSettings.ClientKey == "" { + return http.Response{}, fmt.Errorf("captcha detected but no client key set") + } + var captchaDetect captchaDetected + err = json.Unmarshal(body, &captchaDetect) + if err != nil { + return http.Response{}, fmt.Errorf("error while unmarshalling captcha %v", err) + } + solved, err := in.SolveCaptcha(captchaDetect.Sitekey, cookie,captchaDetect.RqData, captchaDetect.RqToken) + if err != nil { + return http.Response{}, fmt.Errorf("error while solving captcha %v", err) + } + body, err = json.Marshal(&map[string]interface{}{ + "content": x, + "tts": false, + "nonce": Snowflake(), + "captcha_key": solved, + "captcha_rqtoken": captchaDetect.RqToken, + }) if err != nil { - return http.Response{}, fmt.Errorf("error while opening DM %v", err) + return http.Response{}, fmt.Errorf("error while marshalling message %v %v ", index, err) } - resp, err := in.SendMessage(channelSnowflake, memberid) + req, err = http.NewRequest("POST", url, strings.NewReader(string(body))) if err != nil { - return http.Response{}, fmt.Errorf("error while sending message %v", err) + return http.Response{}, fmt.Errorf("error while making request to send message %v", err) } - err = in.ungreet(channelSnowflake, cookie, fingerprint, msgid) + res, err = in.Client.Do(in.SendMessageHeaders(req, cookie, fingerprint, channelSnowflake)) if err != nil { - return http.Response{}, fmt.Errorf("error while opening DM %v", err) + return http.Response{}, fmt.Errorf("error while getting send message response %v", err) } - in.Count++ - return resp, nil } in.Count++ return *res, nil diff --git a/utilities/extra.go b/utilities/extra.go index 64ac5a6..091bd02 100644 --- a/utilities/extra.go +++ b/utilities/extra.go @@ -209,17 +209,21 @@ func Bypass(client *http.Client, serverid string, token string, invite string) e type invitePayload struct { CaptchaKey string `json:"captcha_key,omitempty"` + RqToken string `json:"captcha_rqtoken,omitempty"` } func (in *Instance) Invite(Code string) error { var solvedKey string var payload invitePayload - for i := 0; i < in.Config.OtherSettings.MaxInvite; i++ { + var rqData string + var rqToken string + for i := 0; i < in.Config.CaptchaSettings.MaxCaptcha; i++ { if solvedKey == "" || in.Config.CaptchaSettings.CaptchaAPI == "" { payload = invitePayload{} } else { payload = invitePayload{ CaptchaKey: solvedKey, + RqToken: rqToken, } } payload, err := json.Marshal(payload) @@ -269,8 +273,6 @@ func (in *Instance) Invite(Code string) error { continue } cap := resp["captcha_sitekey"].(string) - var rqData string - var rqToken string if strings.Contains(string(body), "captcha_rqdata") { rqData = resp["captcha_rqdata"].(string) } diff --git a/utilities/files.go b/utilities/files.go index bd9e73e..4228be5 100644 --- a/utilities/files.go +++ b/utilities/files.go @@ -228,10 +228,11 @@ type ScraperSettings struct { type CaptchaSettings struct { ClientKey string `yaml:"captcha_api_key"` CaptchaAPI string `yaml:"captcha_api"` + Timeout int `yaml:"max_captcha_wait"` + MaxCaptcha int `yaml:"max_captcha_retry"` } type OtherSettings struct { - MaxInvite int `yaml:"max_attempt_invite_rejoin"` DisableKL bool `yaml:"disable_keep_alives"` } diff --git a/utilities/instance.go b/utilities/instance.go index 9f77f47..0929163 100644 --- a/utilities/instance.go +++ b/utilities/instance.go @@ -27,7 +27,7 @@ type Instance struct { LastCount int Members []User AllMembers []User - Rejoin int + Retry int ScrapeCount int ID string Receiver bool diff --git a/utilities/invite.go b/utilities/invite.go index 8dd0451..fa54eb9 100644 --- a/utilities/invite.go +++ b/utilities/invite.go @@ -73,12 +73,12 @@ func (in *Instance) Inviter(invitationCode string, mode int, cookie string, fing contextProperties = generateXContext(channelID, channelType, guildID) } site = fmt.Sprintf(`https://ptb.discord.com/api/v10/invites/%v`, invitationCode) - if in.Config.OtherSettings.MaxInvite < 2 { - in.Config.OtherSettings.MaxInvite = 2 + if in.Config.CaptchaSettings.MaxCaptcha < 2 { + in.Config.CaptchaSettings.MaxCaptcha = 2 } var captchaKey string var req *http.Request - for i := 0; i < in.Config.OtherSettings.MaxInvite; i++ { + for i := 0; i < in.Config.CaptchaSettings.MaxCaptcha; i++ { if captchaKey == "" { req, err = http.NewRequest(http.MethodPost, site, strings.NewReader(`{}`)) } else {