we're sending now info about conquer using discord embed, change command syntax

This commit is contained in:
Dawid Wysokiński 2020-06-06 14:28:45 +02:00 committed by Kichiyaki
parent 0d4f3d4b3a
commit 85432c140a
10 changed files with 816 additions and 433 deletions

View File

@ -5,11 +5,14 @@ import (
"log"
"time"
shared_models "github.com/tribalwarshelp/shared/models"
"github.com/tribalwarshelp/golang-sdk/sdk"
"github.com/tribalwarshelp/dcbot/discord"
"github.com/tribalwarshelp/dcbot/server"
"github.com/tribalwarshelp/dcbot/tribe"
"github.com/tribalwarshelp/dcbot/utils"
"github.com/robfig/cron/v3"
)
@ -31,7 +34,7 @@ type handler struct {
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,
@ -65,6 +68,32 @@ func (h *handler) loadEnnoblements(worlds []string) map[string]ennoblements {
return m
}
func (h *handler) loadLangVersions(worlds []string) map[shared_models.LanguageTag]*shared_models.LangVersion {
languageTags := []shared_models.LanguageTag{}
cache := make(map[shared_models.LanguageTag]bool)
for _, world := range worlds {
languageTag := utils.LanguageCodeFromWorldName(world)
if languageTag.IsValid() && !cache[languageTag] {
cache[languageTag] = true
languageTags = append(languageTags, languageTag)
}
}
langVersions := make(map[shared_models.LanguageTag]*shared_models.LangVersion)
langVersionsList, err := h.api.LangVersions.Browse(&shared_models.LangVersionFilter{
Tag: languageTags,
})
if err == nil {
for _, langVersion := range langVersionsList.Items {
langVersions[langVersion.Tag] = langVersion
}
} else {
log.Printf("Cannot load lang versions: %s", err.Error())
}
return langVersions
}
func (h *handler) checkEnnoblements() {
worlds, err := h.tribeRepo.FetchWorlds(context.Background())
if err != nil {
@ -72,40 +101,71 @@ func (h *handler) checkEnnoblements() {
return
}
log.Print("checkEnnoblements: worlds: ", worlds)
servers, total, err := h.serverRepo.Fetch(context.Background(), nil)
if err != nil {
log.Print("checkEnnoblements: " + err.Error())
return
}
log.Print("checkEnnoblements: total number of servers: ", total)
langVersions := h.loadLangVersions(worlds)
data := h.loadEnnoblements(worlds)
h.since = time.Now()
log.Print("checkEnnoblements: scrapped data: ", data)
log.Print("checkEnnoblements: loaded ennoblements: ", len(data))
for _, server := range servers {
if server.ConqueredVillagesChannelID == "" && server.LostVillagesChannelID == "" {
continue
}
for _, tribe := range server.Tribes {
es, ok := data[tribe.World]
if ok {
langVersion, ok2 := langVersions[utils.LanguageCodeFromWorldName(tribe.World)]
if ok && ok2 {
if server.LostVillagesChannelID != "" {
for _, ennoblement := range es.tribeLostVillages(tribe.TribeID) {
if !isPlayerTribeNil(ennoblement.NewOwner) &&
server.Tribes.Contains(tribe.World, ennoblement.NewOwner.Tribe.ID) {
continue
}
msgData := newMessageData(tribe.World, ennoblement)
h.discord.SendMessage(server.LostVillagesChannelID, formatMsgAboutVillageLost(msgData))
newMsgDataConfig := newMessageDataConfig{
host: langVersion.Host,
world: tribe.World,
ennoblement: ennoblement,
timezone: langVersion.Timezone,
}
msgData := newMessageData(newMsgDataConfig)
h.discord.SendEmbed(server.LostVillagesChannelID,
discord.
NewEmbed().
SetTitle("Stracona wioska").
SetTimestamp(msgData.date).
AddField(msgData.world, formatMsgAboutVillageLost(msgData)).
MessageEmbed)
}
}
if server.ConqueredVillagesChannelID != "" {
for _, ennoblement := range es.tribeConqueredVillages(tribe.TribeID) {
if !isPlayerTribeNil(ennoblement.OldOwner) &&
server.Tribes.Contains(tribe.World, ennoblement.OldOwner.Tribe.ID) {
continue
}
msgData := newMessageData(tribe.World, ennoblement)
h.discord.SendMessage(server.ConqueredVillagesChannelID, formatMsgAboutVillageConquest(msgData))
newMsgDataConfig := newMessageDataConfig{
host: langVersion.Host,
world: tribe.World,
ennoblement: ennoblement,
timezone: langVersion.Timezone,
}
msgData := newMessageData(newMsgDataConfig)
h.discord.SendEmbed(server.ConqueredVillagesChannelID,
discord.
NewEmbed().
SetTitle("Podbita wioska").
AddField(msgData.world, formatMsgAboutVillageConquest(msgData)).
SetTimestamp(msgData.date).
MessageEmbed)
}
}
}

View File

@ -30,5 +30,13 @@ func isVillageNil(village *shared_models.Village) bool {
}
func formatDateOfConquest(loc *time.Location, t time.Time) string {
return t.In(loc).Format("15:04:05")
return t.In(loc).Format(time.RFC3339)
}
func getLocation(timezone string) *time.Location {
loc, err := time.LoadLocation(timezone)
if err != nil {
return time.UTC
}
return loc
}

View File

@ -7,62 +7,91 @@ import (
shared_models "github.com/tribalwarshelp/shared/models"
)
const (
endpointTribeProfile = "/game.php?screen=info_ally&id=%d"
endpointPlayerProfile = "/game.php?screen=info_player&id=%d"
endpointVillageProfile = "/game.php?screen=info_village&id=%d"
)
type messageData struct {
world string
date string
village string
villageURL string
oldOwnerName string
oldOwnerURL string
oldOwnerTribeURL string
oldOwnerTribeTag string
newOwnerURL string
newOwnerName string
newOwnerTribeURL string
newOwnerTribeTag string
}
func newMessageData(world string, ennoblement *shared_models.Ennoblement) messageData {
type newMessageDataConfig struct {
host string
world string
ennoblement *shared_models.Ennoblement
timezone string
}
func newMessageData(cfg newMessageDataConfig) messageData {
data := messageData{
date: formatDateOfConquest(utils.GetLocation(utils.LanguageCodeFromWorldName(world)), ennoblement.EnnobledAt),
world: world,
date: formatDateOfConquest(getLocation(cfg.timezone), cfg.ennoblement.EnnobledAt),
world: cfg.world,
village: "-",
oldOwnerName: "-",
oldOwnerTribeTag: "-",
newOwnerName: "-",
newOwnerTribeTag: "-",
}
if !isVillageNil(ennoblement.Village) {
data.village = fmt.Sprintf("%s (%d|%d)", ennoblement.Village.Name, ennoblement.Village.X, ennoblement.Village.Y)
if !isVillageNil(cfg.ennoblement.Village) {
data.village = fmt.Sprintf("%s (%d|%d)",
cfg.ennoblement.Village.Name,
cfg.ennoblement.Village.X,
cfg.ennoblement.Village.Y)
data.villageURL = utils.FormatVillageURL(cfg.world, cfg.host, cfg.ennoblement.VillageID)
}
if !isPlayerNil(ennoblement.OldOwner) {
data.oldOwnerName = ennoblement.OldOwner.Name
if !isPlayerNil(cfg.ennoblement.OldOwner) {
data.oldOwnerName = cfg.ennoblement.OldOwner.Name
data.oldOwnerURL = utils.FormatPlayerURL(cfg.world, cfg.host, cfg.ennoblement.OldOwner.ID)
}
if !isPlayerTribeNil(ennoblement.OldOwner) {
data.oldOwnerTribeTag = ennoblement.OldOwner.Tribe.Tag
if !isPlayerTribeNil(cfg.ennoblement.OldOwner) {
data.oldOwnerTribeTag = cfg.ennoblement.OldOwner.Tribe.Tag
data.oldOwnerTribeURL = utils.FormatTribeURL(cfg.world, cfg.host, cfg.ennoblement.OldOwner.Tribe.ID)
}
if !isPlayerNil(ennoblement.NewOwner) {
data.newOwnerName = ennoblement.NewOwner.Name
if !isPlayerNil(cfg.ennoblement.NewOwner) {
data.newOwnerName = cfg.ennoblement.NewOwner.Name
data.newOwnerURL = utils.FormatPlayerURL(cfg.world, cfg.host, cfg.ennoblement.NewOwner.ID)
}
if !isPlayerTribeNil(ennoblement.NewOwner) {
data.newOwnerTribeTag = ennoblement.NewOwner.Tribe.Tag
if !isPlayerTribeNil(cfg.ennoblement.NewOwner) {
data.newOwnerTribeTag = cfg.ennoblement.NewOwner.Tribe.Tag
data.newOwnerTribeURL = utils.FormatTribeURL(cfg.world, cfg.host, cfg.ennoblement.NewOwner.Tribe.ID)
}
return data
}
func formatMsgLink(text string, url string) string {
if url == "" {
return text
}
return fmt.Sprintf("[%s](%s)", text, url)
}
func formatMsgAboutVillageLost(msgData messageData) string {
return fmt.Sprintf(`**%s** %s: Wioska **%s** gracza **%s** (**%s**) została stracona na rzecz **%s** (**%s**)`,
msgData.world,
msgData.date,
msgData.village,
msgData.oldOwnerName,
msgData.oldOwnerTribeTag,
msgData.newOwnerName,
msgData.newOwnerTribeTag)
return fmt.Sprintf(`Wioska %s gracza %s (%s) została stracona na rzecz %s (%s)`,
formatMsgLink(msgData.village, msgData.villageURL),
formatMsgLink(msgData.oldOwnerName, msgData.oldOwnerURL),
formatMsgLink(msgData.oldOwnerTribeTag, msgData.oldOwnerTribeURL),
formatMsgLink(msgData.newOwnerName, msgData.newOwnerURL),
formatMsgLink(msgData.newOwnerTribeTag, msgData.newOwnerTribeURL))
}
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)
return fmt.Sprintf(`Gracz %s (%s) podbił wioskę %s od gracza %s (%s)`,
formatMsgLink(msgData.newOwnerName, msgData.newOwnerURL),
formatMsgLink(msgData.newOwnerTribeTag, msgData.newOwnerTribeURL),
formatMsgLink(msgData.village, msgData.villageURL),
formatMsgLink(msgData.oldOwnerName, msgData.oldOwnerURL),
formatMsgLink(msgData.oldOwnerTribeTag, msgData.oldOwnerTribeURL))
}

View File

@ -1,14 +1,33 @@
package discord
import (
"context"
"fmt"
"math"
"strconv"
shared_models "github.com/tribalwarshelp/shared/models"
"github.com/bwmarrin/discordgo"
"github.com/tribalwarshelp/dcbot/models"
"github.com/tribalwarshelp/dcbot/utils"
"github.com/tribalwarshelp/golang-sdk/sdk"
)
const (
TribesPerServer = 10
)
type Command string
var (
const (
HelpCommand Command = "help"
AddCommand Command = "add"
ListCommand Command = "list"
DeleteCommand Command = "delete"
LostVillagesCommand Command = "lostvillages"
ConqueredVillagesCommand Command = "conqueredvillages"
TribeCommand Command = "tribe"
TopAttCommand Command = "topatt"
TopDefCommand Command = "topdef"
TopSuppCommand Command = "topsupp"
@ -23,3 +42,342 @@ func (cmd Command) String() string {
func (cmd Command) WithPrefix(prefix string) string {
return prefix + cmd.String()
}
func (s *Session) handleHelpCommand(m *discordgo.MessageCreate) {
tribeCMDWithPrefix := TribeCommand.WithPrefix(s.cfg.CommandPrefix)
commandsForAll := fmt.Sprintf(`
- %s %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największym RA z plemion o podanych id
- %s %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największym RO z plemion o podanych id
- %s %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największym RW z plemion o podanych id
- %s %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największej liczbie pokonanych z plemion o podanych id
- %s %s [serwer] [strona] [id1] [id2] [id3] [n id] - wyświetla graczy o największej liczbie punktów z plemion o podanych id
`,
tribeCMDWithPrefix,
TopAttCommand.String(),
tribeCMDWithPrefix,
TopDefCommand.String(),
tribeCMDWithPrefix,
TopSuppCommand.String(),
tribeCMDWithPrefix,
TopTotalCommand.String(),
tribeCMDWithPrefix,
TopPointsCommand.String())
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 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))
s.SendEmbed(m.ChannelID, NewEmbed().
SetTitle("Pomoc").
SetDescription("Komendy oferowane przez bota").
AddField("Dla wszystkich", commandsForAll).
AddField("Dla adminów", commandsForGuildAdmins).
MessageEmbed)
}
func (s *Session) handleTribeCommand(m *discordgo.MessageCreate, args ...string) {
argsLength := len(args)
if argsLength < 4 {
s.SendMessage(m.ChannelID,
fmt.Sprintf("%s Niepoprawna komenda (sprawdź %s)",
m.Author.Mention(),
HelpCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
command := Command(args[0])
world := args[1]
page, err := strconv.Atoi(args[2])
if err != nil || page <= 0 {
s.SendMessage(m.ChannelID, fmt.Sprintf("%s 3 argument musi być liczbą większą od 0.", m.Author.Mention()))
return
}
ids := []int{}
for _, arg := range args[3: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"
default:
s.SendMessage(m.ChannelID,
fmt.Sprintf("%s Nieznana komenda %s (sprawdź %s)",
m.Author.Mention(),
command.String(),
HelpCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
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
}
langVersion, err := s.cfg.API.LangVersions.Read(utils.LanguageCodeFromWorldName(world))
if err != nil {
s.SendMessage(m.ChannelID, fmt.Sprintf("%s Nie udało się wygenerować listy.", m.Author.Mention()))
return
}
msg := &EmbedMessage{}
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.Append(fmt.Sprintf("**%d**. [%s](%s) (Plemię: [%s](%s) | Ranking ogólny: **%d** | Wynik: **%d**)\n",
offset+i+1,
player.Name,
utils.FormatPlayerURL(world, langVersion.Host, player.ID),
player.Tribe.Tag,
utils.FormatTribeURL(world, langVersion.Host, player.Tribe.ID),
rank,
score))
}
totalPages := int(math.Round(float64(playersList.Total) / float64(limit)))
s.SendEmbed(m.ChannelID, NewEmbed().
SetTitle(title).
SetDescription("A oto lista!").
SetFields(msg.ToMessageEmbedFields()).
SetFooter(fmt.Sprintf("Strona %d z %d", page, totalPages)).
MessageEmbed)
}
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,
}
err := s.cfg.ServerRepository.Store(context.Background(), server)
if err != nil {
return
}
server.LostVillagesChannelID = m.ChannelID
go s.cfg.ServerRepository.Update(context.Background(), server)
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) 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,
}
err := s.cfg.ServerRepository.Store(context.Background(), server)
if err != nil {
return
}
server.ConqueredVillagesChannelID = m.ChannelID
go s.cfg.ServerRepository.Update(context.Background(), server)
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) 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]...)
return
} else if argsLength < 2 {
s.SendMessage(m.ChannelID,
fmt.Sprintf("%s %s [świat] [id plemienia]",
m.Author.Mention(),
AddCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
world := args[0]
id, err := strconv.Atoi(args[1])
if err != nil {
s.SendMessage(m.ChannelID,
fmt.Sprintf("%s %s [świat] [id plemienia]",
m.Author.Mention(),
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,
}
err = s.cfg.ServerRepository.Store(context.Background(), server)
if err != nil {
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,
ServerID: server.ID,
})
if err != nil {
s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie udało się dodać plemienia do obserwowanych.`)
return
}
s.SendMessage(m.ChannelID, m.Author.Mention()+` Dodano.`)
}
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]...)
return
} else if argsLength < 1 {
s.SendMessage(m.ChannelID,
fmt.Sprintf(`%s %s [id z tw!list]`,
m.Author.Mention(),
DeleteCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
id, err := strconv.Atoi(args[0])
if err != nil {
s.SendMessage(m.ChannelID,
fmt.Sprintf(`%s %s [id z tw!list]`,
m.Author.Mention(),
DeleteCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
go s.cfg.TribeRepository.Delete(context.Background(), &models.TribeFilter{
ServerID: []string{m.GuildID},
ID: []int{id},
})
s.SendMessage(m.ChannelID, m.Author.Mention()+` Usunięto.`)
}
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 := ""
for i, tribe := range tribes {
msg += fmt.Sprintf("**%d**. %d - %s - %d\n", i+1, tribe.ID, tribe.World, tribe.TribeID)
}
s.SendEmbed(m.ChannelID, NewEmbed().
SetTitle("Lista obserwowanych plemion").
AddField("Indeks. ID - świat - ID plemienia", msg).
SetFooter("Strona 1 z 1").
MessageEmbed)
}

View File

@ -1,26 +1,16 @@
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
discordEmbedColor = 0x00ff00
)
type SessionConfig struct {
Token string
CommandPrefix string
@ -64,6 +54,20 @@ func (s *Session) init() error {
return nil
}
func (s *Session) Close() error {
return s.dg.Close()
}
func (s *Session) SendMessage(channelID, message string) error {
_, err := s.dg.ChannelMessageSend(channelID, message)
return err
}
func (s *Session) SendEmbed(channelID string, message *discordgo.MessageEmbed) error {
_, err := s.dg.ChannelMessageSendEmbed(channelID, message)
return err
}
func (s *Session) handleNewMessage(_ *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.ID == s.dg.State.User.ID || m.Author.Bot {
return
@ -85,326 +89,11 @@ 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...)
case TribeCommand.WithPrefix(s.cfg.CommandPrefix):
s.handleTribeCommand(m, args...)
}
}
func (s *Session) handleHelpCommand(m *discordgo.MessageCreate) {
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,
}
err := s.cfg.ServerRepository.Store(context.Background(), server)
if err != nil {
return
}
server.LostVillagesChannelID = m.ChannelID
go s.cfg.ServerRepository.Update(context.Background(), server)
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) 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,
}
err := s.cfg.ServerRepository.Store(context.Background(), server)
if err != nil {
return
}
server.ConqueredVillagesChannelID = m.ChannelID
go s.cfg.ServerRepository.Update(context.Background(), server)
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) 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]...)
return
} else if argsLength < 2 {
s.SendMessage(m.ChannelID,
fmt.Sprintf("%s %s [świat] [id plemienia]",
m.Author.Mention(),
AddCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
world := args[0]
id, err := strconv.Atoi(args[1])
if err != nil {
s.SendMessage(m.ChannelID,
fmt.Sprintf("%s %s [świat] [id plemienia]",
m.Author.Mention(),
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,
}
err = s.cfg.ServerRepository.Store(context.Background(), server)
if err != nil {
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,
ServerID: server.ID,
})
if err != nil {
s.SendMessage(m.ChannelID, m.Author.Mention()+` Nie udało się dodać plemienia do obserwowanych.`)
return
}
s.SendMessage(m.ChannelID, m.Author.Mention()+` Dodano.`)
}
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]...)
return
} else if argsLength < 1 {
s.SendMessage(m.ChannelID,
fmt.Sprintf(`%s %s [id z tw!list]`,
m.Author.Mention(),
DeleteCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
id, err := strconv.Atoi(args[0])
if err != nil {
s.SendMessage(m.ChannelID,
fmt.Sprintf(`%s %s [id z tw!list]`,
m.Author.Mention(),
DeleteCommand.WithPrefix(s.cfg.CommandPrefix)))
return
}
go s.cfg.TribeRepository.Delete(context.Background(), &models.TribeFilter{
ServerID: []string{m.GuildID},
ID: []int{id},
})
s.SendMessage(m.ChannelID, m.Author.Mention()+` Usunięto.`)
}
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 := ""
for i, tribe := range tribes {
msg += fmt.Sprintf("**%d**. %d - %s - %d\n", i+1, tribe.ID, tribe.World, tribe.TribeID)
}
s.dg.ChannelMessageSendEmbed(m.ChannelID, &discordgo.MessageEmbed{
Author: &discordgo.MessageEmbedAuthor{},
Title: "Lista obserwowanych plemion",
Color: discordEmbedColor,
Fields: []*discordgo.MessageEmbedField{
&discordgo.MessageEmbedField{
Name: "Indeks. ID - świat - ID plemienia",
Value: msg,
},
},
Footer: &discordgo.MessageEmbedFooter{
Text: "Strona 1 z 1",
},
})
}
func (s *Session) Close() error {
return s.dg.Close()
}
func (s *Session) memberHasPermission(guildID string, userID string, permission int) (bool, error) {
guild, err := s.dg.State.Guild(guildID)
if err != nil {
@ -438,58 +127,6 @@ func (s *Session) memberHasPermission(guildID string, userID string, permission
return false, nil
}
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)),
Inline: false,
},
},
Footer: &discordgo.MessageEmbedFooter{
Text: "https://dawid-wysokinski.pl/",
},
}
s.dg.ChannelMessageSendEmbed(channelID, embed)
}
func (s *Session) sendUnknownCommandError(mention, channelID string, command ...string) {
s.SendMessage(channelID, mention+` Nieznana komenda: `+strings.Join(command, " "))
}
func (s *Session) SendMessage(channelID, message string) {
s.dg.ChannelMessageSend(channelID, message)
}

279
discord/embed.go Normal file
View File

@ -0,0 +1,279 @@
package discord
import "github.com/bwmarrin/discordgo"
// Constants for message embed character limits
const (
discordEmbedColor = 0x00ff00
EmbedLimitTitle = 256
EmbedLimitDescription = 2048
EmbedLimitFieldValue = 1024
EmbedLimitFieldName = 256
EmbedLimitField = 25
EmbedLimitFooter = 2048
EmbedLimit = 4000
)
type Embed struct {
*discordgo.MessageEmbed
}
//NewEmbed returns a new embed object
func NewEmbed() *Embed {
return &Embed{&discordgo.MessageEmbed{
Color: discordEmbedColor,
}}
}
//SetTitle ...
func (e *Embed) SetTitle(name string) *Embed {
e.Title = name
return e
}
func (e *Embed) SetTimestamp(timestamp string) *Embed {
e.Timestamp = timestamp
return e
}
//SetDescription [desc]
func (e *Embed) SetDescription(description string) *Embed {
if len(description) > EmbedLimitDescription {
description = description[:EmbedLimitDescription]
}
e.Description = description
return e
}
//AddField [name] [value]
func (e *Embed) AddField(name, value string) *Embed {
if len(value) > EmbedLimitFieldValue {
value = value[:EmbedLimitFieldValue]
}
if len(name) > EmbedLimitFieldName {
name = name[:EmbedLimitFieldName]
}
e.Fields = append(e.Fields, &discordgo.MessageEmbedField{
Name: name,
Value: value,
})
return e
}
func (e *Embed) SetFields(fields []*discordgo.MessageEmbedField) *Embed {
e.Fields = fields
return e
}
//SetFooter [Text] [iconURL]
func (e *Embed) SetFooter(args ...string) *Embed {
iconURL := ""
text := ""
proxyURL := ""
switch {
case len(args) > 2:
proxyURL = args[2]
fallthrough
case len(args) > 1:
iconURL = args[1]
fallthrough
case len(args) > 0:
text = args[0]
case len(args) == 0:
return e
}
e.Footer = &discordgo.MessageEmbedFooter{
IconURL: iconURL,
Text: text,
ProxyIconURL: proxyURL,
}
return e
}
//SetImage ...
func (e *Embed) SetImage(args ...string) *Embed {
var URL string
var proxyURL string
if len(args) == 0 {
return e
}
if len(args) > 0 {
URL = args[0]
}
if len(args) > 1 {
proxyURL = args[1]
}
e.Image = &discordgo.MessageEmbedImage{
URL: URL,
ProxyURL: proxyURL,
}
return e
}
//SetThumbnail ...
func (e *Embed) SetThumbnail(args ...string) *Embed {
var URL string
var proxyURL string
if len(args) == 0 {
return e
}
if len(args) > 0 {
URL = args[0]
}
if len(args) > 1 {
proxyURL = args[1]
}
e.Thumbnail = &discordgo.MessageEmbedThumbnail{
URL: URL,
ProxyURL: proxyURL,
}
return e
}
//SetAuthor ...
func (e *Embed) SetAuthor(args ...string) *Embed {
var (
name string
iconURL string
URL string
proxyURL string
)
if len(args) == 0 {
return e
}
if len(args) > 0 {
name = args[0]
}
if len(args) > 1 {
iconURL = args[1]
}
if len(args) > 2 {
URL = args[2]
}
if len(args) > 3 {
proxyURL = args[3]
}
e.Author = &discordgo.MessageEmbedAuthor{
Name: name,
IconURL: iconURL,
URL: URL,
ProxyIconURL: proxyURL,
}
return e
}
//SetURL ...
func (e *Embed) SetURL(URL string) *Embed {
e.URL = URL
return e
}
//SetColor ...
func (e *Embed) SetColor(clr int) *Embed {
e.Color = clr
return e
}
// InlineAllFields sets all fields in the embed to be inline
func (e *Embed) InlineAllFields() *Embed {
for _, v := range e.Fields {
v.Inline = true
}
return e
}
// Truncate truncates any embed value over the character limit.
func (e *Embed) Truncate() *Embed {
e.TruncateDescription()
e.TruncateFields()
e.TruncateFooter()
e.TruncateTitle()
return e
}
// TruncateFields truncates fields that are too long
func (e *Embed) TruncateFields() *Embed {
if len(e.Fields) > 25 {
e.Fields = e.Fields[:EmbedLimitField]
}
for _, v := range e.Fields {
if len(v.Name) > EmbedLimitFieldName {
v.Name = v.Name[:EmbedLimitFieldName]
}
if len(v.Value) > EmbedLimitFieldValue {
v.Value = v.Value[:EmbedLimitFieldValue]
}
}
return e
}
// TruncateDescription ...
func (e *Embed) TruncateDescription() *Embed {
if len(e.Description) > EmbedLimitDescription {
e.Description = e.Description[:EmbedLimitDescription]
}
return e
}
// TruncateTitle ...
func (e *Embed) TruncateTitle() *Embed {
if len(e.Title) > EmbedLimitTitle {
e.Title = e.Title[:EmbedLimitTitle]
}
return e
}
// TruncateFooter ...
func (e *Embed) TruncateFooter() *Embed {
if e.Footer != nil && len(e.Footer.Text) > EmbedLimitFooter {
e.Footer.Text = e.Footer.Text[:EmbedLimitFooter]
}
return e
}
type EmbedMessage struct {
Chunks []string
Index int
}
func (msg *EmbedMessage) Append(m string) {
if len(msg.Chunks) < msg.Index+1 {
msg.Chunks = append(msg.Chunks, "")
}
if len(m)+len(msg.Chunks[msg.Index]) > EmbedLimitFieldValue {
msg.Chunks = append(msg.Chunks, m)
msg.Index++
return
}
msg.Chunks[msg.Index] += m
}
func (msg *EmbedMessage) ToMessageEmbedFields() []*discordgo.MessageEmbedField {
fields := []*discordgo.MessageEmbedField{}
for _, chunk := range msg.Chunks {
fields = append(fields, &discordgo.MessageEmbedField{
Name: "-",
Value: chunk,
})
}
return fields
}

View File

@ -29,7 +29,7 @@ func init() {
}
func main() {
api := sdk.New(os.Getenv("API_URI"))
api := sdk.New(os.Getenv("API_URL"))
//postgres
db := pg.Connect(&pg.Options{
User: os.Getenv("DB_USER"),

View File

@ -0,0 +1,21 @@
package utils
import "fmt"
const (
endpointTribeProfile = "/game.php?screen=info_ally&id=%d"
endpointPlayerProfile = "/game.php?screen=info_player&id=%d"
endpointVillageProfile = "/game.php?screen=info_village&id=%d"
)
func FormatVillageURL(world, host string, id int) string {
return fmt.Sprintf("https://%s.%s"+endpointVillageProfile, world, host, id)
}
func FormatTribeURL(world, host string, id int) string {
return fmt.Sprintf("https://%s.%s"+endpointTribeProfile, world, host, id)
}
func FormatPlayerURL(world, host string, id int) string {
return fmt.Sprintf("https://%s.%s"+endpointPlayerProfile, world, host, id)
}

View File

@ -1,8 +1,12 @@
package utils
func LanguageCodeFromWorldName(world string) string {
import (
"github.com/tribalwarshelp/shared/models"
)
func LanguageCodeFromWorldName(world string) models.LanguageTag {
if len(world) < 2 {
return ""
}
return world[0:2]
return models.LanguageTag(world[0:2])
}

View File

@ -1,13 +0,0 @@
package utils
import "time"
func GetLocation(lang string) *time.Location {
switch lang {
case "pl":
loc, _ := time.LoadLocation("Europe/Warsaw")
return loc
default:
return time.UTC
}
}