diff --git a/aid/aid.go b/aid/aid.go index 0ce8439..549e968 100644 --- a/aid/aid.go +++ b/aid/aid.go @@ -4,6 +4,7 @@ import ( m "math/rand" "os" "os/signal" + "regexp" "strconv" "syscall" ) @@ -24,6 +25,10 @@ func RandomString(n int) string { return string(s) } +func RandomInt(min, max int) int { + return m.Intn(max - min) + min +} + func FormatNumber(number int) string { str := "" for i, char := range ReverseString(strconv.Itoa(number)) { @@ -52,4 +57,14 @@ func ToHex(number int) string { } return inta +} + +func Regex(str, regex string) *string { + // reg := regexp.MustCompile(`(?:CID_)(\d+|A_\d+)(?:_.+)`).FindStringSubmatch(strings.Join(split[:], "_")) + reg := regexp.MustCompile(regex).FindStringSubmatch(str) + if len(reg) > 1 { + return ®[1] + } + + return nil } \ No newline at end of file diff --git a/discord/admin.go b/discord/admin.go index d2d2857..ceb3b8c 100644 --- a/discord/admin.go +++ b/discord/admin.go @@ -376,6 +376,7 @@ func fillItemsHandler(s *discordgo.Session, i *discordgo.InteractionCreate, look fortnite.GiveEverything(player) str := player.DisplayName + " has been granted all items." + s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{ Content: &str, }) diff --git a/fortnite/external.go b/fortnite/external.go new file mode 100644 index 0000000..8ba10ee --- /dev/null +++ b/fortnite/external.go @@ -0,0 +1,248 @@ +package fortnite + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "regexp" + "slices" + "strings" + + "github.com/ectrc/snow/aid" + "github.com/ectrc/snow/storage" +) + +var ( + External *ExternalDataClient + assetRegex = regexp.MustCompile(`DAv2_([A-Za-z]+)_`) +) + +type ExternalDataClient struct { + h *http.Client + FortniteSets map[string]*FortniteSet + FortniteItems map[string]*FortniteItem + FortniteItemsWithDisplayAssets map[string]*FortniteItem + FortniteItemsWithFeaturedImage []*FortniteItem + TypedFortniteItems map[string][]*FortniteItem + TypedFortniteItemsWithDisplayAssets map[string][]*FortniteItem + SnowVariantTokens map[string]SnowCosmeticVariantToken +} + +func NewExternalDataClient() *ExternalDataClient { + return &ExternalDataClient{ + h: &http.Client{}, + FortniteItems: make(map[string]*FortniteItem), + FortniteSets: make(map[string]*FortniteSet), + FortniteItemsWithDisplayAssets: make(map[string]*FortniteItem), + FortniteItemsWithFeaturedImage: []*FortniteItem{}, + TypedFortniteItems: make(map[string][]*FortniteItem), + TypedFortniteItemsWithDisplayAssets: make(map[string][]*FortniteItem), + SnowVariantTokens: make(map[string]SnowCosmeticVariantToken), + } +} + +func (c *ExternalDataClient) LoadExternalData() { + req, err := http.NewRequest("GET", "https://fortnite-api.com/v2/cosmetics/br", nil) + if err != nil { + return + } + + resp, err := c.h.Do(req) + if err != nil { + return + } + + bodyBytes, err := io.ReadAll(resp.Body) + if err != nil { + return + } + + content := &FortniteCosmeticsResponse{} + err = json.Unmarshal(bodyBytes, content) + if err != nil { + return + } + + for _, item := range content.Data { + c.LoadItem(&item) + } + + for _, item := range c.TypedFortniteItems["AthenaBackpack"] { + c.AddBackpackToItem(item) + } + + displayAssets := storage.HttpAsset[[]string]("QKnwROGzQjYm1W9xu9uL3VrbSA0tnVj6NJJtEChUdAb3DF8uN.json") + if displayAssets == nil { + return + } + + for _, displayAsset := range *displayAssets { + c.AddDisplayAssetToItem(displayAsset) + } + + variantTokens := storage.HttpAsset[map[string]SnowCosmeticVariantToken]("QF3nHCFt1vhELoU4q1VKTmpxnk20c2iAiBEBzlbzQAY.json") + if variantTokens == nil { + return + } + c.SnowVariantTokens = *variantTokens + + addNumericStylesToSets := []string{"Soccer", "Football", "ScaryBall"} + for _, setValue := range addNumericStylesToSets { + set, found := c.FortniteSets[setValue] + if !found { + aid.Print("set not found: " + setValue) + continue + } + + for _, item := range set.Items { + c.AddNumericStylesToItem(item) + } + } +} + +func (c *ExternalDataClient) LoadItem(item *FortniteItem) { + if item.Introduction.BackendValue > aid.Config.Fortnite.Season || item.Introduction.BackendValue == 0 { + return + } + + if c.FortniteSets[item.Set.BackendValue] == nil { + c.FortniteSets[item.Set.BackendValue] = &FortniteSet{ + BackendName: item.Set.Value, + DisplayName: item.Set.Text, + Items: []*FortniteItem{}, + } + } + + if c.TypedFortniteItems[item.Type.BackendValue] == nil { + c.TypedFortniteItems[item.Type.BackendValue] = []*FortniteItem{} + } + + c.FortniteItems[item.ID] = item + c.FortniteSets[item.Set.BackendValue].Items = append(c.FortniteSets[item.Set.BackendValue].Items, item) + c.TypedFortniteItems[item.Type.BackendValue] = append(c.TypedFortniteItems[item.Type.BackendValue], item) + + if item.Type.BackendValue != "AthenaCharacter" || item.Images.Featured == "" || slices.Contains[[]string]([]string{ + "Soccer", + "Football", + "Waypoint", + }, item.Set.BackendValue) { + return + } + + for _, tag := range item.GameplayTags { + if strings.Contains(tag, "StarterPack") { + return + } + } + + c.FortniteItemsWithFeaturedImage = append(c.FortniteItemsWithFeaturedImage, item) +} + +func (c *ExternalDataClient) AddBackpackToItem(backpack *FortniteItem) { + if backpack.ItemPreviewHeroPath == "" { + return + } + + splitter := strings.Split(backpack.ItemPreviewHeroPath, "/") + character, found := c.FortniteItems[splitter[len(splitter) - 1]] + if !found { + return + } + + character.Backpack = backpack +} + +func (c *ExternalDataClient) AddDisplayAssetToItem(displayAsset string) { + split := strings.Split(displayAsset, "_")[1:] + found := c.FortniteItems[strings.Join(split[:], "_")] + + if found == nil && split[0] == "CID" { + r := aid.Regex(strings.Join(split[:], "_"), `(?:CID_)(\d+|A_\d+)(?:_.+)`) + if r != nil { + found = ItemByShallowID(*r) + } + } + + if found == nil { + return + } + + found.DisplayAssetPath2 = displayAsset + c.FortniteItemsWithDisplayAssets[found.ID] = found + c.TypedFortniteItemsWithDisplayAssets[found.Type.BackendValue] = append(c.TypedFortniteItemsWithDisplayAssets[found.Type.BackendValue], found) +} + +func (c *ExternalDataClient) AddNumericStylesToItem(item *FortniteItem) { + ownedStyles := []FortniteVariantChannel{} + for i := 0; i < 100; i++ { + ownedStyles = append(ownedStyles, FortniteVariantChannel{ + Tag: fmt.Sprint(i), + }) + } + + item.Variants = append(item.Variants, FortniteVariant{ + Channel: "Numeric", + Type: "int", + Options: ownedStyles, + }) +} + +func PreloadCosmetics() error { + External = NewExternalDataClient() + External.LoadExternalData() + + aid.Print("(snow) " + fmt.Sprint(len(External.FortniteItems)) + " cosmetics loaded from fortnite-api.com") + return nil +} + +func ItemByShallowID(shallowID string) *FortniteItem { + for _, item := range External.TypedFortniteItems["AthenaCharacter"] { + if strings.Contains(item.ID, shallowID) { + return item + } + } + + return nil +} + +func RandomItemByType(itemType string) *FortniteItem { + items := External.TypedFortniteItemsWithDisplayAssets[itemType] + if len(items) == 0 { + return nil + } + + return items[aid.RandomInt(0, len(items))] +} + +func RandomItemByNotType(notItemType string) *FortniteItem { + allItems := []*FortniteItem{} + + for key, items := range External.TypedFortniteItemsWithDisplayAssets { + if key == notItemType { + continue + } + + allItems = append(allItems, items...) + } + + return allItems[aid.RandomInt(0, len(allItems))] +} + +func RandomItemWithFeaturedImage() *FortniteItem { + items := External.FortniteItemsWithFeaturedImage + if len(items) == 0 { + return nil + } + + return items[aid.RandomInt(0, len(items))] +} + +func RandomSet() *FortniteSet { + sets := []*FortniteSet{} + for _, set := range External.FortniteSets { + sets = append(sets, set) + } + + return sets[aid.RandomInt(0, len(sets))] +} \ No newline at end of file diff --git a/fortnite/image.go b/fortnite/image.go index 426aecd..dc762e7 100644 --- a/fortnite/image.go +++ b/fortnite/image.go @@ -8,7 +8,6 @@ import ( "io" "math" "net/http" - "strings" "github.com/ectrc/snow/aid" "github.com/ectrc/snow/storage" @@ -71,70 +70,8 @@ func colorDifference(c1, c2 colours) float64 { return math.Sqrt(float64(diffRed*diffRed + diffGreen*diffGreen + diffBlue*diffBlue)) } -func GetCharacterImage(characterId string) image.Image { - character, ok := Cosmetics.Items[characterId] - if !ok { - return getRandomCharacterImage() - } - - response, err := http.Get(character.Images.Featured) - if err != nil { - return getRandomCharacterImage() - } - defer response.Body.Close() - - b, err := io.ReadAll(response.Body) - if err != nil { - panic(err) - } - - image, _, err := image.Decode(bytes.NewReader(b)) - if err != nil { - panic(err) - } - - return image -} - func getRandomCharacterImage() image.Image { - found := false - var character FAPI_Cosmetic - for !found { - character = Cosmetics.GetRandomItemByType("AthenaCharacter") - - if character.Images.Featured == "" { - continue - } - - continueLoop := false - for _, set := range SETS_NOT_ALLOWED { - if strings.Contains(character.Set.Value, set) { - continueLoop = true - break - } - } - - if continueLoop { - continue - } - - if character.Introduction.BackendValue < 2 { - continue - } - - for _, tag := range character.GameplayTags { - if strings.Contains(tag, "StarterPack") { - continueLoop = true - break - } - } - if continueLoop { - continue - } - - found = true - } - + character := RandomItemWithFeaturedImage() response, err := http.Get(character.Images.Featured) if err != nil { panic(err) @@ -168,32 +105,12 @@ func getRandomCharacterImageWithSimilarColour(colour colours) image.Image { func GenerateSoloImage() { background := *storage.Asset("background.png") - itemFound := Cosmetics.GetRandomItemByType("AthenaCharacter") - for itemFound.Images.Featured == "" { - itemFound = Cosmetics.GetRandomItemByType("AthenaCharacter") - } - - res, err := http.Get(itemFound.Images.Featured) - if err != nil { - panic(err) - } - defer res.Body.Close() - - data, err := io.ReadAll(res.Body) - if err != nil { - panic(err) - } - + soloPlayer := getRandomCharacterImage() bg, _, err := image.Decode(bytes.NewReader(background)) if err != nil { panic(err) } - soloPlayer, _, err := image.Decode(bytes.NewReader(data)) - if err != nil { - panic(err) - } - m := image.NewRGBA(bg.Bounds()) draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) @@ -224,7 +141,6 @@ func GenerateDuoImage() { panic(err) } - m := image.NewRGBA(bg.Bounds()) draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) @@ -378,6 +294,5 @@ func GeneratePlaylistImages() { GenerateDuoImage() GenerateTrioImage() GenerateSquadImage() - aid.Print("(snow) generated playlist images") } \ No newline at end of file diff --git a/fortnite/interact.go b/fortnite/interact.go deleted file mode 100644 index 9933853..0000000 --- a/fortnite/interact.go +++ /dev/null @@ -1,595 +0,0 @@ -package fortnite - -import ( - "encoding/json" - "fmt" - "io" - "math/rand" - "net/http" - "slices" - "strings" - - "github.com/ectrc/snow/aid" - "github.com/ectrc/snow/storage" -) - -type FortniteAPI struct { - URL string - C *http.Client -} - -type FAPI_Response struct { - Status int `json:"status"` - Data []FAPI_Cosmetic `json:"data"` -} - -type FAPI_Error struct { - Status int `json:"status"` - Error string `json:"error"` -} - -type FAPI_Cosmetic_Variant struct { - Channel string `json:"channel"` - Type string `json:"type"` - Options []FAPI_Cosmetic_VariantChannel `json:"options"` -} - -type FAPI_Cosmetic_VariantChannel struct { - Tag string `json:"tag"` - Name string `json:"name"` - Image string `json:"image"` -} - -type FAPI_Cosmetic struct { - ID string `json:"id"` - Name string `json:"name"` - Description string `json:"description"` - Type struct { - Value string `json:"value"` - DisplayValue string `json:"displayValue"` - BackendValue string `json:"backendValue"` - } `json:"type"` - Rarity struct { - Value string `json:"value"` - DisplayValue string `json:"displayValue"` - BackendValue string `json:"backendValue"` - } `json:"rarity"` - Series struct { - Value string `json:"value"` - Image string `json:"image"` - BackendValue string `json:"backendValue"` - } `json:"series"` - Set struct { - Value string `json:"value"` - Text string `json:"text"` - BackendValue string `json:"backendValue"` - } `json:"set"` - Introduction struct { - Chapter string `json:"chapter"` - Season string `json:"season"` - Text string `json:"text"` - BackendValue int `json:"backendValue"` - } `json:"introduction"` - Images struct { - Icon string `json:"icon"` - Featured string `json:"featured"` - SmallIcon string `json:"smallIcon"` - Other map[string]string `json:"other"` - } `json:"images"` - Variants []FAPI_Cosmetic_Variant `json:"variants"` - GameplayTags []string `json:"gameplayTags"` - SearchTags []string `json:"searchTags"` - MetaTags []string `json:"metaTags"` - ShowcaseVideo string `json:"showcaseVideo"` - DynamicPakID string `json:"dynamicPakId"` - DisplayAssetPath string `json:"displayAssetPath"` - DisplayAssetPath2 string - ItemPreviewHeroPath string `json:"itemPreviewHeroPath"` - Backpack string `json:"backpack"` - Path string `json:"path"` - Added string `json:"added"` - ShopHistory []string `json:"shopHistory"` - BattlePass bool `json:"battlePass"` -} - -type SnowVariantGrant struct { - Channel string `json:"channel"` - Value string `json:"value"` -} - -type SnowVariant struct { - Grants []SnowVariantGrant `json:"grants"` - Name string `json:"name"` - Gift bool `json:"gift"` - Equip bool `json:"equip"` - Unseen bool `json:"unseen"` -} - -type Set struct { - Items map[string]FAPI_Cosmetic `json:"items"` - Name string `json:"name"` - BackendName string `json:"backendName"` -} - -type CosmeticData struct { - Items map[string]FAPI_Cosmetic `json:"items"` - Sets map[string]Set `json:"sets"` - VariantTokens map[string]SnowVariant `json:"variantTokens"` -} - -func (c *CosmeticData) GetRandomItem() FAPI_Cosmetic { - randomInt := rand.Intn(len(c.Items)) - - i := 0 - for _, item := range c.Items { - if strings.Contains(item.Description, "TBD") { - continue - } - - if i == randomInt { - return item - } - - i++ - } - - return c.GetRandomItem() -} - -func (c *CosmeticData) GetRandomItemByType(itemType string) FAPI_Cosmetic { - randomInt := rand.Intn(len(c.Items)) - - i := 0 - for _, item := range c.Items { - if item.Type.BackendValue != itemType { - continue - } - - if strings.Contains(item.Description, "TBD") { - continue - } - - if i == randomInt { - return item - } - - i++ - } - - return c.GetRandomItemByType(itemType) -} - -func (c *CosmeticData) GetRandomItemByNotType(itemType string) FAPI_Cosmetic { - randomInt := rand.Intn(len(c.Items)) - - i := 0 - for _, item := range c.Items { - if item.Type.BackendValue == itemType { - continue - } - - if strings.Contains(item.Description, "TBD") { - continue - } - - if i == randomInt { - return item - } - - i++ - } - - return c.GetRandomItemByNotType(itemType) -} - -func (c *CosmeticData) GetRandomSet() Set { - randomInt := rand.Intn(len(c.Sets)) - - i := 0 - for _, set := range c.Sets { - if i == randomInt { - return set - } - - i++ - } - - return c.GetRandomSet() -} - -var EXTRA_NUMERIC_STYLES = []string{"Soccer", "Football", "ScaryBall"} - -func (c *CosmeticData) AddItem(item FAPI_Cosmetic) { - if slices.Contains(EXTRA_NUMERIC_STYLES, item.Set.BackendValue) { - item = c.AddNumericVariantChannelToItem(item) - } - - c.Items[item.ID] = item - - if item.Set.BackendValue != "" { - if _, ok := Cosmetics.Sets[item.Set.BackendValue]; !ok { - Cosmetics.Sets[item.Set.BackendValue] = Set{ - Items: make(map[string]FAPI_Cosmetic), - Name: item.Set.Value, - BackendName: item.Set.BackendValue, - } - } - - Cosmetics.Sets[item.Set.BackendValue].Items[item.ID] = item - } -} - -func (c *CosmeticData) AddNumericVariantChannelToItem(item FAPI_Cosmetic) FAPI_Cosmetic { - owned := []FAPI_Cosmetic_VariantChannel{} - for i := 0; i < 100; i++ { - owned = append(owned, FAPI_Cosmetic_VariantChannel{ - Tag: fmt.Sprint(i), - }) - } - - item.Variants = append(item.Variants, FAPI_Cosmetic_Variant{ - Channel: "Numeric", - Type: "int", - Options: owned, - }) - - return item -} - -var ( - StaticAPI = NewFortniteAPI() - Cosmetics = CosmeticData{ - Items: make(map[string]FAPI_Cosmetic), - Sets: make(map[string]Set), - VariantTokens: make(map[string]SnowVariant), - } -) - -func NewFortniteAPI() *FortniteAPI { - return &FortniteAPI{ - URL: "https://fortnite-api.com", - C: &http.Client{}, - } -} - -func (f *FortniteAPI) Get(path string) (*FAPI_Response, error) { - req, err := http.NewRequest("GET", f.URL + path, nil) - if err != nil { - return nil, err - } - - resp, err := f.C.Do(req) - if err != nil { - return nil, err - } - - bodyBytes, err := io.ReadAll(resp.Body) - if err != nil { - return nil, err - } - - var data FAPI_Response - err = json.Unmarshal(bodyBytes, &data) - if err != nil { - return nil, err - } - - return &data, nil -} - -func (f *FortniteAPI) GetAllCosmetics() ([]FAPI_Cosmetic, error) { - resp, err := f.Get("/v2/cosmetics/br") - if err != nil { - return nil, err - } - - return resp.Data, nil -} - -func (f *FortniteAPI) GetPlaylistImage(playlist string) (any, error) { - return nil, nil -} - -func PreloadCosmetics(max int) error { - aid.Print("(external) assets from", StaticAPI.URL) - - list, err := StaticAPI.GetAllCosmetics() - if err != nil { - return err - } - - for _, item := range list { - if item.Introduction.BackendValue == 0 || item.Introduction.BackendValue > max { - continue - } - - if len(item.ShopHistory) == 0 && item.Type.Value == "outfit" { - item.BattlePass = true - } - - Cosmetics.AddItem(item) - } - - for id, item := range Cosmetics.Items { - if item.Type.Value != "backpack" { - continue - } - - if item.ItemPreviewHeroPath == "" { - continue - } - - previewHeroPath := strings.Split(item.ItemPreviewHeroPath, "/") - characterId := previewHeroPath[len(previewHeroPath)-1] - - character, ok := Cosmetics.Items[characterId] - if !ok { - continue - } - character.Backpack = id - Cosmetics.AddItem(character) - } - - assets := storage.HttpAsset("QKnwROGzQjYm1W9xu9uL3VrbSA0tnVj6NJJtEChUdAb3DF8uN.json") - if assets == nil { - panic("Failed to load assets") - } - - var assetData []string - err = json.Unmarshal(*assets, &assetData) - if err != nil { - return err - } - withDisplayAssets := 0 - - for _, asset := range assetData { - asset := strings.ReplaceAll(asset, "DAv2_", "") - parts := strings.Split(asset, "_") - - if strings.Contains(asset, "Bundle") { - withDisplayAssets++ - continue - } - - switch { - case parts[0] == "CID": - addCharacterAsset(parts) - case parts[0] == "Character": - addCharacterAsset(parts) - case parts[0] == "BID": - addBackpackAsset(parts) - case parts[0] == "EID": - addEmoteAsset(parts) - case parts[0] == "Emote": - addEmoteAsset(parts) - case parts[0] == "Pickaxe": - addPickaxeAsset(parts) - case parts[0] == "Wrap": - addWrapAsset(parts) - case parts[0] == "Glider": - addGliderAsset(parts) - case parts[0] == "MusicPack": - addMusicAsset(parts) - } - } - - for _, item := range Cosmetics.Items { - if item.DisplayAssetPath2 == "" { - continue - } - - withDisplayAssets++ - } - - variants := storage.HttpAsset("QF3nHCFt1vhELoU4q1VKTmpxnk20c2iAiBEBzlbzQAY.json") - if variants == nil { - panic("Failed to load variants") - } - - err = json.Unmarshal(*variants, &Cosmetics.VariantTokens) - if err != nil { - return err - } - - aid.Print("(snow) preloaded", len(Cosmetics.Items), "cosmetics") - return nil -} - -func addCharacterAsset(parts []string) { - character := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "outfit" { - continue - } - - if parts[0] == "CID" { - cid := "" - if parts[1] != "A" { - cid = parts[0] + "_" + parts[1] - } - - if parts[1] == "A" { - cid = parts[0] + "_A_" + parts[2] - } - - if strings.Contains(item.ID, cid) { - character = item - break - } - } - - if parts[0] == "Character" { - if strings.Contains(item.ID, parts[1]) { - character = item - break - } - } - } - - if character.ID == "" { - return - } - - character.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(character) -} - -func addBackpackAsset(parts []string) { - backpack := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "backpack" { - continue - } - - bid := "" - if parts[1] != "A" { - bid = parts[0] + "_" + parts[1] - } - - if parts[1] == "A" { - bid = parts[0] + "_A_" + parts[2] - } - - if strings.Contains(item.ID, bid) { - backpack = item - break - } - } - - if backpack.ID == "" { - return - } - - backpack.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(backpack) -} - -func addEmoteAsset(parts []string) { - emote := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "emote" { - continue - } - - if strings.Contains(item.ID, parts[1]) { - emote = item - break - } - } - - if emote.ID == "" { - return - } - - emote.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(emote) -} - -func addPickaxeAsset(parts []string) { - pickaxe := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "pickaxe" { - continue - } - - pickaxeId := "" - if parts[1] != "ID" { - pickaxeId = parts[0] + "_" + parts[1] - } - - if parts[1] == "ID" { - pickaxeId = parts[0] + "_ID_" + parts[2] - } - - if strings.Contains(item.ID, pickaxeId) { - pickaxe = item - break - } - } - - if pickaxe.ID == "" { - return - } - - pickaxe.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(pickaxe) -} - -func addGliderAsset(parts []string) { - glider := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "glider" { - continue - } - - gliderId := "" - if parts[1] != "ID" { - gliderId = parts[0] + "_" + parts[1] - } - - if parts[1] == "ID" { - gliderId = parts[0] + "_ID_" + parts[2] - } - - if strings.Contains(item.ID, gliderId) { - glider = item - break - } - } - - if glider.ID == "" { - return - } - - glider.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(glider) -} - -func addWrapAsset(parts []string) { - wrap := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "wrap" { - continue - } - - if strings.Contains(item.ID, parts[1]) { - wrap = item - break - } - } - - if wrap.ID == "" { - return - } - - wrap.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(wrap) -} - -func addMusicAsset(parts []string) { - music := FAPI_Cosmetic{} - - for _, item := range Cosmetics.Items { - if item.Type.Value != "music" { - continue - } - - if strings.Contains(item.ID, parts[1]) { - music = item - break - } - } - - if music.ID == "" { - return - } - - music.DisplayAssetPath2 = "DAv2_" + strings.Join(parts, "_") - Cosmetics.AddItem(music) -} \ No newline at end of file diff --git a/fortnite/person.go b/fortnite/person.go index 272dd30..3c7a8a5 100644 --- a/fortnite/person.go +++ b/fortnite/person.go @@ -31,7 +31,7 @@ func NewFortnitePerson(displayName string, everything bool) *p.Person { func GiveEverything(person *p.Person) { items := make([]storage.DB_Item, 0) - for _, item := range Cosmetics.Items { + for _, item := range External.FortniteItems { if strings.Contains(strings.ToLower(item.ID), "random") { continue } diff --git a/fortnite/shop.go b/fortnite/shop.go index ea791c2..9af48f8 100644 --- a/fortnite/shop.go +++ b/fortnite/shop.go @@ -360,14 +360,9 @@ func GenerateRandomStorefront() { break } - item := Cosmetics.GetRandomItemByType("AthenaCharacter") + item := RandomItemByType("AthenaCharacter") entry := NewCatalogEntry("athena") entry.SetSection("Daily") - - if item.DisplayAssetPath2 == "" { - i-- - continue - } entry.SetNewDisplayAsset(item.DisplayAssetPath2) if item.DisplayAssetPath != "" { @@ -378,8 +373,8 @@ func GenerateRandomStorefront() { entry.SetTileSize("Normal") entry.Priority = 1 - if item.Backpack != "" { - entry.AddGrant("AthenaBackpack:" + item.Backpack) + if item.Backpack != nil { + entry.AddGrant("AthenaBackpack:" + item.Backpack.ID) } if storefront.CheckIfOfferIsDuplicate(*entry) { @@ -390,7 +385,7 @@ func GenerateRandomStorefront() { } for i := 0; i < 6; i++ { - item := Cosmetics.GetRandomItemByNotType("AthenaCharacter") + item := RandomItemByNotType("AthenaCharacter") entry := NewCatalogEntry("athena") entry.SetSection("Daily") @@ -426,7 +421,7 @@ func GenerateRandomStorefront() { setsAdded := 0 for len(weekly.CatalogEntries) < minimumItems || setsAdded < minimumSets { - set := Cosmetics.GetRandomSet() + set := RandomSet() itemsAdded := 0 itemsToAdd := []*Entry{} @@ -434,10 +429,6 @@ func GenerateRandomStorefront() { entry := NewCatalogEntry("athena") entry.SetSection("Featured") entry.SetPanel(set.BackendName) - - if item.DisplayAssetPath2 == "" { - continue - } entry.SetNewDisplayAsset(item.DisplayAssetPath2) if item.Type.BackendValue == "AthenaCharacter" { diff --git a/fortnite/types.go b/fortnite/types.go new file mode 100644 index 0000000..161c02f --- /dev/null +++ b/fortnite/types.go @@ -0,0 +1,87 @@ +package fortnite + +type FortniteVariantChannel struct { + Tag string `json:"tag"` + Name string `json:"name"` + Image string `json:"image"` +} + +type FortniteVariant struct { + Channel string `json:"channel"` + Type string `json:"type"` + Options []FortniteVariantChannel `json:"options"` +} + +type FortniteItem struct { + ID string `json:"id"` + Name string `json:"name"` + Description string `json:"description"` + Type struct { + Value string `json:"value"` + DisplayValue string `json:"displayValue"` + BackendValue string `json:"backendValue"` + } `json:"type"` + Rarity struct { + Value string `json:"value"` + DisplayValue string `json:"displayValue"` + BackendValue string `json:"backendValue"` + } `json:"rarity"` + Series struct { + Value string `json:"value"` + Image string `json:"image"` + BackendValue string `json:"backendValue"` + } `json:"series"` + Set struct { + Value string `json:"value"` + Text string `json:"text"` + BackendValue string `json:"backendValue"` + } `json:"set"` + Introduction struct { + Chapter string `json:"chapter"` + Season string `json:"season"` + Text string `json:"text"` + BackendValue int `json:"backendValue"` + } `json:"introduction"` + Images struct { + Icon string `json:"icon"` + Featured string `json:"featured"` + SmallIcon string `json:"smallIcon"` + Other map[string]string `json:"other"` + } `json:"images"` + Variants []FortniteVariant `json:"variants"` + GameplayTags []string `json:"gameplayTags"` + SearchTags []string `json:"searchTags"` + MetaTags []string `json:"metaTags"` + ShowcaseVideo string `json:"showcaseVideo"` + DynamicPakID string `json:"dynamicPakId"` + DisplayAssetPath string `json:"displayAssetPath"` + DisplayAssetPath2 string + ItemPreviewHeroPath string `json:"itemPreviewHeroPath"` + Backpack *FortniteItem `json:"backpack"` + Path string `json:"path"` + Added string `json:"added"` + ShopHistory []string `json:"shopHistory"` + BattlePass bool `json:"battlePass"` +} + +type FortniteSet struct { + BackendName string `json:"backendName"` + DisplayName string `json:"displayName"` + Items []*FortniteItem `json:"items"` +} + +type FortniteCosmeticsResponse struct { + Status int `json:"status"` + Data []FortniteItem `json:"data"` +} + +type SnowCosmeticVariantToken struct { + Grants []struct { + Channel string `json:"channel"` + Value string `json:"value"` + } `json:"grants"` + Name string `json:"name"` + Gift bool `json:"gift"` + Equip bool `json:"equip"` + Unseen bool `json:"unseen"` +} \ No newline at end of file diff --git a/handlers/snow.go b/handlers/snow.go index 9e061ef..72f90e3 100644 --- a/handlers/snow.go +++ b/handlers/snow.go @@ -11,7 +11,7 @@ import ( ) func GetPreloadedCosmetics(c *fiber.Ctx) error { - return c.JSON(fortnite.Cosmetics) + return c.JSON(fortnite.External) } func GetPlaylistImage(c *fiber.Ctx) error { diff --git a/main.go b/main.go index e668225..f71b5ef 100644 --- a/main.go +++ b/main.go @@ -45,10 +45,11 @@ func init() { func init() { discord.IntialiseClient() - fortnite.PreloadCosmetics(aid.Config.Fortnite.Season) + fortnite.PreloadCosmetics() fortnite.GenerateRandomStorefront() fortnite.GeneratePlaylistImages() + for _, username := range aid.Config.Accounts.Gods { found := person.FindByDisplay(username) if found == nil { diff --git a/storage/embeds.go b/storage/embeds.go index 8f1b857..bd2d409 100644 --- a/storage/embeds.go +++ b/storage/embeds.go @@ -2,6 +2,7 @@ package storage import ( "embed" + "encoding/json" "io" "net/http" "strings" @@ -21,7 +22,7 @@ func Asset(file string) (*[]byte) { return &data } -func HttpAsset(file string) (*[]byte) { +func HttpAsset[T interface{}](file string) (*T) { client := http.Client{} resp, err := client.Get("https://raw.githubusercontent.com/ectrc/ectrc/main/" + file) @@ -34,5 +35,11 @@ func HttpAsset(file string) (*[]byte) { return nil } - return &data + var assetData T + err = json.Unmarshal(data, &assetData) + if err != nil { + return nil + } + + return &assetData } \ No newline at end of file