Fixed Styled Lobbies; Fixed VBucks on early seasons; ...

Added KeyChain for newer seasons.
This commit is contained in:
eccentric 2023-11-05 22:08:53 +00:00
parent 2e8d2fbc95
commit 188e7dc7b0
9 changed files with 1401 additions and 70 deletions

View File

@ -10,7 +10,7 @@ import (
func FiberLogger() fiber.Handler { func FiberLogger() fiber.Handler {
return logger.New(logger.Config{ return logger.New(logger.Config{
Format: "(${status}) (${latency}) ${path}\n", Format: "(${method}) (${status}) (${latency}) ${path}\n",
}) })
} }

View File

@ -25,5 +25,5 @@ host="0.0.0.0"
secret="secret" secret="secret"
[fortnite] [fortnite]
; the game build to use ; used for account creation + lobby
build=2.5 build=5.41

View File

@ -1,8 +1,6 @@
package handlers package handlers
import ( import (
"encoding/base64"
"math/rand"
"strings" "strings"
"time" "time"
@ -41,15 +39,6 @@ func PostOAuthToken(c *fiber.Ctx) error {
func PostOAuthTokenClientCredentials(c *fiber.Ctx, body *OAuthTokenBody) error { func PostOAuthTokenClientCredentials(c *fiber.Ctx, body *OAuthTokenBody) error {
credentials, err := aid.JWTSign(aid.JSON{ credentials, err := aid.JWTSign(aid.JSON{
"snow_id": 0, // custom "snow_id": 0, // custom
"t": "s",
"am": "client_credentials", // authorization method
"ic": true, // internal client
"mver": false, // mobile version
"clsvc": "snow", // client service
"clid": c.IP(), // client id
"jti": rand.Int63(), // jwt id
"p": base64.StdEncoding.EncodeToString([]byte(c.IP())), // payload
"hours_expire": 1,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"), "creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
}) })
if err != nil { if err != nil {
@ -87,20 +76,6 @@ func PostOAuthTokenPassword(c *fiber.Ctx, body *OAuthTokenBody) error {
access, err := aid.JWTSign(aid.JSON{ access, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom "snow_id": person.ID, // custom
"iai": person.ID, // account id
"dn": person.DisplayName, // display name
"t": "s",
"am": "password", // authorization method
"ic": true, // internal client
"mver": false, // mobile version
"clsvc": "snow", // client service
"app": "com.epicgames.fortnite", // app name
"clid": c.IP(), // client id
"dvid": "default", // device id
"jti": rand.Int63(), // jwt id
"p": base64.StdEncoding.EncodeToString([]byte(c.IP())), // payload
"sec": 1, // security level
"hours_expire": 24,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"), "creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
}) })
if err != nil { if err != nil {
@ -108,13 +83,7 @@ func PostOAuthTokenPassword(c *fiber.Ctx, body *OAuthTokenBody) error {
} }
refresh, err := aid.JWTSign(aid.JSON{ refresh, err := aid.JWTSign(aid.JSON{
"snow_id": person.ID, // custom "snow_id": person.ID,
"sub": person.ID, // account id
"clid": c.IP(), // client id
"jti": rand.Int63(), // jwt id
"t": "s",
"am": "refresh_token", // authorization method
"hours_expire": 24,
"creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"), "creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"),
}) })
if err != nil { if err != nil {
@ -138,6 +107,66 @@ func PostOAuthTokenPassword(c *fiber.Ctx, body *OAuthTokenBody) error {
}) })
} }
func GetOAuthVerify(c *fiber.Ctx) error {
auth := c.Get("Authorization")
if auth == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Authorization Header is empty"))
}
real := strings.ReplaceAll(auth, "bearer eg1~", "")
claims, err := aid.JWTVerify(real)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
if claims["snow_id"] == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId, ok := claims["snow_id"].(string)
if !ok {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
person := p.Find(snowId)
if person == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
return c.SendStatus(fiber.StatusNoContent)
}
func MiddlewareOAuthVerify(c *fiber.Ctx) error {
auth := c.Get("Authorization")
if auth == "" {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Authorization Header is empty"))
}
real := strings.ReplaceAll(auth, "bearer eg1~", "")
claims, err := aid.JWTVerify(real)
if err != nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
if claims["snow_id"] == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
snowId, ok := claims["snow_id"].(string)
if !ok {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
person := p.Find(snowId)
if person == nil {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Token"))
}
c.Locals("person", person)
return c.Next()
}
func DeleteOAuthSessions(c *fiber.Ctx) error { func DeleteOAuthSessions(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{}) return c.Status(fiber.StatusOK).JSON(aid.JSON{})
} }

View File

@ -9,33 +9,35 @@ func AnyNoContent(c *fiber.Ctx) error {
return c.SendStatus(fiber.StatusNoContent) return c.SendStatus(fiber.StatusNoContent)
} }
func PostTryPlayOnPlatform(c *fiber.Ctx) error { func PostGamePlatform(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).SendString("true") return c.Status(fiber.StatusOK).SendString("true")
} }
func GetEnabledFeatures(c *fiber.Ctx) error { func GetGameEnabledFeatures(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]string{}) return c.Status(fiber.StatusOK).JSON([]string{})
} }
func PostGrantAccess(c *fiber.Ctx) error { func PostGameAccess(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).SendString("true") return c.Status(fiber.StatusOK).SendString("true")
} }
func GetAccountReceipts(c *fiber.Ctx) error { func GetFortniteReceipts(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]string{}) return c.Status(fiber.StatusOK).JSON([]string{})
} }
func GetSessionFindPlayer(c *fiber.Ctx) error { func GetMatchmakingSession(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]string{}) return c.Status(fiber.StatusOK).JSON([]string{})
} }
func GetVersionCheck(c *fiber.Ctx) error { func GetFortniteVersion(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{ return c.Status(fiber.StatusOK).JSON(aid.JSON{
"type": "NO_UPDATE", "type": "NO_UPDATE",
}) })
} }
func GetContentPages(c *fiber.Ctx) error { func GetContentPages(c *fiber.Ctx) error {
// seasonString := strconv.Itoa(aid.Config.Fortnite.Season)
return c.Status(fiber.StatusOK).JSON(aid.JSON{ return c.Status(fiber.StatusOK).JSON(aid.JSON{
"battlepassaboutmessages": aid.JSON{ "battlepassaboutmessages": aid.JSON{
"news": aid.JSON{ "news": aid.JSON{
@ -73,6 +75,19 @@ func GetContentPages(c *fiber.Ctx) error {
}, },
"lastModified": "0000-00-00T00:00:00.000Z", "lastModified": "0000-00-00T00:00:00.000Z",
}, },
"dynamicbackgrounds": aid.JSON{
"backgrounds": aid.JSON{"backgrounds": []aid.JSON{
// {
// "key": "lobby",
// "stage": "season"+seasonString,
// },
// {
// "key": "vault",
// "stage": "season"+seasonString,
// },
}},
"lastModified": "0000-00-00T00:00:00.000Z",
},
"lastModified": "0000-00-00T00:00:00.000Z", "lastModified": "0000-00-00T00:00:00.000Z",
}) })
} }

View File

@ -1,7 +1,10 @@
package handlers package handlers
import ( import (
"fmt"
"regexp"
"strconv" "strconv"
"strings"
"time" "time"
"github.com/ectrc/snow/aid" "github.com/ectrc/snow/aid"
@ -12,27 +15,68 @@ func GetLightswitchBulkStatus(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON([]aid.JSON{{ return c.Status(fiber.StatusOK).JSON([]aid.JSON{{
"serviceInstanceId": "fortnite", "serviceInstanceId": "fortnite",
"status" :"UP", "status" :"UP",
"message": "fortnite is up.",
"maintenanceUri": nil,
"allowedActions": []string{"PLAY","DOWNLOAD"},
"banned":false, "banned":false,
"launcherInfoDTO": aid.JSON{
"appName":"Fortnite",
"catalogItemId":"4fe75bbc5a674f4f9b356b5c90567da5",
"namespace":"fn",
},
}}) }})
} }
func GetTimelineCalendar(c *fiber.Ctx) error { func GetFortniteTimeline(c *fiber.Ctx) error {
userAgent := c.Get("User-Agent")
fmt.Println(userAgent)
if !strings.Contains(userAgent, "++Fortnite") {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No User Agent"))
}
build := regexp.MustCompile(`\d+\.\d+`).FindString(userAgent)
if len(strings.Split(build, ".")) != 2 {
return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Build"))
}
season, err := strconv.Atoi(strings.Split(build, ".")[0])
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorBadRequest("Failed to parse Build"))
}
events := []aid.JSON{ events := []aid.JSON{
{ {
"activeUntil": aid.TimeEndOfWeekString(), "activeUntil": "9999-12-31T23:59:59.999Z",
"activeSince": "0001-01-01T00:00:00Z", "activeSince": "0001-01-01T00:00:00Z",
"activeEventId": "EventFlag.Season" + strconv.Itoa(aid.Config.Fortnite.Season), "eventType": "EventFlag.Season" + strings.Split(build, ".")[0],
},
{
"activeUntil": aid.TimeEndOfWeekString(),
"activeSince": "0001-01-01T00:00:00Z",
"activeEventId": "EventFlag.LobbySeason" + strconv.Itoa(aid.Config.Fortnite.Season),
}, },
} }
switch season {
case 2:
events = append(events, aid.JSON{
"activeUntil": "9999-12-31T23:59:59.999Z",
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.LobbyWinterDecor",
})
case 6:
events = append(events, aid.JSON{
"activeUntil": "9999-01-01T00:00:00.000Z",
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.LobbySeason6Halloween",
})
default:
events = append(events, aid.JSON{
"activeUntil": "9999-12-31T23:59:59.999Z",
"activeSince": "0001-01-01T00:00:00Z",
"eventType": "EventFlag.LobbySeason" + strings.Split(build, ".")[0],
})
}
state := aid.JSON{ state := aid.JSON{
"seasonNumber": aid.Config.Fortnite.Season, "eventNamedWeights": aid.JSON{},
"seasonTemplateId": "AthenaSeason:AthenaSeason" + strconv.Itoa(aid.Config.Fortnite.Season), "seasonNumber": season,
"seasonTemplateId": "AthenaSeason:AthenaSeason" + strings.Split(build, ".")[0],
"seasonBegin": time.Now().Add(-time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"), "seasonBegin": time.Now().Add(-time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"),
"seasonEnd": time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"), "seasonEnd": time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"),
"seasonDisplayedEnd": time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"), "seasonDisplayedEnd": time.Now().Add(time.Hour * 24 * 7).Format("2006-01-02T15:04:05.000Z"),
@ -56,6 +100,10 @@ func GetTimelineCalendar(c *fiber.Ctx) error {
return c.Status(fiber.StatusOK).JSON(aid.JSON{ return c.Status(fiber.StatusOK).JSON(aid.JSON{
"channels": aid.JSON{ "channels": aid.JSON{
"client-events": client, "client-events": client,
"client-matchmaking": aid.JSON{
"states": []aid.JSON{},
"cacheExpire": "9999-12-31T23:59:59.999Z",
},
}, },
"currentTime": time.Now().Format("2006-01-02T15:04:05.000Z"), "currentTime": time.Now().Format("2006-01-02T15:04:05.000Z"),
"cacheIntervalMins": 5, "cacheIntervalMins": 5,

1228
handlers/storefront.go Normal file

File diff suppressed because it is too large Load Diff

19
main.go
View File

@ -66,16 +66,21 @@ func main() {
account := r.Group("/account/api") account := r.Group("/account/api")
account.Get("/public/account/:accountId", handlers.GetPublicAccount) account.Get("/public/account/:accountId", handlers.GetPublicAccount)
account.Get("/public/account/:accountId/externalAuths", handlers.GetPublicAccountExternalAuths) account.Get("/public/account/:accountId/externalAuths", handlers.GetPublicAccountExternalAuths)
account.Get("/oauth/verify", handlers.GetOAuthVerify)
account.Post("/oauth/token", handlers.PostOAuthToken) account.Post("/oauth/token", handlers.PostOAuthToken)
account.Delete("/oauth/sessions/kill", handlers.DeleteOAuthSessions) account.Delete("/oauth/sessions/kill", handlers.DeleteOAuthSessions)
fortnite := r.Group("/fortnite/api") fortnite := r.Group("/fortnite/api")
fortnite.Get("/receipts/v1/account/:accountId/receipts", handlers.GetAccountReceipts) fortnite.Get("/receipts/v1/account/:accountId/receipts", handlers.GetFortniteReceipts)
fortnite.Get("/versioncheck/*", handlers.GetVersionCheck) fortnite.Get("/v2/versioncheck/*", handlers.GetFortniteVersion)
fortnite.Get("/calendar/v1/timeline", handlers.GetTimelineCalendar) fortnite.Get("/calendar/v1/timeline", handlers.GetFortniteTimeline)
storefront := fortnite.Group("/storefront/v2")
storefront.Get("/catalog", handlers.GetStorefrontCatalog)
storefront.Get("/keychain", handlers.GetStorefrontKeychain)
matchmaking := fortnite.Group("/matchmaking") matchmaking := fortnite.Group("/matchmaking")
matchmaking.Get("/session/findPlayer/:accountId", handlers.GetSessionFindPlayer) matchmaking.Get("/session/findPlayer/:accountId", handlers.GetMatchmakingSession)
storage := fortnite.Group("/cloudstorage") storage := fortnite.Group("/cloudstorage")
storage.Get("/system", handlers.GetCloudStorageFiles) storage.Get("/system", handlers.GetCloudStorageFiles)
@ -86,9 +91,9 @@ func main() {
storage.Put("/user/:accountId/:fileName", handlers.PutUserStorageFile) storage.Put("/user/:accountId/:fileName", handlers.PutUserStorageFile)
game := fortnite.Group("/game/v2") game := fortnite.Group("/game/v2")
game.Post("/tryPlayOnPlatform/account/:accountId", handlers.PostTryPlayOnPlatform) game.Get("/enabled_features", handlers.GetGameEnabledFeatures)
game.Post("/grant_access/:accountId", handlers.PostGrantAccess) game.Post("/tryPlayOnPlatform/account/:accountId", handlers.PostGamePlatform)
game.Get("/enabled_features", handlers.GetEnabledFeatures) game.Post("/grant_access/:accountId", handlers.PostGameAccess)
profile := game.Group("/profile/:accountId") profile := game.Group("/profile/:accountId")
profile.Post("/client/:action", handlers.PostProfileAction) profile.Post("/client/:action", handlers.PostProfileAction)

View File

@ -71,6 +71,11 @@ func FromDatabaseLoot(item *storage.DB_Loot) *Item {
func (i *Item) GenerateFortniteItemEntry() aid.JSON { func (i *Item) GenerateFortniteItemEntry() aid.JSON {
variants := []aid.JSON{} variants := []aid.JSON{}
attributes := aid.JSON{
"variants": variants,
"favorite": i.Favorite,
"item_seen": i.HasSeen,
}
for _, variant := range i.Variants { for _, variant := range i.Variants {
variants = append(variants, aid.JSON{ variants = append(variants, aid.JSON{
@ -80,13 +85,15 @@ func (i *Item) GenerateFortniteItemEntry() aid.JSON {
}) })
} }
if i.TemplateID == "Currency:MtxPurchased" {
attributes = aid.JSON{
"platform": "Shared",
}
}
return aid.JSON{ return aid.JSON{
"templateId": i.TemplateID, "templateId": i.TemplateID,
"attributes": aid.JSON{ "attributes": attributes,
"variants": variants,
"favorite": i.Favorite,
"item_seen": i.HasSeen,
},
"quantity": i.Quantity, "quantity": i.Quantity,
} }
} }

View File

@ -140,11 +140,10 @@ func (s *PostgresStorage) DeleteGift(giftId string) {
s.Postgres.Delete(&DB_Gift{}, "id = ?", giftId) s.Postgres.Delete(&DB_Gift{}, "id = ?", giftId)
} }
func (s *PostgresStorage) SaveAttribute(attribute *DB_PAttribute) {
s.Postgres.Save(attribute)
}
func (s *PostgresStorage) DeleteAttribute(attributeId string) { func (s *PostgresStorage) DeleteAttribute(attributeId string) {
s.Postgres.Delete(&DB_PAttribute{}, "id = ?", attributeId) s.Postgres.Delete(&DB_PAttribute{}, "id = ?", attributeId)
} }
func (s *PostgresStorage) SaveAttribute(attribute *DB_PAttribute) {
aid.Print("saving attribute", attribute.Key, attribute.ValueJSON)
s.Postgres.Save(attribute)
}