2024-08-21 22:36:45 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
)
|
|
|
|
|
|
|
|
func handleSuggestions(w http.ResponseWriter, r *http.Request) {
|
|
|
|
query := r.URL.Query().Get("q")
|
|
|
|
if query == "" {
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
fmt.Fprintf(w, `["",[]]`)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// Define the fallback sequence with Google lower in the hierarchy
|
|
|
|
suggestionSources := []func(string) []string{
|
|
|
|
fetchDuckDuckGoSuggestions,
|
|
|
|
fetchEdgeSuggestions,
|
|
|
|
fetchBraveSuggestions,
|
|
|
|
fetchEcosiaSuggestions,
|
|
|
|
fetchQwantSuggestions,
|
|
|
|
fetchStartpageSuggestions,
|
|
|
|
// fetchGoogleSuggestions, // I advise against it, but you can use it if you want to
|
|
|
|
}
|
|
|
|
|
|
|
|
var suggestions []string
|
|
|
|
for _, fetchFunc := range suggestionSources {
|
|
|
|
suggestions = fetchFunc(query)
|
|
|
|
if len(suggestions) > 0 {
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Suggestions found using %T", fetchFunc)
|
2024-08-21 22:36:45 +02:00
|
|
|
break
|
|
|
|
} else {
|
2024-08-21 23:23:08 +02:00
|
|
|
printWarn("%T did not return any suggestions or failed.", fetchFunc)
|
2024-08-21 22:36:45 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(suggestions) == 0 {
|
2024-08-21 23:23:08 +02:00
|
|
|
printErr("All suggestion services failed. Returning empty response.")
|
2024-08-21 22:36:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Return the final suggestions as JSON
|
|
|
|
w.Header().Set("Content-Type", "application/json")
|
|
|
|
fmt.Fprintf(w, `["",%s]`, toJSONStringArray(suggestions))
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchGoogleSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("http://suggestqueries.google.com/complete/search?client=firefox&q=%s", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from Google: %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchDuckDuckGoSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("https://duckduckgo.com/ac/?q=%s&type=list", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from DuckDuckGo: %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchEdgeSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("https://api.bing.com/osjson.aspx?query=%s", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from Edge (Bing): %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchBraveSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("https://search.brave.com/api/suggest?q=%s", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from Brave: %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchEcosiaSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("https://ac.ecosia.org/?q=%s&type=list", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from Ecosia: %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchQwantSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("https://api.qwant.com/v3/suggest?q=%s", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from Qwant: %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchStartpageSuggestions(query string) []string {
|
|
|
|
encodedQuery := url.QueryEscape(query)
|
|
|
|
url := fmt.Sprintf("https://startpage.com/suggestions?q=%s", encodedQuery)
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Fetching suggestions from Startpage: %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return fetchSuggestionsFromURL(url)
|
|
|
|
}
|
|
|
|
|
|
|
|
func fetchSuggestionsFromURL(url string) []string {
|
|
|
|
resp, err := http.Get(url)
|
|
|
|
if err != nil {
|
2024-08-21 23:23:08 +02:00
|
|
|
printWarn("Error fetching suggestions from %s: %v", url, err)
|
2024-08-21 22:36:45 +02:00
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
|
|
if err != nil {
|
2024-08-21 23:23:08 +02:00
|
|
|
printWarn("Error reading response body from %s: %v", url, err)
|
2024-08-21 22:36:45 +02:00
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log the Content-Type for debugging
|
|
|
|
contentType := resp.Header.Get("Content-Type")
|
2024-08-21 23:23:08 +02:00
|
|
|
printDebug("Response Content-Type from %s: %s", url, contentType)
|
2024-08-21 22:36:45 +02:00
|
|
|
|
|
|
|
// Check if the body is non-empty
|
|
|
|
if len(body) == 0 {
|
2024-08-21 23:23:08 +02:00
|
|
|
printWarn("Received empty response body from %s", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Attempt to parse the response as JSON regardless of Content-Type
|
|
|
|
var parsedResponse []interface{}
|
|
|
|
if err := json.Unmarshal(body, &parsedResponse); err != nil {
|
2024-08-21 23:23:08 +02:00
|
|
|
printErr("Error parsing JSON from %s: %v", url, err)
|
|
|
|
printDebug("Response body: %s", string(body))
|
2024-08-21 22:36:45 +02:00
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Ensure the response structure is as expected
|
|
|
|
if len(parsedResponse) < 2 {
|
2024-08-21 23:23:08 +02:00
|
|
|
printWarn("Unexpected response format from %v: %v", url, string(body))
|
2024-08-21 22:36:45 +02:00
|
|
|
return []string{}
|
|
|
|
}
|
|
|
|
|
|
|
|
suggestions := []string{}
|
|
|
|
if items, ok := parsedResponse[1].([]interface{}); ok {
|
|
|
|
for _, item := range items {
|
|
|
|
if suggestion, ok := item.(string); ok {
|
|
|
|
suggestions = append(suggestions, suggestion)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2024-08-21 23:23:08 +02:00
|
|
|
printErr("Unexpected suggestions format in response from: %v", url)
|
2024-08-21 22:36:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return suggestions
|
|
|
|
}
|
|
|
|
|
|
|
|
func toJSONStringArray(strings []string) string {
|
|
|
|
result := ""
|
|
|
|
for i, str := range strings {
|
|
|
|
result += fmt.Sprintf(`"%s"`, str)
|
|
|
|
if i < len(strings)-1 {
|
|
|
|
result += ","
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return "[" + result + "]"
|
|
|
|
}
|