package main import ( "encoding/json" "fmt" "log/slog" "math/rand/v2" "net/http" "github.com/PuerkitoBio/goquery" spinhttp "github.com/fermyon/spin/sdk/go/v2/http" "github.com/julienschmidt/httprouter" ) const ( contentTypeHeader = "Content-Type" ) type MonkeyUserEntry struct { Title string `json:"title"` URL string `json:"url"` } type ImageReference struct { URL string `json:"url"` } func init() { router := spinhttp.NewRouter() router.GET("/monkeyuser", monkeyUserComic) spinhttp.Handle(router.ServeHTTP) } func monkeyUserComic(w http.ResponseWriter, re *http.Request, _ httprouter.Params) { indexResponse, err := spinhttp.Get("https://www.monkeyuser.com/index.json") if err != nil { slog.Error("failed to fetch MonkeyUser index", slog.String("err", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) return } slog.Info("Fetched MonkeyUser index") defer indexResponse.Body.Close() if indexResponse.StatusCode != http.StatusOK { w.WriteHeader(indexResponse.StatusCode) return } var entries []MonkeyUserEntry if err := json.NewDecoder(indexResponse.Body).Decode(&entries); err != nil { slog.Error("failed to decode MonkeyUser index", slog.String("err", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) return } slog.Info("Decoded MonkeyUser index") entry := entries[rand.IntN(len(entries))] pageResponse, err := spinhttp.Get(fmt.Sprintf("https://www.monkeyuser.com%s", entry.URL)) if err != nil { slog.Error("failed to fetch monkeyuser comic page", slog.String("err", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) return } defer pageResponse.Body.Close() if pageResponse.StatusCode != http.StatusOK { w.WriteHeader(pageResponse.StatusCode) return } comicDoc, err := goquery.NewDocumentFromReader(pageResponse.Body) if err != nil { slog.Error("failed to parse comic page", slog.String("err", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) return } var ref ImageReference comicDoc.Find("div.content > p > img").Each(func(_ int, s *goquery.Selection) { for _, node := range s.Nodes { for _, attr := range node.Attr { if attr.Key == "src" { ref.URL = fmt.Sprintf("https://www.monkeyuser.com%s", attr.Val) break } } slog.Info("found URL", slog.String("url", ref.URL)) } }) w.Header().Add(contentTypeHeader, "application/json") w.WriteHeader(http.StatusOK) if err := json.NewEncoder(w).Encode(ref); err != nil { slog.Error("failed to encode image ref", slog.String("err", err.Error())) http.Error(w, err.Error(), http.StatusInternalServerError) } }