package serra import ( "errors" "fmt" "math" "os" "strconv" "time" "unicode" "github.com/charmbracelet/log" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" ) type Rarities struct { Rares, Uncommons, Commons, Mythics float64 } var ( Icon = "\U0001F9D9\U0001F3FC" Reset = "\033[0m" Background = "\033[38;5;59m" CurrentLine = "\033[38;5;60m" Foreground = "\033[38;5;231m" Comment = "\033[38;5;103m" Cyan = "\033[38;5;159m" Green = "\033[38;5;120m" Orange = "\033[38;5;222m" Pink = "\033[38;5;212m" Purple = "\033[38;5;183m" Red = "\033[38;5;210m" Yellow = "\033[38;5;229m" ) func Logger() *log.Logger { l := log.New(os.Stderr) l.SetReportTimestamp(false) return l } func getStoredCard(coll *Collection, c *Card) (Card, error) { // find already existing card sort := bson.D{{"_id", 1}} searchFilter := bson.D{{"_id", c.ID}} storedCards, err := coll.storageFind(searchFilter, sort, 0, 0) if err != nil { return Card{}, err } return storedCards[0], nil } func modifyCardCount(coll *Collection, c *Card, amount int64, foil bool) error { l := Logger() storedCard, err := getStoredCard(coll, c) if err != nil { return err } // update card amount var update bson.M if foil { update = bson.M{ "$set": bson.M{"serra_count_foil": storedCard.SerraCountFoil + amount}, } } else { update = bson.M{ "$set": bson.M{"serra_count": storedCard.SerraCount + amount}, } } coll.storageUpdate(bson.M{"_id": bson.M{"$eq": c.ID}}, update) var total int64 if foil { total = storedCard.SerraCountFoil + amount if amount < 0 { l.Warnf("Reduced card amount of \"%s\" (%.2f%s, foil) from %d to %d", storedCard.Name, storedCard.getValue(true), getCurrency(), storedCard.SerraCountFoil, total) } else { l.Warnf("Increased card amount of \"%s\" (%.2f%s, foil) from %d to %d", storedCard.Name, storedCard.getValue(true), getCurrency(), storedCard.SerraCountFoil, total) } } else { total = storedCard.SerraCount + amount if amount < 0 { l.Warnf("Reduced card amount of \"%s\" (%.2f%s) from %d to %d", storedCard.Name, storedCard.getValue(false), getCurrency(), storedCard.SerraCount, total) } else { l.Warnf("Increased card amount of \"%s\" (%.2f%s) from %d to %d", storedCard.Name, storedCard.getValue(false), getCurrency(), storedCard.SerraCount, total) } } return nil } func modifyCardDeckCount(coll *Collection, c *Card, amount int64, foil bool) error { l := Logger() storedCard, err := getStoredCard(coll, c) if err != nil { return err } // update card amount var update bson.M if foil { newAmount := min(max(storedCard.SerraCountFoilDeck + amount, 0), storedCard.SerraCountFoil) l.Infof("%d / %d available", storedCard.SerraCountFoil - newAmount, storedCard.SerraCountFoil) update = bson.M{ "$set": bson.M{"serra_count_foil_deck": newAmount}, } } else { newAmount := min(max(storedCard.SerraCountDeck + amount, 0), storedCard.SerraCount) l.Infof("%d / %d available", storedCard.SerraCount - newAmount, storedCard.SerraCount) update = bson.M{ "$set": bson.M{"serra_count_deck": newAmount}, } } coll.storageUpdate(bson.M{"_id": bson.M{"$eq": c.ID}}, update) return nil } func findCardByCollectorNumber(coll *Collection, setCode string, collectorNumber string) (*Card, error) { sort := bson.D{{"_id", 1}} searchFilter := bson.D{{"set", setCode}, {"collectornumber", collectorNumber}} storedCards, err := coll.storageFind(searchFilter, sort, 0, 0) if err != nil { return &Card{}, err } if len(storedCards) < 1 { return &Card{}, errors.New("Card not found") } return &storedCards[0], nil } func stringToTime(s primitive.DateTime) string { return time.UnixMilli(int64(s)).Format("2006-01-02") } // missing compares two slices and returns slice of differences func missing(a, b []string) []string { type void struct{} // create map with length of the 'a' slice ma := make(map[string]void, len(a)) diffs := []string{} // Convert first slice to map with empty struct (0 bytes) for _, ka := range a { ma[ka] = void{} } // find missing values in a for _, kb := range b { if _, ok := ma[kb]; !ok { diffs = append(diffs, kb) } } return diffs } func findSetByCode(coll *Collection, setcode string) (*Set, error) { storedSets, err := coll.storageFindSet(bson.D{{"code", setcode}}, bson.D{{"_id", 1}}) if err != nil { return &Set{}, err } if len(storedSets) < 1 { return &Set{}, errors.New("Set not found") } return &storedSets[0], nil } func convertManaSymbols(sym []interface{}) string { var mana string if len(sym) == 0 { mana = mana + "None" //probibited sign for lands } for _, v := range sym { switch v { case "B": mana = mana + "Black" //black case "R": mana = mana + "Red" //red case "G": mana = mana + "Green" //green case "U": mana = mana + "Blue" //blue case "W": mana = mana + "White" //white } } return mana } func convertRarities(rar []primitive.M) Rarities { // this is maybe the ugliest way someone could choose to verify, if a rarity type is missing // [ // { _id: { rarity: 'common' }, count: 20 }, // { _id: { rarity: 'uncommon' }, count: 2 } // ] // if a result like this is there, 1 rarity type "rare" is not in the array. and needs to be // initialized with 0, otherwise we get a panic var ri Rarities for _, r := range rar { switch r["_id"] { case "rare": ri.Rares = r["count"].(float64) case "uncommon": ri.Uncommons = r["count"].(float64) case "common": ri.Commons = r["count"].(float64) case "mythic": ri.Mythics = r["count"].(float64) } } return ri } func showPriceHistory(prices []PriceEntry, prefix string, total bool) { var before float64 for _, e := range prices { var value float64 if total { if getCurrency() == EUR { value = e.Eur + e.EurFoil } else { value = e.Usd + e.UsdFoil } } else { if getCurrency() == EUR { value = e.Eur } else { value = e.Usd } } if value > before && before != 0 { fmt.Printf("%s%s%s %.2f%s%s (%+.2f%%, %+.2f%s)\n", prefix, stringToTime(e.Date), Green, value, getCurrency(), Reset, (value/before*100)-100, value-before, getCurrency()) } else if value < before { fmt.Printf("%s%s%s %.2f%s%s (%+.2f%%, %+.2f%s)\n", prefix, stringToTime(e.Date), Red, value, getCurrency(), Reset, (value/before*100)-100, value-before, getCurrency()) } else { fmt.Printf("%s%s %.2f%s%s\n", prefix, stringToTime(e.Date), value, getCurrency(), Reset) } before = value } } func filterForDigits(str string) int { var numStr string for _, c := range str { if unicode.IsDigit(c) { numStr += string(c) } } s, _ := strconv.Atoi(numStr) return s } func getFloat64(unknown interface{}) (float64, error) { switch i := unknown.(type) { case float64: return i, nil case float32: return float64(i), nil case int64: return float64(i), nil case int32: return float64(i), nil case int: return float64(i), nil case uint64: return float64(i), nil case uint32: return float64(i), nil case uint: return float64(i), nil default: return math.NaN(), errors.New("non-numeric type could not be converted to float") } } func coloredValue(value float64) string { outputColor := Reset if value > 1 { outputColor = Green } if value > 5 { outputColor = Yellow } if value > 10 { outputColor = Red } return outputColor } func askConfirmation(card *Card) bool { drawImage(card) response := "some invalid response" for response != "y" && response != "n" { fmt.Println("Is this correct (y/n)?") fmt.Scanln(&response) } return response == "y" }