Refactor external data to improve startup time

This commit is contained in:
Eccentric 2024-02-11 00:27:08 +00:00
parent 7ae70063b4
commit 6af8610fe6
11 changed files with 371 additions and 701 deletions

View File

@ -4,6 +4,7 @@ import (
m "math/rand" m "math/rand"
"os" "os"
"os/signal" "os/signal"
"regexp"
"strconv" "strconv"
"syscall" "syscall"
) )
@ -24,6 +25,10 @@ func RandomString(n int) string {
return string(s) return string(s)
} }
func RandomInt(min, max int) int {
return m.Intn(max - min) + min
}
func FormatNumber(number int) string { func FormatNumber(number int) string {
str := "" str := ""
for i, char := range ReverseString(strconv.Itoa(number)) { for i, char := range ReverseString(strconv.Itoa(number)) {
@ -53,3 +58,13 @@ func ToHex(number int) string {
return inta 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 &reg[1]
}
return nil
}

View File

@ -376,6 +376,7 @@ func fillItemsHandler(s *discordgo.Session, i *discordgo.InteractionCreate, look
fortnite.GiveEverything(player) fortnite.GiveEverything(player)
str := player.DisplayName + " has been granted all items." str := player.DisplayName + " has been granted all items."
s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{ s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{
Content: &str, Content: &str,
}) })

248
fortnite/external.go Normal file
View File

@ -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))]
}

View File

@ -8,7 +8,6 @@ import (
"io" "io"
"math" "math"
"net/http" "net/http"
"strings"
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
"github.com/ectrc/snow/storage" "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)) 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 { func getRandomCharacterImage() image.Image {
found := false character := RandomItemWithFeaturedImage()
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
}
response, err := http.Get(character.Images.Featured) response, err := http.Get(character.Images.Featured)
if err != nil { if err != nil {
panic(err) panic(err)
@ -168,32 +105,12 @@ func getRandomCharacterImageWithSimilarColour(colour colours) image.Image {
func GenerateSoloImage() { func GenerateSoloImage() {
background := *storage.Asset("background.png") background := *storage.Asset("background.png")
itemFound := Cosmetics.GetRandomItemByType("AthenaCharacter") soloPlayer := getRandomCharacterImage()
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)
}
bg, _, err := image.Decode(bytes.NewReader(background)) bg, _, err := image.Decode(bytes.NewReader(background))
if err != nil { if err != nil {
panic(err) panic(err)
} }
soloPlayer, _, err := image.Decode(bytes.NewReader(data))
if err != nil {
panic(err)
}
m := image.NewRGBA(bg.Bounds()) m := image.NewRGBA(bg.Bounds())
draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src)
@ -224,7 +141,6 @@ func GenerateDuoImage() {
panic(err) panic(err)
} }
m := image.NewRGBA(bg.Bounds()) m := image.NewRGBA(bg.Bounds())
draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src) draw.Draw(m, m.Bounds(), bg, image.Point{0, 0}, draw.Src)
@ -378,6 +294,5 @@ func GeneratePlaylistImages() {
GenerateDuoImage() GenerateDuoImage()
GenerateTrioImage() GenerateTrioImage()
GenerateSquadImage() GenerateSquadImage()
aid.Print("(snow) generated playlist images") aid.Print("(snow) generated playlist images")
} }

View File

@ -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)
}

View File

