From 449297e71b4a8874afad0ea367583e2435486773 Mon Sep 17 00:00:00 2001 From: Kichiyaki Date: Fri, 26 Jun 2020 20:30:25 +0200 Subject: [PATCH] add group system --- .gitignore | 3 +- cron/cron.go | 3 + cron/handler.go | 48 ++- discord/commands.go | 405 ++++++++++++++---- discord/discord.go | 25 +- group/repository.go | 16 + group/repository/group_pg_repo.go | 109 +++++ main.go | 7 + models/group.go | 18 + models/observation.go | 29 +- models/server.go | 6 +- observation/repository.go | 2 +- observation/repository/observation_pg_repo.go | 8 +- server/repository/server_pg_repo.go | 4 +- 14 files changed, 556 insertions(+), 127 deletions(-) create mode 100644 group/repository.go create mode 100644 group/repository/group_pg_repo.go create mode 100644 models/group.go diff --git a/.gitignore b/.gitignore index 78ff441..6704ab4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .env.development .env.production .env -.netrc \ No newline at end of file +.netrc +todo.sql \ No newline at end of file diff --git a/cron/cron.go b/cron/cron.go index eaaaf75..3186b60 100644 --- a/cron/cron.go +++ b/cron/cron.go @@ -6,6 +6,7 @@ import ( "github.com/tribalwarshelp/golang-sdk/sdk" "github.com/tribalwarshelp/dcbot/discord" + "github.com/tribalwarshelp/dcbot/group" "github.com/tribalwarshelp/dcbot/observation" "github.com/tribalwarshelp/dcbot/server" @@ -16,6 +17,7 @@ type Config struct { ServerRepo server.Repository ObservationRepo observation.Repository Discord *discord.Session + GroupRepo group.Repository API *sdk.SDK } @@ -24,6 +26,7 @@ func Attach(c *cron.Cron, cfg Config) { lastEnnoblementAt: make(map[string]time.Time), serverRepo: cfg.ServerRepo, observationRepo: cfg.ObservationRepo, + groupRepo: cfg.GroupRepo, discord: cfg.Discord, api: cfg.API, } diff --git a/cron/handler.go b/cron/handler.go index b57aca8..64f1bb3 100644 --- a/cron/handler.go +++ b/cron/handler.go @@ -8,6 +8,7 @@ import ( shared_models "github.com/tribalwarshelp/shared/models" "github.com/tribalwarshelp/dcbot/discord" + "github.com/tribalwarshelp/dcbot/group" "github.com/tribalwarshelp/dcbot/models" "github.com/tribalwarshelp/dcbot/observation" "github.com/tribalwarshelp/dcbot/server" @@ -19,6 +20,7 @@ type handler struct { lastEnnoblementAt map[string]time.Time serverRepo server.Repository observationRepo observation.Repository + groupRepo group.Repository discord *discord.Session api *sdk.SDK } @@ -87,44 +89,44 @@ func (h *handler) loadLangVersions(worlds []string) map[shared_models.LanguageTa } func (h *handler) checkLastEnnoblements() { - worlds, err := h.observationRepo.FetchWorlds(context.Background()) + servers, err := h.observationRepo.FetchServers(context.Background()) if err != nil { log.Print("checkLastEnnoblements error: " + err.Error()) return } - log.Print("checkLastEnnoblements: worlds: ", worlds) + log.Print("checkLastEnnoblements: servers: ", servers) - servers, total, err := h.serverRepo.Fetch(context.Background(), nil) + groups, total, err := h.groupRepo.Fetch(context.Background(), nil) if err != nil { log.Print("checkLastEnnoblements error: " + err.Error()) return } - log.Print("checkLastEnnoblements: number of loaded discord servers: ", total) + log.Print("checkLastEnnoblements: number of loaded groups: ", total) - langVersions := h.loadLangVersions(worlds) - ennoblementsByServerKey := h.loadEnnoblements(worlds) + langVersions := h.loadLangVersions(servers) + ennoblementsByServerKey := h.loadEnnoblements(servers) - for _, server := range servers { - if server.ConqueredVillagesChannelID == "" && server.LostVillagesChannelID == "" { + for _, group := range groups { + if group.ConqueredVillagesChannelID == "" && group.LostVillagesChannelID == "" { continue } - for _, tribe := range server.Observations { - ennoblements, ok := ennoblementsByServerKey[tribe.World] - langVersion, ok2 := langVersions[utils.LanguageTagFromWorldName(tribe.World)] + for _, observation := range group.Observations { + ennoblements, ok := ennoblementsByServerKey[observation.Server] + langVersion, ok2 := langVersions[utils.LanguageTagFromWorldName(observation.Server)] if ok && ok2 { - if server.LostVillagesChannelID != "" { - for _, ennoblement := range ennoblements.getLostVillagesByTribe(tribe.TribeID) { + if group.LostVillagesChannelID != "" { + for _, ennoblement := range ennoblements.getLostVillagesByTribe(observation.TribeID) { if !isPlayerTribeNil(ennoblement.NewOwner) && - server.Observations.Contains(tribe.World, ennoblement.NewOwner.Tribe.ID) { + group.Observations.Contains(observation.Server, ennoblement.NewOwner.Tribe.ID) { continue } newMsgDataConfig := newMessageDataConfig{ host: langVersion.Host, - world: tribe.World, + world: observation.Server, ennoblement: ennoblement, } msgData := newMessageData(newMsgDataConfig) - h.discord.SendEmbed(server.LostVillagesChannelID, + h.discord.SendEmbed(group.LostVillagesChannelID, discord. NewEmbed(). SetTitle("Stracona wioska"). @@ -134,19 +136,19 @@ func (h *handler) checkLastEnnoblements() { } } - if server.ConqueredVillagesChannelID != "" { - for _, ennoblement := range ennoblements.getConqueredVillagesByTribe(tribe.TribeID) { + if group.ConqueredVillagesChannelID != "" { + for _, ennoblement := range ennoblements.getConqueredVillagesByTribe(observation.TribeID) { if !isPlayerTribeNil(ennoblement.OldOwner) && - server.Observations.Contains(tribe.World, ennoblement.OldOwner.Tribe.ID) { + group.Observations.Contains(observation.Server, ennoblement.OldOwner.Tribe.ID) { continue } newMsgDataConfig := newMessageDataConfig{ host: langVersion.Host, - world: tribe.World, + world: observation.Server, ennoblement: ennoblement, } msgData := newMessageData(newMsgDataConfig) - h.discord.SendEmbed(server.ConqueredVillagesChannelID, + h.discord.SendEmbed(group.ConqueredVillagesChannelID, discord. NewEmbed(). SetTitle("Podbita wioska"). @@ -188,7 +190,7 @@ func (h *handler) checkBotMembershipOnServers() { } func (h *handler) deleteClosedTribalWarsServers() { - servers, err := h.observationRepo.FetchWorlds(context.Background()) + servers, err := h.observationRepo.FetchServers(context.Background()) if err != nil { log.Print("deleteClosedTribalWarsServers: " + err.Error()) return @@ -214,7 +216,7 @@ func (h *handler) deleteClosedTribalWarsServers() { if len(keys) > 0 { deleted, err := h.observationRepo.Delete(context.Background(), &models.ObservationFilter{ - World: keys, + Server: keys, }) if err != nil { log.Print("deleteClosedTribalWarsServers error: " + err.Error()) diff --git a/discord/commands.go b/discord/commands.go index f4bc74d..be4daff 100644 --- a/discord/commands.go +++ b/discord/commands.go @@ -15,13 +15,17 @@ import ( ) const ( - ObservationsPerServer = 10 + ObservationsPerGroup = 10 + GroupsPerServer = 5 ) type Command string const ( HelpCommand Command = "help" + AddGroupCommand Command = "addgroup" + DeleteGroupCommand Command = "deletegroup" + GroupsCommand Command = "groups" ObserveCommand Command = "observe" ObservationsCommand Command = "observations" UnObserveCommand Command = "unobserve" @@ -70,22 +74,35 @@ func (s *Session) handleHelpCommand(m *discordgo.MessageCreate) { ) commandsForGuildAdmins := fmt.Sprintf(` -- **%s** [świat] [id] - dodaje plemię z danego świata do obserwowanych -- **%s** - wyświetla wszystkie obserwowane plemiona -- **%s** [id z %s] - usuwa plemię z obserwowanych -- **%s** - ustawia kanał na którym będą wyświetlać się informacje o podbitych wioskach -- **%s** - informacje o podbitych wioskach na wybranym kanale nie będą się już pojawiały -- **%s** - ustawia kanał na którym będą wyświetlać się informacje o straconych wioskach -- **%s** - informacje o straconych wioskach na wybranym kanale nie będą się już pojawiały +- **%s** - tworzy nową grupę +- **%s** - usuwa grupę +- **%s** - lista grup +- **%s** [id grupy z %s] [świat] [id plemienia] - dodaje plemię z danego świata do obserwowanych +- **%s** [id grupy z %s] - wyświetla wszystkie obserwowane plemiona +- **%s** [id grupy z %s] [id z %s] - usuwa plemię z obserwowanych +- **%s** [id grupy z %s] - ustawia kanał na którym będą wyświetlać się informacje o podbitych wioskach +- **%s** [id grupy z %s] - informacje o podbitych wioskach na wybranym kanale nie będą się już pojawiały +- **%s** [id grupy z %s] - ustawia kanał na którym będą wyświetlać się informacje o straconych wioskach +- **%s** [id grupy z %s] - informacje o straconych wioskach na wybranym kanale nie będą się już pojawiały `, + AddGroupCommand.WithPrefix(s.cfg.CommandPrefix), + DeleteGroupCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), ObserveCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), ObservationsCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), UnObserveCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), ObservationsCommand.WithPrefix(s.cfg.CommandPrefix), ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), UnObserveConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), LostVillagesCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), UnObserveLostVillagesCommand.WithPrefix(s.cfg.CommandPrefix), + GroupsCommand.WithPrefix(s.cfg.CommandPrefix), ) s.SendEmbed(m.ChannelID, NewEmbed(). @@ -245,10 +262,11 @@ func (s *Session) handleTribeCommand(m *discordgo.MessageCreate, args ...string) MessageEmbed) } -func (s *Session) handleConqueredVillagesCommand(m *discordgo.MessageCreate) { +func (s *Session) handleAddGroupCommand(m *discordgo.MessageCreate) { if m.GuildID == "" { return } + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { return } @@ -256,17 +274,142 @@ func (s *Session) handleConqueredVillagesCommand(m *discordgo.MessageCreate) { server := &models.Server{ ID: m.GuildID, } - err := s.cfg.ServerRepository.Store(context.Background(), server) + if err := s.cfg.ServerRepository.Store(context.Background(), server); err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Nie udało się dodać grupy", m.Author.Mention())) + return + } + if len(server.Groups) >= GroupsPerServer { + s.SendMessage(m.ChannelID, + m.Author.Mention()+fmt.Sprintf(` Osiągnięto limit grup na serwerze (%d/%d).`, GroupsPerServer, GroupsPerServer)) + return + } + + group := &models.Group{ + ServerID: m.GuildID, + } + if err := s.cfg.GroupRepository.Store(context.Background(), group); err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Nie udało się dodać grupy", m.Author.Mention())) + return + } + + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Utworzono nową grupę o ID %d.", m.Author.Mention(), group.ID)) +} +func (s *Session) handleDeleteGroupCommand(m *discordgo.MessageCreate, args ...string) { + if m.GuildID == "" { + return + } + + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + return + } + + argsLength := len(args) + if argsLength > 1 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + return + } else if argsLength < 1 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [id grupy]", + m.Author.Mention(), + DeleteGroupCommand.WithPrefix(s.cfg.CommandPrefix))) + return + } + + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Niepoprawne ID grupy", m.Author.Mention())) + return + } + + go s.cfg.GroupRepository.Delete(context.Background(), &models.GroupFilter{ + ID: []int{groupID}, + ServerID: []string{m.GuildID}, + }) + + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Usunięto grupę.", m.Author.Mention())) +} + +func (s *Session) handleGroupsCommand(m *discordgo.MessageCreate) { + if m.GuildID == "" { + return + } + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + return + } + + groups, _, err := s.cfg.GroupRepository.Fetch(context.Background(), &models.GroupFilter{ + ServerID: []string{m.GuildID}, + }) if err != nil { return } - server.ConqueredVillagesChannelID = m.ChannelID - go s.cfg.ServerRepository.Update(context.Background(), server) + + msg := "" + for i, groups := range groups { + msg += fmt.Sprintf("**%d**. %d\n", i+1, groups.ID) + } + + if msg == "" { + msg = "Brak dodanych grup" + } + + s.SendEmbed(m.ChannelID, NewEmbed(). + SetTitle("Lista grup"). + AddField("Indeks. ID", msg). + SetFooter("Strona 1 z 1"). + MessageEmbed) +} + +func (s *Session) handleConqueredVillagesCommand(m *discordgo.MessageCreate, args ...string) { + if m.GuildID == "" { + return + } + + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + return + } + + argsLength := len(args) + if argsLength > 1 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + return + } else if argsLength < 1 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [id grupy]", + m.Author.Mention(), + ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix))) + return + } + + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Niepoprawne ID grupy", m.Author.Mention())) + return + } + + groups, _, err := s.cfg.GroupRepository.Fetch(context.Background(), &models.GroupFilter{ + ID: []int{groupID}, + ServerID: []string{m.GuildID}, + }) + if err != nil || len(groups) == 0 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s nie znaleziono grupy.", m.Author.Mention())) + return + } + + groups[0].ConqueredVillagesChannelID = m.ChannelID + go s.cfg.GroupRepository.Update(context.Background(), groups[0]) s.SendMessage(m.ChannelID, fmt.Sprintf("%s Pomyślnie zmieniono kanał na którym będą się wyświetlać informacje o podbitych wioskach.", m.Author.Mention())) } -func (s *Session) handleUnObserveConqueredVillagesCommand(m *discordgo.MessageCreate) { +func (s *Session) handleUnObserveConqueredVillagesCommand(m *discordgo.MessageCreate, args ...string) { if m.GuildID == "" { return } @@ -274,22 +417,44 @@ func (s *Session) handleUnObserveConqueredVillagesCommand(m *discordgo.MessageCr return } - server := &models.Server{ - ID: m.GuildID, - } - err := s.cfg.ServerRepository.Store(context.Background(), server) - if err != nil { + argsLength := len(args) + if argsLength > 1 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + return + } else if argsLength < 1 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [id grupy]", + m.Author.Mention(), + UnObserveConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix))) return } - if server.ConqueredVillagesChannelID != "" { - server.ConqueredVillagesChannelID = "" - go s.cfg.ServerRepository.Update(context.Background(), server) + + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Niepoprawne ID grupy", m.Author.Mention())) + return + } + + groups, _, err := s.cfg.GroupRepository.Fetch(context.Background(), &models.GroupFilter{ + ID: []int{groupID}, + ServerID: []string{m.GuildID}, + }) + if err != nil || len(groups) == 0 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s nie znaleziono grupy.", m.Author.Mention())) + return + } + + if groups[0].ConqueredVillagesChannelID != "" { + groups[0].ConqueredVillagesChannelID = "" + go s.cfg.GroupRepository.Update(context.Background(), groups[0]) } s.SendMessage(m.ChannelID, fmt.Sprintf("%s Informacje o podbitych wioskach nie będą się już pojawiały.", m.Author.Mention())) } -func (s *Session) handleLostVillagesCommand(m *discordgo.MessageCreate) { +func (s *Session) handleLostVillagesCommand(m *discordgo.MessageCreate, args ...string) { if m.GuildID == "" { return } @@ -297,20 +462,42 @@ func (s *Session) handleLostVillagesCommand(m *discordgo.MessageCreate) { return } - server := &models.Server{ - ID: m.GuildID, - } - err := s.cfg.ServerRepository.Store(context.Background(), server) - if err != nil { + argsLength := len(args) + if argsLength > 1 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + return + } else if argsLength < 1 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [id grupy]", + m.Author.Mention(), + LostVillagesCommand.WithPrefix(s.cfg.CommandPrefix))) return } - server.LostVillagesChannelID = m.ChannelID - go s.cfg.ServerRepository.Update(context.Background(), server) + + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Niepoprawne ID grupy", m.Author.Mention())) + return + } + + groups, _, err := s.cfg.GroupRepository.Fetch(context.Background(), &models.GroupFilter{ + ID: []int{groupID}, + ServerID: []string{m.GuildID}, + }) + if err != nil || len(groups) == 0 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s nie znaleziono grupy.", m.Author.Mention())) + return + } + groups[0].LostVillagesChannelID = m.ChannelID + go s.cfg.GroupRepository.Update(context.Background(), groups[0]) + s.SendMessage(m.ChannelID, fmt.Sprintf("%s Pomyślnie zmieniono kanał na którym będą się wyświetlać informacje o straconych wioskach.", m.Author.Mention())) } -func (s *Session) handleUnObserveLostVillagesCommand(m *discordgo.MessageCreate) { +func (s *Session) handleUnObserveLostVillagesCommand(m *discordgo.MessageCreate, args ...string) { if m.GuildID == "" { return } @@ -318,17 +505,40 @@ func (s *Session) handleUnObserveLostVillagesCommand(m *discordgo.MessageCreate) return } - server := &models.Server{ - ID: m.GuildID, - } - err := s.cfg.ServerRepository.Store(context.Background(), server) - if err != nil { + argsLength := len(args) + if argsLength > 1 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + return + } else if argsLength < 1 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [id grupy]", + m.Author.Mention(), + UnObserveLostVillagesCommand.WithPrefix(s.cfg.CommandPrefix))) return } - if server.LostVillagesChannelID != "" { - server.LostVillagesChannelID = "" - go s.cfg.ServerRepository.Update(context.Background(), server) + + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s Niepoprawne ID grupy", m.Author.Mention())) + return } + + groups, _, err := s.cfg.GroupRepository.Fetch(context.Background(), &models.GroupFilter{ + ID: []int{groupID}, + ServerID: []string{m.GuildID}, + }) + if err != nil || len(groups) == 0 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s nie znaleziono grupy.", m.Author.Mention())) + return + } + + if groups[0].LostVillagesChannelID != "" { + groups[0].LostVillagesChannelID = "" + go s.cfg.GroupRepository.Update(context.Background(), groups[0]) + } + s.SendMessage(m.ChannelID, fmt.Sprintf("%s Informacje o straconych wioskach nie będą się już pojawiały.", m.Author.Mention())) } @@ -342,19 +552,27 @@ func (s *Session) handleObserveCommand(m *discordgo.MessageCreate, args ...strin } argsLength := len(args) - if argsLength > 2 { - s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[2:argsLength]...) + if argsLength > 3 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[3:argsLength]...) return - } else if argsLength < 2 { + } else if argsLength < 3 { s.SendMessage(m.ChannelID, - fmt.Sprintf("%s %s [świat] [id plemienia]", + fmt.Sprintf("%s %s [id grupy] [świat] [id plemienia]", m.Author.Mention(), ObserveCommand.WithPrefix(s.cfg.CommandPrefix))) return } - world := args[0] - id, err := strconv.Atoi(args[1]) + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [id grupy] [świat] [id plemienia]", + m.Author.Mention(), + ObserveCommand.WithPrefix(s.cfg.CommandPrefix))) + return + } + serverKey := args[1] + tribeID, err := strconv.Atoi(args[2]) if err != nil { s.SendMessage(m.ChannelID, fmt.Sprintf("%s %s [świat] [id plemienia]", @@ -363,40 +581,38 @@ func (s *Session) handleObserveCommand(m *discordgo.MessageCreate, args ...strin return } - server, err := s.cfg.API.Servers.Read(world, nil) + server, err := s.cfg.API.Servers.Read(serverKey, nil) if err != nil || server == nil { - s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` świat %s jest nieobsługiwany.`, world)) + s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` świat %s jest nieobsługiwany.`, serverKey)) return } if server.Status == shared_models.ServerStatusClosed { - s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` świat %s jest zamknięty.`, world)) + s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` świat %s jest zamknięty.`, serverKey)) return } - tribe, err := s.cfg.API.Tribes.Read(world, id) + tribe, err := s.cfg.API.Tribes.Read(server.Key, tribeID) if err != nil || tribe == nil { - s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` Plemię o ID: %d nie istnieje na świecie %s.`, id, world)) + s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` Plemię o ID: %d nie istnieje na świecie %s.`, tribeID, server.Key)) return } - dcServer := &models.Server{ - ID: m.GuildID, - } - err = s.cfg.ServerRepository.Store(context.Background(), dcServer) - if err != nil { - s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie udało się dodać plemienia do obserwowanych.`) + group, err := s.cfg.GroupRepository.GetByID(context.Background(), groupID) + if err != nil || group.ServerID != m.GuildID { + s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie znaleziono grupy.`) return } - if len(dcServer.Observations) >= ObservationsPerServer { - s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` Osiągnięto limit plemion (%d/%d).`, ObservationsPerServer, ObservationsPerServer)) + if len(group.Observations) >= ObservationsPerGroup { + s.SendMessage(m.ChannelID, + m.Author.Mention()+fmt.Sprintf(` Osiągnięto limit plemion w grupie (%d/%d).`, ObservationsPerGroup, ObservationsPerGroup)) return } err = s.cfg.ObservationRepository.Store(context.Background(), &models.Observation{ - World: world, - TribeID: id, - ServerID: dcServer.ID, + Server: server.Key, + TribeID: tribeID, + GroupID: groupID, }) if err != nil { s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie udało się dodać plemienia do obserwowanych.`) @@ -415,35 +631,49 @@ func (s *Session) handleUnObserveCommand(m *discordgo.MessageCreate, args ...str } argsLength := len(args) - if argsLength > 1 { - s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + if argsLength > 2 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[2:argsLength]...) return - } else if argsLength < 1 { + } else if argsLength < 2 { s.SendMessage(m.ChannelID, - fmt.Sprintf(`%s %s [id z tw!list]`, + fmt.Sprintf(`%s %s [id grupy] [id obserwacji]`, m.Author.Mention(), UnObserveCommand.WithPrefix(s.cfg.CommandPrefix))) return } - id, err := strconv.Atoi(args[0]) + groupID, err := strconv.Atoi(args[0]) if err != nil { s.SendMessage(m.ChannelID, - fmt.Sprintf(`%s %s [id z tw!list]`, + fmt.Sprintf(`%s %s [id grupy] [id obserwacji]`, m.Author.Mention(), UnObserveCommand.WithPrefix(s.cfg.CommandPrefix))) return } + observationID, err := strconv.Atoi(args[1]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf(`%s %s [id grupy] [id obserwacji]`, + m.Author.Mention(), + UnObserveCommand.WithPrefix(s.cfg.CommandPrefix))) + return + } + + group, err := s.cfg.GroupRepository.GetByID(context.Background(), groupID) + if err != nil || group.ServerID != m.GuildID { + s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie znaleziono grupy.`) + return + } go s.cfg.ObservationRepository.Delete(context.Background(), &models.ObservationFilter{ - ServerID: []string{m.GuildID}, - ID: []int{id}, + GroupID: []int{groupID}, + ID: []int{observationID}, }) s.SendMessage(m.ChannelID, m.Author.Mention()+` Usunięto.`) } -func (s *Session) handleObservationsCommand(m *discordgo.MessageCreate) { +func (s *Session) handleObservationsCommand(m *discordgo.MessageCreate, args ...string) { if m.GuildID == "" { return } @@ -451,8 +681,35 @@ func (s *Session) handleObservationsCommand(m *discordgo.MessageCreate) { return } + argsLength := len(args) + if argsLength > 1 { + s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[1:argsLength]...) + return + } else if argsLength < 1 { + s.SendMessage(m.ChannelID, + fmt.Sprintf(`%s %s [id grupy]`, + m.Author.Mention(), + ObservationsCommand.WithPrefix(s.cfg.CommandPrefix))) + return + } + + groupID, err := strconv.Atoi(args[0]) + if err != nil { + s.SendMessage(m.ChannelID, + fmt.Sprintf(`%s %s [id grupy]`, + m.Author.Mention(), + ObservationsCommand.WithPrefix(s.cfg.CommandPrefix))) + return + } + + group, err := s.cfg.GroupRepository.GetByID(context.Background(), groupID) + if err != nil || group.ServerID != m.GuildID { + s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie znaleziono grupy.`) + return + } + observations, _, err := s.cfg.ObservationRepository.Fetch(context.Background(), &models.ObservationFilter{ - ServerID: []string{m.GuildID}, + GroupID: []int{groupID}, }) if err != nil { return @@ -460,12 +717,16 @@ func (s *Session) handleObservationsCommand(m *discordgo.MessageCreate) { msg := "" for i, observation := range observations { - msg += fmt.Sprintf("**%d**. %d - %s - %d\n", i+1, observation.ID, observation.World, observation.TribeID) + msg += fmt.Sprintf("**%d**. %d - %s - %d\n", i+1, observation.ID, observation.Server, observation.TribeID) + } + + if msg == "" { + msg = "Brak dodanych obserwacji do tej grupy" } s.SendEmbed(m.ChannelID, NewEmbed(). SetTitle("Lista obserwowanych plemion"). - AddField("Indeks. ID - świat - ID plemienia", msg). + AddField("Indeks. ID - Serwer - ID plemienia", msg). SetFooter("Strona 1 z 1"). MessageEmbed) } diff --git a/discord/discord.go b/discord/discord.go index d0524c2..883548b 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -4,6 +4,7 @@ import ( "fmt" "strings" + "github.com/tribalwarshelp/dcbot/group" "github.com/tribalwarshelp/dcbot/observation" "github.com/tribalwarshelp/dcbot/server" "github.com/tribalwarshelp/golang-sdk/sdk" @@ -16,6 +17,7 @@ type SessionConfig struct { CommandPrefix string Status string ServerRepository server.Repository + GroupRepository group.Repository ObservationRepository observation.Repository API *sdk.SDK } @@ -81,22 +83,31 @@ func (s *Session) handleNewMessage(_ *discordgo.Session, m *discordgo.MessageCre s.handleHelpCommand(m) case AuthorCommand.WithPrefix(s.cfg.CommandPrefix): s.handleAuthorCommand(m) + case TribeCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleTribeCommand(m, args...) + + case AddGroupCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleAddGroupCommand(m) + case DeleteGroupCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleDeleteGroupCommand(m, args...) + case GroupsCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleGroupsCommand(m) + case ObserveCommand.WithPrefix(s.cfg.CommandPrefix): s.handleObserveCommand(m, args...) case UnObserveCommand.WithPrefix(s.cfg.CommandPrefix): s.handleUnObserveCommand(m, args...) case ObservationsCommand.WithPrefix(s.cfg.CommandPrefix): - s.handleObservationsCommand(m) + s.handleObservationsCommand(m, args...) case ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix): - s.handleConqueredVillagesCommand(m) + s.handleConqueredVillagesCommand(m, args...) case UnObserveConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix): - s.handleUnObserveConqueredVillagesCommand(m) + s.handleUnObserveConqueredVillagesCommand(m, args...) case LostVillagesCommand.WithPrefix(s.cfg.CommandPrefix): - s.handleLostVillagesCommand(m) + s.handleLostVillagesCommand(m, args...) case UnObserveLostVillagesCommand.WithPrefix(s.cfg.CommandPrefix): - s.handleUnObserveLostVillagesCommand(m) - case TribeCommand.WithPrefix(s.cfg.CommandPrefix): - s.handleTribeCommand(m, args...) + s.handleUnObserveLostVillagesCommand(m, args...) + } } diff --git a/group/repository.go b/group/repository.go new file mode 100644 index 0000000..8945641 --- /dev/null +++ b/group/repository.go @@ -0,0 +1,16 @@ +package group + +import ( + "context" + + "github.com/tribalwarshelp/dcbot/models" +) + +type Repository interface { + Store(ctx context.Context, group *models.Group) error + StoreMany(ctx context.Context, groups []*models.Group) error + Update(ctx context.Context, group *models.Group) error + Delete(ctx context.Context, filter *models.GroupFilter) ([]*models.Group, error) + GetByID(ctx context.Context, id int) (*models.Group, error) + Fetch(ctx context.Context, filter *models.GroupFilter) ([]*models.Group, int, error) +} diff --git a/group/repository/group_pg_repo.go b/group/repository/group_pg_repo.go new file mode 100644 index 0000000..39055f3 --- /dev/null +++ b/group/repository/group_pg_repo.go @@ -0,0 +1,109 @@ +package repository + +import ( + "context" + + "github.com/tribalwarshelp/dcbot/group" + "github.com/tribalwarshelp/dcbot/models" + + "github.com/go-pg/pg/v10" + "github.com/go-pg/pg/v10/orm" + "github.com/pkg/errors" +) + +type pgRepo struct { + *pg.DB +} + +func NewPgRepo(db *pg.DB) (group.Repository, error) { + if err := db.CreateTable((*models.Group)(nil), &orm.CreateTableOptions{ + IfNotExists: true, + FKConstraints: true, + }); err != nil { + return nil, errors.Wrap(err, "Cannot create 'groups' table") + } + return &pgRepo{db}, nil +} + +func (repo *pgRepo) Store(ctx context.Context, group *models.Group) error { + if _, err := repo.Model(group).Returning("*").Context(ctx).Insert(); err != nil { + return err + } + return nil +} + +func (repo *pgRepo) StoreMany(ctx context.Context, groups []*models.Group) error { + if _, err := repo.Model(&groups).Returning("*").Context(ctx).Insert(); err != nil { + return err + } + return nil +} + +func (repo *pgRepo) Update(ctx context.Context, group *models.Group) error { + if _, err := repo. + Model(group). + WherePK(). + Returning("*"). + Context(ctx). + Update(); err != nil { + return err + } + return nil +} + +func (repo *pgRepo) GetByID(ctx context.Context, id int) (*models.Group, error) { + group := &models.Group{ + ID: id, + } + if err := repo. + Model(group). + WherePK(). + Returning("*"). + Relation("Observations"). + Context(ctx). + Select(); err != nil { + return nil, err + } + return group, nil +} + +func (repo *pgRepo) Fetch(ctx context.Context, f *models.GroupFilter) ([]*models.Group, int, error) { + var err error + data := []*models.Group{} + query := repo.Model(&data).Relation("Observations").Context(ctx) + + if f != nil { + query = query. + WhereStruct(f). + Limit(f.Limit). + Offset(f.Offset) + + if len(f.Order) > 0 { + query = query.Order(f.Order...) + } + } + + total, err := query.SelectAndCount() + if err != nil && err != pg.ErrNoRows { + return nil, 0, err + } + + return data, total, nil +} + +func (repo *pgRepo) Delete(ctx context.Context, f *models.GroupFilter) ([]*models.Group, error) { + data := []*models.Group{} + query := repo.Model(&data).Context(ctx) + + if f != nil { + query = query.WhereStruct(f) + } + + _, err := query. + Returning("*"). + Delete() + if err != nil && err != pg.ErrNoRows { + return nil, err + } + return data, nil +} diff --git a/main.go b/main.go index 425ed87..56f2ad0 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,7 @@ import ( _cron "github.com/tribalwarshelp/dcbot/cron" "github.com/tribalwarshelp/dcbot/discord" + group_repository "github.com/tribalwarshelp/dcbot/group/repository" observation_repository "github.com/tribalwarshelp/dcbot/observation/repository" server_repository "github.com/tribalwarshelp/dcbot/server/repository" @@ -46,6 +47,10 @@ func main() { if err != nil { log.Fatal(err) } + groupRepo, err := group_repository.NewPgRepo(db) + if err != nil { + log.Fatal(err) + } observationRepo, err := observation_repository.NewPgRepo(db) if err != nil { log.Fatal(err) @@ -56,6 +61,7 @@ func main() { Status: "Tribal Wars | tw!help", ObservationRepository: observationRepo, ServerRepository: serverRepo, + GroupRepository: groupRepo, API: api, }) if err != nil { @@ -70,6 +76,7 @@ func main() { ServerRepo: serverRepo, ObservationRepo: observationRepo, Discord: sess, + GroupRepo: groupRepo, API: api, }) go func() { diff --git a/models/group.go b/models/group.go new file mode 100644 index 0000000..7242830 --- /dev/null +++ b/models/group.go @@ -0,0 +1,18 @@ +package models + +type Group struct { + ID int `pg:",pk" json:"id" gqlgen:"id"` + ConqueredVillagesChannelID string `pg:",use_zero" json:"conqueredVillagesChannelID" gqlgen:"conqueredVillagesChannelID"` + LostVillagesChannelID string `pg:",use_zero" json:"lostVillagesChannelID" gqlgen:"lostVillagesChannelID"` + ServerID string `pg:"on_delete:CASCADE,use_zero" json:"serverID" gqlgen:"serverID"` + Server *Server `json:"server,omitempty" gqlgen:"server"` + Observations Observations `json:"observation,omitempty" gqlgen:"observation"` +} + +type GroupFilter struct { + ID []int + ServerID []string + Limit int `urlstruct:",nowhere"` + Offset int `urlstruct:",nowhere"` + Order []string `urlstruct:",nowhere"` +} diff --git a/models/observation.go b/models/observation.go index 4f6c6f8..705e65d 100644 --- a/models/observation.go +++ b/models/observation.go @@ -1,20 +1,23 @@ package models +import "time" + type Observation struct { tableName struct{} `pg:",alias:observation"` - ID int `json:"id" gqlgen:"id"` - World string `pg:"unique:group_1,use_zero" json:"world" gqlgen:"world"` - TribeID int `pg:"unique:group_1,use_zero" json:"tribeID" gqlgen:"tribeID"` - ServerID string `pg:"on_delete:CASCADE,unique:group_1,use_zero" json:"serverID" gqlgen:"serverID"` - Server *Server `json:"server,omitempty" gqlgen:"server"` + ID int `json:"id" gqlgen:"id"` + Server string `pg:"unique:group_1,use_zero" json:"server" gqlgen:"server"` + TribeID int `pg:"unique:group_1,use_zero" json:"tribeID" gqlgen:"tribeID"` + GroupID int `pg:"on_delete:CASCADE,unique:group_1,use_zero" json:"groupID" gqlgen:"groupID"` + Group *Group `json:"group,omitempty" gqlgen:"group"` + CreatedAt time.Time `pg:"default:now()" json:"createdAt" gqlgen:"createdAt" xml:"createdAt"` } type Observations []*Observation -func (o Observations) Contains(world string, id int) bool { +func (o Observations) Contains(server string, id int) bool { for _, observation := range o { - if observation.TribeID == id && observation.World == world { + if observation.TribeID == id && observation.Server == server { return true } } @@ -22,10 +25,10 @@ func (o Observations) Contains(world string, id int) bool { } type ObservationFilter struct { - ID []int - World []string - ServerID []string - Limit int `urlstruct:",nowhere"` - Offset int `urlstruct:",nowhere"` - Order []string `urlstruct:",nowhere"` + ID []int + Server []string + GroupID []int + Limit int `urlstruct:",nowhere"` + Offset int `urlstruct:",nowhere"` + Order []string `urlstruct:",nowhere"` } diff --git a/models/server.go b/models/server.go index 2b0a7e5..a1885e0 100644 --- a/models/server.go +++ b/models/server.go @@ -3,10 +3,8 @@ package models type Server struct { tableName struct{} `pg:",alias:server"` - ID string `pg:",pk" json:"id" gqlgen:"id"` - ConqueredVillagesChannelID string `pg:",use_zero" json:"conqueredVillagesChannelID" gqlgen:"conqueredVillagesChannelID"` - LostVillagesChannelID string `pg:",use_zero" json:"lostVillagesChannelID" gqlgen:"lostVillagesChannelID"` - Observations Observations `json:"observation,omitempty" gqlgen:"observation"` + ID string `pg:",pk" json:"id" gqlgen:"id"` + Groups []*Group } type ServerFilter struct { diff --git a/observation/repository.go b/observation/repository.go index df3c2e5..111a737 100644 --- a/observation/repository.go +++ b/observation/repository.go @@ -12,5 +12,5 @@ type Repository interface { Update(ctx context.Context, observation *models.Observation) error Delete(ctx context.Context, filter *models.ObservationFilter) ([]*models.Observation, error) Fetch(ctx context.Context, filter *models.ObservationFilter) ([]*models.Observation, int, error) - FetchWorlds(ctx context.Context) ([]string, error) + FetchServers(ctx context.Context) ([]string, error) } diff --git a/observation/repository/observation_pg_repo.go b/observation/repository/observation_pg_repo.go index ac4ab75..7c5fdf0 100644 --- a/observation/repository/observation_pg_repo.go +++ b/observation/repository/observation_pg_repo.go @@ -75,15 +75,15 @@ func (repo *pgRepo) Fetch(ctx context.Context, f *models.ObservationFilter) ([]* return data, total, nil } -func (repo *pgRepo) FetchWorlds(ctx context.Context) ([]string, error) { +func (repo *pgRepo) FetchServers(ctx context.Context) ([]string, error) { data := []*models.Observation{} res := []string{} err := repo. Model(&data). - Column("world"). + Column("server"). Context(ctx). - Group("world"). - Order("world ASC"). + Group("server"). + Order("server ASC"). Select(&res) return res, err } diff --git a/server/repository/server_pg_repo.go b/server/repository/server_pg_repo.go index f4945c6..1c2eecf 100644 --- a/server/repository/server_pg_repo.go +++ b/server/repository/server_pg_repo.go @@ -30,7 +30,7 @@ func (repo *pgRepo) Store(ctx context.Context, server *models.Server) error { Where("id = ?id"). Returning("*"). Context(ctx). - Relation("Observations"). + Relation("Groups"). SelectOrInsert(); err != nil { return err } @@ -52,7 +52,7 @@ func (repo *pgRepo) Update(ctx context.Context, server *models.Server) error { func (repo *pgRepo) Fetch(ctx context.Context, f *models.ServerFilter) ([]*models.Server, int, error) { var err error data := []*models.Server{} - query := repo.Model(&data).Context(ctx).Relation("Observations") + query := repo.Model(&data).Context(ctx).Relation("Groups") if f != nil { query = query.