package main import ( "encoding/json" "fmt" "net/http" "net/url" "os" "time" ) // Card represents the structure of a card in the API response. type Card struct { ID string `json:"id"` Slug string `json:"slug"` Name string `json:"name"` Hotscore int `json:"hotscore"` Guardian Guardian `json:"guardian"` Elements []Element `json:"elements"` Variants []Variant `json:"variants"` } // Guardian represents the structure of a guardian in the card details. type Guardian struct { ID string `json:"id"` Type string `json:"type"` Rarity string `json:"rarity"` TypeText string `json:"typeText"` SubType string `json:"subType"` RulesText string `json:"rulesText"` Cost int `json:"cost"` Attack *int `json:"attack"` Defense *int `json:"defense"` Life *int `json:"life"` WaterThreshold int `json:"waterThreshold"` EarthThreshold int `json:"earthThreshold"` FireThreshold int `json:"fireThreshold"` AirThreshold int `json:"airThreshold"` CardID string `json:"cardId"` } // Element represents the structure of an element in the card details. type Element struct { ID string `json:"id"` Name string `json:"name"` } // Variant represents the structure of a variant in the card details. type Variant struct { ID string `json:"id"` Slug string `json:"slug"` Src string `json:"src"` Finish string `json:"finish"` Product string `json:"product"` Artist string `json:"artist"` FlavorText string `json:"flavorText"` CardID string `json:"cardId"` SetCardID string `json:"setCardId"` SetCard SetCard `json:"setCard"` } // SetCard represents the structure of a set card in the card details. type SetCard struct { ID string `json:"id"` Slug string `json:"slug"` SetID string `json:"setId"` CardID string `json:"cardId"` Meta MetaData `json:"meta"` SetDetails SetDetails `json:"set"` } // MetaData represents the structure of meta data in the set card details. type MetaData struct { ID string `json:"id"` Type string `json:"type"` Rarity string `json:"rarity"` TypeText string `json:"typeText"` SubType string `json:"subType"` RulesText string `json:"rulesText"` Cost int `json:"cost"` Attack *int `json:"attack"` Defense *int `json:"defense"` Life *int `json:"life"` WaterThreshold int `json:"waterThreshold"` EarthThreshold int `json:"earthThreshold"` FireThreshold int `json:"fireThreshold"` AirThreshold int `json:"airThreshold"` SetCardID string `json:"setCardId"` } // SetDetails represents the structure of set details in the set card. type SetDetails struct { ID string `json:"id"` Name string `json:"name"` ReleaseDate string `json:"releaseDate"` } // JSONPayload represents the structure of the nested JSON for the API request. type JSONPayload struct { Query string `json:"query"` Sort string `json:"sort"` Set string `json:"set"` Filters []interface{} `json:"filters"` Limit int `json:"limit"` VariantLimit bool `json:"variantLimit"` CollectionLimit bool `json:"collectionLimit"` Cursor int `json:"cursor"` Direction string `json:"direction"` } // Input represents the top-level structure of the input JSON for the API request. type Input struct { Part0 struct { JSON JSONPayload `json:"json"` } `json:"0"` } // fetchCards fetches cards from the API using the provided limit and cursor. func fetchCards(limit, cursor int) ([]*Card, error) { set := "bet" // Define the input data structure input := Input{ Part0: struct { JSON JSONPayload `json:"json"` }{ JSON: JSONPayload{ Query: "", Sort: "relevance", Set: set, Filters: []interface{}{}, Limit: limit, VariantLimit: false, CollectionLimit: false, Cursor: cursor, Direction: "forward", }, }, } // Serialize the input data structure to JSON jsonData, err := json.Marshal(input) if err != nil { return nil, fmt.Errorf("failed to marshal input data: %w", err) } // URL encode the JSON data urlEncodedInput := url.QueryEscape(string(jsonData)) // Construct the full URL apiURL := fmt.Sprintf("https://curiosa.io/api/trpc/card.search?batch=1&input=%s", urlEncodedInput) // Create a new HTTP request req, err := http.NewRequest("GET", apiURL, nil) if err != nil { return nil, fmt.Errorf("failed to create request: %w", err) } // Set essential headers setHeaders(req) // Perform the HTTP request start := time.Now() client := &http.Client{} resp, err := client.Do(req) duration := time.Since(start) if err != nil { return nil, fmt.Errorf("failed to perform request: %w", err) } defer resp.Body.Close() // Check for non-200 status codes if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("non-200 response: %s", resp.Status) } // Read and decode the JSON response var result []struct { Result struct { Data struct { JSON struct { Cards []*Card `json:"cards"` } `json:"json"` } `json:"data"` } `json:"result"` } err = json.NewDecoder(resp.Body).Decode(&result) if err != nil { return nil, fmt.Errorf("failed to decode response body: %w", err) } // Extract cards from the result var cards []*Card if len(result) > 0 { cards = result[0].Result.Data.JSON.Cards } fmt.Printf("Cursor: %d, Limit: %d, Duration: %s, Status: %d, Cards: %d\n", cursor, limit, duration, resp.StatusCode, len(cards)) return cards, nil } // fetchAllCards fetches all cards by making multiple requests with different cursor values. func fetchAllCards(limit int) ([]*Card, error) { var allCards []*Card cursor := 0 for { cards, err := fetchCards(limit, cursor) if err != nil { return nil, err } allCards = append(allCards, cards...) // If the number of returned cards is less than the limit, we've fetched all cards. if len(cards) < limit { break } // Update cursor for the next batch cursor += limit } return allCards, nil } func main() { const limit = 100 allCards, err := fetchAllCards(limit) if err != nil { logErrorAndExit(err) } // Write all cards to cards.json in the current working directory file, err := os.Create("cards.json") if err != nil { logErrorAndExit(fmt.Errorf("failed to create cards.json: %w", err)) } defer file.Close() prettyData, err := json.MarshalIndent(allCards, "", " ") if err != nil { logErrorAndExit(fmt.Errorf("failed to marshal pretty JSON: %w", err)) } _, err = file.Write(prettyData) if err != nil { logErrorAndExit(fmt.Errorf("failed to write to cards.json: %w", err)) } fmt.Printf("All cards have been written to cards.json. Total cards: %d\n", len(allCards)) } // setHeaders sets the necessary headers for the HTTP request. func setHeaders(req *http.Request) { req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:126.0) Gecko/20100101 Firefox/126.0") req.Header.Set("Accept", "*/*") req.Header.Set("content-type", "application/json") } // logErrorAndExit logs the error to stderr and exits with a non-zero status code. func logErrorAndExit(err error) { fmt.Fprintln(os.Stderr, err) os.Exit(1) }