Compare commits
1 Commits
master
...
feat/coord
Author | SHA1 | Date |
---|---|---|
Dawid Wysokiński | 8a19630a9f |
|
@ -49,9 +49,10 @@ func New() *cli.Command {
|
|||
groupRepo := adapter.NewGroupBun(db)
|
||||
|
||||
choiceSvc := service.NewChoice(twhelpService)
|
||||
coordsSvc := service.NewCoords(twhelpService)
|
||||
groupSvc := service.NewGroup(groupRepo, twhelpService, logger, cfg.MaxGroupsPerServer, cfg.MaxMonitorsPerGroup)
|
||||
|
||||
bot, err := discord.NewBot(cfg.Token, groupSvc, choiceSvc, logger)
|
||||
bot, err := discord.NewBot(cfg.Token, groupSvc, choiceSvc, coordsSvc, logger)
|
||||
if err != nil {
|
||||
return fmt.Errorf("discord.NewBot: %w", err)
|
||||
}
|
||||
|
@ -69,8 +70,6 @@ func New() *cli.Command {
|
|||
return fmt.Errorf("couldn't create file (path=%s): %w", healthyFilePath, err)
|
||||
}
|
||||
|
||||
logger.Info("Bot is up and running")
|
||||
|
||||
waitForSignal(c.Context)
|
||||
|
||||
return nil
|
||||
|
|
|
@ -126,7 +126,7 @@ func (t *TWHelpHTTP) GetExistingTribeByTag(ctx context.Context, versionCode, ser
|
|||
return t.convertTribeToDomain(tribes[0]), nil
|
||||
}
|
||||
|
||||
func (t *TWHelpHTTP) ListTribesTag(
|
||||
func (t *TWHelpHTTP) ListTribesByTag(
|
||||
ctx context.Context,
|
||||
versionCode, serverKey string,
|
||||
tribeTags []string,
|
||||
|
@ -153,12 +153,16 @@ func (t *TWHelpHTTP) ListTribesTag(
|
|||
return res, nil
|
||||
}
|
||||
|
||||
func (t *TWHelpHTTP) ListVillagesCoords(
|
||||
func (t *TWHelpHTTP) ListVillagesByCoords(
|
||||
ctx context.Context,
|
||||
versionCode, serverKey string,
|
||||
coords []string,
|
||||
offset, limit int32,
|
||||
) ([]domain.Village, error) {
|
||||
if len(coords) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
villages, err := t.client.ListVillages(ctx, versionCode, serverKey, twhelp.ListVillagesQueryParams{
|
||||
Limit: limit,
|
||||
Offset: offset,
|
||||
|
@ -247,6 +251,8 @@ func (t *TWHelpHTTP) convertVillageToDomain(v twhelp.Village) domain.Village {
|
|||
FullName: v.FullName,
|
||||
ProfileURL: v.ProfileURL,
|
||||
Points: v.Points,
|
||||
X: v.X,
|
||||
Y: v.Y,
|
||||
Player: t.convertNullPlayerMetaToDomain(v.Player),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,10 @@ type GroupService interface {
|
|||
Delete(ctx context.Context, id, serverID string) error
|
||||
}
|
||||
|
||||
type CoordsService interface {
|
||||
Translate(ctx context.Context, params domain.TranslateCoordsParams) (domain.TranslateCoordsResult, error)
|
||||
}
|
||||
|
||||
type ChoiceService interface {
|
||||
Versions(ctx context.Context) ([]domain.Choice, error)
|
||||
}
|
||||
|
@ -37,7 +41,8 @@ type Bot struct {
|
|||
session *discordgo.Session
|
||||
cron *cron.Cron
|
||||
groupSvc GroupService
|
||||
choiceSvc ChoiceService
|
||||
choiceSvc *choiceService
|
||||
coordsSvc CoordsService
|
||||
logger *zap.Logger
|
||||
localizer *discordi18n.Localizer
|
||||
}
|
||||
|
@ -45,7 +50,8 @@ type Bot struct {
|
|||
func NewBot(
|
||||
token string,
|
||||
groupSvc GroupService,
|
||||
client ChoiceService,
|
||||
choiceSvc ChoiceService,
|
||||
coordsSvc CoordsService,
|
||||
logger *zap.Logger,
|
||||
) (*Bot, error) {
|
||||
localizer, err := discordi18n.NewLocalizer(language.English)
|
||||
|
@ -68,7 +74,8 @@ func NewBot(
|
|||
),
|
||||
),
|
||||
groupSvc: groupSvc,
|
||||
choiceSvc: client,
|
||||
choiceSvc: &choiceService{choiceSvc: choiceSvc, localizer: localizer},
|
||||
coordsSvc: coordsSvc,
|
||||
logger: logger,
|
||||
localizer: localizer,
|
||||
}
|
||||
|
@ -106,6 +113,7 @@ type command interface {
|
|||
func (b *Bot) registerCommands() error {
|
||||
commands := []command{
|
||||
&groupCommand{localizer: b.localizer, logger: b.logger, groupSvc: b.groupSvc, choiceSvc: b.choiceSvc},
|
||||
&coordsCommand{localizer: b.localizer, logger: b.logger, coordsSvc: b.coordsSvc, choiceSvc: b.choiceSvc},
|
||||
}
|
||||
|
||||
for _, cmd := range commands {
|
||||
|
@ -143,6 +151,7 @@ func (b *Bot) registerCronJobs() error {
|
|||
|
||||
func (b *Bot) handleSessionReady(s *discordgo.Session, _ *discordgo.Ready) {
|
||||
_ = s.UpdateGameStatus(0, "Tribal Wars")
|
||||
b.logger.Info("Bot is up and running")
|
||||
}
|
||||
|
||||
func (b *Bot) logCommands(_ *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
package discord
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/dcbot/internal/discord/internal/discordi18n"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
)
|
||||
|
||||
type choiceService struct {
|
||||
localizer *discordi18n.Localizer
|
||||
choiceSvc ChoiceService
|
||||
}
|
||||
|
||||
func (s *choiceService) versions(ctx context.Context) ([]*discordgo.ApplicationCommandOptionChoice, error) {
|
||||
choices, err := s.choiceSvc.Versions(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ChoiceService.Versions: %w", err)
|
||||
}
|
||||
|
||||
dcChoices := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(choices))
|
||||
for _, v := range choices {
|
||||
dcChoices = append(dcChoices, &discordgo.ApplicationCommandOptionChoice{
|
||||
Name: v.Name,
|
||||
Value: v.Value,
|
||||
})
|
||||
}
|
||||
|
||||
return dcChoices, nil
|
||||
}
|
||||
|
||||
func (s *choiceService) languages() ([]*discordgo.ApplicationCommandOptionChoice, error) {
|
||||
tags := s.localizer.LanguageTags()
|
||||
|
||||
choices := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
lang, langLocalizations, err := s.localizer.LocalizeDiscord(buildLangMessageID(tag.String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{
|
||||
Name: lang,
|
||||
NameLocalizations: langLocalizations,
|
||||
Value: tag.String(),
|
||||
})
|
||||
}
|
||||
|
||||
return choices, nil
|
||||
}
|
|
@ -0,0 +1,236 @@
|
|||
package discord
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/dcbot/internal/discord/internal/discordi18n"
|
||||
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/nicksnyder/go-i18n/v2/i18n"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
type coordsCommand struct {
|
||||
localizer *discordi18n.Localizer
|
||||
logger *zap.Logger
|
||||
coordsSvc CoordsService
|
||||
choiceSvc *choiceService
|
||||
}
|
||||
|
||||
func (c *coordsCommand) name() string {
|
||||
return "coords"
|
||||
}
|
||||
|
||||
func (c *coordsCommand) register(s *discordgo.Session) error {
|
||||
if err := c.create(s); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.AddHandler(c.handle)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *coordsCommand) create(s *discordgo.Session) error {
|
||||
dm := true
|
||||
|
||||
optionVersionDefault, optionVersionLocalizations, err := c.localizer.LocalizeDiscord("cmd.coords.option.version.description")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
optionServerDefault, optionServerLocalizations, err := c.localizer.LocalizeDiscord("cmd.coords.option.server.description")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
optionCoordsDefault, optionCoordsLocalizations, err := c.localizer.LocalizeDiscord("cmd.coords.option.coords.description")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cmdDescriptionDefault, cmdDescriptionLocalizations, err := c.localizer.LocalizeDiscord("cmd.coords.description")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
versionChoices, err := c.choiceSvc.versions(context.Background())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.ApplicationCommandCreate(s.State.User.ID, "", &discordgo.ApplicationCommand{
|
||||
Name: c.name(),
|
||||
Description: cmdDescriptionDefault,
|
||||
DescriptionLocalizations: &cmdDescriptionLocalizations,
|
||||
DMPermission: &dm,
|
||||
Options: []*discordgo.ApplicationCommandOption{
|
||||
{
|
||||
Name: "version",
|
||||
Description: optionVersionDefault,
|
||||
DescriptionLocalizations: optionVersionLocalizations,
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Choices: versionChoices,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "server",
|
||||
Description: optionServerDefault,
|
||||
DescriptionLocalizations: optionServerLocalizations,
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Required: true,
|
||||
},
|
||||
{
|
||||
Name: "coords",
|
||||
Description: optionCoordsDefault,
|
||||
DescriptionLocalizations: optionCoordsLocalizations,
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Required: true,
|
||||
MaxLength: domain.CoordsStringMaxLength,
|
||||
},
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *coordsCommand) handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
ctx := context.Background()
|
||||
|
||||
//nolint:exhaustive
|
||||
switch i.Type {
|
||||
case discordgo.InteractionApplicationCommand:
|
||||
c.handleCommand(ctx, s, i)
|
||||
case discordgo.InteractionMessageComponent:
|
||||
c.handleMessageComponent(ctx, s, i)
|
||||
}
|
||||
}
|
||||
|
||||
const coordsTranslationVillagesPerPage = 7
|
||||
|
||||
func (c *coordsCommand) handleCommand(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
cmdData := i.ApplicationCommandData()
|
||||
|
||||
if cmdData.Name != c.name() {
|
||||
return
|
||||
}
|
||||
|
||||
locale := string(i.Locale)
|
||||
|
||||
params, err := c.optionsToTranslateCoordsParams(cmdData.Options)
|
||||
if err != nil {
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
res, err := c.coordsSvc.Translate(ctx, params)
|
||||
if err != nil {
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
var builderDescription strings.Builder
|
||||
for i, v := range res.Villages {
|
||||
if i > 0 {
|
||||
builderDescription.WriteString("\n")
|
||||
}
|
||||
builderDescription.WriteString(v.FullName)
|
||||
}
|
||||
|
||||
titleMessageID := "cmd.coords.embed.title"
|
||||
title, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: titleMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified id", zap.String("id", titleMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Embeds: []*discordgo.MessageEmbed{
|
||||
{
|
||||
Type: discordgo.EmbedTypeRich,
|
||||
Title: title,
|
||||
Description: builderDescription.String(),
|
||||
Timestamp: formatTimestamp(time.Now()),
|
||||
Footer: &discordgo.MessageEmbedFooter{
|
||||
Text: fmt.Sprintf("Page %d/%d", res.Page, res.MaxPage),
|
||||
},
|
||||
},
|
||||
},
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.ActionsRow{
|
||||
Components: []discordgo.MessageComponent{
|
||||
discordgo.Button{
|
||||
Style: discordgo.PrimaryButton,
|
||||
CustomID: "coords-" + res.PrevPageID.String(),
|
||||
Label: "Previous page",
|
||||
Disabled: res.PrevPageID.IsZero(),
|
||||
},
|
||||
discordgo.Button{
|
||||
Style: discordgo.PrimaryButton,
|
||||
CustomID: "coords-" + res.NextPageID.String(),
|
||||
Label: "Next page",
|
||||
Disabled: res.NextPageID.IsZero(),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func (c *coordsCommand) optionsToTranslateCoordsParams(
|
||||
options []*discordgo.ApplicationCommandInteractionDataOption,
|
||||
) (domain.TranslateCoordsParams, error) {
|
||||
version := ""
|
||||
server := ""
|
||||
coordsStr := ""
|
||||
|
||||
for _, opt := range options {
|
||||
switch opt.Name {
|
||||
case "version":
|
||||
version = opt.StringValue()
|
||||
case "server":
|
||||
server = opt.StringValue()
|
||||
case "coords":
|
||||
coordsStr = opt.StringValue()
|
||||
}
|
||||
}
|
||||
|
||||
return domain.NewTranslateCoordsParams(version, server, coordsStr, coordsTranslationVillagesPerPage)
|
||||
}
|
||||
|
||||
func (c *coordsCommand) handleMessageComponent(ctx context.Context, s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
cmpData := i.MessageComponentData()
|
||||
|
||||
b, _ := json.MarshalIndent(i, "", "\t")
|
||||
fmt.Println(cmpData, string(b))
|
||||
|
||||
if cmpData.ComponentType != discordgo.ButtonComponent {
|
||||
return
|
||||
}
|
||||
}
|
|
@ -2,7 +2,6 @@ package discord
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -21,7 +20,7 @@ type groupCommand struct {
|
|||
localizer *discordi18n.Localizer
|
||||
logger *zap.Logger
|
||||
groupSvc GroupService
|
||||
choiceSvc ChoiceService
|
||||
choiceSvc *choiceService
|
||||
}
|
||||
|
||||
func (c *groupCommand) name() string {
|
||||
|
@ -59,15 +58,15 @@ func (c *groupCommand) create(s *discordgo.Session) error {
|
|||
subcommands = append(subcommands, cmd)
|
||||
}
|
||||
|
||||
groupCmdDescriptionDefault, groupCmdDescriptionLocalizations, err := c.localizer.LocalizeDiscord("cmd.group.description")
|
||||
cmdDescriptionDefault, cmdDescriptionLocalizations, err := c.localizer.LocalizeDiscord("cmd.group.description")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = s.ApplicationCommandCreate(s.State.User.ID, "", &discordgo.ApplicationCommand{
|
||||
Name: c.name(),
|
||||
Description: groupCmdDescriptionDefault,
|
||||
DescriptionLocalizations: &groupCmdDescriptionLocalizations,
|
||||
Description: cmdDescriptionDefault,
|
||||
DescriptionLocalizations: &cmdDescriptionLocalizations,
|
||||
DefaultMemberPermissions: &perm,
|
||||
DMPermission: &dm,
|
||||
Options: subcommands,
|
||||
|
@ -100,12 +99,12 @@ func (c *groupCommand) buildSubcommandCreate() (*discordgo.ApplicationCommandOpt
|
|||
}
|
||||
|
||||
func (c *groupCommand) buildSubcommandCreateOptions() ([]*discordgo.ApplicationCommandOption, error) {
|
||||
versionChoices, err := c.getVersionChoices()
|
||||
versionChoices, err := c.choiceSvc.versions(context.Background())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
languageChoices, err := c.getLanguageChoices()
|
||||
languageChoices, err := c.choiceSvc.languages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -208,40 +207,6 @@ func (c *groupCommand) buildSubcommandCreateOptions() ([]*discordgo.ApplicationC
|
|||
}, nil
|
||||
}
|
||||
|
||||
func (c *groupCommand) getVersionChoices() ([]*discordgo.ApplicationCommandOptionChoice, error) {
|
||||
choices, err := c.choiceSvc.Versions(context.Background())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ChoiceService.Versions: %w", err)
|
||||
}
|
||||
|
||||
dcChoices := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(choices))
|
||||
for _, v := range choices {
|
||||
dcChoices = append(dcChoices, &discordgo.ApplicationCommandOptionChoice{
|
||||
Name: v.Name,
|
||||
Value: v.Value,
|
||||
})
|
||||
}
|
||||
|
||||
return dcChoices, nil
|
||||
}
|
||||
|
||||
func (c *groupCommand) getLanguageChoices() ([]*discordgo.ApplicationCommandOptionChoice, error) {
|
||||
tags := c.localizer.LanguageTags()
|
||||
choices := make([]*discordgo.ApplicationCommandOptionChoice, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
lang, langLocalizations, err := c.localizer.LocalizeDiscord(buildLangMessageID(tag.String()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
choices = append(choices, &discordgo.ApplicationCommandOptionChoice{
|
||||
Name: lang,
|
||||
NameLocalizations: langLocalizations,
|
||||
Value: tag.String(),
|
||||
})
|
||||
}
|
||||
return choices, nil
|
||||
}
|
||||
|
||||
func (c *groupCommand) buildSubcommandList() (*discordgo.ApplicationCommandOption, error) {
|
||||
cmdDescriptionDefault, cmdDescriptionLocalizations, err := c.localizer.LocalizeDiscord("cmd.group.list.description")
|
||||
if err != nil {
|
||||
|
@ -420,7 +385,7 @@ func (c *groupCommand) buildSubcommandSet() (*discordgo.ApplicationCommandOption
|
|||
}
|
||||
|
||||
func (c *groupCommand) buildSubcommandSetLanguage() (*discordgo.ApplicationCommandOption, error) {
|
||||
languageChoices, err := c.getLanguageChoices()
|
||||
languageChoices, err := c.choiceSvc.languages()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -658,6 +623,10 @@ func (c *groupCommand) buildSubcommandDelete() (*discordgo.ApplicationCommandOpt
|
|||
}
|
||||
|
||||
func (c *groupCommand) handle(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if i.Type != discordgo.InteractionApplicationCommand {
|
||||
return
|
||||
}
|
||||
|
||||
cmdData := i.ApplicationCommandData()
|
||||
|
||||
if cmdData.Name != c.name() {
|
||||
|
@ -718,7 +687,13 @@ func (c *groupCommand) handleCreate(ctx context.Context, s *discordgo.Session, i
|
|||
},
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -742,6 +717,7 @@ func (c *groupCommand) optionsToCreateGroupParams(
|
|||
internals := false
|
||||
barbarians := false
|
||||
languageTag := ""
|
||||
|
||||
for _, opt := range options {
|
||||
switch opt.Name {
|
||||
case "version":
|
||||
|
@ -760,6 +736,7 @@ func (c *groupCommand) optionsToCreateGroupParams(
|
|||
barbarians = opt.BoolValue()
|
||||
}
|
||||
}
|
||||
|
||||
return domain.NewCreateGroupParams(
|
||||
guildID,
|
||||
version,
|
||||
|
@ -804,7 +781,13 @@ func (c *groupCommand) handleList(ctx context.Context, s *discordgo.Session, i *
|
|||
MessageID: langMessageID,
|
||||
})
|
||||
if localizeErr != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", langMessageID), zap.Error(localizeErr))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", langMessageID), zap.Error(localizeErr))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, localizeErr),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -821,7 +804,13 @@ func (c *groupCommand) handleList(ctx context.Context, s *discordgo.Session, i *
|
|||
},
|
||||
})
|
||||
if localizeErr != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", fieldValueMessageID), zap.Error(localizeErr))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", fieldValueMessageID), zap.Error(localizeErr))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, localizeErr),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -835,7 +824,13 @@ func (c *groupCommand) handleList(ctx context.Context, s *discordgo.Session, i *
|
|||
titleMessageID := "cmd.group.list.embed.title"
|
||||
title, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: titleMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", titleMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", titleMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -893,7 +888,13 @@ func (c *groupCommand) handleDetails(ctx context.Context, s *discordgo.Session,
|
|||
MessageID: langMessageID,
|
||||
})
|
||||
if localizeErr != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", langMessageID), zap.Error(localizeErr))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", langMessageID), zap.Error(localizeErr))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, localizeErr),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -911,7 +912,13 @@ func (c *groupCommand) handleDetails(ctx context.Context, s *discordgo.Session,
|
|||
},
|
||||
})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", descriptionMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", descriptionMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
|
@ -978,7 +985,13 @@ func (c *groupCommand) handleSetLanguage(ctx context.Context, s *discordgo.Sessi
|
|||
successMessageID := "cmd.group.set.language.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1021,7 +1034,13 @@ func (c *groupCommand) handleSetChannelGains(ctx context.Context, s *discordgo.S
|
|||
successMessageID := "cmd.group.set.channel-gains.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1064,7 +1083,13 @@ func (c *groupCommand) handleSetChannelLosses(ctx context.Context, s *discordgo.
|
|||
successMessageID := "cmd.group.set.channel-losses.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1107,7 +1132,13 @@ func (c *groupCommand) handleSetInternals(ctx context.Context, s *discordgo.Sess
|
|||
successMessageID := "cmd.group.set.internals.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1150,7 +1181,13 @@ func (c *groupCommand) handleSetBarbarians(ctx context.Context, s *discordgo.Ses
|
|||
successMessageID := "cmd.group.set.barbarians.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1205,7 +1242,13 @@ func (c *groupCommand) handleTribeAdd(ctx context.Context, s *discordgo.Session,
|
|||
successMessageID := "cmd.group.tribe.add.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1248,7 +1291,13 @@ func (c *groupCommand) handleTribeRemove(ctx context.Context, s *discordgo.Sessi
|
|||
successMessageID := "cmd.group.tribe.remove.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -1277,7 +1326,13 @@ func (c *groupCommand) handleDelete(ctx context.Context, s *discordgo.Session, i
|
|||
successMessageID := "cmd.group.delete.success"
|
||||
successMsg, err := c.localizer.Localize(locale, &i18n.LocalizeConfig{MessageID: successMessageID})
|
||||
if err != nil {
|
||||
c.logger.Error("no message with the specified identifier", zap.String("id", successMessageID), zap.Error(err))
|
||||
c.logger.Error("no message with the specified id", zap.String("id", successMessageID), zap.Error(err))
|
||||
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: c.localizer.LocalizeError(locale, err),
|
||||
},
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
"err.tribe-tag-not-found": "Tribe (tag={{ .Tag }}) not found.",
|
||||
"err.tribe-id-not-found": "Tribe (id={{ .ID }}) not found.",
|
||||
"err.tribe-does-not-exist": "The tribe (tag={{ .Tag }}) doesn't exist.",
|
||||
"err.coords-string-too-long": "The given string is too long ({{ len .Str }}/{{ .Max }}).",
|
||||
"err.no-coords-found-in-string": "No coords found in the given string.",
|
||||
|
||||
"job.execute-monitors.embed.description": "{{ buildPlayerMarkdown .Ennoblement.NewOwner }} has taken {{ buildLink .Ennoblement.Village.FullName .Ennoblement.Village.ProfileURL }} (Old owner: {{ buildPlayerMarkdown .Ennoblement.Village.Player }}).",
|
||||
|
||||
|
@ -77,5 +79,11 @@
|
|||
|
||||
"cmd.group.delete.description": "Deletes a group",
|
||||
"cmd.group.delete.option.group.description": "Group ID",
|
||||
"cmd.group.delete.success": "The group has been successfully deleted."
|
||||
"cmd.group.delete.success": "The group has been successfully deleted.",
|
||||
|
||||
"cmd.coords.description": "Translates village coords",
|
||||
"cmd.coords.option.version.description": "e.g. www.tribalwars.net, www.plemiona.pl",
|
||||
"cmd.coords.option.server.description": "Tribal Wars server (e.g. en115, pl170)",
|
||||
"cmd.coords.option.coords.description": "Coords",
|
||||
"cmd.coords.embed.title": "Villages"
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
"err.tribe-tag-not-found": "Plemię (skrót={{ .Tag }}) nie istnieje.",
|
||||
"err.tribe-id-not-found": "Plemię (id={{ .ID }}) nie istnieje.",
|
||||
"err.tribe-does-not-exist": "Plemię (skrót={{ .Tag }}) nie istnieje.",
|
||||
"err.coords-string-too-long": "Podany ciąg znaków jest zbyt długi ({{ len .Str }}/{{ .Max }}).",
|
||||
"err.no-coords-found-in-string": "Nie znaleziono współrzędnych w podanym ciągu znaków.",
|
||||
|
||||
"job.execute-monitors.embed.description": "{{ buildPlayerMarkdown .Ennoblement.NewOwner }} przejął {{ buildLink .Ennoblement.Village.FullName .Ennoblement.Village.ProfileURL }} (Poprzedni właściciel: {{ buildPlayerMarkdown .Ennoblement.Village.Player }}).",
|
||||
|
||||
|
@ -77,5 +79,11 @@
|
|||
|
||||
"cmd.group.delete.description": "Usuwa grupę",
|
||||
"cmd.group.delete.option.group.description": "ID grupy",
|
||||
"cmd.group.delete.success": "Grupa została pomyślnie usunięta."
|
||||
"cmd.group.delete.success": "Grupa została pomyślnie usunięta.",
|
||||
|
||||
"cmd.coords.description": "Tłumaczy kordy wiosek",
|
||||
"cmd.coords.option.version.description": "np. www.tribalwars.net, www.plemiona.pl",
|
||||
"cmd.coords.option.server.description": "Serwer (np. en115, pl170)",
|
||||
"cmd.coords.option.coords.description": "Kordy",
|
||||
"cmd.coords.embed.title": "Wioski"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
type CoordsStringTooLongError struct {
|
||||
Str string
|
||||
Max int
|
||||
}
|
||||
|
||||
var _ TranslatableError = CoordsStringTooLongError{}
|
||||
|
||||
func (e CoordsStringTooLongError) Error() string {
|
||||
return fmt.Sprintf("coords string is too long (%d/%d)", len(e.Str), e.Max)
|
||||
}
|
||||
|
||||
func (e CoordsStringTooLongError) Slug() string {
|
||||
return "coords-string-too-long"
|
||||
}
|
||||
|
||||
func (e CoordsStringTooLongError) Params() map[string]any {
|
||||
return map[string]any{
|
||||
"Str": e.Str,
|
||||
"Max": e.Max,
|
||||
}
|
||||
}
|
||||
|
||||
type NoCoordsFoundInStringError struct {
|
||||
Str string
|
||||
}
|
||||
|
||||
var _ TranslatableError = NoCoordsFoundInStringError{}
|
||||
|
||||
func (e NoCoordsFoundInStringError) Error() string {
|
||||
return fmt.Sprintf("no coords found in string: '%s'", e.Str)
|
||||
}
|
||||
|
||||
func (e NoCoordsFoundInStringError) Slug() string {
|
||||
return "no-coords-found-in-string"
|
||||
}
|
||||
|
||||
func (e NoCoordsFoundInStringError) Params() map[string]any {
|
||||
return map[string]any{
|
||||
"Str": e.Str,
|
||||
}
|
||||
}
|
||||
|
||||
type TranslateCoordsParams struct {
|
||||
versionCode string
|
||||
serverKey string
|
||||
coords []string
|
||||
perPage int32
|
||||
maxPage int32
|
||||
}
|
||||
|
||||
var coordsRegex = regexp.MustCompile(`(\d+)\|(\d+)`)
|
||||
|
||||
const CoordsStringMaxLength = 1000
|
||||
|
||||
func NewTranslateCoordsParams(versionCode, serverKey, coordsStr string, perPage int32) (TranslateCoordsParams, error) {
|
||||
if versionCode == "" {
|
||||
return TranslateCoordsParams{}, RequiredError{Field: "VersionCode"}
|
||||
}
|
||||
|
||||
if serverKey == "" {
|
||||
return TranslateCoordsParams{}, RequiredError{Field: "ServerKey"}
|
||||
}
|
||||
|
||||
if len(coordsStr) > CoordsStringMaxLength {
|
||||
return TranslateCoordsParams{}, CoordsStringTooLongError{
|
||||
Max: CoordsStringMaxLength,
|
||||
Str: coordsStr,
|
||||
}
|
||||
}
|
||||
|
||||
coords := uniq(coordsRegex.FindAllString(coordsStr, -1))
|
||||
if len(coords) == 0 {
|
||||
return TranslateCoordsParams{}, NoCoordsFoundInStringError{
|
||||
Str: coordsStr,
|
||||
}
|
||||
}
|
||||
|
||||
return TranslateCoordsParams{
|
||||
versionCode: versionCode,
|
||||
serverKey: serverKey,
|
||||
coords: coords,
|
||||
perPage: perPage,
|
||||
maxPage: int32(math.Ceil(float64(len(coords)) / float64(perPage))),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c TranslateCoordsParams) VersionCode() string {
|
||||
return c.versionCode
|
||||
}
|
||||
|
||||
func (c TranslateCoordsParams) ServerKey() string {
|
||||
return c.serverKey
|
||||
}
|
||||
|
||||
func (c TranslateCoordsParams) Coords() []string {
|
||||
return c.coords
|
||||
}
|
||||
|
||||
func (c TranslateCoordsParams) PerPage() int32 {
|
||||
return c.perPage
|
||||
}
|
||||
|
||||
func (c TranslateCoordsParams) MaxPage() int32 {
|
||||
return c.maxPage
|
||||
}
|
||||
|
||||
type TranslateCoordsResult struct {
|
||||
Villages []Village
|
||||
Untranslated []string
|
||||
Page int32
|
||||
MaxPage int32
|
||||
NextPageID PaginationPageID
|
||||
PrevPageID PaginationPageID
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewTranslateCoordsParams(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tooLongStr := ""
|
||||
for len(tooLongStr) <= 1000 {
|
||||
tooLongStr += "too long"
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
versionCode string
|
||||
serverKey string
|
||||
coordsStr string
|
||||
coords []string
|
||||
perPage int32
|
||||
maxPage int32
|
||||
err error
|
||||
}{
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "pl181",
|
||||
coordsStr: "test string 123|123 898|123",
|
||||
coords: []string{"123|123", "898|123"},
|
||||
perPage: 5,
|
||||
maxPage: 1,
|
||||
},
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "pl181",
|
||||
coordsStr: "123|123 898|123 988|123 123|158 785|567",
|
||||
coords: []string{"123|123", "898|123", "988|123", "123|158", "785|567"},
|
||||
perPage: 4,
|
||||
maxPage: 2,
|
||||
},
|
||||
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "pl181",
|
||||
coordsStr: "123|123 123|123 898|898 898|898",
|
||||
coords: []string{"123|123", "898|898"},
|
||||
perPage: 4,
|
||||
maxPage: 1,
|
||||
},
|
||||
{
|
||||
versionCode: "",
|
||||
serverKey: "pl181",
|
||||
coordsStr: "123|123",
|
||||
err: domain.RequiredError{
|
||||
Field: "VersionCode",
|
||||
},
|
||||
},
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "",
|
||||
coordsStr: "123|123",
|
||||
err: domain.RequiredError{
|
||||
Field: "ServerKey",
|
||||
},
|
||||
},
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "pl181",
|
||||
coordsStr: "no coords",
|
||||
err: domain.NoCoordsFoundInStringError{
|
||||
Str: "no coords",
|
||||
},
|
||||
},
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "pl181",
|
||||
coordsStr: "no coords",
|
||||
err: domain.NoCoordsFoundInStringError{
|
||||
Str: "no coords",
|
||||
},
|
||||
},
|
||||
{
|
||||
versionCode: "pl",
|
||||
serverKey: "pl181",
|
||||
coordsStr: tooLongStr,
|
||||
err: domain.CoordsStringTooLongError{
|
||||
Str: tooLongStr,
|
||||
Max: 1000,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
tt := tt
|
||||
|
||||
testName := fmt.Sprintf("versionCode=%s | serverKey=%s | coordsStr=%s", tt.versionCode, tt.serverKey, tt.coordsStr)
|
||||
if len(testName) > 100 {
|
||||
testName = testName[:97] + "..."
|
||||
}
|
||||
|
||||
t.Run(testName, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
res, err := domain.NewTranslateCoordsParams(tt.versionCode, tt.serverKey, tt.coordsStr, tt.perPage)
|
||||
if tt.err != nil {
|
||||
assert.ErrorIs(t, err, tt.err)
|
||||
assert.Zero(t, res)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tt.versionCode, res.VersionCode())
|
||||
assert.Equal(t, tt.serverKey, res.ServerKey())
|
||||
assert.Equal(t, tt.coords, res.Coords())
|
||||
assert.Equal(t, tt.perPage, res.PerPage())
|
||||
assert.Equal(t, tt.maxPage, res.MaxPage())
|
||||
})
|
||||
}
|
||||
}
|
|
@ -9,3 +9,19 @@ type NullBool struct {
|
|||
Bool bool
|
||||
Valid bool // Valid is true if Bool is not NULL
|
||||
}
|
||||
|
||||
func uniq[T comparable](sl []T) []T {
|
||||
res := make([]T, 0, len(sl))
|
||||
seen := make(map[T]struct{}, len(sl))
|
||||
|
||||
for _, it := range sl {
|
||||
if _, ok := seen[it]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
seen[it] = struct{}{}
|
||||
res = append(res, it)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
package domain_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewPaginationPageID(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
id := domain.NewPaginationPageID(15, []byte("data"))
|
||||
assert.NotEmpty(t, id.String())
|
||||
assert.False(t, id.IsZero())
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
package domain
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type PaginationPageID struct {
|
||||
s string
|
||||
}
|
||||
|
||||
func NewPaginationPageID(page int32, data []byte) PaginationPageID {
|
||||
hash := sha256.Sum256(fmt.Append(data, "page", page))
|
||||
return PaginationPageID{
|
||||
s: hex.EncodeToString(hash[:]),
|
||||
}
|
||||
}
|
||||
|
||||
func (p PaginationPageID) String() string {
|
||||
return p.s
|
||||
}
|
||||
|
||||
func (p PaginationPageID) IsZero() bool {
|
||||
return len(p.s) == 0
|
||||
}
|
|
@ -55,9 +55,15 @@ type Village struct {
|
|||
FullName string
|
||||
ProfileURL string
|
||||
Points int64
|
||||
X int64
|
||||
Y int64
|
||||
Player NullPlayerMeta
|
||||
}
|
||||
|
||||
func (v Village) Coords() string {
|
||||
return fmt.Sprintf("%d|%d", v.X, v.Y)
|
||||
}
|
||||
|
||||
type VillageMeta struct {
|
||||
ID int64
|
||||
FullName string
|
||||
|
|
|
@ -14,9 +14,39 @@ func NewCoords(twhelpSvc TWHelpService) *Coords {
|
|||
return &Coords{twhelpSvc: twhelpSvc}
|
||||
}
|
||||
|
||||
func (c *Coords) Translate(ctx context.Context, version, server string, coords ...string) ([]domain.Village, error) {
|
||||
if len(coords) == 0 {
|
||||
return nil, nil
|
||||
func (c *Coords) Translate(ctx context.Context, params domain.TranslateCoordsParams) (domain.TranslateCoordsResult, error) {
|
||||
perPage := params.PerPage()
|
||||
if coordsLen := int32(len(params.Coords())); coordsLen < perPage {
|
||||
perPage = coordsLen
|
||||
}
|
||||
return c.twhelpSvc.ListVillagesCoords(ctx, version, server, coords, 0, int32(len(coords)))
|
||||
|
||||
coords := params.Coords()[:perPage]
|
||||
|
||||
villages, err := c.twhelpSvc.ListVillagesByCoords(ctx, params.VersionCode(), params.ServerKey(), coords, 0, perPage)
|
||||
if err != nil {
|
||||
return domain.TranslateCoordsResult{}, err
|
||||
}
|
||||
|
||||
untranslated := make([]string, 0, perPage)
|
||||
for _, co := range coords {
|
||||
found := false
|
||||
|
||||
for _, v := range villages {
|
||||
if co == v.Coords() {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
untranslated = append(untranslated, co)
|
||||
}
|
||||
}
|
||||
|
||||
return domain.TranslateCoordsResult{
|
||||
Villages: villages,
|
||||
Page: 1,
|
||||
MaxPage: params.MaxPage(),
|
||||
Untranslated: untranslated,
|
||||
}, nil
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ func (g *Group) RemoveTribe(ctx context.Context, id, serverID, tribeTag string)
|
|||
return domain.GroupWithMonitors{}, err
|
||||
}
|
||||
|
||||
tribes, err := g.twhelpSvc.ListTribesTag(ctx, groupBeforeUpdate.VersionCode, groupBeforeUpdate.ServerKey, []string{tribeTag}, 0, listTribesLimit)
|
||||
tribes, err := g.twhelpSvc.ListTribesByTag(ctx, groupBeforeUpdate.VersionCode, groupBeforeUpdate.ServerKey, []string{tribeTag}, 0, listTribesLimit)
|
||||
if err != nil {
|
||||
return domain.GroupWithMonitors{}, fmt.Errorf("TWHelpClient.ListTribes: %w", err)
|
||||
}
|
||||
|
|
|
@ -266,7 +266,7 @@ func TestGroup_RemoveTribe(t *testing.T) {
|
|||
ID: group.Monitors[0].TribeID,
|
||||
Tag: "*TAG*",
|
||||
}
|
||||
twhelpSvc.ListTribesTagReturns([]domain.Tribe{tribe}, nil)
|
||||
twhelpSvc.ListTribesByTagReturns([]domain.Tribe{tribe}, nil)
|
||||
|
||||
g, err := service.
|
||||
NewGroup(repo, twhelpSvc, zap.NewNop(), 1, 1).
|
||||
|
|
|
@ -17,7 +17,7 @@ type TWHelpService interface {
|
|||
GetOpenServer(ctx context.Context, versionCode, serverKey string) (domain.TWServer, error)
|
||||
GetTribeByID(ctx context.Context, versionCode, serverKey string, id int64) (domain.Tribe, error)
|
||||
GetExistingTribeByTag(ctx context.Context, versionCode, serverKey, tribeTag string) (domain.Tribe, error)
|
||||
ListTribesTag(ctx context.Context, versionCode, serverKey string, tribeTags []string, offset, limit int32) ([]domain.Tribe, error)
|
||||
ListVillagesCoords(ctx context.Context, versionCode, serverKey string, coords []string, offset, limit int32) ([]domain.Village, error)
|
||||
ListTribesByTag(ctx context.Context, versionCode, serverKey string, tribeTags []string, offset, limit int32) ([]domain.Tribe, error)
|
||||
ListVillagesByCoords(ctx context.Context, versionCode, serverKey string, coords []string, offset, limit int32) ([]domain.Village, error)
|
||||
ListEnnoblementsSince(ctx context.Context, versionCode, serverKey string, since time.Time, offset, limit int32) ([]domain.Ennoblement, error)
|
||||
}
|
||||
|
|
|
@ -5,6 +5,11 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
type NullBool struct {
|
||||
Bool bool
|
||||
Valid bool // Valid is true if Bool is not NULL
|
||||
}
|
||||
|
||||
type ErrorCode string
|
||||
|
||||
const (
|
||||
|
@ -143,6 +148,20 @@ func (p *NullPlayerMeta) UnmarshalJSON(data []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type Village struct {
|
||||
ID int64 `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
ProfileURL string `json:"profileUrl"`
|
||||
Points int64 `json:"points"`
|
||||
X int64 `json:"x"`
|
||||
Y int64 `json:"y"`
|
||||
Player NullPlayerMeta `json:"player"`
|
||||
}
|
||||
|
||||
type listVillagesResp struct {
|
||||
Data []Village `json:"data"`
|
||||
}
|
||||
|
||||
type VillageMeta struct {
|
||||
ID int64 `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
|
@ -160,20 +179,3 @@ type Ennoblement struct {
|
|||
type listEnnoblementsResp struct {
|
||||
Data []Ennoblement `json:"data"`
|
||||
}
|
||||
|
||||
type NullBool struct {
|
||||
Bool bool
|
||||
Valid bool // Valid is true if Bool is not NULL
|
||||
}
|
||||
|
||||
type Village struct {
|
||||
ID int64 `json:"id"`
|
||||
FullName string `json:"fullName"`
|
||||
ProfileURL string `json:"profileUrl"`
|
||||
Points int64 `json:"points"`
|
||||
Player NullPlayerMeta `json:"player"`
|
||||
}
|
||||
|
||||
type listVillagesResp struct {
|
||||
Data []Village `json:"data"`
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue