From 3fdaf1c7f70516fab50ccf7d313b8445b9b50b7a Mon Sep 17 00:00:00 2001 From: Antony Ramos Date: Sat, 7 Oct 2023 23:08:58 +0200 Subject: [PATCH 1/2] feat: register command globally --- pkg/discord/discord.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/discord/discord.go b/pkg/discord/discord.go index f31d10c7..086fe556 100644 --- a/pkg/discord/discord.go +++ b/pkg/discord/discord.go @@ -81,7 +81,6 @@ func (d *Discord) Run(ctx context.Context) error { logger.FromContext(ctx).Debug("register commands to discord") registeredCommands := make([]*discordgo.ApplicationCommand, len(d.commands)) - pool := pond.New(100, 1000) group, _ := pool.GroupContext(ctx) @@ -90,7 +89,7 @@ func (d *Discord) Run(ctx context.Context) error { command := v group.Submit(func() error { logger.FromContext(ctx).Info("register command " + command.Name) - cmd, err := d.s.ApplicationCommandCreate(d.s.State.User.ID, strconv.Itoa(d.guildID), command) + cmd, err := d.s.ApplicationCommandCreate(d.s.State.User.ID, "", command) if err != nil { return errors.Wrap(err, "try to create command "+command.Name) } From 54764f97f6772690df24532d54f67ee53cb6207d Mon Sep 17 00:00:00 2001 From: Antony Ramos Date: Sat, 7 Oct 2023 23:09:09 +0200 Subject: [PATCH 2/2] feat(raid/create-range): new command to generate raids It generates raids over a range. See USAGE.md for more infos. --- docs/USAGE.md | 28 ++ go.sum | 30 +- internal/app/app.go | 3 +- internal/controller/discord/raid.go | 149 ++++++++- internal/controller/discord/raid_test.go | 371 +++++++++++++++++++++++ 5 files changed, 549 insertions(+), 32 deletions(-) diff --git a/docs/USAGE.md b/docs/USAGE.md index adfe0c4a..b9e59190 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -671,4 +671,32 @@ It creates or delete an absence for a player. ``` Error while creating absence: date cannot be in the past``` +### Create a range of raids +It creates a range of raids with the name, date and difficulty specified. It outputs the raid id. + +```shell +/guildops-raid-create-multiple from: 30/09/23 to: 30/10/23 difficulty: Mythic weekdays: Monday, Wendesday + +TODO +``` + +**Requirements:** +* Difficulty should be : Normal, Heroic, Mythic. +* Date must be in format : dd/mm/yy. +* Weekdays should be a list of weekdays separated by a comma. If there is uppercase, it will be converted to lowercase. +* to must be equal or after from. + +**Errors :** +* If weekdays is malformed + + ```week days must be one of: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday``` +* if to is before from + + ```error while creating multiple raids: endDate is before startDate``` +* if difficulty is incorrect + + ```difficulty must be one of: Normal, Heroic, Mythic``` +* if all raids already exists + + ```no raid created``` diff --git a/go.sum b/go.sum index 3fbc74e4..6b4115ff 100644 --- a/go.sum +++ b/go.sum @@ -37,7 +37,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB github.com/bwmarrin/discordgo v0.27.1 h1:ib9AIc/dom1E/fSIulrBwnez0CToJE113ZGt4HoliGY= github.com/bwmarrin/discordgo v0.27.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ= -github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= @@ -164,7 +163,6 @@ github.com/ilyakaznacheev/cleanenv v1.5.0 h1:0VNZXggJE2OYdXE87bfSSwGxeiGt9moSR2l github.com/ilyakaznacheev/cleanenv v1.5.0/go.mod h1:a5aDzaJrLCQZsazHol1w8InnDcOX0OColm64SlIi6gk= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= -github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8= @@ -190,7 +188,6 @@ github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5W github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A= github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= @@ -325,7 +322,6 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9 github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -337,8 +333,6 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.3.0/go.mod h1:hJaj2vgQTGQmVCsAACORcieXFeDPbaTKGT+JTgUa3og= -github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= -github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= @@ -346,10 +340,6 @@ github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1: github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.1.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 h1:v7DLqVdK4VrYkVD5diGdl4sxJurKJEMnODWRJlxV9oM= -github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= @@ -423,28 +413,16 @@ go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mI go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opentelemetry.io/otel v1.18.0 h1:TgVozPGZ01nHyDZxK5WGPFB9QexeTMXEH7+tIClWfzs= -go.opentelemetry.io/otel v1.18.0/go.mod h1:9lWqYO0Db579XzVuCKFNPDl4s73Voa+zEck3wHaAYQI= go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0 h1:IAtl+7gua134xcV3NieDhJHjjOVeJhXAnYf/0hswjUY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.18.0/go.mod h1:w+pXobnBzh95MNIkeIuAKcHe/Uu/CX2PKIvBP6ipKRA= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0 h1:6pu8ttx76BxHf+xz/H77AUZkPF3cwWzXqAUsXhVKI18= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.18.0/go.mod h1:IOmXxPrxoxFMXdNy7lfDmE8MzE61YPcurbUm0SMjerI= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.18.0 h1:JwVzw94UYmbx3ej++CwLUQZxEODDj/pOuTCvzhtRrSQ= -go.opentelemetry.io/otel/metric v1.18.0/go.mod h1:nNSpsVDjWGfb7chbRLUNW+PBNdcSTHD4Uu5pfFMOI0k= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= -go.opentelemetry.io/otel/sdk v1.18.0 h1:e3bAB0wB3MljH38sHzpV/qWrOTCFrdZF2ct9F8rBkcY= -go.opentelemetry.io/otel/sdk v1.18.0/go.mod h1:1RCygWV7plY2KmdskZEDDBs4tJeHG92MdHZIluiYs/M= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= -go.opentelemetry.io/otel/trace v1.18.0 h1:NY+czwbHbmndxojTEKiSMHkG2ClNH2PwmcHrdo0JY10= -go.opentelemetry.io/otel/trace v1.18.0/go.mod h1:T2+SGJGuYZY3bjj5rgh/hN7KIrlpWC5nS8Mjvzckz+0= go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= @@ -607,14 +585,10 @@ google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRn google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190530194941-fb225487d101/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb h1:XFBgcDwm7irdHTbz4Zk2h7Mh+eis4nfJEFQFYzJzuIA= -google.golang.org/genproto v0.0.0-20230913181813-007df8e322eb/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13 h1:U7+wNaVuSTaUqNvK2+osJ9ejEZxbjHHk8F2b6Hpx0AE= -google.golang.org/genproto/googleapis/api v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:RdyHbowztCGQySiCvQPgWQWgWhGnouTdCflKoDBt32U= +google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0= +google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU= google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97 h1:W18sezcAYs+3tDZX4F80yctqa12jcP1PUS2gQu1zTPU= google.golang.org/genproto/googleapis/api v0.0.0-20231002182017-d307bd883b97/go.mod h1:iargEX0SFPm3xcfMI0d1domjg0ZF4Aa0p2awqyxhvF0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13 h1:N3bU/SQDCDyD6R528GJ/PwW9KjYcJA3dgyH+MovAkIM= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:KSqppvjFjtoCI+KGd4PELB0qLNxdJHRGqRI09mB6pQA= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97 h1:6GQBEOdGkX6MMTLT9V+TjtIRZCw9VPD5Z+yHY9wMgS0= google.golang.org/genproto/googleapis/rpc v0.0.0-20231002182017-d307bd883b97/go.mod h1:v7nGkzlmW8P3n/bKmWBn2WpBjpOEx8Q6gMueudAmKfY= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= diff --git a/internal/app/app.go b/internal/app/app.go index 3a6bd68d..8130972d 100644 --- a/internal/app/app.go +++ b/internal/app/app.go @@ -79,7 +79,8 @@ func Run(ctx context.Context, cfg *config.Config) { &discordHandler.PlayerDescriptors[0], &discordHandler.PlayerDescriptors[1], &discordHandler.PlayerDescriptors[2], &discordHandler.PlayerDescriptors[3], &discordHandler.PlayerDescriptors[4]) handlers = append(handlers, - &discordHandler.RaidDescriptors[0], &discordHandler.RaidDescriptors[1], &discordHandler.RaidDescriptors[2]) + &discordHandler.RaidDescriptors[0], &discordHandler.RaidDescriptors[1], + &discordHandler.RaidDescriptors[2], &discordHandler.RaidDescriptors[3]) handlers = append(handlers, &discordHandler.StrikeDescriptors[0], &discordHandler.StrikeDescriptors[1], &discordHandler.StrikeDescriptors[2]) handlers = append(handlers, diff --git a/internal/controller/discord/raid.go b/internal/controller/discord/raid.go index 30c83e3b..b44ca440 100644 --- a/internal/controller/discord/raid.go +++ b/internal/controller/discord/raid.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "strconv" + "strings" "sync" "time" @@ -19,9 +20,10 @@ import ( func (d Discord) InitRaid() map[string]func( ctx context.Context, interaction *discordgo.InteractionCreate) (string, error) { return map[string]func(ctx context.Context, interaction *discordgo.InteractionCreate) (string, error){ - "guildops-raid-create": d.CreateRaidHandler, - "guildops-raid-delete": d.DeleteRaidHandler, - "guildops-raid-list": d.ListRaidHandler, + "guildops-raid-create": d.CreateRaidHandler, + "guildops-raid-delete": d.DeleteRaidHandler, + "guildops-raid-list": d.ListRaidHandler, + "guildops-raid-create-multiple": d.GenerateRaidsOnRangeHandler, } } @@ -80,6 +82,36 @@ var RaidDescriptors = []discordgo.ApplicationCommand{ }, }, }, + { + Name: "guildops-raid-create-multiple", + Description: "Create multiple raids on a date range", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Description: "ex: 02/10/23", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "to", + Description: "ex: 02/10/23", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Description: "Must be one of: Normal, Heroic, Mythic", + Required: true, + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Description: "Must be one of: Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday", + Required: true, + }, + }, + }, } // CreateRaidHandler call an usecase to create a raid @@ -239,3 +271,114 @@ func (d Discord) ListRaidHandler( return msg, nil } } + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + +func (d Discord) GenerateRaidsOnRangeHandler( + ctx context.Context, interaction *discordgo.InteractionCreate, +) (string, error) { + ctx, cancel := context.WithTimeout(ctx, 2*time.Second) + defer cancel() + + ctx, span := otel.Tracer("Discord").Start(ctx, "Raid/GenerateRaidsOnRangeHandler") + defer span.End() + span.SetAttributes( + attribute.String("request_from", interaction.Member.User.Username), + ) + + select { + case <-ctx.Done(): + msg := "error while creating multiple raids: " + HumanReadableError(ctx.Err()) + return msg, fmt.Errorf("create multiple raids wait goroutines: %w", ctx.Err()) + default: + + options := interaction.ApplicationCommandData().Options + optionMap := make(map[string]*discordgo.ApplicationCommandInteractionDataOption, len(options)) + + for _, opt := range options { + optionMap[opt.Name] = opt + } + + from := optionMap["from"].StringValue() + toDate := "" + if len(optionMap) > 3 { + toDate = optionMap["to"].StringValue() + } + + dates, err := ParseDate(from, toDate) + if err != nil { + msg := "error while creating multiple raids: " + HumanReadableError(err) + return msg, fmt.Errorf("create multiple raids parse date: %w", err) + } + + difficulty := optionMap["difficulty"].StringValue() + difficulty = strings.ToLower(difficulty) + if difficulty != "normal" && difficulty != "heroic" && difficulty != "mythic" { + return "difficulty must be one of: Normal, Heroic, Mythic", + fmt.Errorf("create multiple raids parse difficulty: %w", err) + } + + onWeekDays := optionMap["weekdays"].StringValue() + onWeekDays = strings.ReplaceAll(onWeekDays, " ", "") + onWeekDays = strings.ToLower(onWeekDays) + weekDays := strings.Split(onWeekDays, ",") + for _, weekDay := range weekDays { + weekDay = strings.ToLower(weekDay) + if weekDay != "monday" && weekDay != "tuesday" && + weekDay != "wednesday" && weekDay != "thursday" && + weekDay != "friday" && weekDay != "saturday" && + weekDay != "sunday" { + return "week days must be one of: Monday, Tuesday, Wednesday, " + + "Thursday, Friday, Saturday, Sunday", fmt.Errorf("create multiple raids parse week days: %w", err) + } + } + + raidsDays := make([]time.Time, 0) + for index, date := range dates { + weekDay := date.Weekday().String() + weekDay = strings.ToLower(weekDay) + if contains(weekDays, weekDay) { + raidsDays = append(raidsDays, dates[index]) + } + } + + raidsLock := &sync.Mutex{} + var raids []entity.Raid + pool := pond.New(len(dates), 5, pond.Context(ctx)) + + for _, date := range raidsDays { + date := date + pool.Submit(func() { + raid, err := d.CreateRaid(ctx, "Raid", difficulty, date) + if err == nil { + raidsLock.Lock() + raids = append(raids, raid) + raidsLock.Unlock() + } + }) + } + pool.StopAndWait() + + if len(raids) == 0 || raids == nil { + msg := "no raid created" + return msg, nil + } + + msg := "Raid List:\n" + for _, raid := range raids { + msg += "* " + raid.Name + " " + + raid.Date.Format("Mon 02/01/06") + " " + + raid.Difficulty + " " + + strconv.Itoa(raid.ID) + "\n" + } + return msg, nil + } +} diff --git a/internal/controller/discord/raid_test.go b/internal/controller/discord/raid_test.go index 6e654bbc..be4dd8f4 100644 --- a/internal/controller/discord/raid_test.go +++ b/internal/controller/discord/raid_test.go @@ -179,3 +179,374 @@ func TestDiscord_ListRaidHandler(t *testing.T) { mockRaidUseCase.AssertExpectations(t) }) } + +//nolint:maintidx +func TestDiscord_GenerateRaidsOnRangeHandler(t *testing.T) { + t.Parallel() + + t.Run("Context is done", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + ctx, cancel := context.WithCancel(context.Background()) + cancel() + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "to", + Value: "05/10/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "Heroic", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekday", + Value: "Monday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(ctx, interaction) + assert.EqualError(t, err, "create multiple raids wait goroutines: context canceled") + assert.Equal(t, msg, "error while creating multiple raids: context canceled") + mockRaidUseCase.AssertExpectations(t) + }) + + t.Run("Create a range of raids", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + mockRaidUseCase.On("CreateRaid", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(entity.Raid{}, nil).Times(8) + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "to", + Value: "05/10/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "Heroic", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Value: "Monday, Tuesday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(context.Background(), interaction) + assert.NoError(t, err) + assert.Equal(t, msg, "Raid List:\n* Mon 01/01/01 0\n* Mon 01/01/01 0\n* Mon 01/01/01"+ + " 0\n* Mon 01/01/01 0\n* Mon 01/01/01 0\n* "+ + "Mon 01/01/01 0\n* Mon 01/01/01 0\n* Mon 01/01/01 0\n") + mockRaidUseCase.AssertExpectations(t) + }) + + t.Run("Create a single raid", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + mockRaidUseCase.On("CreateRaid", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(entity.Raid{}, nil).Times(1) + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "Heroic", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Value: "Thursday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(context.Background(), interaction) + assert.NoError(t, err) + assert.Equal(t, msg, "Raid List:\n* Mon 01/01/01 0\n") + mockRaidUseCase.AssertExpectations(t) + }) + + t.Run("Create a no raid", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + mockRaidUseCase.On("CreateRaid", mock.Anything, mock.Anything, mock.Anything, mock.Anything). + Return(entity.Raid{}, errors.New("already exists")) + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "Heroic", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Value: "Thursday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(context.Background(), interaction) + assert.NoError(t, err) + assert.Equal(t, msg, "no raid created") + mockRaidUseCase.AssertExpectations(t) + }) + + t.Run("check params: to is before from", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/10/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "to", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "Heroic", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Value: "Monday, Tuesday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(context.Background(), interaction) + assert.Error(t, err) + assert.Equal(t, msg, "error while creating multiple raids: endDate is before startDate") + mockRaidUseCase.AssertExpectations(t) + }) + + t.Run("check params: difficulty is incorrect", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "IncorrectDifficulty", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Value: "Thursday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(context.Background(), interaction) + assert.Error(t, err) + assert.Equal(t, msg, "difficulty must be one of: Normal, Heroic, Mythic") + mockRaidUseCase.AssertExpectations(t) + }) + + t.Run("check params: weekday is incorrect", func(t *testing.T) { + t.Parallel() + mockRaidUseCase := mocks.NewRaidUseCase(t) + + discord := discordHandler.Discord{ + RaidUseCase: mockRaidUseCase, + } + + interaction := &discordgo.InteractionCreate{ + Interaction: &discordgo.Interaction{ + Type: discordgo.InteractionApplicationCommand, + Member: &discordgo.Member{ + User: &discordgo.User{ + Username: "test", + }, + }, + Data: discordgo.ApplicationCommandInteractionData{ + ID: "mock", + Name: "mock", + TargetID: "mock", + Resolved: &discordgo.ApplicationCommandInteractionDataResolved{}, + Options: []*discordgo.ApplicationCommandInteractionDataOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "from", + Value: "05/09/30", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "difficulty", + Value: "Heroic", + }, + { + Type: discordgo.ApplicationCommandOptionString, + Name: "weekdays", + Value: "IncorrectWeekday", + }, + }, + }, + }, + } + + msg, err := discord.GenerateRaidsOnRangeHandler(context.Background(), interaction) + assert.Error(t, err) + assert.Equal(t, msg, "week days must be one of: Monday, Tuesday, "+ + "Wednesday, Thursday, Friday, Saturday, Sunday") + mockRaidUseCase.AssertExpectations(t) + }) +}