Refactor external data to improve startup time
This commit is contained in:
parent
7ae70063b4
commit
6af8610fe6
15
aid/aid.go
15
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
|
||||
}
|
|
@ -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,
|
||||
})
|
||||
|
|
248
fortnite/external.go
Normal file
248
fortnite/external.go
Normal 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))]
|
||||
}
|
|
@ -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")
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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" {
|
||||
|
|
87
fortnite/types.go
Normal file
87
fortnite/types.go
Normal 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"`
|
||||
}
|
|
@ -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 {
|
||||
|
|
3
main.go
3
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 {
|
||||
|
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue
Block a user