diff --git a/discord/discord.go b/discord/discord.go index c2b23f1..54d3e76 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -48,8 +48,9 @@ func IntialiseClient() { addCommands() - for _, command := range StaticClient.Commands { - StaticClient.RegisterCommand(command) + if len(StaticClient.Commands) < len(StaticClient.GetRegisteredCommands()) { + StaticClient.UnregisterCommands() + StaticClient.RegisterCommands() } err := StaticClient.Client.Open() @@ -58,17 +59,66 @@ func IntialiseClient() { } } -func (c *DiscordClient) RegisterCommand(command *DiscordCommand) { - if command.AdminOnly { - adminDefaultPermission := int64(discordgo.PermissionAdministrator) - command.Command.DefaultMemberPermissions = &adminDefaultPermission - } - - _, err := c.Client.ApplicationCommandCreate(aid.Config.Discord.ID, aid.Config.Discord.Guild, command.Command) - if err != nil { - aid.Print("Failed to register command: " + command.Command.Name) +func (c *DiscordClient) UnregisterCommands() { + commands := c.GetRegisteredCommands() + if commands == nil { return } + + for _, command := range commands { + err := c.Client.ApplicationCommandDelete(aid.Config.Discord.ID, aid.Config.Discord.Guild, command.ID) + if err != nil { + aid.Print("Failed to delete command: " + command.Name) + } + } + + commands = c.GetGlobalRegisteredCommands() + + for _, command := range commands { + err := c.Client.ApplicationCommandDelete(aid.Config.Discord.ID, "", command.ID) + if err != nil { + aid.Print("Failed to delete command: " + command.Name) + } + } +} + +func (c *DiscordClient) RegisterCommands() { + adminPermission := int64(discordgo.PermissionAdministrator) + + update := []*discordgo.ApplicationCommand{} + for _, command := range c.Commands { + if command.AdminOnly { + command.Command.DefaultMemberPermissions = &adminPermission + } + + update = append(update, command.Command) + } + + _, err := c.Client.ApplicationCommandBulkOverwrite(aid.Config.Discord.ID, aid.Config.Discord.Guild, update) + if err != nil { + aid.Print("Failed to register commands", err) + return + } +} + +func (c *DiscordClient) GetRegisteredCommands() []*discordgo.ApplicationCommand { + commands, err := c.Client.ApplicationCommands(aid.Config.Discord.ID, aid.Config.Discord.Guild) + if err != nil { + aid.Print("Failed to get commands") + return nil + } + + return commands +} + +func (c *DiscordClient) GetGlobalRegisteredCommands() []*discordgo.ApplicationCommand { + commands, err := c.Client.ApplicationCommands(aid.Config.Discord.ID, "") + if err != nil { + aid.Print("Failed to get commands") + return nil + } + + return commands } func (c *DiscordClient) readyHandler(s *discordgo.Session, event *discordgo.Ready) { diff --git a/discord/errors.go b/discord/errors.go new file mode 100644 index 0000000..dc8b42f --- /dev/null +++ b/discord/errors.go @@ -0,0 +1,51 @@ +package discord + +import "github.com/bwmarrin/discordgo" + +var ErrorNoAccount = discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "You do not have an account with the bot.", + Flags: discordgo.MessageFlagsEphemeral, + }, +} + +var ErrorNoPermission = discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "You do not have permission to use this command.", + Flags: discordgo.MessageFlagsEphemeral, + }, +} + +var ErrorInvalidArguments = discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Please provide valid arguments.", + Flags: discordgo.MessageFlagsEphemeral, + }, +} + +var ErrorInvalidDisplayOrDiscord = discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "Please provide a valid display name or discord user.", + Flags: discordgo.MessageFlagsEphemeral, + }, +} + +var ErrorNoDisplay = discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "No display name found with that name, please try again with a valid name.", + Flags: discordgo.MessageFlagsEphemeral, + }, +} + +var ErrorNoDiscordUser = discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: "No discord user found with that ID, please try again with a valid ID.", + Flags: discordgo.MessageFlagsEphemeral, + }, +} \ No newline at end of file diff --git a/discord/handlers.go b/discord/handlers.go index 3758056..6c78121 100644 --- a/discord/handlers.go +++ b/discord/handlers.go @@ -1,6 +1,8 @@ package discord import ( + "strings" + "github.com/bwmarrin/discordgo" "github.com/ectrc/snow/aid" "github.com/ectrc/snow/fortnite" @@ -8,9 +10,6 @@ import ( "github.com/ectrc/snow/storage" ) -var ( -) - func addCommand(command *DiscordCommand) { StaticClient.Commands[command.Command.Name] = command } @@ -46,6 +45,37 @@ func addCommands() { AdminOnly: true, }) + addCommand(&DiscordCommand{ + 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, + }, + }, + }, + Handler: whoHandler, + AdminOnly: true, + }) + + addCommand(&DiscordCommand{ + Command: &discordgo.ApplicationCommand{ + Name: "me", + Description: "Lookup your own information.", + }, + Handler: meHandler, + }) + addCommand(&DiscordCommand{ Command: &discordgo.ApplicationCommand{ Name: "delete", @@ -53,6 +83,134 @@ func addCommands() { }, Handler: deleteHandler, }) + + addCommand(&DiscordCommand{ + 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, + }, + }, + }, + Handler: banHandler, + AdminOnly: true, + }) + + addCommand(&DiscordCommand{ + 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, + }, + }, + }, + Handler: unbanHandler, + AdminOnly: true, + }) + + 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, + }, + }, + }, + Handler: giveItemHandler, + AdminOnly: true, + }) + + addCommand(&DiscordCommand{ + 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, + }, + }, + }, + Handler: takeItemHandler, + AdminOnly: true, + }) } func createHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { @@ -106,9 +264,7 @@ func createModalHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - NewEmbedBuilder().SetTitle("Account already exists").SetDescription("You already have an account with the display name: `"+ found.DisplayName +"`").SetColor(0xda373c).Build(), - }, + Content: "You already have an account with the display name: `"+ found.DisplayName +"`", }, }) return @@ -119,15 +275,13 @@ func createModalHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - NewEmbedBuilder().SetTitle("Account already exists").SetDescription("An account with that display name already exists, please try a different name.").SetColor(0xda373c).Build(), - }, + Content: "Someone already has an account with the display name: `"+ found.DisplayName +"`, please choose another one.", }, }) return } - account := fortnite.NewFortnitePerson(display.Value, aid.RandomString(10), false) // or aid.Config.Fortnite.Everything + account := fortnite.NewFortnitePerson(display.Value, false) // or aid.Config.Fortnite.Everything discord := &storage.DB_DiscordPerson{ ID: i.Member.User.ID, PersonID: account.ID, @@ -140,9 +294,7 @@ func createModalHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - NewEmbedBuilder().SetTitle("Account created").SetDescription("Your account has been created with the display name: `"+ account.DisplayName +"`").SetColor(0x2093dc).Build(), - }, + Content: "Your account has been created with the display name: `"+ account.DisplayName +"`", }, }) } @@ -153,9 +305,8 @@ func deleteHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - NewEmbedBuilder().SetTitle("Account not found").SetDescription("You don't have an account with the bot.").SetColor(0xda373c).Build(), - }, + Content: "You do not have an account with the bot.", + Flags: discordgo.MessageFlagsEphemeral, }, }) return @@ -167,14 +318,24 @@ func deleteHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - NewEmbedBuilder().SetTitle("Account deleted").SetDescription("Your account has been deleted.").SetColor(0x2093dc).Build(), - }, + Content: "Your account has been deleted.", + Flags: discordgo.MessageFlagsEphemeral, }, }) } func informationHandler(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.PermissionInformation) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + playerCount := storage.Repo.GetPersonsCount() totalVbucks := storage.Repo.TotalVBucks() @@ -184,13 +345,344 @@ func informationHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { Embeds: []*discordgo.MessageEmbed{ NewEmbedBuilder(). SetTitle("Information"). - SetDescription("Useful information about this server's activity!"). - SetColor(0x2093dc). + SetColor(0x2b2d31). AddField("Players Registered", aid.FormatNumber(playerCount), true). AddField("Players Online", aid.FormatNumber(0), true). AddField("VBucks in Circulation", aid.FormatNumber(totalVbucks), false). Build(), }, + Flags: discordgo.MessageFlagsEphemeral, }, }) +} + +func whoHandler(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.PermissionLookup) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + player := getPersonFromOptions(i.ApplicationCommandData(), s) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) + return + } + + playerVbucks := player.CommonCoreProfile.Items.GetItemByTemplateID("Currency:MtxPurchased") + if playerVbucks == nil { + return + } + + activeCharacter := func() string { + if player.AthenaProfile == nil { + return "None" + } + + characterId := "" + player.AthenaProfile.Loadouts.RangeLoadouts(func(key string, value *person.Loadout) bool { + characterId = value.CharacterID + return false + }) + + if characterId == "" { + return "None" + } + + character := player.AthenaProfile.Items.GetItem(characterId) + if character == nil { + return "None" + } + + return character.TemplateID + }() + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + NewEmbedBuilder(). + SetTitle("Player Lookup"). + SetColor(0x2b2d31). + AddField("Display Name", player.DisplayName, true). + AddField("VBucks", aid.FormatNumber(playerVbucks.Quantity), true). + AddField("Discord Account", "<@"+player.Discord.ID+">", true). + AddField("ID", player.ID, true). + SetThumbnail("https://fortnite-api.com/images/cosmetics/br/"+ strings.Split(activeCharacter, ":")[1] +"/icon.png"). + Build(), + }, + Flags: discordgo.MessageFlagsEphemeral, + }, + }) +} + +func meHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { + player := person.FindByDiscord(i.Member.User.ID) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorNoAccount) + return + } + + playerVbucks := player.CommonCoreProfile.Items.GetItemByTemplateID("Currency:MtxPurchased") + if playerVbucks == nil { + return + } + + activeCharacter := func() string { + if player.AthenaProfile == nil { + return "None" + } + + characterId := "" + player.AthenaProfile.Loadouts.RangeLoadouts(func(key string, value *person.Loadout) bool { + characterId = value.CharacterID + return false + }) + + if characterId == "" { + return "None" + } + + character := player.AthenaProfile.Items.GetItem(characterId) + if character == nil { + return "None" + } + + return character.TemplateID + }() + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: []*discordgo.MessageEmbed{ + NewEmbedBuilder(). + SetTitle("Player Lookup"). + SetColor(0x2b2d31). + AddField("Display Name", player.DisplayName, true). + AddField("VBucks", aid.FormatNumber(playerVbucks.Quantity), true). + AddField("Discord Account", "<@"+player.Discord.ID+">", true). + AddField("ID", player.ID, true). + SetThumbnail("https://fortnite-api.com/images/cosmetics/br/"+ strings.Split(activeCharacter, ":")[1] +"/icon.png"). + Build(), + }, + Flags: discordgo.MessageFlagsEphemeral, + }, + }) +} + +func banHandler(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.PermissionBan) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + player := getPersonFromOptions(i.ApplicationCommandData(), s) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) + return + } + + player.Ban() + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: player.DisplayName + " has been banned.", + }, + }) +} + +func unbanHandler(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.PermissionBan) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + player := getPersonFromOptions(i.ApplicationCommandData(), s) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) + return + } + + player.Unban() + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: player.DisplayName + " has been unbanned.", + }, + }) +} + +func giveItemHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { + looker := person.FindByDiscord(i.Member.User.ID) + if looker == nil { + s.InteractionRespond(i.Interaction, &ErrorNoAccount) + return + } + + if !looker.HasPermission(person.PermissionGiveItem) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + player := getPersonFromOptions(i.ApplicationCommandData(), s) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) + return + } + + item := i.ApplicationCommandData().Options[0].StringValue() + if item == "" { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + qty := i.ApplicationCommandData().Options[1].IntValue() + if qty <= 0 { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + profile := i.ApplicationCommandData().Options[2].StringValue() + if profile == "" { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + if player.GetProfileFromType(profile) == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + snapshot := player.GetProfileFromType(profile).Snapshot() + foundItem := player.GetProfileFromType(profile).Items.GetItemByTemplateID(item) + switch (foundItem) { + case nil: + foundItem = person.NewItem(item, int(qty)) + player.GetProfileFromType(profile).Items.AddItem(foundItem) + default: + foundItem.Quantity += int(qty) + } + foundItem.Save() + player.GetProfileFromType(profile).Diff(snapshot) + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: player.DisplayName + " has been given or updated `" + item + "` in `" + profile + "`.", + }, + }) +} + +func takeItemHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { + looker := person.FindByDiscord(i.Member.User.ID) + if looker == nil { + s.InteractionRespond(i.Interaction, &ErrorNoAccount) + return + } + + if !looker.HasPermission(person.PermissionTakeItem) { + s.InteractionRespond(i.Interaction, &ErrorNoPermission) + return + } + + player := getPersonFromOptions(i.ApplicationCommandData(), s) + if player == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidDisplayOrDiscord) + return + } + + item := i.ApplicationCommandData().Options[0].StringValue() + if item == "" { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + qty := i.ApplicationCommandData().Options[1].IntValue() + if qty <= 0 { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + profile := i.ApplicationCommandData().Options[2].StringValue() + if profile == "" { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + if player.GetProfileFromType(profile) == nil { + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + } + + snapshot := player.GetProfileFromType(profile).Snapshot() + foundItem := player.GetProfileFromType(profile).Items.GetItemByTemplateID(item) + remove := false + switch (foundItem) { + case nil: + s.InteractionRespond(i.Interaction, &ErrorInvalidArguments) + return + default: + foundItem.Quantity -= int(qty) + foundItem.Save() + + if foundItem.Quantity <= 0 { + player.GetProfileFromType(profile).Items.DeleteItem(foundItem.ID) + remove = true + } + } + player.GetProfileFromType(profile).Diff(snapshot) + + str := player.DisplayName + " has had `" + aid.FormatNumber(int(qty)) + "` of `" + item + "` removed from `" + profile + "`." + if remove { + str = player.DisplayName + " has had `" + item + "` removed from `" + profile + "`." + } + + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: str, + }, + }) +} + +func getPersonFromOptions(data discordgo.ApplicationCommandInteractionData, s *discordgo.Session) *person.Person { + options := data.Options + + if len(options) <= 0 { + return nil + } + + for _, option := range options { + switch option.Type { + case discordgo.ApplicationCommandOptionUser: + if option.Name != "discord" { + continue + } + return person.FindByDiscord(option.UserValue(s).ID) + case discordgo.ApplicationCommandOptionString: + if option.Name != "display" { + continue + } + return person.FindByDisplay(option.StringValue()) + } + } + + return nil } \ No newline at end of file diff --git a/fortnite/person.go b/fortnite/person.go index 9e94b91..2ab6fa3 100644 --- a/fortnite/person.go +++ b/fortnite/person.go @@ -22,10 +22,9 @@ var ( } ) -func NewFortnitePerson(displayName string, key string, everything bool) *p.Person { +func NewFortnitePerson(displayName string, everything bool) *p.Person { person := p.NewPerson() person.DisplayName = displayName - person.AccessKey = key for _, item := range defaultAthenaItems { item := p.NewItem(item, 1) diff --git a/handlers/auth.go b/handlers/auth.go index ae44cad..cdf47df 100644 --- a/handlers/auth.go +++ b/handlers/auth.go @@ -66,14 +66,6 @@ func PostTokenPassword(c *fiber.Ctx, body *FortniteTokenBody) error { return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("No Account Found")) } - if person.AccessKey == "" { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Activation Required")) - } - - if person.AccessKey != body.Password { - return c.Status(fiber.StatusBadRequest).JSON(aid.ErrorBadRequest("Invalid Access Key")) - } - access, err := aid.JWTSign(aid.JSON{ "snow_id": person.ID, // custom "creation_date": time.Now().Format("2006-01-02T15:04:05.999Z"), diff --git a/handlers/lightswitch.go b/handlers/lightswitch.go index 9484d4b..5433c34 100644 --- a/handlers/lightswitch.go +++ b/handlers/lightswitch.go @@ -7,17 +7,20 @@ import ( "time" "github.com/ectrc/snow/aid" + "github.com/ectrc/snow/person" "github.com/gofiber/fiber/v2" ) func GetLightswitchBulkStatus(c *fiber.Ctx) error { + person := c.Locals("person").(*person.Person) + return c.Status(fiber.StatusOK).JSON([]aid.JSON{{ "serviceInstanceId": "fortnite", "status" :"UP", "message": "fortnite is up.", "maintenanceUri": nil, "allowedActions": []string{"PLAY","DOWNLOAD"}, - "banned":false, + "banned": person.IsBanned, "launcherInfoDTO": aid.JSON{ "appName":"Fortnite", "catalogItemId":"4fe75bbc5a674f4f9b356b5c90567da5", diff --git a/main.go b/main.go index 5fe0b61..9946b13 100644 --- a/main.go +++ b/main.go @@ -106,6 +106,7 @@ func main() { profile.Post("/client/:action", handlers.PostProfileAction) lightswitch := r.Group("/lightswitch/api") + lightswitch.Use(handlers.FortniteMiddleware) lightswitch.Get("/service/bulk/status", handlers.GetLightswitchBulkStatus) snow := r.Group("/snow") diff --git a/person/permissions.go b/person/permissions.go new file mode 100644 index 0000000..ea500eb --- /dev/null +++ b/person/permissions.go @@ -0,0 +1,15 @@ +package person + +type Permission string + +const ( + PermissionLookup Permission = "lookup" + PermissionBan Permission = "ban" + PermissionInformation Permission = "information" + PermissionDonator Permission = "donator" + PermissionGiveItem Permission = "give_item" + PermissionTakeItem Permission = "take_item" + PermissionReset Permission = "reset" + + PermissionAll Permission = "all" +) \ No newline at end of file diff --git a/person/person.go b/person/person.go index c4a3bbf..fe342f8 100644 --- a/person/person.go +++ b/person/person.go @@ -8,14 +8,14 @@ import ( type Person struct { ID string DisplayName string - AccessKey string + Permissions []string + IsBanned bool AthenaProfile *Profile CommonCoreProfile *Profile CommonPublicProfile *Profile Profile0Profile *Profile CollectionsProfile *Profile CreativeProfile *Profile - // DiscordID string Discord *storage.DB_DiscordPerson } @@ -28,7 +28,8 @@ func NewPerson() *Person { return &Person{ ID: uuid.New().String(), DisplayName: uuid.New().String(), - AccessKey: "", + Permissions: []string{}, + IsBanned: false, AthenaProfile: NewProfile("athena"), CommonCoreProfile: NewProfile("common_core"), CommonPublicProfile: NewProfile("common_public"), @@ -135,7 +136,8 @@ func findHelper(databasePerson *storage.DB_Person) *Person { person := &Person{ ID: databasePerson.ID, DisplayName: databasePerson.DisplayName, - AccessKey: databasePerson.AccessKey, + Permissions: databasePerson.Permissions, + IsBanned: databasePerson.IsBanned, AthenaProfile: athenaProfile, CommonCoreProfile: commonCoreProfile, CommonPublicProfile: commonPublicProfile, @@ -143,7 +145,6 @@ func findHelper(databasePerson *storage.DB_Person) *Person { CollectionsProfile: collectionsProfile, CreativeProfile: creativeProfile, Discord: &databasePerson.Discord, - // DiscordID: databasePerson.DiscordID, } cache.SavePerson(person) @@ -197,14 +198,54 @@ func (p *Person) Save() { storage.Repo.SavePerson(dbPerson) } +func (p *Person) Ban() { + p.IsBanned = true + p.Save() +} + +func (p *Person) Unban() { + p.IsBanned = false + p.Save() +} + +func (p *Person) AddPermission(permission string) { + p.Permissions = append(p.Permissions, permission) + p.Save() +} + +func (p *Person) RemovePermission(permission string) { + for i, perm := range p.Permissions { + if perm == permission { + p.Permissions = append(p.Permissions[:i], p.Permissions[i+1:]...) + break + } + } + p.Save() +} + +func (p *Person) HasPermission(permission Permission) bool { + for _, perm := range p.Permissions { + if perm == string(PermissionAll) { + return true + } + + if perm == string(permission) { + return true + } + } + + return false +} + func (p *Person) ToDatabase() *storage.DB_Person { dbPerson := storage.DB_Person{ ID: p.ID, DisplayName: p.DisplayName, + Permissions: p.Permissions, + IsBanned: p.IsBanned, Profiles: []storage.DB_Profile{}, Stats: []storage.DB_SeasonStat{}, - AccessKey: p.AccessKey, - // DiscordID: p.DiscordID, + Discord: storage.DB_DiscordPerson{}, } if p.Discord != nil { @@ -281,6 +322,8 @@ func (p *Person) Snapshot() *PersonSnapshot { return &PersonSnapshot{ ID: p.ID, DisplayName: p.DisplayName, + Permissions: p.Permissions, + IsBanned: p.IsBanned, AthenaProfile: *p.AthenaProfile.Snapshot(), CommonCoreProfile: *p.CommonCoreProfile.Snapshot(), CommonPublicProfile: *p.CommonPublicProfile.Snapshot(), @@ -288,7 +331,6 @@ func (p *Person) Snapshot() *PersonSnapshot { CollectionsProfile: *p.CollectionsProfile.Snapshot(), CreativeProfile: *p.CreativeProfile.Snapshot(), Discord: *p.Discord, - DiscordID: p.Discord.ID, } } diff --git a/person/snapshot.go b/person/snapshot.go index da024ae..72f3ab0 100644 --- a/person/snapshot.go +++ b/person/snapshot.go @@ -5,6 +5,8 @@ import "github.com/ectrc/snow/storage" type PersonSnapshot struct { ID string DisplayName string + Permissions []string + IsBanned bool AthenaProfile ProfileSnapshot CommonCoreProfile ProfileSnapshot CommonPublicProfile ProfileSnapshot @@ -12,7 +14,6 @@ type PersonSnapshot struct { CollectionsProfile ProfileSnapshot CreativeProfile ProfileSnapshot Discord storage.DB_DiscordPerson - DiscordID string } type ProfileSnapshot struct { diff --git a/storage/tables.go b/storage/tables.go index a7fbae1..e77e404 100644 --- a/storage/tables.go +++ b/storage/tables.go @@ -9,10 +9,10 @@ type Tabler interface { type DB_Person struct { ID string DisplayName string - AccessKey string + Permissions pq.StringArray `gorm:"type:text[]"` + IsBanned bool Profiles []DB_Profile `gorm:"foreignkey:PersonID"` Stats []DB_SeasonStat `gorm:"foreignkey:PersonID"` - // DiscordID string Discord DB_DiscordPerson `gorm:"foreignkey:PersonID"` }