diff --git a/forums.go b/forums.go new file mode 100644 index 0000000..348bc6c --- /dev/null +++ b/forums.go @@ -0,0 +1,149 @@ +// forums.go +package main + +import ( + "encoding/json" + "fmt" + "html/template" + "math" + "net/http" + "net/url" + "time" +) + +type ForumSearchResult struct { + URL string `json:"url"` + Header string `json:"header"` + Description string `json:"description"` + PublishedDate time.Time `json:"publishedDate"` + ImgSrc string `json:"imgSrc,omitempty"` + ThumbnailSrc string `json:"thumbnailSrc,omitempty"` +} + +func PerformRedditSearch(query string, safe string, page int) ([]ForumSearchResult, error) { + const ( + pageSize = 25 + baseURL = "https://www.reddit.com/" + maxRetries = 5 + initialBackoff = 2 * time.Second + ) + var results []ForumSearchResult + + searchURL := fmt.Sprintf("%ssearch.json?q=%s&limit=%d&start=%d", baseURL, url.QueryEscape(query), pageSize, page*pageSize) + var resp *http.Response + var err error + + // Retry logic with exponential backoff + for i := 0; i <= maxRetries; i++ { + resp, err = http.Get(searchURL) + if err != nil { + return nil, fmt.Errorf("making request: %v", err) + } + if resp.StatusCode != http.StatusTooManyRequests { + break + } + + // Wait for some time before retrying + backoff := time.Duration(math.Pow(2, float64(i))) * initialBackoff + time.Sleep(backoff) + } + + 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 searchResults map[string]interface{} + if err := json.NewDecoder(resp.Body).Decode(&searchResults); err != nil { + return nil, fmt.Errorf("decoding response: %v", err) + } + + data, ok := searchResults["data"].(map[string]interface{}) + if !ok { + return nil, fmt.Errorf("no data field in response") + } + + posts, ok := data["children"].([]interface{}) + if !ok { + return nil, fmt.Errorf("no children field in data") + } + + for _, post := range posts { + postData := post.(map[string]interface{})["data"].(map[string]interface{}) + + if safe == "active" && postData["over_18"].(bool) { + continue + } + + header := postData["title"].(string) + description := postData["selftext"].(string) + if len(description) > 500 { + description = description[:500] + "..." + } + publishedDate := time.Unix(int64(postData["created_utc"].(float64)), 0) + permalink := postData["permalink"].(string) + resultURL := baseURL + permalink + + result := ForumSearchResult{ + URL: resultURL, + Header: header, + Description: description, + PublishedDate: publishedDate, + } + + thumbnail := postData["thumbnail"].(string) + if parsedURL, err := url.Parse(thumbnail); err == nil && parsedURL.Scheme != "" { + result.ImgSrc = postData["url"].(string) + result.ThumbnailSrc = thumbnail + } + + results = append(results, result) + } + + return results, nil +} + +func handleForumsSearch(w http.ResponseWriter, query, safe, lang string, page int) { + results, err := PerformRedditSearch(query, safe, page) + if err != nil { + http.Error(w, fmt.Sprintf("Error performing search: %v", err), http.StatusInternalServerError) + return + } + + data := struct { + Query string + Results []ForumSearchResult + LanguageOptions []LanguageOption + CurrentLang string + Page int + HasPrevPage bool + HasNextPage bool + }{ + Query: query, + Results: results, + LanguageOptions: languageOptions, + CurrentLang: lang, + Page: page, + HasPrevPage: page > 1, + HasNextPage: len(results) == 25, + } + + funcMap := template.FuncMap{ + "sub": func(a, b int) int { return a - b }, + "add": func(a, b int) int { return a + b }, + } + + tmpl, err := template.New("forums.html").Funcs(funcMap).ParseFiles("templates/forums.html") + if err != nil { + http.Error(w, fmt.Sprintf("Error loading template: %v", err), http.StatusInternalServerError) + return + } + + if err := tmpl.Execute(w, data); err != nil { + http.Error(w, fmt.Sprintf("Error rendering template: %v", err), http.StatusInternalServerError) + } +} diff --git a/main.go b/main.go index 585e98f..9a5ec3a 100644 --- a/main.go +++ b/main.go @@ -91,6 +91,8 @@ func handleSearch(w http.ResponseWriter, r *http.Request) { videoSearchEndpointHandler(w, r) case "map": handleMapSearch(w, query, safe) + case "forum": + handleForumsSearch(w, query, safe, lang, page) case "text": fallthrough default: diff --git a/map.go b/map.go index c260fa7..7ad21ff 100644 --- a/map.go +++ b/map.go @@ -14,7 +14,7 @@ type NominatimResponse struct { Lon string `json:"lon"` } -func geocodeQuery(query string) (latitude, longitude string, err error) { +func geocodeQuery(query string) (latitude, longitude string, found bool, err error) { // URL encode the query query = url.QueryEscape(query) @@ -24,29 +24,29 @@ func geocodeQuery(query string) (latitude, longitude string, err error) { // Make the HTTP GET request resp, err := http.Get(urlString) if err != nil { - return "", "", err + return "", "", false, err } defer resp.Body.Close() // Read the response var result []NominatimResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { - return "", "", err + return "", "", false, err } // Check if there are any results if len(result) > 0 { latitude = result[0].Lat longitude = result[0].Lon - return latitude, longitude, nil + return latitude, longitude, true, nil } - return "", "", fmt.Errorf("no results found") + return "", "", false, nil } func handleMapSearch(w http.ResponseWriter, query string, lang string) { // Geocode the query to get coordinates - latitude, longitude, err := geocodeQuery(query) + latitude, longitude, found, err := geocodeQuery(query) if err != nil { log.Printf("Error geocoding query: %s, error: %v", query, err) http.Error(w, "Failed to find location", http.StatusInternalServerError) @@ -58,6 +58,7 @@ func handleMapSearch(w http.ResponseWriter, query string, lang string) { "Query": query, "Latitude": latitude, "Longitude": longitude, + "Found": found, } tmpl, err := template.ParseFiles("templates/map.html") diff --git a/run.sh b/run.sh index 5758a55..f56f9fc 100755 --- a/run.sh +++ b/run.sh @@ -1,3 +1,3 @@ #!/bin/bash -go run main.go text-google.go images.go imageproxy.go video.go map.go text.go text-quant.go text-duckduckgo.go cache.go --debug \ No newline at end of file +go run main.go text-google.go images.go imageproxy.go video.go map.go text.go text-quant.go text-duckduckgo.go cache.go forums.go --debug \ No newline at end of file diff --git a/static/css/style.css b/static/css/style.css index 2b84caf..c444071 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1349,6 +1349,23 @@ p { letter-spacing: normal; } +.message { + position: absolute; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + padding: 10px; + background-color: var(--html-bg); + border: 1px solid var(--border); + border-radius: 15px; + box-shadow: 0 0 10px rgba(0,0,0,0.1); + z-index: 1000; + width: auto; + max-width: 80%; + text-align: center; + color: var(--text-color); +} + /* Variables for light theme */ :root { --background-color: #ffffff; diff --git a/templates/forums.html b/templates/forums.html new file mode 100644 index 0000000..ce52762 --- /dev/null +++ b/templates/forums.html @@ -0,0 +1,90 @@ + + +
+ + +