This commit is contained in:
Dawid Wysokiński 2020-06-05 17:49:51 +02:00 committed by Kichiyaki
parent 9ea3ab5f78
commit a724fe29b9
10 changed files with 413 additions and 260 deletions

View File

@ -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)
}

31
cron/ennoblements.go Normal file
View File

@ -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
}

30
cron/helpers.go Normal file
View File

@ -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
}

View File

@ -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) {

View File

@ -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 {

12
go.mod
View File

@ -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

32
go.sum
View File

@ -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=

View File

@ -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()

View File

@ -1,7 +0,0 @@
package scraper
var (
TwstatsURLs = map[string]string{
"pl": "https://pl.twstats.com",
}
)

View File

@ -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
}