diff --git a/fortnite/person.go b/fortnite/person.go index 27e8699..272dd30 100644 --- a/fortnite/person.go +++ b/fortnite/person.go @@ -141,6 +141,7 @@ func NewFortnitePersonWithId(id string, displayName string, everything bool) *p. person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("mfa_enabled", true)).Save() person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("mtx_affiliate", "")).Save() + person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("mtx_affiliate_set_time", 0)).Save() person.CommonCoreProfile.Attributes.AddAttribute(p.NewAttribute("mtx_purchase_history", aid.JSON{ "refundsUsed": 0, "refundCredits": 3, diff --git a/handlers/client.go b/handlers/client.go index e58c347..cdb046e 100644 --- a/handlers/client.go +++ b/handlers/client.go @@ -31,6 +31,7 @@ var ( "RefundMtxPurchase": clientRefundMtxPurchaseAction, "GiftCatalogEntry": clientGiftCatalogEntryAction, "RemoveGiftBox": clientRemoveGiftBoxAction, + "SetAffiliateName": clientSetAffiliateNameAction, } ) @@ -824,5 +825,33 @@ func clientRemoveGiftBoxAction(c *fiber.Ctx, person *p.Person, profile *p.Profil profile.Gifts.DeleteGift(gift.ID) + return nil +} + +func clientSetAffiliateNameAction(c *fiber.Ctx, person *p.Person, profile *p.Profile, notifications *[]aid.JSON) error { + var body struct { + AffiliateName string `json:"affiliateName" binding:"required"` + } + + if err := c.BodyParser(&body); err != nil { + return fmt.Errorf("invalid Body") + } + + affiliate := person.CommonCoreProfile.Attributes.GetAttributeByKey("mtx_affiliate") + if affiliate == nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid affiliate attribute")) + } + + affiliate.ValueJSON = aid.JSONStringify(body.AffiliateName) + affiliate.Save() + + setTime := person.CommonCoreProfile.Attributes.GetAttributeByKey("mtx_affiliate_set_time") + if setTime == nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid affiliate set time attribute")) + } + + setTime.ValueJSON = aid.JSONStringify(time.Now().Format("2006-01-02T15:04:05.999Z")) + setTime.Save() + return nil } \ No newline at end of file diff --git a/handlers/common.go b/handlers/common.go index cc420ad..8744a53 100644 --- a/handlers/common.go +++ b/handlers/common.go @@ -2,6 +2,7 @@ package handlers import ( "github.com/ectrc/snow/aid" + p "github.com/ectrc/snow/person" "github.com/gofiber/fiber/v2" ) @@ -10,41 +11,56 @@ func RedirectSocket(c *fiber.Ctx) error { } func AnyNoContent(c *fiber.Ctx) error { - return c.SendStatus(fiber.StatusNoContent) + return c.SendStatus(204) } func PostGamePlatform(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).SendString("true") + return c.Status(200).SendString("true") } func GetGameEnabledFeatures(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).JSON([]string{}) + return c.Status(200).JSON([]string{}) } func PostGameAccess(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).SendString("true") + return c.Status(200).SendString("true") } func GetFortniteReceipts(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).JSON([]string{}) + return c.Status(200).JSON([]string{}) } func GetMatchmakingSession(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).Send([]byte{}) + return c.Status(200).Send([]byte{}) } func GetFortniteVersion(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "type": "NO_UPDATE", }) } func GetWaitingRoomStatus(c *fiber.Ctx) error { - return c.SendStatus(fiber.StatusNoContent) + return c.SendStatus(204) +} + +func GetAffiliate(c *fiber.Ctx) error { + slugger := p.FindByDisplay(c.Params("slug")) + if slugger == nil { + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid affiliate slug")) + } + + return c.Status(200).JSON(aid.JSON{ + "id": slugger.ID, + "displayName": slugger.DisplayName, + "slug": slugger.DisplayName, + "status": "ACTIVE", + "verified": false, + }) } func GetRegion(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "continent": aid.JSON{ "code": "EU", }, diff --git a/main.go b/main.go index f0a89e6..67b6bab 100644 --- a/main.go +++ b/main.go @@ -75,12 +75,13 @@ func main() { r.Use(aid.FiberLimiter()) r.Use(aid.FiberCors()) + r.Post("/api/v1/assets/Fortnite/:versionId/:assetName", handlers.PostAssets) r.Get("/region", handlers.GetRegion) - r.Put("/profile/play_region", handlers.AnyNoContent) r.Get("/content/api/pages/fortnite-game", handlers.GetContentPages) r.Get("/waitingroom/api/waitingroom", handlers.GetWaitingRoomStatus) r.Get("/api/v1/search/:accountId", handlers.GetPersonSearch) - r.Post("/api/v1/assets/Fortnite/:versionId/:assetName", handlers.PostAssets) + r.Get("/affiliate/api/public/affiliates/slug/:slug", handlers.GetAffiliate) + r.Put("/profile/play_region", handlers.AnyNoContent) r.Get("/", handlers.RedirectSocket) r.Get("/socket", handlers.MiddlewareWebsocket, websocket.New(handlers.WebsocketConnection)) diff --git a/readme.md b/readme.md index a78142a..9fdaa4c 100644 --- a/readme.md +++ b/readme.md @@ -15,7 +15,6 @@ Performance first, universal Fortnite private server backend written in Go. > The backend is very feature rich and is constantly being updated. Below are the features that are not yet implemented, but are coming soon. -- Amazon S3 integration to store the **Player Settings** externally. Using the bucket will allow for horizontal scaling which may be required for very large player counts. - **Party System V2** Currently it relies on the automatic XMPP solution which is very hard to keep track of. - Purchasing the **Battle Pass**. This will require the Battle Pass Storefront ID for every build. I am yet to think of a solution for this. - Seeded randomization for the **Item Shop** instead of a random number generator. This will ensure that even if the backend is restarted, the same random items will be in the shop during that day. @@ -27,7 +26,7 @@ Performance first, universal Fortnite private server backend written in Go. > These are request made from Fortnite to the server to perform actions on the profile. -`QueryProfile`, `ClientQuestLogin`, `MarkItemSeen`, `SetItemFavoriteStatusBatch`, `EquipBattleRoyaleCustomization`, `SetBattleRoyaleBanner`, `SetCosmeticLockerSlot`, `SetCosmeticLockerBanner`, `SetCosmeticLockerName`, `CopyCosmeticLoadout`, `DeleteCosmeticLoadout`, `PurchaseCatalogEntry`, `GiftCatalogEntry`, `RemoveGiftBox` +`QueryProfile`, `ClientQuestLogin`, `MarkItemSeen`, `SetItemFavoriteStatusBatch`, `EquipBattleRoyaleCustomization`, `SetBattleRoyaleBanner`, `SetCosmeticLockerSlot`, `SetCosmeticLockerBanner`, `SetCosmeticLockerName`, `CopyCosmeticLoadout`, `DeleteCosmeticLoadout`, `PurchaseCatalogEntry`, `GiftCatalogEntry`, `RemoveGiftBox`, `RefundMtxPurchase`, `SetAffiliateName` ## Support