Update main.go
Browse files
main.go
CHANGED
@@ -10,6 +10,7 @@ import (
|
|
10 |
"net/http"
|
11 |
"net/url"
|
12 |
"os"
|
|
|
13 |
"strings"
|
14 |
"time"
|
15 |
|
@@ -368,66 +369,204 @@ func requestToken() (string, error) {
|
|
368 |
}
|
369 |
|
370 |
func requestTokenAndHash() (string, string, error) {
|
371 |
-
|
|
|
|
|
|
|
|
|
372 |
if err != nil {
|
373 |
-
return "", "", fmt.Errorf("
|
374 |
}
|
|
|
375 |
for k, v := range config.FakeHeaders {
|
376 |
req.Header.Set(k, v)
|
377 |
}
|
378 |
-
|
379 |
-
|
380 |
-
client := createHTTPClient(10 * time.Second)
|
381 |
-
|
382 |
-
log.Println("发送 token 请求")
|
383 |
resp, err := client.Do(req)
|
384 |
if err != nil {
|
385 |
-
return "", "", fmt.Errorf("
|
386 |
}
|
387 |
defer resp.Body.Close()
|
388 |
-
|
389 |
if resp.StatusCode != http.StatusOK {
|
390 |
bodyBytes, _ := io.ReadAll(resp.Body)
|
391 |
-
|
392 |
-
log.Printf("requestToken: 非200响应: %d, 内容: %s\n", resp.StatusCode, bodyString)
|
393 |
-
return "", "", fmt.Errorf("非200响应: %d, 内容: %s", resp.StatusCode, bodyString)
|
394 |
}
|
395 |
-
|
396 |
-
//
|
397 |
-
|
398 |
-
if
|
399 |
-
return "", "",
|
400 |
}
|
401 |
-
|
402 |
-
|
403 |
-
|
404 |
-
|
405 |
-
|
406 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
407 |
if err != nil {
|
408 |
-
log.Printf("
|
409 |
-
|
410 |
-
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
419 |
}
|
420 |
}
|
421 |
}
|
422 |
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
428 |
}
|
429 |
|
430 |
-
return
|
431 |
}
|
432 |
|
433 |
func prepareMessages(messages []struct {
|
@@ -532,4 +671,4 @@ func createHTTPClient(timeout time.Duration) *http.Client {
|
|
532 |
}
|
533 |
|
534 |
return client
|
535 |
-
}
|
|
|
10 |
"net/http"
|
11 |
"net/url"
|
12 |
"os"
|
13 |
+
"regexp"
|
14 |
"strings"
|
15 |
"time"
|
16 |
|
|
|
369 |
}
|
370 |
|
371 |
func requestTokenAndHash() (string, string, error) {
|
372 |
+
// Create a client with proper settings
|
373 |
+
client := createHTTPClient(30 * time.Second)
|
374 |
+
|
375 |
+
// First, visit the DuckDuckGo main page
|
376 |
+
req, err := http.NewRequest("GET", "https://duckduckgo.com/", nil)
|
377 |
if err != nil {
|
378 |
+
return "", "", fmt.Errorf("failed to create homepage request: %v", err)
|
379 |
}
|
380 |
+
|
381 |
for k, v := range config.FakeHeaders {
|
382 |
req.Header.Set(k, v)
|
383 |
}
|
384 |
+
|
385 |
+
log.Println("Sending request to DuckDuckGo homepage")
|
|
|
|
|
|
|
386 |
resp, err := client.Do(req)
|
387 |
if err != nil {
|
388 |
+
return "", "", fmt.Errorf("homepage request failed: %v", err)
|
389 |
}
|
390 |
defer resp.Body.Close()
|
391 |
+
|
392 |
if resp.StatusCode != http.StatusOK {
|
393 |
bodyBytes, _ := io.ReadAll(resp.Body)
|
394 |
+
return "", "", fmt.Errorf("homepage request returned status %d: %s", resp.StatusCode, string(bodyBytes))
|
|
|
|
|
395 |
}
|
396 |
+
|
397 |
+
// Read homepage content to extract vqd-related JavaScript
|
398 |
+
bodyBytes, err := io.ReadAll(resp.Body)
|
399 |
+
if err != nil {
|
400 |
+
return "", "", fmt.Errorf("failed to read homepage: %v", err)
|
401 |
}
|
402 |
+
|
403 |
+
bodyStr := string(bodyBytes)
|
404 |
+
|
405 |
+
// Method 1: Try to extract the vqd directly from the HTML attributes
|
406 |
+
// DuckDuckGo often embeds this in data attributes or JavaScript variables
|
407 |
+
vqdRegex := regexp.MustCompile(`vqd=["']([^"']+)["']`)
|
408 |
+
matches := vqdRegex.FindStringSubmatch(bodyStr)
|
409 |
+
|
410 |
+
if len(matches) < 2 {
|
411 |
+
// Try alternate regex patterns if the first one didn't match
|
412 |
+
alternatePatterns := []string{
|
413 |
+
`vqd[:=]["']([^"']+)["']`,
|
414 |
+
`"vqd":"([^"]+)"`,
|
415 |
+
`'vqd':'([^']+)'`,
|
416 |
+
}
|
417 |
+
|
418 |
+
for _, pattern := range alternatePatterns {
|
419 |
+
r := regexp.MustCompile(pattern)
|
420 |
+
matches = r.FindStringSubmatch(bodyStr)
|
421 |
+
if len(matches) >= 2 {
|
422 |
+
break
|
423 |
+
}
|
424 |
+
}
|
425 |
+
}
|
426 |
+
|
427 |
+
if len(matches) >= 2 {
|
428 |
+
token := matches[1]
|
429 |
+
log.Printf("Successfully extracted vqd token: %s", token)
|
430 |
+
return token, "", nil
|
431 |
+
}
|
432 |
+
|
433 |
+
// Method 2: Get the JavaScript file that contains the token generation logic
|
434 |
+
jsURLRegex := regexp.MustCompile(`(\/dist\/[^"']+\.js)`)
|
435 |
+
jsMatches := jsURLRegex.FindAllStringSubmatch(bodyStr, -1)
|
436 |
+
|
437 |
+
for _, match := range jsMatches {
|
438 |
+
if len(match) < 2 {
|
439 |
+
continue
|
440 |
+
}
|
441 |
+
|
442 |
+
jsURL := "https://duckduckgo.com" + match[1]
|
443 |
+
log.Printf("Trying to get vqd from JavaScript file: %s", jsURL)
|
444 |
+
|
445 |
+
jsReq, err := http.NewRequest("GET", jsURL, nil)
|
446 |
if err != nil {
|
447 |
+
log.Printf("Failed to create request for JS file: %v", err)
|
448 |
+
continue
|
449 |
+
}
|
450 |
+
|
451 |
+
for k, v := range config.FakeHeaders {
|
452 |
+
jsReq.Header.Set(k, v)
|
453 |
+
}
|
454 |
+
|
455 |
+
jsResp, err := client.Do(jsReq)
|
456 |
+
if err != nil {
|
457 |
+
log.Printf("Failed to get JS file: %v", err)
|
458 |
+
continue
|
459 |
+
}
|
460 |
+
|
461 |
+
jsBytes, err := io.ReadAll(jsResp.Body)
|
462 |
+
jsResp.Body.Close()
|
463 |
+
|
464 |
+
if err != nil {
|
465 |
+
log.Printf("Failed to read JS file: %v", err)
|
466 |
+
continue
|
467 |
+
}
|
468 |
+
|
469 |
+
jsContent := string(jsBytes)
|
470 |
+
|
471 |
+
// Look for vqd token patterns in the JS file
|
472 |
+
vqdInJSRegex := regexp.MustCompile(`vqd\s*[:=]\s*["']([^"']+)["']`)
|
473 |
+
jsMatches := vqdInJSRegex.FindStringSubmatch(jsContent)
|
474 |
+
|
475 |
+
if len(jsMatches) >= 2 {
|
476 |
+
token := jsMatches[1]
|
477 |
+
log.Printf("Found vqd token in JS file: %s", token)
|
478 |
+
return token, "", nil
|
479 |
+
}
|
480 |
+
|
481 |
+
// Try another pattern specific to the format in the JS file
|
482 |
+
vqdInJSRegex2 := regexp.MustCompile(`"vqd"\s*:\s*"([^"]+)"`)
|
483 |
+
jsMatches2 := vqdInJSRegex2.FindStringSubmatch(jsContent)
|
484 |
+
|
485 |
+
if len(jsMatches2) >= 2 {
|
486 |
+
token := jsMatches2[1]
|
487 |
+
log.Printf("Found vqd token in JS file (pattern 2): %s", token)
|
488 |
+
return token, "", nil
|
489 |
+
}
|
490 |
+
}
|
491 |
+
|
492 |
+
// Method 3 (Fallback): Try to request duck.js or related files which might contain the token
|
493 |
+
fallbackURLs := []string{
|
494 |
+
"https://duckduckgo.com/duck.js",
|
495 |
+
"https://duckduckgo.com/chat.js",
|
496 |
+
"https://duckduckgo.com/d.js",
|
497 |
+
}
|
498 |
+
|
499 |
+
for _, url := range fallbackURLs {
|
500 |
+
fallbackReq, err := http.NewRequest("GET", url, nil)
|
501 |
+
if err != nil {
|
502 |
+
log.Printf("Failed to create request for fallback URL %s: %v", url, err)
|
503 |
+
continue
|
504 |
+
}
|
505 |
+
|
506 |
+
for k, v := range config.FakeHeaders {
|
507 |
+
fallbackReq.Header.Set(k, v)
|
508 |
+
}
|
509 |
+
|
510 |
+
fallbackResp, err := client.Do(fallbackReq)
|
511 |
+
if err != nil {
|
512 |
+
log.Printf("Failed to get fallback URL %s: %v", url, err)
|
513 |
+
continue
|
514 |
+
}
|
515 |
+
|
516 |
+
fallbackBytes, err := io.ReadAll(fallbackResp.Body)
|
517 |
+
fallbackResp.Body.Close()
|
518 |
+
|
519 |
+
if err != nil {
|
520 |
+
log.Printf("Failed to read fallback URL %s: %v", url, err)
|
521 |
+
continue
|
522 |
+
}
|
523 |
+
|
524 |
+
fallbackContent := string(fallbackBytes)
|
525 |
+
|
526 |
+
// Try multiple regex patterns to find the vqd token
|
527 |
+
patterns := []string{
|
528 |
+
`vqd\s*[:=]\s*["']([^"']+)["']`,
|
529 |
+
`"vqd"\s*:\s*"([^"]+)"`,
|
530 |
+
`'vqd'\s*:\s*'([^']+)'`,
|
531 |
+
}
|
532 |
+
|
533 |
+
for _, pattern := range patterns {
|
534 |
+
r := regexp.MustCompile(pattern)
|
535 |
+
m := r.FindStringSubmatch(fallbackContent)
|
536 |
+
if len(m) >= 2 {
|
537 |
+
token := m[1]
|
538 |
+
log.Printf("Found vqd token in fallback URL %s: %s", url, token)
|
539 |
+
return token, "", nil
|
540 |
}
|
541 |
}
|
542 |
}
|
543 |
|
544 |
+
// Last resort: try the duckchat/v1/status endpoint which we know returns the token in headers
|
545 |
+
statusReq, err := http.NewRequest("GET", "https://duckduckgo.com/duckchat/v1/status", nil)
|
546 |
+
if err != nil {
|
547 |
+
return "", "", fmt.Errorf("failed to create status request: %v", err)
|
548 |
+
}
|
549 |
+
|
550 |
+
for k, v := range config.FakeHeaders {
|
551 |
+
statusReq.Header.Set(k, v)
|
552 |
+
}
|
553 |
+
statusReq.Header.Set("x-vqd-accept", "1")
|
554 |
+
|
555 |
+
log.Println("Trying duckchat/v1/status as last resort")
|
556 |
+
statusResp, err := client.Do(statusReq)
|
557 |
+
if err != nil {
|
558 |
+
return "", "", fmt.Errorf("status request failed: %v", err)
|
559 |
+
}
|
560 |
+
defer statusResp.Body.Close()
|
561 |
+
|
562 |
+
// Check X-VQD-4 header
|
563 |
+
token := statusResp.Header.Get("x-vqd-4")
|
564 |
+
if token != "" {
|
565 |
+
log.Printf("Found vqd token in status response header: %s", token)
|
566 |
+
return token, "", nil
|
567 |
}
|
568 |
|
569 |
+
return "", "", errors.New("could not find vqd token using any method")
|
570 |
}
|
571 |
|
572 |
func prepareMessages(messages []struct {
|
|
|
671 |
}
|
672 |
|
673 |
return client
|
674 |
+
}
|