148 lines
3.9 KiB
Go
148 lines
3.9 KiB
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("%s/search.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 := fmt.Sprintf("%s%s", 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)
|
|
}
|
|
}
|