357 lines
11 KiB
Go
357 lines
11 KiB
Go
package serra
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/bson/primitive"
|
|
)
|
|
|
|
type Card struct {
|
|
// Added by Serra
|
|
SerraCount int64 `bson:"serra_count"`
|
|
SerraCountFoil int64 `bson:"serra_count_foil"`
|
|
SerraCountEtched int64 `bson:"serra_count_etched"`
|
|
SerraPrices []PriceEntry `bson:"serra_prices"`
|
|
SerraCreated primitive.DateTime `bson:"serra_created"`
|
|
SerraUpdated primitive.DateTime `bson:"serra_updated"`
|
|
|
|
Artist string `json:"artist"`
|
|
ArtistIds []string `json:"artist_ids"`
|
|
Booster bool `json:"booster"`
|
|
BorderColor string `json:"border_color"`
|
|
CardBackID string `json:"card_back_id"`
|
|
CardmarketID float64 `json:"cardmarket_id"`
|
|
Cmc float64 `json:"cmc"`
|
|
CollectorNumber string `json:"collector_number"`
|
|
ColorIdentity []string `json:"color_identity"`
|
|
Colors []string `json:"colors"`
|
|
Digital bool `json:"digital"`
|
|
EdhrecRank int64 `json:"edhrec_rank"`
|
|
Finishes []string `json:"finishes"`
|
|
Foil bool `json:"foil"`
|
|
Frame string `json:"frame"`
|
|
FullArt bool `json:"full_art"`
|
|
Games []string `json:"games"`
|
|
HighresImage bool `json:"highres_image"`
|
|
ID string `json:"id" bson:"_id"`
|
|
IllustrationID string `json:"illustration_id"`
|
|
ImageStatus string `json:"image_status"`
|
|
ImageUris struct {
|
|
ArtCrop string `json:"art_crop"`
|
|
BorderCrop string `json:"border_crop"`
|
|
Large string `json:"large"`
|
|
Normal string `json:"normal"`
|
|
Png string `json:"png"`
|
|
Small string `json:"small"`
|
|
} `json:"image_uris"`
|
|
Keywords []interface{} `json:"keywords"`
|
|
Lang string `json:"lang"`
|
|
Layout string `json:"layout"`
|
|
Legalities struct {
|
|
Alchemy string `json:"alchemy"`
|
|
Brawl string `json:"brawl"`
|
|
Commander string `json:"commander"`
|
|
Duel string `json:"duel"`
|
|
Future string `json:"future"`
|
|
Gladiator string `json:"gladiator"`
|
|
Historic string `json:"historic"`
|
|
Historicbrawl string `json:"historicbrawl"`
|
|
Legacy string `json:"legacy"`
|
|
Modern string `json:"modern"`
|
|
Oldschool string `json:"oldschool"`
|
|
Pauper string `json:"pauper"`
|
|
Paupercommander string `json:"paupercommander"`
|
|
Penny string `json:"penny"`
|
|
Pioneer string `json:"pioneer"`
|
|
Premodern string `json:"premodern"`
|
|
Standard string `json:"standard"`
|
|
Vintage string `json:"vintage"`
|
|
} `json:"legalities"`
|
|
ManaCost string `json:"mana_cost"`
|
|
MultiverseIds []interface{} `json:"multiverse_ids"`
|
|
Name string `json:"name"`
|
|
Nonfoil bool `json:"nonfoil"`
|
|
Object string `json:"object"`
|
|
OracleID string `json:"oracle_id"`
|
|
OracleText string `json:"oracle_text"`
|
|
Oversized bool `json:"oversized"`
|
|
Prices PriceEntry `json:"prices"`
|
|
PrintedName string `json:"printed_name"`
|
|
PrintedText string `json:"printed_text"`
|
|
PrintedTypeLine string `json:"printed_type_line"`
|
|
PrintsSearchURI string `json:"prints_search_uri"`
|
|
Promo bool `json:"promo"`
|
|
PurchaseUris struct {
|
|
Cardhoarder string `json:"cardhoarder"`
|
|
Cardmarket string `json:"cardmarket"`
|
|
Tcgplayer string `json:"tcgplayer"`
|
|
} `json:"purchase_uris"`
|
|
Rarity string `json:"rarity"`
|
|
RelatedUris struct {
|
|
Edhrec string `json:"edhrec"`
|
|
Mtgtop8 string `json:"mtgtop8"`
|
|
TcgplayerInfiniteArticles string `json:"tcgplayer_infinite_articles"`
|
|
TcgplayerInfiniteDecks string `json:"tcgplayer_infinite_decks"`
|
|
} `json:"related_uris"`
|
|
ReleasedAt string `json:"released_at"`
|
|
Reprint bool `json:"reprint"`
|
|
Reserved bool `json:"reserved"`
|
|
RulingsURI string `json:"rulings_uri"`
|
|
ScryfallSetURI string `json:"scryfall_set_uri"`
|
|
ScryfallURI string `json:"scryfall_uri"`
|
|
Set string `json:"set"`
|
|
SetID string `json:"set_id"`
|
|
SetName string `json:"set_name"`
|
|
SetSearchURI string `json:"set_search_uri"`
|
|
SetType string `json:"set_type"`
|
|
SetURI string `json:"set_uri"`
|
|
StorySpotlight bool `json:"story_spotlight"`
|
|
Textless bool `json:"textless"`
|
|
TCGPlayerID float64 `json:"tcgplayer_id"`
|
|
TypeLine string `json:"type_line"`
|
|
URI string `json:"uri"`
|
|
Variation bool `json:"variation"`
|
|
}
|
|
|
|
type BulkIndex struct {
|
|
Object string `json:"object"`
|
|
HasMore bool `json:"has_more"`
|
|
Data []struct {
|
|
Object string `json:"object"`
|
|
ID string `json:"id"`
|
|
Type string `json:"type"`
|
|
UpdatedAt time.Time `json:"updated_at"`
|
|
URI string `json:"uri"`
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
Size int `json:"size"`
|
|
DownloadURI string `json:"download_uri"`
|
|
ContentType string `json:"content_type"`
|
|
ContentEncoding string `json:"content_encoding"`
|
|
} `json:"data"`
|
|
}
|
|
|
|
func fetchBulkDownloadURL() (string, error) {
|
|
url := "https://api.scryfall.com/bulk-data"
|
|
downloadURL := ""
|
|
|
|
// Make an HTTP GET request
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
log.Fatalf("Error fetching data: %v", err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
// Read the response body
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatalf("Error reading response body: %v", err)
|
|
}
|
|
|
|
// Unmarshal the JSON response
|
|
var bulkData BulkIndex
|
|
if err := json.Unmarshal(body, &bulkData); err != nil {
|
|
log.Fatalf("Error unmarshaling JSON: %v", err)
|
|
}
|
|
|
|
// Find and print the unique cards URL
|
|
for _, item := range bulkData.Data {
|
|
if item.Type == "default_cards" {
|
|
downloadURL = item.DownloadURI
|
|
}
|
|
}
|
|
|
|
return downloadURL, nil
|
|
}
|
|
|
|
func downloadBulkData(downloadURL string) (string, error) {
|
|
|
|
// Create a temporary directory
|
|
tempDir, err := os.MkdirTemp("", "download")
|
|
if err != nil {
|
|
log.Fatalf("Error creating temporary directory: %v", err)
|
|
}
|
|
// defer os.RemoveAll(tempDir) // Clean up the directory when done
|
|
|
|
// Create a temporary file in the temporary directory
|
|
tempFile, err := os.CreateTemp(tempDir, "downloaded-*.json") // Adjust the extension if necessary
|
|
if err != nil {
|
|
log.Fatalf("Error creating temporary file: %v", err)
|
|
}
|
|
// defer tempFile.Close() // Ensure we close the file when we're done
|
|
|
|
// Download the file
|
|
resp, err := http.Get(downloadURL)
|
|
if err != nil {
|
|
log.Fatalf("Error downloading file: %v", err)
|
|
}
|
|
defer resp.Body.Close() // Make sure to close the response body
|
|
|
|
// Check for a successful response
|
|
if resp.StatusCode != http.StatusOK {
|
|
log.Fatalf("Error: received status code %d", resp.StatusCode)
|
|
}
|
|
|
|
// Copy the response body to the temporary file
|
|
_, err = io.Copy(tempFile, resp.Body)
|
|
if err != nil {
|
|
log.Fatalf("Error saving file: %v", err)
|
|
}
|
|
|
|
return tempFile.Name(), nil
|
|
}
|
|
|
|
func loadBulkFile(bulkFilePath string) ([]Card, error) {
|
|
|
|
var cards []Card
|
|
fileBytes, _ := os.ReadFile(bulkFilePath)
|
|
defer os.Remove(bulkFilePath)
|
|
|
|
err := json.Unmarshal(fileBytes, &cards)
|
|
if err != nil {
|
|
fmt.Println("Error unmarshalling bulk file:", err)
|
|
return cards, nil
|
|
}
|
|
|
|
return cards, nil
|
|
|
|
}
|
|
|
|
func getCardFromBulk(cards []Card, setName, collectorNumber string) (*Card, error) {
|
|
var foundCard Card
|
|
for _, v := range cards {
|
|
if v.CollectorNumber == collectorNumber && v.Set == setName {
|
|
foundCard = v
|
|
return &foundCard, nil
|
|
}
|
|
}
|
|
return &Card{}, fmt.Errorf("Card %s/%s not found in bulk data", setName, collectorNumber)
|
|
}
|
|
|
|
type PriceEntry struct {
|
|
Date primitive.DateTime `bson:"date"`
|
|
Eur float64 `json:"eur,string" bson:"eur,float64"`
|
|
EurFoil float64 `json:"eur_foil,string" bson:"eur_foil,float64"`
|
|
Tix float64 `json:"tix,string" bson:"tix,float64"`
|
|
Usd float64 `json:"usd,string" bson:"usd,float64"`
|
|
UsdEtched float64 `json:"usd_etched,string" bson:"usd_etched,float64"`
|
|
UsdFoil float64 `json:"usd_foil,string" bson:"usd_foil,float64"`
|
|
}
|
|
|
|
// Sets
|
|
|
|
type SetList struct {
|
|
Data []Set `json:"data"`
|
|
}
|
|
|
|
type Set struct {
|
|
SerraPrices []PriceEntry `bson:"serra_prices"`
|
|
SerraCreated primitive.DateTime `bson:"serra_created"`
|
|
SerraUpdated primitive.DateTime `bson:"serra_updated"`
|
|
CardCount int64 `json:"card_count" bson:"cardcount"`
|
|
Code string `json:"code"`
|
|
Digital bool `json:"digital"`
|
|
FoilOnly bool `json:"foil_only"`
|
|
IconSvgURI string `json:"icon_svg_uri"`
|
|
ID string `json:"id" bson:"_id"`
|
|
Name string `json:"name"`
|
|
NonfoilOnly bool `json:"nonfoil_only"`
|
|
Object string `json:"object"`
|
|
ReleasedAt string `json:"released_at"`
|
|
ScryfallURI string `json:"scryfall_uri"`
|
|
SearchURI string `json:"search_uri"`
|
|
SetType string `json:"set_type"`
|
|
TcgplayerID int64 `json:"tcgplayer_id"`
|
|
URI string `json:"uri"`
|
|
}
|
|
|
|
// Getter for currency specific value
|
|
func (c Card) getValue(foil bool) float64 {
|
|
if getCurrency() == EUR {
|
|
if foil {
|
|
return c.Prices.EurFoil
|
|
}
|
|
return c.Prices.Eur
|
|
}
|
|
if foil {
|
|
return c.Prices.UsdFoil
|
|
}
|
|
return c.Prices.Usd
|
|
}
|
|
|
|
func fetchCard(setName, collectorNumber string) (*Card, error) {
|
|
resp, err := http.Get(fmt.Sprintf("https://api.scryfall.com/cards/%s/%s/", setName, collectorNumber))
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
return &Card{}, err
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
return &Card{}, fmt.Errorf("Card %s/%s not found", setName, collectorNumber)
|
|
}
|
|
|
|
//we read the response body on the line below.
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatalf("%s", err)
|
|
return &Card{}, err
|
|
}
|
|
|
|
r := bytes.NewReader(body)
|
|
decoder := json.NewDecoder(r)
|
|
val := &Card{}
|
|
|
|
err = decoder.Decode(val)
|
|
if err != nil {
|
|
log.Fatalf("%s", err)
|
|
}
|
|
|
|
// Set created Time
|
|
val.SerraCreated = primitive.NewDateTimeFromTime(time.Now())
|
|
|
|
// Increase Price
|
|
val.Prices.Date = primitive.NewDateTimeFromTime(time.Now())
|
|
val.SerraPrices = append(val.SerraPrices, val.Prices)
|
|
|
|
return val, nil
|
|
}
|
|
|
|
func fetchSets() (*SetList, error) {
|
|
// TODO: better URL Building...
|
|
resp, err := http.Get("https://api.scryfall.com/sets")
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
return &SetList{}, err
|
|
}
|
|
|
|
if resp.StatusCode != 200 {
|
|
return &SetList{}, fmt.Errorf("/sets not found")
|
|
}
|
|
|
|
//We Read the response body on the line below.
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
return &SetList{}, err
|
|
}
|
|
|
|
r := bytes.NewReader(body)
|
|
decoder := json.NewDecoder(r)
|
|
val := &SetList{}
|
|
|
|
err = decoder.Decode(val)
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
|
|
return val, nil
|
|
}
|