feat: i18n (#108)
continuous-integration/drone/push Build is passing Details

Reviewed-on: #108
This commit is contained in:
Dawid Wysokiński 2023-06-25 06:31:12 +00:00
parent 5de9348f67
commit 09ffc7644a
12 changed files with 946 additions and 461 deletions

2
go.mod
View File

@ -8,6 +8,7 @@ require (
github.com/google/uuid v1.3.0
github.com/jackc/pgerrcode v0.0.0-20220416144525-469b46aa5efa
github.com/kelseyhightower/envconfig v1.4.0
github.com/nicksnyder/go-i18n/v2 v2.2.1
github.com/ory/dockertest/v3 v3.10.0
github.com/pressly/goose/v3 v3.11.2
github.com/robfig/cron/v3 v3.0.1
@ -18,6 +19,7 @@ require (
github.com/uptrace/bun/driver/pgdriver v1.1.14
github.com/urfave/cli/v2 v2.25.5
go.uber.org/zap v1.24.0
golang.org/x/text v0.9.0
golang.org/x/time v0.3.0
)

19
go.sum
View File

@ -1,5 +1,7 @@
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
@ -55,6 +57,8 @@ github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyua
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA=
github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
@ -110,6 +114,7 @@ github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRT
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
@ -121,10 +126,12 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -132,27 +139,38 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.8.0 h1:vSDcovVPld282ceKgDimkRSC8kpaH1dgyc9UMzlt84Y=
golang.org/x/tools v0.8.0/go.mod h1:JxBZ99ISMI5ViVkT1tr6tdNmXeTrcpVSD3vZ1RsRdN4=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@ -161,6 +179,7 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -5,16 +5,18 @@ import (
"fmt"
"time"
"gitea.dwysokinski.me/twhelp/dcbot/internal/discord/internal/discordi18n"
"gitea.dwysokinski.me/twhelp/dcbot/internal/domain"
"github.com/bwmarrin/discordgo"
"github.com/robfig/cron/v3"
"go.uber.org/zap"
"golang.org/x/text/language"
)
type GroupService interface {
Create(ctx context.Context, params domain.CreateGroupParams) (domain.GroupWithMonitors, error)
AddMonitor(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error)
DeleteMonitor(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error)
AddTribe(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error)
RemoveTribe(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error)
SetChannelGains(ctx context.Context, id, serverID, channel string) (domain.GroupWithMonitors, error)
SetChannelLosses(ctx context.Context, id, serverID, channel string) (domain.GroupWithMonitors, error)
SetInternals(ctx context.Context, id, serverID string, internals bool) (domain.GroupWithMonitors, error)
@ -31,11 +33,12 @@ type ChoiceService interface {
}
type Bot struct {
s *discordgo.Session
c *cron.Cron
session *discordgo.Session
cron *cron.Cron
groupSvc GroupService
choiceSvc ChoiceService
logger *zap.Logger
localizer *discordi18n.Localizer
}
func NewBot(
@ -44,16 +47,20 @@ func NewBot(
client ChoiceService,
logger *zap.Logger,
) (*Bot, error) {
localizer, err := discordi18n.NewLocalizer(language.English)
if err != nil {
return nil, err
}
s, err := discordgo.New("Bot " + token)
if err != nil {
return nil, fmt.Errorf("discordgo.New: %w", err)
}
s.Identify.Intents = discordgo.IntentsNone
b := &Bot{
s: s,
c: cron.New(
session: s,
cron: cron.New(
cron.WithLocation(time.UTC),
cron.WithChain(
cron.SkipIfStillRunning(cron.DiscardLogger),
@ -62,29 +69,30 @@ func NewBot(
groupSvc: groupSvc,
choiceSvc: client,
logger: logger,
localizer: localizer,
}
b.s.AddHandler(b.handleSessionReady)
b.s.AddHandler(b.logCommands)
b.session.AddHandler(b.handleSessionReady)
b.session.AddHandler(b.logCommands)
return b, nil
}
func (b *Bot) Run() error {
if err := b.initCron(); err != nil {
if err := b.registerCronJobs(); err != nil {
return fmt.Errorf("initCron: %w", err)
}
if err := b.s.Open(); err != nil {
if err := b.session.Open(); err != nil {
return fmt.Errorf("s.Open: %w", err)
}
if err := b.registerCommands(); err != nil {
_ = b.s.Close()
_ = b.session.Close()
return fmt.Errorf("couldn't register commands: %w", err)
}
b.c.Run()
b.cron.Run()
return nil
}
@ -96,33 +104,26 @@ type command interface {
func (b *Bot) registerCommands() error {
commands := []command{
&groupCommand{groupSvc: b.groupSvc, choiceSvc: b.choiceSvc},
&groupCommand{localizer: b.localizer, logger: b.logger, groupSvc: b.groupSvc, choiceSvc: b.choiceSvc},
}
for _, c := range commands {
if err := b.registerCommand(c); err != nil {
return err
for _, cmd := range commands {
if err := cmd.register(b.session); err != nil {
return fmt.Errorf("couldn't register command '%s': %w", cmd.name(), err)
}
}
return nil
}
func (b *Bot) registerCommand(cmd command) error {
if err := cmd.register(b.s); err != nil {
return fmt.Errorf("couldn't register command '%s': %w", cmd.name(), err)
}
return nil
}
func (b *Bot) initCron() error {
func (b *Bot) registerCronJobs() error {
jobs := []struct {
spec string
job cron.Job
}{
{
spec: "@every 1m",
job: &executeMonitorsJob{svc: b.groupSvc, s: b.s, logger: b.logger},
job: &executeMonitorsJob{svc: b.groupSvc, s: b.session, logger: b.logger},
},
{
spec: "0 */8 * * *",
@ -131,8 +132,7 @@ func (b *Bot) initCron() error {
}
for _, j := range jobs {
_, err := b.c.AddJob(j.spec, j.job)
if err != nil {
if _, err := b.cron.AddJob(j.spec, j.job); err != nil {
return err
}
}
@ -175,6 +175,6 @@ func (b *Bot) logCommands(_ *discordgo.Session, i *discordgo.InteractionCreate)
}
func (b *Bot) Close() error {
<-b.c.Stop().Done()
return b.s.Close()
<-b.cron.Stop().Done()
return b.session.Close()
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
package discordi18n
import (
"embed"
"encoding/json"
"io/fs"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
//go:embed locale.*.json
var localeFS embed.FS
func newBundle(defaultLanguage language.Tag) (*i18n.Bundle, error) {
b := i18n.NewBundle(defaultLanguage)
b.RegisterUnmarshalFunc("json", json.Unmarshal)
err := fs.WalkDir(localeFS, ".", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
_, err = b.LoadMessageFileFS(localeFS, path)
return err
})
if err != nil {
return nil, err
}
return b, nil
}

View File

@ -0,0 +1,57 @@
{
"cmd.group.description": "Manages groups on this server",
"cmd.group.create.description": "Creates a new monitor group",
"cmd.group.create.option.version.description": "e.g. www.tribalwars.net, www.plemiona.pl",
"cmd.group.create.option.server.description": "Tribal Wars server (e.g. en115, pl170)",
"cmd.group.create.option.internals.description": "Show conquers in the same group",
"cmd.group.create.option.barbarians.description": "Show barbarian conquers",
"cmd.group.create.option.channel-gains.description": "Channel where notifications of gained villages will appear",
"cmd.group.create.option.channel-losses.description": "Channel where notifications of lost villages will appear",
"cmd.group.create.success": "The group has been successfully created (id={{ .GroupID }}).",
"cmd.group.list.description": "Lists all created groups on this server",
"cmd.group.list.embed.title": "Group list",
"cmd.group.list.embed.field.value": "**Server**: {{ .ServerKey }}\n**Channel gains**: {{ .ChannelGains }}\n**Channel losses**: {{ .ChannelLosses }}\n**Internals**: {{ .Internals }}\n **Barbarians**: {{ .Barbarians }}\n **Number of monitored tribes**: {{ .NumTribes }}",
"cmd.group.details.description": "Displays group details (including added tribes)",
"cmd.group.details.embed.description": "**Server**: {{ .ServerKey }}\n**Channel gains**: {{ .ChannelGains }}\n**Channel losses**: {{ .ChannelLosses }}\n**Internals**: {{ .Internals }}\n **Barbarians**: {{ .Barbarians }}\n **Tribes**: {{ .Tribes }}",
"cmd.group.tribe.description": "Manages tribes in a group",
"cmd.group.tribe.add.description": "Adds a tribe to a group",
"cmd.group.tribe.add.option.group.description": "Group ID",
"cmd.group.tribe.add.option.tag.description": "Tribe tag",
"cmd.group.tribe.add.success": "The tribe has been successfully added to the group.",
"cmd.group.tribe.remove.description": "Removes a tribe from a group",
"cmd.group.tribe.remove.option.group.description": "Group ID",
"cmd.group.tribe.remove.option.tag.description": "Tribe tag",
"cmd.group.tribe.remove.success": "The tribe has been successfully removed from the group.",
"cmd.group.set.description": "Sets various properties in group configuration",
"cmd.group.set.channel-gains.description": "Enables/disables notifications of gained villages",
"cmd.group.set.channel-gains.option.group.description": "Group ID",
"cmd.group.set.channel-gains.option.channel.description": "Channel where notifications of gained villages will appear",
"cmd.group.set.channel-gains.success": "The group has been successfully updated.",
"cmd.group.set.channel-losses.description": "Enables/disables notifications of lost villages",
"cmd.group.set.channel-losses.option.group.description": "Group ID",
"cmd.group.set.channel-losses.option.channel.description": "Channel where notifications of lost villages will appear",
"cmd.group.set.channel-losses.success": "The group has been successfully updated.",
"cmd.group.set.internals.description": "Enables/disables notifications of internal conquers",
"cmd.group.set.internals.option.group.description": "Group ID",
"cmd.group.set.internals.option.internals.description": "Show conquers in the same group",
"cmd.group.set.internals.success": "The group has been successfully updated.",
"cmd.group.set.barbarians.description": "Enables/disables notifications of barbarian conquers",
"cmd.group.set.barbarians.option.group.description": "Group ID",
"cmd.group.set.barbarians.option.barbarians.description": "Show barbarian conquers",
"cmd.group.set.barbarians.success": "The group has been successfully updated.",
"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."
}

View File

@ -0,0 +1,57 @@
{
"cmd.group.description": "Umożliwia zarządzanie grupami na tym serwerze",
"cmd.group.create.description": "Tworzy nową grupę",
"cmd.group.create.option.version.description": "np. www.tribalwars.net, www.plemiona.pl",
"cmd.group.create.option.server.description": "Serwer (np. en115, pl170)",
"cmd.group.create.option.internals.description": "Informuj o przejęciach wewnętrznych",
"cmd.group.create.option.barbarians.description": "Informuj o przejęciach wiosek barbarzyńskich",
"cmd.group.create.option.channel-gains.description": "Kanał na którym będą pojawiać się powiadomienia o zdobytych wioskach",
"cmd.group.create.option.channel-losses.description": "Kanał na którym będą pojawiać się powiadomienia o straconych wioskach",
"cmd.group.create.success": "Grupa została pomyślnie utworzona (id={{ .GroupID }})",
"cmd.group.list.description": "Wyświetla wszystkie utworzone grupy na tym serwerze",
"cmd.group.list.embed.title": "Grupy",
"cmd.group.list.embed.field.value": "**Serwer**: {{ .ServerKey }}\n**Kanał zdobyte wioski**: {{ .ChannelGains }}\n**Kanał stracone wioski**: {{ .ChannelLosses }}\n**Przejęcia wewnętrzne**: {{ .Internals }}\n **Przejęcia barbarek**: {{ .Barbarians }}\n **Liczba monitorowanych plemion**: {{ .NumTribes }}",
"cmd.group.details.description": "Wyświetla szczegóły grupy (w tym dodane plemiona)",
"cmd.group.details.embed.description": "**Serwer**: {{ .ServerKey }}\n**Kanał zdobyte wioski**: {{ .ChannelGains }}\n**Kanał stracone wioski**: {{ .ChannelLosses }}\n**Przejęcia wewnętrzne**: {{ .Internals }}\n **Przejęcia barbarek**: {{ .Barbarians }}\n **Plemiona**: {{ .Tribes }}",
"cmd.group.tribe.description": "Zarządza plemionami w grupie",
"cmd.group.tribe.add.description": "Dodaje plemię do grupy",
"cmd.group.tribe.add.option.group.description": "ID grupy",
"cmd.group.tribe.add.option.tag.description": "Skrót plemienia",
"cmd.group.tribe.add.success": "Plemię zostało dodane do grupy.",
"cmd.group.tribe.remove.description": "Usuwa plemię z grupy",
"cmd.group.tribe.remove.option.group.description": "ID grupy",
"cmd.group.tribe.remove.option.tag.description": "Skrót plemienia",
"cmd.group.tribe.remove.success": "Plemię zostało usunięte z grupy.",
"cmd.group.set.description": "Ustawia różne właściwości w konfiguracji grupy",
"cmd.group.set.channel-gains.description": "Włącza/wyłącza powiadomienia o podbitych wioskach",
"cmd.group.set.channel-gains.option.group.description": "ID grupy",
"cmd.group.set.channel-gains.option.channel.description": "Kanał na którym będą pojawiać się powiadomienia o zdobytych wioskach",
"cmd.group.set.channel-gains.success": "Grupa została pomyślnie zaaktualizowana.",
"cmd.group.set.channel-losses.description": "Włącza/wyłącza powiadomienia o straconych wioskach",
"cmd.group.set.channel-losses.option.group.description": "ID grupy",
"cmd.group.set.channel-losses.option.channel.description": "Kanał na którym będą pojawiać się powiadomienia o straconych wioskach",
"cmd.group.set.channel-losses.success": "Grupa została pomyślnie zaaktualizowana.",
"cmd.group.set.internals.description": "Włącza/wyłącza powiadomienia o wewnętrznych przejęciach",
"cmd.group.set.internals.option.group.description": "ID grupy",
"cmd.group.set.internals.option.internals.description": "Informuj o przejęciach wewnętrznych",
"cmd.group.set.internals.success": "Grupa została pomyślnie zaaktualizowana.",
"cmd.group.set.barbarians.description": "Włącza/wyłącza powiadomienia o przejęciach wiosek barbarzyńskich",
"cmd.group.set.barbarians.option.group.description": "ID grupy",
"cmd.group.set.barbarians.option.barbarians.description": "Informuj o przejęciach wiosek barbarzyńskich",
"cmd.group.set.barbarians.success": "Grupa została pomyślnie zaaktualizowana.",
"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."
}

View File

@ -0,0 +1,68 @@
package discordi18n
import (
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/nicksnyder/go-i18n/v2/i18n"
"golang.org/x/text/language"
)
type Localizer struct {
bundle *i18n.Bundle
defaultLanguage language.Tag
}
func NewLocalizer(defaultLanguage language.Tag) (*Localizer, error) {
b, err := newBundle(defaultLanguage)
if err != nil {
return nil, err
}
return &Localizer{
bundle: b,
}, nil
}
func (l *Localizer) Localize(lang string, lc *i18n.LocalizeConfig) (string, error) {
return i18n.NewLocalizer(l.bundle, lang, l.defaultLanguage.String()).Localize(lc)
}
func (l *Localizer) LocalizeWithTag(lang string, lc *i18n.LocalizeConfig) (string, language.Tag, error) {
return i18n.NewLocalizer(l.bundle, lang, l.defaultLanguage.String()).LocalizeWithTag(lc)
}
func (l *Localizer) LocalizeDiscord(messageID string) (string, map[discordgo.Locale]string, error) {
return l.LocalizeDiscordWithConfig(&i18n.LocalizeConfig{
MessageID: messageID,
})
}
func (l *Localizer) LocalizeDiscordWithConfig(lc *i18n.LocalizeConfig) (string, map[discordgo.Locale]string, error) {
m := make(map[discordgo.Locale]string)
for _, tag := range l.bundle.LanguageTags() {
locale := discordgo.Locale(tag.String())
if discordgo.Locales[locale] == "" {
continue
}
msg, msgLang, err := l.LocalizeWithTag(tag.String(), lc)
if err != nil {
return "", nil, fmt.Errorf("couldn't lookup message in locale '%s': %w", locale, err)
}
if msgLang == l.defaultLanguage {
continue
}
m[locale] = msg
}
defaultMsg, err := l.Localize(l.defaultLanguage.String(), lc)
if err != nil {
return "", nil, fmt.Errorf("couldn't lookup message in default language: %w", err)
}
return defaultMsg, m, nil
}

View File

@ -45,19 +45,3 @@ func (e ValidationError) Code() ErrorCode {
func (e ValidationError) Unwrap() error {
return e.Err
}
type MinError struct {
Min int
}
func (e MinError) Error() string {
return fmt.Sprintf("must be no less than %d", e.Min)
}
func (e MinError) UserError() string {
return e.Error()
}
func (e MinError) Code() ErrorCode {
return ErrorCodeValidationError
}

View File

@ -1,6 +1,7 @@
package domain_test
import (
"errors"
"fmt"
"testing"
@ -13,9 +14,7 @@ func TestValidationError(t *testing.T) {
err := domain.ValidationError{
Field: "test",
Err: domain.MinError{
Min: 25,
},
Err: errors.New("err"),
}
var _ domain.Error = err
assert.Equal(t, fmt.Sprintf("%s: %s", err.Field, err.Err.Error()), err.Error())
@ -23,15 +22,3 @@ func TestValidationError(t *testing.T) {
assert.ErrorIs(t, err, err.Err)
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}
func TestMinError(t *testing.T) {
t.Parallel()
err := domain.MinError{
Min: 25,
}
var _ domain.Error = err
assert.Equal(t, "must be no less than 25", err.Error())
assert.Equal(t, err.Error(), err.UserError())
assert.Equal(t, domain.ErrorCodeValidationError, err.Code())
}

View File

@ -94,7 +94,7 @@ func (g *Group) checkTWServer(ctx context.Context, versionCode, serverKey string
return nil
}
func (g *Group) AddMonitor(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error) {
func (g *Group) AddTribe(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error) {
// check if group exists
groupBeforeUpdate, err := g.Get(ctx, id, serverID)
if err != nil {
@ -137,7 +137,7 @@ func (g *Group) AddMonitor(ctx context.Context, id, serverID, tribeTag string) (
return group, nil
}
func (g *Group) DeleteMonitor(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error) {
func (g *Group) RemoveTribe(ctx context.Context, id, serverID, tribeTag string) (domain.GroupWithMonitors, error) {
// check if group exists
groupBeforeUpdate, err := g.Get(ctx, id, serverID)
if err != nil {

View File

@ -143,7 +143,7 @@ func TestGroup_Create(t *testing.T) {
})
}
func TestGroup_AddMonitor(t *testing.T) {
func TestGroup_AddTribe(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
@ -178,7 +178,7 @@ func TestGroup_AddMonitor(t *testing.T) {
g, err := service.
NewGroup(repo, client, zap.NewNop(), 1, 1).
AddMonitor(context.Background(), group.ID, group.ServerID, tribe.Tag)
AddTribe(context.Background(), group.ID, group.ServerID, tribe.Tag)
assert.NoError(t, err)
require.Len(t, g.Monitors, 1)
assert.NotZero(t, g.Monitors[0].ID)
@ -227,7 +227,7 @@ func TestGroup_AddMonitor(t *testing.T) {
g, err := service.
NewGroup(repo, nil, zap.NewNop(), 1, 1).
AddMonitor(context.Background(), group.ID, uuid.NewString(), "*TAG*")
AddTribe(context.Background(), group.ID, uuid.NewString(), "*TAG*")
assert.ErrorIs(t, err, domain.GroupDoesNotExistError{
ID: group.ID,
})
@ -253,7 +253,7 @@ func TestGroup_AddMonitor(t *testing.T) {
g, err := service.
NewGroup(repo, nil, zap.NewNop(), 1, maxMonitorsPerGroup).
AddMonitor(context.Background(), group.ID, group.ServerID, "*TAG*")
AddTribe(context.Background(), group.ID, group.ServerID, "*TAG*")
assert.ErrorIs(t, err, domain.MonitorLimitReachedError{
Current: len(group.Monitors),
Limit: maxMonitorsPerGroup,
@ -279,7 +279,7 @@ func TestGroup_AddMonitor(t *testing.T) {
tribeTag := "*TAG*"
g, err := service.
NewGroup(repo, client, zap.NewNop(), 1, 1).
AddMonitor(context.Background(), group.ID, group.ServerID, tribeTag)
AddTribe(context.Background(), group.ID, group.ServerID, tribeTag)
assert.ErrorIs(t, err, domain.TribeDoesNotExistError{
Tag: tribeTag,
})
@ -287,7 +287,7 @@ func TestGroup_AddMonitor(t *testing.T) {
})
}
func TestGroup_DeleteMonitor(t *testing.T) {
func TestGroup_RemoveTribe(t *testing.T) {
t.Parallel()
t.Run("OK", func(t *testing.T) {
@ -341,7 +341,7 @@ func TestGroup_DeleteMonitor(t *testing.T) {
g, err := service.
NewGroup(repo, client, zap.NewNop(), 1, 1).
DeleteMonitor(context.Background(), group.ID, group.ServerID, tribe.Tag)
RemoveTribe(context.Background(), group.ID, group.ServerID, tribe.Tag)
assert.NoError(t, err)
assert.Len(t, g.Monitors, len(group.Monitors)-1)
assert.NotContains(t, g.Monitors, group.Monitors[0])
@ -393,7 +393,7 @@ func TestGroup_DeleteMonitor(t *testing.T) {
g, err := service.
NewGroup(repo, nil, zap.NewNop(), 1, 1).
DeleteMonitor(context.Background(), group.ID, tt.serverID, group.Monitors[0].ID)
RemoveTribe(context.Background(), group.ID, tt.serverID, group.Monitors[0].ID)
assert.ErrorIs(t, err, domain.GroupNotFoundError{
ID: group.ID,
})
@ -449,7 +449,7 @@ func TestGroup_DeleteMonitor(t *testing.T) {
tag := "*X*"
g, err := service.
NewGroup(repo, client, zap.NewNop(), 1, 1).
DeleteMonitor(context.Background(), group.ID, group.ServerID, tag)
RemoveTribe(context.Background(), group.ID, group.ServerID, tag)
assert.ErrorIs(t, err, domain.TribeNotFoundError{
Tag: tag,
})