package main import ( "encoding/json" "fmt" "net/http" "net/url" "time" ) // QwantTextAPIResponse represents the JSON response structure from Qwant API type QwantTextAPIResponse struct { Data struct { Result struct { Items struct { Mainline []struct { Items []struct { URL string `json:"url"` Title string `json:"title"` Description string `json:"desc"` } `json:"items"` } `json:"mainline"` } `json:"items"` } `json:"result"` } `json:"data"` } // PerformQwantTextSearch contacts the Qwant API and returns a slice of TextSearchResult func PerformQwantTextSearch(query, safe, lang string) ([]TextSearchResult, error) { const resultsPerPage = 10 const offset = 0 // Ensure safe search is disabled by default if not specified if safe == "" { safe = "0" } // Default to English Canada locale if not specified if lang == "" { lang = "en_CA" } apiURL := fmt.Sprintf("https://api.qwant.com/v3/search/web?q=%s&count=%d&locale=%s&offset=%d&device=desktop", url.QueryEscape(query), resultsPerPage, lang, offset) client := &http.Client{Timeout: 10 * time.Second} req, err := http.NewRequest("GET", apiURL, nil) if err != nil { return nil, fmt.Errorf("creating request: %v", err) } req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36") resp, err := client.Do(req) if err != nil { return nil, fmt.Errorf("making request: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } var apiResp QwantTextAPIResponse if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { return nil, fmt.Errorf("decoding response: %v", err) } // Extracting results from the nested JSON structure if len(apiResp.Data.Result.Items.Mainline) == 0 { return nil, fmt.Errorf("no search results found") } var results []TextSearchResult for _, item := range apiResp.Data.Result.Items.Mainline[0].Items { cleanURL := cleanQwantURL(item.URL) results = append(results, TextSearchResult{ URL: cleanURL, Header: item.Title, Description: item.Description, Source: "Qwant", }) } return results, nil } // cleanQwantURL extracts the main part of the URL, removing tracking information func cleanQwantURL(rawURL string) string { u, err := url.Parse(rawURL) if err != nil { return rawURL } return u.Scheme + "://" + u.Host + u.Path }