diff --git a/cron/cron.go b/cron/cron.go index 49e5bf0..fd4c19d 100644 --- a/cron/cron.go +++ b/cron/cron.go @@ -7,9 +7,9 @@ import ( "time" "github.com/tribalwarshelp/golang-sdk/sdk" + shared_models "github.com/tribalwarshelp/shared/models" "github.com/tribalwarshelp/dcbot/discord" - "github.com/tribalwarshelp/dcbot/scraper" "github.com/tribalwarshelp/dcbot/server" "github.com/tribalwarshelp/dcbot/tribalwars" "github.com/tribalwarshelp/dcbot/tribe" @@ -30,18 +30,44 @@ type handler struct { serverRepo server.Repository tribeRepo tribe.Repository discord *discord.Session + api *sdk.SDK } func AttachHandlers(c *cron.Cron, cfg Config) { h := &handler{ - since: time.Now(), + since: time.Now().Add(-30 * time.Minute), serverRepo: cfg.ServerRepo, tribeRepo: cfg.TribeRepo, discord: cfg.Discord, + api: cfg.API, } c.AddFunc("@every 1m", h.checkEnnoblements) } +func (h *handler) loadEnnoblements(worlds []string) map[string]ennoblements { + m := make(map[string]ennoblements) + + for _, w := range worlds { + es, err := h.api.Ennoblements.Browse(w, &sdk.EnnoblementInclude{ + NewOwner: true, + Village: true, + NewOwnerInclude: sdk.PlayerInclude{ + Tribe: true, + }, + OldOwner: true, + OldOwnerInclude: sdk.PlayerInclude{ + Tribe: true, + }, + }) + if err != nil { + log.Printf("%s: %s", w, err.Error()) + } + m[w] = filterEnnoblements(es, h.since) + } + + return m +} + func (h *handler) checkEnnoblements() { worlds, err := h.tribeRepo.FetchWorlds(context.Background()) if err != nil { @@ -55,7 +81,7 @@ func (h *handler) checkEnnoblements() { return } log.Print("checkEnnoblements: total number of servers: ", total) - data := scraper.New(worlds, h.since).Scrap() + data := h.loadEnnoblements(worlds) h.since = time.Now() log.Print("checkEnnoblements: scrapped data: ", data) for _, server := range servers { @@ -63,22 +89,26 @@ func (h *handler) checkEnnoblements() { continue } for _, tribe := range server.Tribes { - conquests, ok := data[tribe.World] + es, ok := data[tribe.World] if ok { if server.LostVillagesChannelID != "" { - for _, conquest := range conquests.LostVillages(tribe.TribeID) { - if server.Tribes.Contains(tribe.World, conquest.NewOwnerTribeID) { + for _, ennoblement := range es.tribeLostVillages(tribe.TribeID) { + if !isPlayerTribeNil(ennoblement.NewOwner) && + server.Tribes.Contains(tribe.World, ennoblement.NewOwner.Tribe.ID) { continue } - h.discord.SendMessage(server.LostVillagesChannelID, formatMsgAboutVillageLost(tribe.World, conquest)) + msgData := newMessageData(tribe.World, ennoblement) + h.discord.SendMessage(server.LostVillagesChannelID, formatMsgAboutVillageLost(msgData)) } } if server.ConqueredVillagesChannelID != "" { - for _, conquest := range conquests.ConqueredVillages(tribe.TribeID) { - if server.Tribes.Contains(tribe.World, conquest.OldOwnerTribeID) { + for _, ennoblement := range es.tribeConqueredVillages(tribe.TribeID) { + if !isPlayerTribeNil(ennoblement.OldOwner) && + server.Tribes.Contains(tribe.World, ennoblement.OldOwner.Tribe.ID) { continue } - h.discord.SendMessage(server.ConqueredVillagesChannelID, formatMsgAboutVillageConquest(tribe.World, conquest)) + msgData := newMessageData(tribe.World, ennoblement) + h.discord.SendMessage(server.ConqueredVillagesChannelID, formatMsgAboutVillageConquest(msgData)) } } } @@ -90,24 +120,58 @@ func formatDateOfConquest(loc *time.Location, t time.Time) string { return t.In(loc).Format("15:04:05") } -func formatMsgAboutVillageLost(world string, conquest *scraper.Conquest) string { - return fmt.Sprintf(`**%s** %s: Wioska %s (właściciel: %s [%s]) została stracona na rzecz gracza %s (%s)`, - world, - formatDateOfConquest(utils.GetLocation(tribalwars.LanguageCodeFromWorldName(world)), conquest.ConqueredAt), - conquest.Village, - conquest.OldOwnerName, - conquest.OldOwnerTribeName, - conquest.NewOwnerName, - conquest.NewOwnerTribeName) +type messageData struct { + world string + date string + village string + oldOwnerName string + oldOwnerTribeTag string + newOwnerName string + newOwnerTribeTag string } -func formatMsgAboutVillageConquest(world string, conquest *scraper.Conquest) string { - return fmt.Sprintf(`**%s** %s: Gracz %s (%s) podbił wioskę %s od gracza %s (%s)`, - world, - formatDateOfConquest(utils.GetLocation(tribalwars.LanguageCodeFromWorldName(world)), conquest.ConqueredAt), - conquest.NewOwnerName, - conquest.NewOwnerTribeName, - conquest.Village, - conquest.OldOwnerName, - conquest.OldOwnerTribeName) +func newMessageData(world string, ennoblement *shared_models.Ennoblement) messageData { + data := messageData{ + date: formatDateOfConquest(utils.GetLocation(tribalwars.LanguageCodeFromWorldName(world)), ennoblement.EnnobledAt), + world: world, + } + if !isVillageNil(ennoblement.Village) { + data.village = fmt.Sprintf("%s (%d|%d)", ennoblement.Village.Name, ennoblement.Village.X, ennoblement.Village.Y) + } + if !isPlayerNil(ennoblement.OldOwner) { + data.oldOwnerName = ennoblement.OldOwner.Name + } + if !isPlayerTribeNil(ennoblement.OldOwner) { + data.oldOwnerTribeTag = ennoblement.OldOwner.Tribe.Tag + } + if !isPlayerNil(ennoblement.NewOwner) { + data.newOwnerName = ennoblement.NewOwner.Name + } + if !isPlayerTribeNil(ennoblement.NewOwner) { + data.newOwnerTribeTag = ennoblement.NewOwner.Tribe.Tag + } + return data +} + +func formatMsgAboutVillageLost(msgData messageData) string { + + return fmt.Sprintf(`**%s** %s: Wioska %s (właściciel: %s [%s]) została stracona na rzecz gracza %s (%s)`, + msgData.world, + msgData.date, + msgData.village, + msgData.oldOwnerName, + msgData.oldOwnerTribeTag, + msgData.newOwnerName, + msgData.newOwnerTribeTag) +} + +func formatMsgAboutVillageConquest(msgData messageData) string { + return fmt.Sprintf(`**%s** %s: Gracz %s (%s) podbił wioskę %s od gracza %s (%s)`, + msgData.world, + msgData.date, + msgData.newOwnerName, + msgData.newOwnerTribeTag, + msgData.village, + msgData.oldOwnerName, + msgData.oldOwnerTribeTag) } diff --git a/cron/ennoblements.go b/cron/ennoblements.go new file mode 100644 index 0000000..7afa8b6 --- /dev/null +++ b/cron/ennoblements.go @@ -0,0 +1,31 @@ +package cron + +import shared_models "github.com/tribalwarshelp/shared/models" + +type ennoblements []*shared_models.Ennoblement + +func (e ennoblements) tribeLostVillages(tribeID int) ennoblements { + filtered := ennoblements{} + for _, ennoblement := range e { + if (!isPlayerTribeNil(ennoblement.NewOwner) && ennoblement.NewOwner.Tribe.ID == tribeID) || + isPlayerTribeNil(ennoblement.OldOwner) || + ennoblement.OldOwner.Tribe.ID != tribeID { + continue + } + filtered = append(filtered, ennoblement) + } + return filtered +} + +func (e ennoblements) tribeConqueredVillages(tribeID int) ennoblements { + filtered := ennoblements{} + for _, ennoblement := range e { + if isPlayerTribeNil(ennoblement.NewOwner) || + ennoblement.NewOwner.Tribe.ID != tribeID || + (!isPlayerTribeNil(ennoblement.OldOwner) && ennoblement.OldOwner.Tribe.ID == tribeID) { + continue + } + filtered = append(filtered, ennoblement) + } + return filtered +} diff --git a/cron/helpers.go b/cron/helpers.go new file mode 100644 index 0000000..e979f2b --- /dev/null +++ b/cron/helpers.go @@ -0,0 +1,30 @@ +package cron + +import ( + "time" + + shared_models "github.com/tribalwarshelp/shared/models" +) + +func filterEnnoblements(ennoblements []*shared_models.Ennoblement, t time.Time) []*shared_models.Ennoblement { + filtered := []*shared_models.Ennoblement{} + for _, ennoblement := range ennoblements { + if ennoblement.EnnobledAt.In(time.UTC).Before(t) { + continue + } + filtered = append(filtered, ennoblement) + } + return filtered +} + +func isPlayerNil(player *shared_models.Player) bool { + return player == nil +} + +func isPlayerTribeNil(player *shared_models.Player) bool { + return isPlayerNil(player) || player.Tribe == nil +} + +func isVillageNil(village *shared_models.Village) bool { + return village == nil +} diff --git a/discord/commands.go b/discord/commands.go index df9abcb..5cc04a9 100644 --- a/discord/commands.go +++ b/discord/commands.go @@ -3,6 +3,8 @@ package discord import ( "fmt" "strings" + + "github.com/bwmarrin/discordgo" ) type Command string @@ -14,6 +16,11 @@ var ( DeleteCommand Command = "delete" LostVillagesCommand Command = "lostvillages" ConqueredVillagesCommand Command = "conqueredvillages" + TopAttCommand Command = "topatt" + TopDefCommand Command = "topdef" + TopSuppCommand Command = "topsupp" + TopTotalCommand Command = "toptotal" + TopPointsCommand Command = "toppoints" ) func (cmd Command) String() string { @@ -24,20 +31,52 @@ func (cmd Command) WithPrefix(prefix string) string { return prefix + cmd.String() } -func (s *Session) sendHelpMessage(mention, channelID string) { - s.SendMessage(channelID, mention+"```Dostępne komendy \n"+fmt.Sprintf(` +func (s *Session) sendHelpMessage(channelID string) { + embed := &discordgo.MessageEmbed{ + Author: &discordgo.MessageEmbedAuthor{}, + Color: discordEmbedColor, + Title: "Pomoc", + Description: "Komendy oferowane przez bota", + Fields: []*discordgo.MessageEmbedField{ + &discordgo.MessageEmbedField{ + Name: "Dla wszystkich", + Value: fmt.Sprintf(` +- %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największym RA z plemion o podanych id +- %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największym RO z plemion o podanych id +- %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największym RW z plemion o podanych id +- %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największej liczbie pokonanych z plemion o podanych id +- %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największej liczbie punktów z plemion o podanych id + `, + TopAttCommand.WithPrefix(s.cfg.CommandPrefix), + TopDefCommand.WithPrefix(s.cfg.CommandPrefix), + TopSuppCommand.WithPrefix(s.cfg.CommandPrefix), + TopTotalCommand.WithPrefix(s.cfg.CommandPrefix), + TopPointsCommand.WithPrefix(s.cfg.CommandPrefix)), + Inline: false, + }, + &discordgo.MessageEmbedField{ + Name: "Dla adminów", + Value: 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 straconych wioskach - %s - ustawia kanał na którym będą wyświetlać się informacje o podbitych wioskach - `, - AddCommand.WithPrefix(s.cfg.CommandPrefix), - ListCommand.WithPrefix(s.cfg.CommandPrefix), - DeleteCommand.WithPrefix(s.cfg.CommandPrefix), - ListCommand.WithPrefix(s.cfg.CommandPrefix), - LostVillagesCommand.WithPrefix(s.cfg.CommandPrefix), - ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix))+"```") + `, + AddCommand.WithPrefix(s.cfg.CommandPrefix), + ListCommand.WithPrefix(s.cfg.CommandPrefix), + DeleteCommand.WithPrefix(s.cfg.CommandPrefix), + ListCommand.WithPrefix(s.cfg.CommandPrefix), + LostVillagesCommand.WithPrefix(s.cfg.CommandPrefix), + ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix)), + Inline: false, + }, + }, + Footer: &discordgo.MessageEmbedFooter{ + Text: "https://dawid-wysokinski.pl/", + }, + } + s.dg.ChannelMessageSendEmbed(channelID, embed) } func (s *Session) sendUnknownCommandError(mention, channelID string, command ...string) { diff --git a/discord/discord.go b/discord/discord.go index ef5fd6c..8124491 100644 --- a/discord/discord.go +++ b/discord/discord.go @@ -3,18 +3,22 @@ package discord import ( "context" "fmt" + "math" "strconv" "strings" "github.com/tribalwarshelp/dcbot/models" "github.com/tribalwarshelp/dcbot/server" "github.com/tribalwarshelp/dcbot/tribe" + "github.com/tribalwarshelp/golang-sdk/sdk" + shared_models "github.com/tribalwarshelp/shared/models" "github.com/bwmarrin/discordgo" ) const ( - TribesPerServer = 10 + TribesPerServer = 10 + discordEmbedColor = 0x00ff00 ) type SessionConfig struct { @@ -23,6 +27,7 @@ type SessionConfig struct { Status string ServerRepository server.Repository TribeRepository tribe.Repository + API *sdk.SDK } type Session struct { @@ -60,12 +65,10 @@ func (s *Session) init() error { } func (s *Session) handleNewMessage(_ *discordgo.Session, m *discordgo.MessageCreate) { - if m.Author.ID == s.dg.State.User.ID || m.Author.Bot || m.GuildID == "" { - return - } - if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + if m.Author.ID == s.dg.State.User.ID || m.Author.Bot { return } + splitted := strings.Split(m.Content, " ") argsLength := len(splitted) - 1 args := splitted[1 : argsLength+1] @@ -82,14 +85,152 @@ func (s *Session) handleNewMessage(_ *discordgo.Session, m *discordgo.MessageCre s.handleLostVillagesCommand(m) case ConqueredVillagesCommand.WithPrefix(s.cfg.CommandPrefix): s.handleConqueredVillagesCommand(m) + case TopAttCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleTopCommands(m, TopAttCommand, args...) + case TopDefCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleTopCommands(m, TopDefCommand, args...) + case TopSuppCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleTopCommands(m, TopSuppCommand, args...) + case TopTotalCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleTopCommands(m, TopTotalCommand, args...) + case TopPointsCommand.WithPrefix(s.cfg.CommandPrefix): + s.handleTopCommands(m, TopPointsCommand, args...) } } func (s *Session) handleHelpCommand(m *discordgo.MessageCreate) { - s.sendHelpMessage(m.Author.Mention(), m.ChannelID) + s.sendHelpMessage(m.ChannelID) +} + +func (s *Session) handleTopCommands(m *discordgo.MessageCreate, command Command, args ...string) { + argsLength := len(args) + if argsLength < 3 { + s.SendMessage(m.ChannelID, + fmt.Sprintf("%s %s [świat] [strona] [id...]", + m.Author.Mention(), + command.WithPrefix(s.cfg.CommandPrefix))) + return + } + + world := args[0] + page, err := strconv.Atoi(args[1]) + if err != nil || page <= 0 { + s.SendMessage(m.ChannelID, fmt.Sprintf("%s 2 argument musi być liczbą większą od 0.", m.Author.Mention())) + return + } + ids := []int{} + for _, arg := range args[2:argsLength] { + id, err := strconv.Atoi(arg) + if err != nil { + continue + } + ids = append(ids, id) + } + if len(ids) == 0 { + s.SendMessage(m.ChannelID, fmt.Sprintf("%s Nie wprowadziłeś ID plemion.", m.Author.Mention())) + return + } + + exist := true + limit := 10 + offset := (page - 1) * limit + filter := &shared_models.PlayerFilter{ + Exist: &exist, + TribeID: ids, + Limit: limit, + Offset: offset, + } + title := "" + switch command { + case TopAttCommand: + filter.RankAttGTE = 1 + filter.Sort = "rankAtt ASC" + title = "Top pokonani w ataku" + case TopDefCommand: + filter.RankDefGTE = 1 + filter.Sort = "rankDef ASC" + title = "Top pokonani w obronie" + case TopSuppCommand: + filter.RankSupGTE = 1 + filter.Sort = "rankSup ASC" + title = "Top pokonani jako wspierający" + case TopTotalCommand: + filter.RankTotalGTE = 1 + filter.Sort = "rankTotal ASC" + title = "Top pokonani ogólnie" + case TopPointsCommand: + filter.Sort = "rank ASC" + title = "Najwięcej punktów" + } + + playersList, err := s.cfg.API.Players.Browse(world, filter, &sdk.PlayerInclude{ + Tribe: true, + }) + if err != nil { + s.SendMessage(m.ChannelID, fmt.Sprintf("%s Nie udało się wygenerować listy.", m.Author.Mention())) + return + } + if playersList.Total == 0 { + s.SendMessage(m.ChannelID, fmt.Sprintf("%s Nie znaleziono plemion o podanych ID.", m.Author.Mention())) + return + } + + msg := "" + for i, player := range playersList.Items { + rank := 0 + score := 0 + switch command { + case TopAttCommand: + rank = player.RankAtt + score = player.ScoreAtt + case TopDefCommand: + rank = player.RankDef + score = player.ScoreDef + case TopSuppCommand: + rank = player.RankSup + score = player.ScoreSup + case TopTotalCommand: + rank = player.RankTotal + score = player.ScoreTotal + case TopPointsCommand: + rank = player.Rank + score = player.Points + } + + msg += fmt.Sprintf("**%d**. **__%s__** (Plemię: **%s** | Ranking ogólny: **%d** | Wynik: **%d**)\n", + offset+i+1, + player.Name, + player.Tribe.Tag, + rank, score) + } + + totalPages := int(math.Round(float64(playersList.Total) / float64(limit))) + s.dg.ChannelMessageSendEmbed(m.ChannelID, &discordgo.MessageEmbed{ + Author: &discordgo.MessageEmbedAuthor{}, + Color: discordEmbedColor, + Title: title, + Description: "A oto lista!", + Fields: []*discordgo.MessageEmbedField{ + &discordgo.MessageEmbedField{ + Name: "-", + Value: msg, + Inline: false, + }, + }, + Footer: &discordgo.MessageEmbedFooter{ + Text: fmt.Sprintf("Strona %d z %d", page, totalPages), + }, + }) } func (s *Session) handleLostVillagesCommand(m *discordgo.MessageCreate) { + if m.GuildID == "" { + return + } + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + return + } + server := &models.Server{ ID: m.GuildID, } @@ -104,6 +245,13 @@ func (s *Session) handleLostVillagesCommand(m *discordgo.MessageCreate) { } func (s *Session) handleConqueredVillagesCommand(m *discordgo.MessageCreate) { + if m.GuildID == "" { + return + } + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + return + } + server := &models.Server{ ID: m.GuildID, } @@ -118,6 +266,13 @@ func (s *Session) handleConqueredVillagesCommand(m *discordgo.MessageCreate) { } func (s *Session) handleAddCommand(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 > 2 { s.sendUnknownCommandError(m.Author.Mention(), m.ChannelID, args[2:argsLength]...) @@ -129,6 +284,7 @@ func (s *Session) handleAddCommand(m *discordgo.MessageCreate, args ...string) { AddCommand.WithPrefix(s.cfg.CommandPrefix))) return } + world := args[0] id, err := strconv.Atoi(args[1]) if err != nil { @@ -138,6 +294,13 @@ func (s *Session) handleAddCommand(m *discordgo.MessageCreate, args ...string) { AddCommand.WithPrefix(s.cfg.CommandPrefix))) return } + + tribe, err := s.cfg.API.Tribes.Read(world, id) + 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)) + return + } + server := &models.Server{ ID: m.GuildID, } @@ -146,10 +309,12 @@ func (s *Session) handleAddCommand(m *discordgo.MessageCreate, args ...string) { s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie udało się dodać plemienia do obserwowanych.`) return } + if len(server.Tribes) >= TribesPerServer { s.SendMessage(m.ChannelID, m.Author.Mention()+fmt.Sprintf(` Osiągnięto limit plemion (%d/%d).`, TribesPerServer, TribesPerServer)) return } + err = s.cfg.TribeRepository.Store(context.Background(), &models.Tribe{ World: world, TribeID: id, @@ -164,6 +329,13 @@ func (s *Session) handleAddCommand(m *discordgo.MessageCreate, args ...string) { } func (s *Session) handleDeleteCommand(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]...) @@ -194,18 +366,39 @@ func (s *Session) handleDeleteCommand(m *discordgo.MessageCreate, args ...string } func (s *Session) handleListCommand(m *discordgo.MessageCreate) { + if m.GuildID == "" { + return + } + if has, err := s.memberHasPermission(m.GuildID, m.Author.ID, discordgo.PermissionAdministrator); err != nil || !has { + return + } + tribes, _, err := s.cfg.TribeRepository.Fetch(context.Background(), &models.TribeFilter{ ServerID: []string{m.GuildID}, }) if err != nil { return } - msg := m.Author.Mention() + " ```ID w bazie - Świat - ID plemienia \n\n" - for _, tribe := range tribes { - msg += fmt.Sprintf(">>> %d - %s - %d\n", tribe.ID, tribe.World, tribe.TribeID) + + msg := "" + for i, tribe := range tribes { + msg += fmt.Sprintf("**%d**. %d - %s - %d\n", i+1, tribe.ID, tribe.World, tribe.TribeID) } - msg += "```" - s.SendMessage(m.ChannelID, msg) + + s.dg.ChannelMessageSendEmbed(m.ChannelID, &discordgo.MessageEmbed{ + Author: &discordgo.MessageEmbedAuthor{}, + Title: "Lista obserwowanych plemion", + Color: discordEmbedColor, + Fields: []*discordgo.MessageEmbedField{ + &discordgo.MessageEmbedField{ + Name: "ID - świat - ID plemienia", + Value: msg, + }, + }, + Footer: &discordgo.MessageEmbedFooter{ + Text: "Strona 1 z 1", + }, + }) } func (s *Session) Close() error { diff --git a/go.mod b/go.mod index bffa4ce..dbf441d 100644 --- a/go.mod +++ b/go.mod @@ -3,23 +3,13 @@ module github.com/tribalwarshelp/dcbot go 1.14 require ( - github.com/PuerkitoBio/goquery v1.5.1 - github.com/andybalholm/cascadia v1.2.0 // indirect - github.com/antchfx/htmlquery v1.2.3 // indirect - github.com/antchfx/xmlquery v1.2.4 // indirect - github.com/antchfx/xpath v1.1.8 // indirect github.com/bwmarrin/discordgo v0.20.3 github.com/go-pg/pg/v10 v10.0.0-alpha.0 - github.com/gobwas/glob v0.2.3 // indirect - github.com/gocolly/colly v1.2.0 github.com/golang/protobuf v1.4.2 // indirect github.com/joho/godotenv v1.3.0 - github.com/kennygrant/sanitize v1.2.4 // indirect github.com/pkg/errors v0.9.1 github.com/robfig/cron/v3 v3.0.1 - github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca // indirect - github.com/temoto/robotstxt v1.1.1 // indirect - github.com/tribalwarshelp/golang-sdk v0.0.0-20200604163817-64920bab73bb // indirect + github.com/tribalwarshelp/golang-sdk v0.0.0-20200604163817-64920bab73bb github.com/tribalwarshelp/shared v0.0.0-20200604161459-deeeccf99815 golang.org/x/net v0.0.0-20200528225125-3c3fba18258b // indirect google.golang.org/protobuf v1.24.0 // indirect diff --git a/go.sum b/go.sum index 9c2d92c..39fa8a5 100644 --- a/go.sum +++ b/go.sum @@ -4,20 +4,6 @@ github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7 h1:qELHH0AWCvf github.com/DataDog/sketches-go v0.0.0-20190923095040-43f19ad77ff7/go.mod h1:Q5DbzQ+3AkgGwymQO7aZFNP7ns2lZKGtvRBzRXfdi60= github.com/Kichiyaki/gqlgen-client v0.0.0-20200604145848-274796c104f4 h1:QiOarkkKHdFYI+0m6F1H3rRzP6DqJsKJVLirGXEHGSU= github.com/Kichiyaki/gqlgen-client v0.0.0-20200604145848-274796c104f4/go.mod h1:weCVl47ZANyeX60sdsSl0bWHf8HWXyVFmlGHHCR/i5M= -github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.2.0 h1:vuRCkM5Ozh/BfmsaTm26kbjm0mIOM3yS5Ek/F5h18aE= -github.com/andybalholm/cascadia v1.2.0/go.mod h1:YCyR8vOZT9aZ1CHEd8ap0gMVm2aFgxBp0T0eFw1RUQY= -github.com/antchfx/htmlquery v1.2.3 h1:sP3NFDneHx2stfNXCKbhHFo8XgNjCACnU/4AO5gWz6M= -github.com/antchfx/htmlquery v1.2.3/go.mod h1:B0ABL+F5irhhMWg54ymEZinzMSi0Kt3I2if0BLYa3V0= -github.com/antchfx/xmlquery v1.2.4 h1:T/SH1bYdzdjTMoz2RgsfVKbM5uWh3gjDYYepFqQmFv4= -github.com/antchfx/xmlquery v1.2.4/go.mod h1:KQQuESaxSlqugE2ZBcM/qn+ebIpt+d+4Xx7YcSGAIrM= -github.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0= -github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= -github.com/antchfx/xpath v1.1.8 h1:PcL6bIX42Px5usSx6xRYw/wjB3wYGkj0MJ9MBzEKVgk= -github.com/antchfx/xpath v1.1.8/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= github.com/benbjohnson/clock v1.0.0 h1:78Jk/r6m4wCi6sndMpty7A//t4dw/RW5fV4ZgDVfX1w= github.com/benbjohnson/clock v1.0.0/go.mod h1:bGMdMPoPVvcYyt1gHDf4J2KE153Yf9BuiUKYMaxlTDM= github.com/bwmarrin/discordgo v0.20.3 h1:AxjcHGbyBFSC0a3Zx5nDQwbOjU7xai5dXjRnZ0YB7nU= @@ -50,13 +36,7 @@ github.com/go-pg/urlstruct v0.4.0 h1:3lmbUGYQclB3UOx9akDs2T251zwkKQuPkvPTmCm07+A github.com/go-pg/urlstruct v0.4.0/go.mod h1:/XKyiUOUUS3onjF+LJxbfmSywYAdl6qMfVbX33Q8rgg= github.com/go-pg/zerochecker v0.1.1 h1:av77Qe7Gs+1oYGGh51k0sbZ0bUaxJEdeP0r8YE64Dco= github.com/go-pg/zerochecker v0.1.1/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= -github.com/gocolly/colly v1.2.0 h1:qRz9YAn8FIH0qzgNUw+HT9UN7wm1oF9OBAilwEWpyrI= -github.com/gocolly/colly v1.2.0/go.mod h1:Hof5T3ZswNVsOHYmba1u03W65HDWgpV5HifSuueE0EA= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= @@ -89,8 +69,6 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc= github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= -github.com/kennygrant/sanitize v1.2.4 h1:gN25/otpP5vAsO2djbMhF/LQX6R7+O1TB4yv8NzpJ3o= -github.com/kennygrant/sanitize v1.2.4/go.mod h1:LGsjYYtgxbetdg5owWB2mpgUL6e2nfw2eObZ0u0qvak= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -109,26 +87,20 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= -github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca h1:NugYot0LIVPxTvN8n+Kvkn6TrbMyxQiuvKdEwFdR9vI= -github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU= github.com/segmentio/encoding v0.1.10/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= github.com/segmentio/encoding v0.1.11 h1:Qy9+DK2pmQnF6KjD5IclHekzfZFN+pZrHWyUbE2bhag= github.com/segmentio/encoding v0.1.11/go.mod h1:RWhr02uzMB9gQC1x+MfYxedtmBibb9cZ6Vv9VxRSSbw= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.0 h1:jlIyCplCJFULU/01vCkhKuTyc3OorI3bJFuw6obfgho= github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/temoto/robotstxt v1.1.1 h1:Gh8RCs8ouX3hRSxxK7B1mO5RFByQ4CmJZDwgom++JaA= -github.com/temoto/robotstxt v1.1.1/go.mod h1:+1AmkuG3IYkh1kv0d2qEB9Le88ehNO0zwOr3ujewlOo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo= github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs= github.com/tribalwarshelp/golang-sdk v0.0.0-20200604163817-64920bab73bb h1:3arC78RszJkHTskhfPg/X4TXzlxHJFpF8FvDdFaavL8= github.com/tribalwarshelp/golang-sdk v0.0.0-20200604163817-64920bab73bb/go.mod h1:8ALIvjiYLt0RVXz8TwDTJ3X5KAHv3M7+iYF/SvcA+1A= -github.com/tribalwarshelp/shared v0.0.0-20200602060635-0b3cb090edc5 h1:LeReuMU2zNi7tftm8nytrmkuD4BtLFUYZ3nlSZLE6ow= -github.com/tribalwarshelp/shared v0.0.0-20200602060635-0b3cb090edc5/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y= github.com/tribalwarshelp/shared v0.0.0-20200604161459-deeeccf99815 h1:c+BTJgJyif/HDt8bRxqNaK7zQReI59ho9yWdk7a/gNo= github.com/tribalwarshelp/shared v0.0.0-20200604161459-deeeccf99815/go.mod h1:tf+2yTHasV6jAF3V2deZ9slNoCyBzC0fMdTjI7clf6Y= github.com/vmihailenco/bufpool v0.1.5/go.mod h1:fL9i/PRTuS7AELqAHwSU1Zf1c70xhkhGe/cD5ud9pJk= @@ -158,7 +130,6 @@ golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -244,6 +215,7 @@ gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo= gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8 h1:jL/vaozO53FMfZLySWM+4nulF3gQEC6q5jH90LPomDo= gopkg.in/yaml.v3 v3.0.0-20200603094226-e3079894b1e8/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 286c2a4..270c79a 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,8 @@ import ( "os/signal" "syscall" + "github.com/tribalwarshelp/golang-sdk/sdk" + _cron "github.com/tribalwarshelp/dcbot/cron" "github.com/tribalwarshelp/dcbot/discord" server_repository "github.com/tribalwarshelp/dcbot/server/repository" @@ -27,6 +29,7 @@ func init() { } func main() { + api := sdk.New(os.Getenv("API_URI")) //postgres db := pg.Connect(&pg.Options{ User: os.Getenv("DB_USER"), @@ -53,6 +56,7 @@ func main() { Status: "Tribalwars | tw!help", TribeRepository: tribeRepo, ServerRepository: serverRepo, + API: api, }) if err != nil { log.Fatal(err) @@ -66,6 +70,7 @@ func main() { ServerRepo: serverRepo, TribeRepo: tribeRepo, Discord: sess, + API: api, }) go func() { c.Run() diff --git a/scraper/helpers.go b/scraper/helpers.go deleted file mode 100644 index 943d8ae..0000000 --- a/scraper/helpers.go +++ /dev/null @@ -1,7 +0,0 @@ -package scraper - -var ( - TwstatsURLs = map[string]string{ - "pl": "https://pl.twstats.com", - } -) diff --git a/scraper/scraper.go b/scraper/scraper.go deleted file mode 100644 index fe3e1de..0000000 --- a/scraper/scraper.go +++ /dev/null @@ -1,164 +0,0 @@ -package scraper - -import ( - "fmt" - "net/url" - "strconv" - "strings" - "sync" - "time" - - "github.com/tribalwarshelp/dcbot/tribalwars" - "github.com/tribalwarshelp/dcbot/utils" - - "github.com/PuerkitoBio/goquery" - "github.com/gocolly/colly" -) - -const ( - pathEnnoblementsLive = "/%s/index.php?page=ennoblements&live=live" -) - -type Conquest struct { - Village string - VillageID int - NewOwnerID int - NewOwnerName string - NewOwnerTribeID int - NewOwnerTribeName string - OldOwnerID int - OldOwnerName string - OldOwnerTribeID int - OldOwnerTribeName string - ConqueredAt time.Time -} - -type Conquests []*Conquest - -func (c Conquests) LostVillages(tribeID int) Conquests { - filtered := Conquests{} - for _, conquer := range c { - if conquer.OldOwnerTribeID == tribeID && conquer.OldOwnerTribeID != conquer.NewOwnerTribeID { - filtered = append(filtered, conquer) - } - } - return filtered -} - -func (c Conquests) ConqueredVillages(tribeID int) Conquests { - filtered := Conquests{} - for _, conquer := range c { - if conquer.NewOwnerTribeID == tribeID && conquer.NewOwnerTribeID != conquer.OldOwnerTribeID { - filtered = append(filtered, conquer) - } - } - return filtered -} - -type Scraper struct { - worlds []string - since time.Time - collector *colly.Collector - mutex sync.Mutex - result map[string]Conquests -} - -func New(worlds []string, since time.Time) *Scraper { - s := &Scraper{ - since: since, - worlds: worlds, - collector: colly.NewCollector( - colly.Async(true), - ), - } - s.collector.Limit(&colly.LimitRule{ - RandomDelay: time.Second, - DomainGlob: "*", - Parallelism: 5, - }) - return s -} - -func (s *Scraper) getIDFromNodeHref(node *goquery.Selection) int { - if node != nil { - nodeHref, ok := node.Attr("href") - if ok { - u, err := url.Parse(nodeHref) - if err == nil { - if idStr := u.Query().Get("id"); idStr != "" { - id, err := strconv.Atoi(idStr) - if err == nil { - return id - } - } - } - } - } - - return 0 -} - -func (s *Scraper) handleHTML(row *colly.HTMLElement) { - world := strings.Split(row.Request.URL.Path, "/")[1] - var err error - c := &Conquest{} - - conqueredAtString := strings.TrimSpace(row.DOM.Find("td:last-child").Text()) - location := utils.GetLocation(tribalwars.LanguageCodeFromWorldName(world)) - c.ConqueredAt, err = time.ParseInLocation("2006-01-02 - 15:04:05", - conqueredAtString, - location) - if err != nil || c.ConqueredAt.Before(s.since.In(location)) { - return - } - - villageAnchor := row.DOM.Find("a:first-child").First() - c.VillageID = s.getIDFromNodeHref(villageAnchor) - c.Village = strings.TrimSpace(villageAnchor.Text()) - - oldOwnerNode := row.DOM.Find("td:nth-child(3) a:first-child") - if len(oldOwnerNode.Nodes) == 0 { - c.OldOwnerName = "-" - c.OldOwnerTribeName = "-" - } else { - c.OldOwnerID = s.getIDFromNodeHref(oldOwnerNode) - c.OldOwnerName = strings.TrimSpace(oldOwnerNode.Text()) - oldOwnerTribeNode := row.DOM.Find("td:nth-child(3) .tribelink") - if len(oldOwnerTribeNode.Nodes) != 0 { - c.OldOwnerTribeName = strings.TrimSpace(oldOwnerTribeNode.Text()) - c.OldOwnerTribeID = s.getIDFromNodeHref(oldOwnerTribeNode) - } else { - c.OldOwnerTribeName = "-" - } - } - - newOwnerNode := row.DOM.Find("td:nth-child(4) a:first-child") - c.NewOwnerID = s.getIDFromNodeHref(newOwnerNode) - c.NewOwnerName = strings.TrimSpace(newOwnerNode.Text()) - newOwnerTribeNode := row.DOM.Find("td:nth-child(4) .tribelink") - if len(newOwnerTribeNode.Nodes) != 0 { - c.NewOwnerTribeID = s.getIDFromNodeHref(newOwnerTribeNode) - c.NewOwnerTribeName = strings.TrimSpace(newOwnerTribeNode.Text()) - } else { - c.NewOwnerTribeName = "-" - } - - s.mutex.Lock() - s.result[world] = append(s.result[world], c) - s.mutex.Unlock() -} - -func (s *Scraper) Scrap() map[string]Conquests { - s.result = make(map[string]Conquests) - s.collector.OnHTML(".r1", s.handleHTML) - s.collector.OnHTML(".r2", s.handleHTML) - - for _, world := range s.worlds { - url := TwstatsURLs[tribalwars.LanguageCodeFromWorldName(world)] - if url != "" { - s.collector.Visit(fmt.Sprintf(url+pathEnnoblementsLive, world)) - } - } - s.collector.Wait() - return s.result -}