@ -31,7 +31,7 @@ func NewFortnitePerson(displayName string, everything bool) *p.Person {
func GiveEverything(person *p.Person) { func GiveEverything(person *p.Person) {
items := make([]storage.DB_Item, 0) items := make([]storage.DB_Item, 0)
for _, item := range Cosmetics.Items { for _, item := range External.FortniteItems {
if strings.Contains(strings.ToLower(item.ID), "random") { if strings.Contains(strings.ToLower(item.ID), "random") {
continue continue
} }

View File

@ -360,14 +360,9 @@ func GenerateRandomStorefront() {
break break
} }
item := Cosmetics.GetRandomItemByType("AthenaCharacter") item := RandomItemByType("AthenaCharacter")
entry := NewCatalogEntry("athena") entry := NewCatalogEntry("athena")
entry.SetSection("Daily") entry.SetSection("Daily")
if item.DisplayAssetPath2 == "" {
i--
continue
}
entry.SetNewDisplayAsset(item.DisplayAssetPath2) entry.SetNewDisplayAsset(item.DisplayAssetPath2)
if item.DisplayAssetPath != "" { if item.DisplayAssetPath != "" {
@ -378,8 +373,8 @@ func GenerateRandomStorefront() {
entry.SetTileSize("Normal") entry.SetTileSize("Normal")
entry.Priority = 1 entry.Priority = 1
if item.Backpack != "" { if item.Backpack != nil {
entry.AddGrant("AthenaBackpack:" + item.Backpack) entry.AddGrant("AthenaBackpack:" + item.Backpack.ID)
} }
if storefront.CheckIfOfferIsDuplicate(*entry) { if storefront.CheckIfOfferIsDuplicate(*entry) {
@ -390,7 +385,7 @@ func GenerateRandomStorefront() {
} }
for i := 0; i < 6; i++ { for i := 0; i < 6; i++ {
item := Cosmetics.GetRandomItemByNotType("AthenaCharacter") item := RandomItemByNotType("AthenaCharacter")
entry := NewCatalogEntry("athena") entry := NewCatalogEntry("athena")
entry.SetSection("Daily") entry.SetSection("Daily")
@ -426,7 +421,7 @@ func GenerateRandomStorefront() {
setsAdded := 0 setsAdded := 0
for len(weekly.CatalogEntries) < minimumItems || setsAdded < minimumSets { for len(weekly.CatalogEntries) < minimumItems || setsAdded < minimumSets {
set := Cosmetics.GetRandomSet() set := RandomSet()
itemsAdded := 0 itemsAdded := 0
itemsToAdd := []*Entry{} itemsToAdd := []*Entry{}
@ -434,10 +429,6 @@ func GenerateRandomStorefront() {
entry := NewCatalogEntry("athena") entry := NewCatalogEntry("athena")
entry.SetSection("Featured") entry.SetSection("Featured")
entry.SetPanel(set.BackendName) entry.SetPanel(set.BackendName)
if item.DisplayAssetPath2 == "" {
continue
}
entry.SetNewDisplayAsset(item.DisplayAssetPath2) entry.SetNewDisplayAsset(item.DisplayAssetPath2)
if item.Type.BackendValue == "AthenaCharacter" { if item.Type.BackendValue == "AthenaCharacter" {

87
fortnite/types.go Normal file
View File

@ -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"`
}

View File

@ -11,7 +11,7 @@ import (
) )
func GetPreloadedCosmetics(c *fiber.Ctx) error { func GetPreloadedCosmetics(c *fiber.Ctx) error {
return c.JSON(fortnite.Cosmetics) return c.JSON(fortnite.External)
} }
func GetPlaylistImage(c *fiber.Ctx) error { func GetPlaylistImage(c *fiber.Ctx) error {

View File

@ -45,10 +45,11 @@ func init() {
func init() { func init() {
discord.IntialiseClient() discord.IntialiseClient()
fortnite.PreloadCosmetics(aid.Config.Fortnite.Season) fortnite.PreloadCosmetics()
fortnite.GenerateRandomStorefront() fortnite.GenerateRandomStorefront()
fortnite.GeneratePlaylistImages() fortnite.GeneratePlaylistImages()
for _, username := range aid.Config.Accounts.Gods { for _, username := range aid.Config.Accounts.Gods {
found := person.FindByDisplay(username) found := person.FindByDisplay(username)
if found == nil { if found == nil {

View File

@ -2,6 +2,7 @@ package storage
import ( import (
"embed" "embed"
"encoding/json"
"io" "io"
"net/http" "net/http"
"strings" "strings"
@ -21,7 +22,7 @@ func Asset(file string) (*[]byte) {
return &data return &data
} }
func HttpAsset(file string) (*[]byte) { func HttpAsset[T interface{}](file string) (*T) {
client := http.Client{} client := http.Client{}
resp, err := client.Get("https://raw.githubusercontent.com/ectrc/ectrc/main/" + file) resp, err := client.Get("https://raw.githubusercontent.com/ectrc/ectrc/main/" + file)
@ -34,5 +35,11 @@ func HttpAsset(file string) (*[]byte) {
return nil return nil
} }
return &data var assetData T
err = json.Unmarshal(data, &assetData)
if err != nil {
return nil
}
return &assetData
} }