From b0d2d6fd2ce57f831a35f3bb6a2331279ec12fe3 Mon Sep 17 00:00:00 2001 From: Eccentric Date: Sat, 10 Feb 2024 00:34:12 +0000 Subject: [PATCH] Better --- aid/aid.go | 10 ++ default.config.ini | 16 +-- discord/admin.go | 98 +++++++++++++--- discord/discord.go | 1 + discord/handlers.go | 242 +++++++++++++++++++--------------------- fortnite/image.go | 31 ++++- handlers/auth.go | 70 +++++++----- handlers/lightswitch.go | 17 ++- main.go | 23 ++-- makefile | 2 +- person/snapshot.go | 2 +- readme.md | 4 +- 12 files changed, 318 insertions(+), 198 deletions(-) diff --git a/aid/aid.go b/aid/aid.go index 3297234..0ce8439 100644 --- a/aid/aid.go +++ b/aid/aid.go @@ -42,4 +42,14 @@ func ReverseString(input string) string { str = string(char) + str } return str +} + +func ToHex(number int) string { + inta := strconv.FormatInt(int64(number), 16) + + if len(inta) == 1 { + return "0" + inta + } + + return inta } \ No newline at end of file diff --git a/default.config.ini b/default.config.ini index 7926190..697dec7 100644 --- a/default.config.ini +++ b/default.config.ini @@ -1,9 +1,11 @@ [accounts] -; comma separated list of usernames that are considered administrators or server accounts +; comma separated list of usernames that are considered server accounts ; these accounts will be created upon the first run of snow -; they will have max permissions with snow -; to disable this, remove the line +; they will have max permissions with snow and will have no discord account linked gods=god,snow +; comma separated list of usernames that are considered owner accounts +; these accounts will have max permissions with snow and can have a discord account linked +owners=ectrc [database] ; connect string @@ -56,15 +58,15 @@ host="127.0.0.1" secret="secret" [fortnite] -; used for account creation + lobby +; fortnite build version build=5.41 -; own every cosmetic in the game. this applies to all accounts +; on every account creation, all cosmetics will be added to the account +; if you want to disable this, set this to false everything=true ; enable or disable the requirement of password to login to an account ; if this is set to false, you can login to any account with just the username ; if this is true you must login using an exchange code given by the bot -password=true - +disable_password=true ; if you recieve lots of /account/api/oauth/token requests, set this to true ; this will disable the client credentials grant type ; however this will also disable a user to get the hotfixes before login diff --git a/discord/admin.go b/discord/admin.go index bf99060..956baa5 100644 --- a/discord/admin.go +++ b/discord/admin.go @@ -54,7 +54,7 @@ func whoHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { return } - player := getPersonFromOptions(i.ApplicationCommandData(), s) + player := getPersonFromOptions(i.ApplicationCommandData().Options, s) if player == nil { s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) return @@ -119,7 +119,7 @@ func banHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { return } - player := getPersonFromOptions(i.ApplicationCommandData(), s) + player := getPersonFromOptions(i.ApplicationCommandData().Options, s) if player == nil { s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) return @@ -146,7 +146,7 @@ func unbanHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { return } - player := getPersonFromOptions(i.ApplicationCommandData(), s) + player := getPersonFromOptions(i.ApplicationCommandData().Options, s) if player == nil { s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) return @@ -168,12 +168,12 @@ func giveItemHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { return } - if !looker.HasPermission(person.PermissionGiveItem) { + if !looker.HasPermission(person.PermissionItemControl) { s.InteractionRespond(i.Interaction, &ErrorNoPermission) return } - player := getPersonFromOptions(i.ApplicationCommandData(), s) + player := getPersonFromOptions(i.ApplicationCommandData().Options, s) if player == nil { s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) return @@ -229,12 +229,12 @@ func takeItemHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { return } - if !looker.HasPermission(person.PermissionTakeItem) { + if !looker.HasPermission(person.PermissionItemControl) { s.InteractionRespond(i.Interaction, &ErrorNoPermission) return } - player := getPersonFromOptions(i.ApplicationCommandData(), s) + player := getPersonFromOptions(i.ApplicationCommandData().Options, s) if player == nil { s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) return @@ -301,21 +301,16 @@ func giveEverythingHandler(s *discordgo.Session, i *discordgo.InteractionCreate) return } - if !looker.HasPermission(person.PermissionGiveItem) { + if !looker.HasPermission(person.PermissionItemControl) || !looker.HasPermission(person.PermissionLockerControl) { s.InteractionRespond(i.Interaction, &ErrorNoPermission) return } - player := getPersonFromOptions(i.ApplicationCommandData(), s) + player := getPersonFromOptions(i.ApplicationCommandData().Options, s) if player == nil { s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) return } - - if !player.HasPermission(person.PermissionFullLocker) { - s.InteractionRespond(i.Interaction, &ErrorNoPermission) - return - } s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseDeferredChannelMessageWithSource, @@ -327,4 +322,79 @@ func giveEverythingHandler(s *discordgo.Session, i *discordgo.InteractionCreate) s.InteractionResponseEdit(i.Interaction, &discordgo.WebhookEdit{ Content: &str, }) +} + +func permissionHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { + looker := person.FindByDiscord(i.Member.User.ID) + if looker == nil { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + if !looker.HasPermission(person.PermissionPermissionControl) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + if len(i.ApplicationCommandData().Options) <= 0 { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + subCommand := i.ApplicationCommandData().Options[0] + if len(subCommand.Options) <= 0 { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + player := getPersonFromOptions(subCommand.Options, s) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) + return + } + + permission := person.IntToPermission(subCommand.Options[0].IntValue()) + if permission == 0 { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + if permission == person.PermissionAll && !looker.HasPermission(person.PermissionOwner) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + if player.HasPermission(person.PermissionOwner) && !looker.HasPermission(person.PermissionOwner) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + if player.HasPermission(person.PermissionAll) && !looker.HasPermission(person.PermissionOwner) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + switch subCommand.Name { + case "add": + player.AddPermission(permission) + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: player.DisplayName + " has been given permission `" + permission.GetName() + "`.", + }, + }) + case "remove": + player.RemovePermission(permission) + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: player.DisplayName + " has had permission `" + permission.GetName() + "` removed.", + }, + }) + default: + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) } \ No newline at end of file diff --git a/discord/discord.go b/discord/discord.go index ffa0a5a..1b40f8a 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -89,6 +89,7 @@ func (c *DiscordClient) RegisterCommands() { for _, command := range c.Commands { if command.AdminOnly { command.Command.DefaultMemberPermissions = &adminPermission + command.Command.Description += " (admin only)" } update = append(update, command.Command) diff --git a/discord/handlers.go b/discord/handlers.go index de8beac..641e3bd 100644 --- a/discord/handlers.go +++ b/discord/handlers.go @@ -18,6 +18,21 @@ func addCommands() { panic("StaticClient is nil") } + personOptions := []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionUser, + Name: "discord", + Description: "The discord account of the player.", + Required: false, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "display", + Description: "The display name of the player.", + Required: false, + }, + } + addCommand(&DiscordCommand{ Command: &discordgo.ApplicationCommand{ Name: "create", @@ -58,7 +73,7 @@ func addCommands() { addCommand(&DiscordCommand{ Command: &discordgo.ApplicationCommand{ Name: "information", - Description: "Useful information about this server's activity! Admin Only.", + Description: "Useful information about this server's activity!", }, Handler: informationHandler, AdminOnly: true, @@ -68,20 +83,7 @@ func addCommands() { Command: &discordgo.ApplicationCommand{ Name: "who", Description: "Lookup a player's information.", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "display", - Description: "The display name of the player.", - Required: false, - }, - { - Type: discordgo.ApplicationCommandOptionUser, - Name: "discord", - Description: "The discord account of the player.", - Required: false, - }, - }, + Options: personOptions, }, Handler: whoHandler, AdminOnly: true, @@ -91,20 +93,7 @@ func addCommands() { Command: &discordgo.ApplicationCommand{ Name: "ban", Description: "Ban a player from using the bot.", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionUser, - Name: "discord", - Description: "The discord account of the player.", - Required: false, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "display", - Description: "The display name of the player.", - Required: false, - }, - }, + Options: personOptions, }, Handler: banHandler, AdminOnly: true, @@ -114,61 +103,38 @@ func addCommands() { Command: &discordgo.ApplicationCommand{ Name: "unban", Description: "Unban a player from using the bot.", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionUser, - Name: "discord", - Description: "The discord account of the player.", - Required: false, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "display", - Description: "The display name of the player.", - Required: false, - }, - }, + Options: personOptions, }, Handler: unbanHandler, AdminOnly: true, }) + grantOptions := append([]*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "template_id", + Description: "The item id of the cosmetic to give/take.", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionInteger, + Name: "quantity", + Description: "The amount of the item to give/take.", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "profile", + Description: "common_core, athena, common_public, profile0, collections, creative", + Required: true, + }, + }, personOptions...) + addCommand(&DiscordCommand{ Command: &discordgo.ApplicationCommand{ Name: "give", Description: "Grant a player an item in the game.", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "template_id", - Description: "The item id of the cosmetic to give.", - Required: true, - }, - { - Type: discordgo.ApplicationCommandOptionInteger, - Name: "quantity", - Description: "The amount of the item to give.", - Required: true, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "profile", - Description: "common_core, athena, common_public, profile0, collections, creative", - Required: true, - }, - { - Type: discordgo.ApplicationCommandOptionUser, - Name: "discord", - Description: "The discord account of the player.", - Required: false, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "display", - Description: "The display name of the player.", - Required: false, - }, - }, + Options: grantOptions, }, Handler: giveItemHandler, AdminOnly: true, @@ -178,38 +144,7 @@ func addCommands() { Command: &discordgo.ApplicationCommand{ Name: "take", Description: "Take an item from a player in the game.", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionString, - Name: "template_id", - Description: "The item id of the cosmetic to take.", - Required: true, - }, - { - Type: discordgo.ApplicationCommandOptionInteger, - Name: "quantity", - Description: "The amount of the item to take.", - Required: true, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "profile", - Description: "common_core, athena, common_public, profile0, collections, creative", - Required: true, - }, - { - Type: discordgo.ApplicationCommandOptionUser, - Name: "discord", - Description: "The discord account of the player.", - Required: false, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "display", - Description: "The display name of the player.", - Required: false, - }, - }, + Options: grantOptions, }, Handler: takeItemHandler, AdminOnly: true, @@ -219,34 +154,89 @@ func addCommands() { Command: &discordgo.ApplicationCommand{ Name: "everything", Description: "Give a player full locker", - Options: []*discordgo.ApplicationCommandOption{ - { - Type: discordgo.ApplicationCommandOptionUser, - Name: "discord", - Description: "The discord account of the player.", - Required: false, - }, - { - Type: discordgo.ApplicationCommandOptionString, - Name: "display", - Description: "The display name of the player.", - Required: false, - }, - }, + Options: personOptions, }, Handler: giveEverythingHandler, AdminOnly: true, }) + + permissionOptionChoices := []*discordgo.ApplicationCommandOptionChoice{ + { + Name: "All", + Value: person.PermissionAll, + }, + { + Name: "Lookup", + Value: person.PermissionLookup, + }, + { + Name: "Information", + Value: person.PermissionInformation, + }, + { + Name: "Donator", + Value: person.PermissionDonator, + }, + { + Name: "ItemControl", + Value: person.PermissionItemControl, + }, + { + Name: "LockerControl", + Value: person.PermissionLockerControl, + }, + { + Name: "Owner", + Value: person.PermissionOwner, + }, + { + Name: "PermissionControl", + Value: person.PermissionPermissionControl, + }, + } + + permissionOptions := append([]*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionInteger, + Name: "permission", + Description: "The permission to add/take.", + Required: true, + Choices: permissionOptionChoices, + }, + }, personOptions...) + + permissionSubCommands := []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionSubCommand, + Name: "add", + Description: "Add a permission to a player.", + Options: permissionOptions, + }, + { + Type: discordgo.ApplicationCommandOptionSubCommand, + Name: "remove", + Description: "Rake a permission from a player.", + Options: permissionOptions, + }, + } + + addCommand(&DiscordCommand{ + Command: &discordgo.ApplicationCommand{ + Name: "permission", + Description: "Give or take permissions from a player.", + Options: permissionSubCommands, + }, + Handler: permissionHandler, + AdminOnly: true, + }) } -func getPersonFromOptions(data discordgo.ApplicationCommandInteractionData, s *discordgo.Session) *person.Person { - options := data.Options - - if len(options) <= 0 { +func getPersonFromOptions(opts []*discordgo.ApplicationCommandInteractionDataOption, s *discordgo.Session) *person.Person { + if len(opts) <= 0 { return nil } - for _, option := range options { + for _, option := range opts { switch option.Type { case discordgo.ApplicationCommandOptionUser: if option.Name != "discord" { diff --git a/fortnite/image.go b/fortnite/image.go index 5b57cf0..426aecd 100644 --- a/fortnite/image.go +++ b/fortnite/image.go @@ -58,15 +58,44 @@ func getAverageColour(img image.Image) colours { } } +func GetAverageHexColour(img image.Image) string { + colour := getAverageColour(img) + return "#" + aid.ToHex(int(colour.averageRed)) + aid.ToHex(int(colour.averageGreen)) + aid.ToHex(int(colour.averageBlue)) +} + func colorDifference(c1, c2 colours) float64 { diffRed := int(c1.averageRed) - int(c2.averageRed) diffGreen := int(c1.averageGreen) - int(c2.averageGreen) diffBlue := int(c1.averageBlue) - int(c2.averageBlue) - // Using Euclidean distance to calculate color difference 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 diff --git a/handlers/auth.go b/handlers/auth.go index b8409c4..f9edba1 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -29,22 +29,22 @@ func PostFortniteToken(c *fiber.Ctx) error { var body FortniteTokenBody if err := c.BodyParser(&body); err != nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Request Body")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Request Body")) } if action, ok := oauthTokenGrantTypes[body.GrantType]; ok { return action(c, &body) } - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Grant Type")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Grant Type")) } func PostTokenClientCredentials(c *fiber.Ctx, body *FortniteTokenBody) error { if aid.Config.Fortnite.DisableClientCredentials { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Client Credentials is disabled.")) + return c.Status(400).JSON(aid.ErrorBadRequest("Client Credentials is disabled.")) } - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "access_token": "snow", "token_type": "bearer", "client_id": aid.Hash([]byte(c.IP())), @@ -57,37 +57,37 @@ func PostTokenClientCredentials(c *fiber.Ctx, body *FortniteTokenBody) error { func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error { if body.ExchangeCode == "" { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Exchange Code is empty")) + return c.Status(400).JSON(aid.ErrorBadRequest("Exchange Code is empty")) } codeParts := strings.Split(body.ExchangeCode, ".") if len(codeParts) != 2 { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) } code, failed := aid.KeyPair.DecryptAndVerifyB64(codeParts[0], codeParts[1]) if failed { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) } personParts := strings.Split(string(code), "=") if len(personParts) != 2 { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) } personId := personParts[0] expire, err := time.Parse("2006-01-02T15:04:05.999Z", personParts[1]) if err != nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) } if expire.Add(time.Hour).Before(time.Now()) { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) } person := p.Find(personId) if person == nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) + return c.Status(400).JSON(aid.ErrorBadRequest("Invalid Exchange Code")) } access, err := aid.JWTSign(aid.JSON{ @@ -108,7 +108,7 @@ func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error { return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer) } - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "access_token": "eg1~" + access, "account_id": person.ID, "client_id": c.IP(), @@ -117,9 +117,9 @@ func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error { "device_id": "default", "display_name": person.DisplayName, "expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"), - "expires_in": 86400, + "expires_in": 86200, "internal_client": true, - "refresh_expires": 86400, + "refresh_expires": 86200, "refresh_expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"), "refresh_token": "eg1~" + refresh, "token_type": "bearer", @@ -130,16 +130,16 @@ func PostTokenExchangeCode(c *fiber.Ctx, body *FortniteTokenBody) error { func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error { if aid.Config.Fortnite.Password { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Username and password authentication is disabled for security reasons. Please use an exchange code given by the discord bot.")) + return c.Status(400).JSON(aid.ErrorBadRequest("Username and password authentication is disabled for security reasons. Please use an exchange code given by the discord bot.")) } if body.Username == "" || body.Password == "" { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Username/Password is empty")) + return c.Status(400).JSON(aid.ErrorBadRequest("Username/Password is empty")) } person := p.FindByDisplay(strings.Split(body.Username, "@")[0]) if person == nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found")) + return c.Status(400).JSON(aid.ErrorBadRequest("No Account Found")) } access, err := aid.JWTSign(aid.JSON{ @@ -160,7 +160,7 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error { return c.Status(fiber.StatusInternalServerError).JSON(aid.ErrorInternalServer) } - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "access_token": "eg1~" + access, "account_id": person.ID, "client_id": c.IP(), @@ -169,9 +169,9 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error { "device_id": "default", "display_name": person.DisplayName, "expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"), - "expires_in": 86400, + "expires_in": 86200, "internal_client": true, - "refresh_expires": 86400, + "refresh_expires": 86200, "refresh_expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"), "refresh_token": "eg1~" + refresh, "token_type": "bearer", @@ -191,12 +191,12 @@ func GetTokenVerify(c *fiber.Ctx) error { return c.Status(fiber.StatusForbidden).JSON(aid.ErrorBadRequest("Invalid Access Token")) } - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "app": "fortnite", "token": strings.ReplaceAll(c.Get("Authorization"), "bearer eg1~", ""), "token_type": "bearer", "expires_at": time.Now().Add(time.Hour * 24).Format("2006-01-02T15:04:05.999Z"), - "expires_in": 86400, + "expires_in": 86200, "client_id": c.IP(), "session_id": "0", "device_id": "default", @@ -211,7 +211,7 @@ func GetTokenVerify(c *fiber.Ctx) error { } func DeleteToken(c *fiber.Ctx) error { - return c.Status(fiber.StatusOK).JSON(aid.JSON{}) + return c.Status(200).JSON(aid.JSON{}) } func MiddlewareFortnite(c *fiber.Ctx) error { @@ -247,10 +247,10 @@ func MiddlewareWeb(c *fiber.Ctx) error { func GetPublicAccount(c *fiber.Ctx) error { person := p.Find(c.Params("accountId")) if person == nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found")) + return c.Status(400).JSON(aid.ErrorBadRequest("No Account Found")) } - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "id": person.ID, "displayName": person.DisplayName, "externalAuths": []aid.JSON{}, @@ -274,27 +274,37 @@ func GetPublicAccounts(c *fiber.Ctx) error { }) } - return c.Status(fiber.StatusOK).JSON(response) + return c.Status(200).JSON(response) } func GetPublicAccountExternalAuths(c *fiber.Ctx) error { person := p.Find(c.Params("accountId")) if person == nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found")) + return c.Status(400).JSON(aid.ErrorBadRequest("No Account Found")) } - return c.Status(fiber.StatusOK).JSON([]aid.JSON{}) + return c.Status(200).JSON([]aid.JSON{}) } func GetPublicAccountByDisplayName(c *fiber.Ctx) error { person := p.FindByDisplay(c.Params("displayName")) if person == nil { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found")) + return c.Status(400).JSON(aid.ErrorBadRequest("No Account Found")) } - return c.Status(fiber.StatusOK).JSON(aid.JSON{ + return c.Status(200).JSON(aid.JSON{ "id": person.ID, "displayName": person.DisplayName, "externalAuths": []aid.JSON{}, }) +} + +func GetPrivacySettings(c *fiber.Ctx) error { + return c.Status(200).JSON(aid.JSON{ + "privacySettings": aid.JSON{ + "playRegion": "PUBLIC", + "badges": "PUBLIC", + "languages": "PUBLIC", + }, + }) } \ No newline at end of file diff --git a/handlers/lightswitch.go b/handlers/lightswitch.go index a1c9ded..e4966b1 100644 --- a/handlers/lightswitch.go +++ b/handlers/lightswitch.go @@ -32,7 +32,6 @@ func GetLightswitchBulkStatus(c *fiber.Ctx) error { "banned": isBanned, "launcherInfoDTO": aid.JSON{ "appName":"Fortnite", - "catalogItemId":"4fe75bbc5a674f4f9b356b5c90567da5", "namespace":"fn", }, }}) @@ -65,34 +64,34 @@ func GetFortniteTimeline(c *fiber.Ctx) error { switch season { case 2: events = append(events, aid.JSON{ - "activeUntil": "9999-12-31T23:59:59.999Z", - "activeSince": "0001-01-01T00:00:00Z", + "activeUntil": "9999-01-01T00:00:00.000Z", + "activeSince": "9999-01-01T00:00:00.000Z", "eventType": "EventFlag.LobbyWinterDecor", }) case 6: events = append(events, aid.JSON{ "activeUntil": "9999-01-01T00:00:00.000Z", - "activeSince": "0001-01-01T00:00:00Z", + "activeSince": "9999-01-01T00:00:00.000Z", "eventType": "EventFlag.LobbySeason6Halloween", }) case 11: events = append(events, aid.JSON{ "activeUntil": "9999-01-01T00:00:00.000Z", - "activeSince": "0001-01-01T00:00:00Z", + "activeSince": "9999-01-01T00:00:00.000Z", "eventType": "EventFlag.LTE_WinterFest2019", }, aid.JSON{ "activeUntil": "9999-01-01T00:00:00.000Z", - "activeSince": "0001-01-01T00:00:00Z", + "activeSince": "9999-01-01T00:00:00.000Z", "eventType": "EventFlag.LTE_WinterFest", }, aid.JSON{ "activeUntil": "9999-01-01T00:00:00.000Z", - "activeSince": "0001-01-01T00:00:00Z", + "activeSince": "9999-01-01T00:00:00.000Z", "eventType": "EventFlag.Winterfest.Tree", }) default: events = append(events, aid.JSON{ - "activeUntil": "9999-12-31T23:59:59.999Z", - "activeSince": "0001-01-01T00:00:00Z", + "activeUntil": "9999-01-01T00:00:00.000Z", + "activeSince": "9999-01-01T00:00:00.000Z", "eventType": "EventFlag.LobbySeason" + strings.Split(build, ".")[0], }) } diff --git a/main.go b/main.go index 5d57d37..964cb74 100644 --- a/main.go +++ b/main.go @@ -55,14 +55,21 @@ func init() { found = fortnite.NewFortnitePersonWithId(username, username, true) } - for _, perm := range found.Permissions { - found.RemovePermission(perm) - } - - found.AddPermission("all") + found.AddPermission(person.PermissionAllWithRoles) aid.Print("(snow) max account " + username + " loaded") } + + for _, username := range aid.Config.Accounts.Owners { + found := person.FindByDisplay(username) + if found == nil { + continue + } + + found.AddPermission(person.PermissionOwner) + aid.Print("(snow) owner account " + username + " loaded") + } } + func main() { r := fiber.New(fiber.Config{ DisableStartupMessage: true, @@ -70,7 +77,6 @@ func main() { JSONDecoder: json.Unmarshal, }) - r.Use(aid.FiberLogger()) r.Use(aid.FiberLimiter()) r.Use(aid.FiberCors()) @@ -78,9 +84,12 @@ func main() { r.Get("/region", handlers.GetRegion) r.Get("/content/api/pages/fortnite-game", handlers.GetContentPages) r.Get("/waitingroom/api/waitingroom", handlers.GetWaitingRoomStatus) + r.Get("/affiliate/api/public/affiliates/slug/:slug", handlers.GetAffiliate) + 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.Get("/profile/privacy_settings", handlers.MiddlewareFortnite, handlers.GetPrivacySettings) r.Put("/profile/play_region", handlers.AnyNoContent) r.Get("/", handlers.RedirectSocket) diff --git a/makefile b/makefile index b453082..04b3cbe 100644 --- a/makefile +++ b/makefile @@ -8,4 +8,4 @@ run: go run main.go test: - go test -v ./... \ No newline at end of file + go test \ No newline at end of file diff --git a/person/snapshot.go b/person/snapshot.go index 434bac0..9596007 100644 --- a/person/snapshot.go +++ b/person/snapshot.go @@ -5,7 +5,7 @@ import "github.com/ectrc/snow/storage" type PersonSnapshot struct { ID string DisplayName string - Permissions []string + Permissions int64 AthenaProfile ProfileSnapshot CommonCoreProfile ProfileSnapshot CommonPublicProfile ProfileSnapshot diff --git a/readme.md b/readme.md index 58d70b4..f8f8c10 100644 --- a/readme.md +++ b/readme.md @@ -19,9 +19,9 @@ - Interaction with a Game Server to handle **Event Tracking** for player statistics and challenges. This will be a very large task as a new specialised game server will need to be created. - After the game server addition, a **Matchmaking System** will be added to match players together for a game. It will use a bin packing algorithm to ensure that games are filled as much as possible. -And finally, the biggest task of all... +And once battle royale is completed ... -- **Save The World**. This is a very large task and will require a lot of work. It is not a priority at the moment and might be done after the Battle Royale experience is complete. +- **Save The World** ## Supported MCP Actions