diff --git a/Makefile b/Makefile index b792ad9..e0b5bef 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ MOCKERY_VERSION=github.com/vektra/mockery/v2@v2.50 -GOLANGCI_LINT_IMAGE=golangci/golangci-lint:v1.62-alpine +GOLANGCI_LINT_IMAGE=golangci/golangci-lint:v1.63-alpine GOOSE_VERSION=github.com/pressly/goose/v3/cmd/goose@v3.24 .PHONY: deps diff --git a/cmd/service/main.go b/cmd/service/main.go index fff3734..7f199a6 100644 --- a/cmd/service/main.go +++ b/cmd/service/main.go @@ -8,7 +8,7 @@ import ( "syscall" "github.com/nijeti/healthcheck" - "github.com/nijeti/healthcheck/servers/fasthttp" + hcHTTP "github.com/nijeti/healthcheck/servers/http" dcAdapterPkg "github.com/nijeti/cinema-keeper/internal/adapters/discord" "github.com/nijeti/cinema-keeper/internal/db" @@ -149,11 +149,11 @@ func run() (code int) { healthcheck.WithProbe("db", dbProbe), healthcheck.WithProbe("discord", dcProbe), ) - hcs := fasthttp.New( + hcs := hcHTTP.New( hc, - fasthttp.WithLogger(hcLogger), - fasthttp.WithAddress(":8080"), - fasthttp.WithRoute("/health"), + hcHTTP.WithLogger(hcLogger), + hcHTTP.WithAddress(":8080"), + hcHTTP.WithRoute("/health"), ) hcs.Start() defer hcs.Stop() diff --git a/go.mod b/go.mod index cd1f610..ff8ad9b 100644 --- a/go.mod +++ b/go.mod @@ -4,52 +4,36 @@ go 1.23 require ( github.com/bwmarrin/discordgo v0.28.1 - github.com/bytedance/sonic v1.12.5 + github.com/jmoiron/sqlx v1.4.0 github.com/knadh/koanf/parsers/yaml v0.1.0 github.com/knadh/koanf/providers/env v1.0.0 github.com/knadh/koanf/providers/file v1.1.2 github.com/knadh/koanf/v2 v2.1.2 github.com/lib/pq v1.10.9 github.com/nijeti/healthcheck v1.0.0-beta.2 - github.com/nijeti/healthcheck/servers/fasthttp v1.0.0-beta.4 + github.com/nijeti/healthcheck/servers/http v1.0.0-beta.4 + github.com/pressly/goose/v3 v3.24.0 github.com/stretchr/testify v1.10.0 ) require ( - github.com/andybalholm/brotli v1.1.1 // indirect - github.com/avito-tech/go-transaction-manager/drivers/sql/v2 v2.0.0-rc9.1 // indirect - github.com/avito-tech/go-transaction-manager/drivers/sqlx/v2 v2.0.0 // indirect - github.com/avito-tech/go-transaction-manager/trm/v2 v2.0.0 // indirect - github.com/bytedance/sonic/loader v0.2.1 // indirect - github.com/cloudwego/base64x v0.1.4 // indirect - github.com/cloudwego/iasm v0.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect - github.com/diamondburned/arikawa/v3 v3.4.0 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect github.com/go-viper/mapstructure/v2 v2.2.1 // indirect - github.com/gorilla/schema v1.4.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/jmoiron/sqlx v1.4.0 // indirect - github.com/klauspost/compress v1.17.11 // indirect - github.com/klauspost/cpuid/v2 v2.2.9 // indirect github.com/knadh/koanf/maps v0.1.1 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/mfridman/interpolate v0.0.2 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/pressly/goose/v3 v3.24.0 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sethvargo/go-retry v0.3.0 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/twitchyliquid64/golang-asm v0.15.1 // indirect - github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.58.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/arch v0.12.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/time v0.6.0 // indirect + golang.org/x/sys v0.29.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ea1e02b..fda4213 100644 --- a/go.sum +++ b/go.sum @@ -1,102 +1,49 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/DATA-DOG/go-sqlmock v1.5.1/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= -github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA= -github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA= -github.com/avito-tech/go-transaction-manager/drivers/sql/v2 v2.0.0-rc9.1 h1:Fv24aVI5ltsIa9bqMbq52DKrczJ3bXrIl4FN6Lpb85Y= -github.com/avito-tech/go-transaction-manager/drivers/sql/v2 v2.0.0-rc9.1/go.mod h1:2pDyunC3mxoDcpEp8Gd0qxOYt5p8NLMlMZqW9Im35hY= -github.com/avito-tech/go-transaction-manager/drivers/sqlx/v2 v2.0.0 h1:QGNNG7+D7APKfqnnY9WIwAzeqoSMm3GlC1gi1QfhfCk= -github.com/avito-tech/go-transaction-manager/drivers/sqlx/v2 v2.0.0/go.mod h1:M8qpDTLZa/vngsE8zhICIGmS+GD/nk7tW5eBnx4u6D8= -github.com/avito-tech/go-transaction-manager/trm/v2 v2.0.0-rc10/go.mod h1:qUNVecb/ahohzAvtGvjfWTeCOejgRRiO/2C4cDvtLjI= -github.com/avito-tech/go-transaction-manager/trm/v2 v2.0.0 h1:C6FaIadZFy435YH9UQQbbY3gHgswhiyhmlKY4eMGXOI= -github.com/avito-tech/go-transaction-manager/trm/v2 v2.0.0/go.mod h1:hR++XAHqj8JIwnCWaSkEpFyBumYoX95BqHwxzyuMykM= github.com/bwmarrin/discordgo v0.28.1 h1:gXsuo2GBO7NbR6uqmrrBDplPUx2T3nzu775q/Rd1aG4= github.com/bwmarrin/discordgo v0.28.1/go.mod h1:NJZpH+1AfhIcyQsPeuBKsUtYrRnjkyu0kIVMCHkZtRY= -github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg= -github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= -github.com/bytedance/sonic v1.12.5 h1:hoZxY8uW+mT+OpkcUWw4k0fDINtOcVavEsGfzwzFU/w= -github.com/bytedance/sonic v1.12.5/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= -github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= -github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E= -github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= -github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= -github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/diamondburned/arikawa/v3 v3.4.0 h1:wI3Qv8h2E2dkeddF1I35nv4T6OQ3RtA21rbghW/fnd0= -github.com/diamondburned/arikawa/v3 v3.4.0/go.mod h1:WVkbdenUfsCCkptIlqSglF4eo2/HSXv74eCqGnOZaYY= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M= github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= -github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w= -github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= -github.com/gorilla/schema v1.4.1 h1:jUg5hUjCSDZpNGLuXQOgIWGdlgrIdYvgQ0wZtdK1M3E= -github.com/gorilla/schema v1.4.1/go.mod h1:Dg5SSm5PV60mhF2NFaTV1xuYYj8tV8NOPRo4FggUMnM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/jinzhu/copier v0.4.0/go.mod h1:DfbEm0FYsaqBcKcFuvmOZb218JkPGtvSHsKg8S8hyyg= -github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= +github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= -github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= -github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= -github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= github.com/knadh/koanf/maps v0.1.1 h1:G5TjmUh2D7G2YWf5SQQqSiHRJEjaicvU0KpypqB3NIs= github.com/knadh/koanf/maps v0.1.1/go.mod h1:npD/QZY3V6ghQDdcQzl1W4ICNVTkohC8E73eI2xW4yI= github.com/knadh/koanf/parsers/yaml v0.1.0 h1:ZZ8/iGfRLvKSaMEECEBPM1HQslrZADk8fP1XFUxVI5w= github.com/knadh/koanf/parsers/yaml v0.1.0/go.mod h1:cvbUDC7AL23pImuQP0oRw/hPuccrNBS2bps8asS0CwY= -github.com/knadh/koanf/providers/env v0.1.0 h1:LqKteXqfOWyx5Ab9VfGHmjY9BvRXi+clwyZozgVRiKg= -github.com/knadh/koanf/providers/env v0.1.0/go.mod h1:RE8K9GbACJkeEnkl8L/Qcj8p4ZyPXZIQ191HJi44ZaQ= github.com/knadh/koanf/providers/env v1.0.0 h1:ufePaI9BnWH+ajuxGGiJ8pdTG0uLEUWC7/HDDPGLah0= github.com/knadh/koanf/providers/env v1.0.0/go.mod h1:mzFyRZueYhb37oPmC1HAv/oGEEuyvJDA98r3XAa8Gak= -github.com/knadh/koanf/providers/file v1.1.0 h1:MTjA+gRrVl1zqgetEAIaXHqYje0XSosxSiMD4/7kz0o= -github.com/knadh/koanf/providers/file v1.1.0/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= github.com/knadh/koanf/providers/file v1.1.2 h1:aCC36YGOgV5lTtAFz2qkgtWdeQsgfxUkxDOe+2nQY3w= github.com/knadh/koanf/providers/file v1.1.2/go.mod h1:/faSBcv2mxPVjFrXck95qeoyoZ5myJ6uxN8OOVNJJCI= -github.com/knadh/koanf/v2 v2.1.1 h1:/R8eXqasSTsmDCsAyYj+81Wteg8AqrV9CP6gvsTsOmM= -github.com/knadh/koanf/v2 v2.1.1/go.mod h1:4mnTRbZCK+ALuBXHZMjDfG9y714L7TykVnZkXbMU3Es= github.com/knadh/koanf/v2 v2.1.2 h1:I2rtLRqXRy1p01m/utEtpZSSA6dcJbgGVuE27kW2PzQ= github.com/knadh/koanf/v2 v2.1.2/go.mod h1:Gphfaen0q1Fc1HTgJgSTC4oRX9R2R5ErYMZJy8fLJBo= -github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mfridman/interpolate v0.0.2 h1:pnuTK7MQIxxFz1Gr+rjSIx9u7qVjf5VOoM/u6BbAxPY= github.com/mfridman/interpolate v0.0.2/go.mod h1:p+7uk6oE07mpE/Ik1b8EckO0O4ZXiGAfshKBWLUM9Xg= @@ -104,116 +51,58 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1 github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4= +github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls= github.com/nijeti/healthcheck v1.0.0-beta.2 h1:99dRxvH5rSj7mseQgb4e9nt/PGuAwVeMJW40RTmFWTc= github.com/nijeti/healthcheck v1.0.0-beta.2/go.mod h1:YOvJznQqQ7qUbHOJhjS/gNMxEWyBkgsNeYWxLViOi70= -github.com/nijeti/healthcheck/servers/fasthttp v1.0.0-beta.3 h1:xGz+O3lke/bV0ny6fT5pNg3QyrjlgmJH8770n1K/dAY= -github.com/nijeti/healthcheck/servers/fasthttp v1.0.0-beta.3/go.mod h1:sqZTsoePDrrqFWVOQYPPSNeLuG0WWlhumZzSkWDCrnw= -github.com/nijeti/healthcheck/servers/fasthttp v1.0.0-beta.4 h1:XLNltoPAKeltCFhbbLyhfefE7tc4p1kiMCbik7PAHpM= -github.com/nijeti/healthcheck/servers/fasthttp v1.0.0-beta.4/go.mod h1:sqZTsoePDrrqFWVOQYPPSNeLuG0WWlhumZzSkWDCrnw= +github.com/nijeti/healthcheck/servers/http v1.0.0-beta.4 h1:1hJfahDfo70a41jVDpZ8IYhhlalU/ubmDLXWw7hJvH0= +github.com/nijeti/healthcheck/servers/http v1.0.0-beta.4/go.mod h1:7P2cMRoGbsn35cQetvITU6sGHWxJjsqKs2rWrO5W8oE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pressly/goose/v3 v3.24.0 h1:sFbNms7Bd++2VMq6HSgDHDLWa7kHz1qXzPb3ZIU72VU= github.com/pressly/goose/v3 v3.24.0/go.mod h1:rEWreU9uVtt0DHCyLzF9gRcWiiTF/V+528DV+4DORug= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/sethvargo/go-retry v0.3.0 h1:EEt31A35QhrcRZtrYFDTBg91cqZVnFL2navjDrah2SE= github.com/sethvargo/go-retry v0.3.0/go.mod h1:mNX17F0C/HguQMyMyJxcnU471gOZGxCLyYaFyAZraas= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/objx v0.5.1/go.mod h1:/iHQpkQwBD6DLUmQ4pE+s1TXdob1mORJ4/UFdrifcy0= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= -github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= -github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= -github.com/valyala/fasthttp v1.58.0 h1:GGB2dWxSbEprU9j0iMJHgdKYJVDyjrOwF9RE59PbRuE= -github.com/valyala/fasthttp v1.58.0/go.mod h1:SYXvHHaFp7QZHGKSHmoMipInhrI5StHrhDTYVEjK/Kw= -github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= -go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8= -golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/arch v0.12.0 h1:UsYJhbzPYGsT0HbEdmYcqtCv8UNGvnaL561NnIUvaKg= -golang.org/x/arch v0.12.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= -golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= -golang.org/x/mod v0.4.2/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.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -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-20210220032951-036812b2e83c/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.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -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-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/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-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= -golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 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.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -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-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6 h1:5D53IMaUuA5InSeMu9eJtlQXS2NxAhyWQvkKEgXZhHI= +modernc.org/gc/v3 v3.0.0-20240107210532-573471604cb6/go.mod h1:Qz0X07sNOR1jWYCrJMEnbW/X55x206Q7Vt4mz6/wHp4= +modernc.org/libc v1.55.3 h1:AzcW1mhlPNrRtjS5sS+eW2ISCgSOLLNyFzRh/V3Qj/U= +modernc.org/libc v1.55.3/go.mod h1:qFXepLhz+JjFThQ4kzwzOjA/y/artDeg+pcYnY+Q83w= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.8.0 h1:IqGTL6eFMaDZZhEWwcREgeMXYwmW83LYW8cROZYkg+E= +modernc.org/memory v1.8.0/go.mod h1:XPZ936zp5OMKGWPqbD3JShgd/ZoQ7899TUuQqxY+peU= +modernc.org/sqlite v1.34.1 h1:u3Yi6M0N8t9yKRDwhXcyp1eS5/ErhPTBggxWFuR6Hfk= +modernc.org/sqlite v1.34.1/go.mod h1:pXV2xHxhzXZsgT/RtTFAPY6JJDEvOTcTdwADQCCWD4k= +modernc.org/strutil v1.2.0 h1:agBi9dp1I+eOnxXeiZawM8F4LawKv4NzGWSaLfyeNZA= +modernc.org/strutil v1.2.0/go.mod h1:/mdcBmfOibveCTBxUl5B5l6W+TTH1FXPLHZE6bTosX0= +modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y= +modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= diff --git a/internal/adapters/discord/adapter.go b/internal/adapters/discord/adapter.go index 10f4ee0..2836c51 100644 --- a/internal/adapters/discord/adapter.go +++ b/internal/adapters/discord/adapter.go @@ -26,6 +26,10 @@ func (a *Adapter) Respond( i *discordgo.Interaction, response *discordgo.InteractionResponse, ) error { + if i.Type == discordgo.InteractionMessageComponent { + response.Type = discordgo.InteractionResponseUpdateMessage + } + err := a.session.InteractionRespond( i, response, discordgo.WithContext(ctx), ) @@ -36,19 +40,6 @@ func (a *Adapter) Respond( return nil } -func (a *Adapter) SendEmbeds( - ctx context.Context, channelID models.ID, embeds []*discordgo.MessageEmbed, -) error { - _, err := a.session.ChannelMessageSendEmbeds( - channelID.String(), embeds, discordgo.WithContext(ctx), - ) - if err != nil { - return fmt.Errorf("failed to send embeds: %w", err) - } - - return nil -} - func (a *Adapter) GuildMember( ctx context.Context, guildID models.ID, userID models.ID, ) (*discordgo.Member, error) { diff --git a/internal/db/quotes.go b/internal/db/quotes.go index 55da671..4dfcd1f 100644 --- a/internal/db/quotes.go +++ b/internal/db/quotes.go @@ -35,16 +35,47 @@ func NewQuotesRepo( } } -func (r *QuotesRepo) GetUserQuotesInGuild( +func (r *QuotesRepo) CountUserQuotesInGuild( ctx context.Context, guildID models.ID, authorID models.ID, +) (int, error) { + const query = ` + select count(*) from quotes + where guild_id = $1 and author_id = $2` + + var rows []int + err := r.db.SelectContext(ctx, &rows, query, guildID, authorID) + if err != nil { + return 0, fmt.Errorf( + "failed to select user quotes count in guild: %w", err, + ) + } + + return rows[0], nil +} + +func (r *QuotesRepo) GetUserQuotesInGuild( + ctx context.Context, + guildID models.ID, + authorID models.ID, + offset, limit int, ) ([]*models.Quote, error) { + if offset < 0 { + panic("offset must be greater than or equal to 0") + } + if limit <= 0 { + panic("limit must be greater than 0") + } + const query = ` select author_id, text, guild_id, added_by_id, timestamp from quotes where guild_id = $1 and author_id = $2 - order by timestamp` + order by timestamp + offset $3 limit $4` var rows []quoteRow - err := r.db.SelectContext(ctx, &rows, query, guildID, authorID) + err := r.db.SelectContext( + ctx, &rows, query, guildID, authorID, offset, limit, + ) if err != nil { return nil, fmt.Errorf( "failed to select user quotes in guild: %w", err, diff --git a/internal/discord/commands/quote.go b/internal/discord/commands/quote.go index a0a05ef..3d7f850 100644 --- a/internal/discord/commands/quote.go +++ b/internal/discord/commands/quote.go @@ -1,7 +1,14 @@ package commands import ( + "fmt" + "regexp" + "strconv" + "github.com/bwmarrin/discordgo" + + "github.com/nijeti/cinema-keeper/internal/models" + "github.com/nijeti/cinema-keeper/internal/pkg/discordUtils" ) const ( @@ -14,7 +21,16 @@ const ( QuoteOptionText = "text" ) -const QuoteMaxQuotesPerPage = 10 +const QuoteMaxQuotesPerPage = 5 + +const ( + quotesPageCustomIDAuthorIDIndex = 3 + quotesPageCustomIDPageIndex = 4 +) + +var quotesPageCustomIDRegex = regexp.MustCompile( + `^([a-z]+)_([a-z]+)_(\d+)_(\d+)$`, +) func Quote() *discordgo.ApplicationCommand { return &discordgo.ApplicationCommand{ @@ -55,3 +71,29 @@ func Quote() *discordgo.ApplicationCommand { }, } } + +func QuotesButtonCustomID(authorID models.ID, page int) string { + if page < 0 { + panic("page must be positive") + } + + return fmt.Sprintf( + "%s_%s_%s_%d", QuoteName, QuoteSubCommandGet, authorID, page, + ) +} + +func QuoteParseButtonCustomID(id string) (authorID models.ID, page int) { + matches := quotesPageCustomIDRegex.FindStringSubmatch(id) + if len(matches) < quotesPageCustomIDRegex.NumSubexp() { + panic(discordUtils.ErrInvalidCustomID) + } + + authorID = models.ID(matches[quotesPageCustomIDAuthorIDIndex]) + + page, err := strconv.Atoi(matches[quotesPageCustomIDPageIndex]) + if err != nil { + panic(fmt.Errorf("failed to parse page: %w", err)) + } + + return authorID, page +} diff --git a/internal/discord/commands/responses/quote.go b/internal/discord/commands/responses/quote.go index 18630ab..b404b84 100644 --- a/internal/discord/commands/responses/quote.go +++ b/internal/discord/commands/responses/quote.go @@ -6,6 +6,7 @@ import ( "github.com/bwmarrin/discordgo" + "github.com/nijeti/cinema-keeper/internal/discord/commands" "github.com/nijeti/cinema-keeper/internal/models" "github.com/nijeti/cinema-keeper/internal/pkg/utils" ) @@ -42,9 +43,9 @@ func QuoteRandomQuote(quote *models.Quote) *discordgo.InteractionResponse { Title: quote.Text, Timestamp: quote.Timestamp.Format(time.RFC3339), Color: utils.RandomColor(), - Author: &discordgo.MessageEmbedAuthor{ - Name: quote.Author.DisplayName(), - IconURL: quote.Author.AvatarURL("32x32"), + Footer: &discordgo.MessageEmbedFooter{ + Text: quote.Author.DisplayName(), + IconURL: quote.Author.AvatarURL("16x16"), }, }, }, @@ -52,28 +53,58 @@ func QuoteRandomQuote(quote *models.Quote) *discordgo.InteractionResponse { } } -func QuoteListHeader(author *discordgo.Member) *discordgo.InteractionResponse { - return &discordgo.InteractionResponse{ - Type: discordgo.InteractionResponseChannelMessageWithSource, - Data: &discordgo.InteractionResponseData{ - Embeds: []*discordgo.MessageEmbed{ - { - Title: "Quotes", - Description: fmt.Sprintf( - "The most stunning quotes of %s", author.Mention(), - ), - Color: utils.RandomColor(), - Thumbnail: &discordgo.MessageEmbedThumbnail{ - URL: author.AvatarURL("64x64"), - }, - }, - }, +func QuoteList( + quotes []*models.Quote, page int, lastPage int, +) *discordgo.InteractionResponse { + if len(quotes) > commands.QuoteMaxQuotesPerPage { + panic("too many quotes per page") + } + + author := quotes[0].Author + + prevButton := discordgo.Button{ + Style: discordgo.PrimaryButton, + Emoji: &discordgo.ComponentEmoji{ + Name: "◀️", }, } -} + if page == 0 { + prevButton.Disabled = true + prevButton.CustomID = "prev" + } else { + prevButton.Disabled = false + prevButton.CustomID = commands.QuotesButtonCustomID( + models.ID(author.User.ID), page-1, + ) + } -func QuoteList(quotes []*models.Quote) []*discordgo.MessageEmbed { - embeds := make([]*discordgo.MessageEmbed, 0, len(quotes)) + nextButton := discordgo.Button{ + Style: discordgo.PrimaryButton, + Emoji: &discordgo.ComponentEmoji{ + Name: "▶️", + }, + } + if page == lastPage { + nextButton.Disabled = true + nextButton.CustomID = "next" + } else { + nextButton.Disabled = false + nextButton.CustomID = commands.QuotesButtonCustomID( + models.ID(author.User.ID), page+1, + ) + } + + headerEmbed := &discordgo.MessageEmbed{ + Description: fmt.Sprintf( + "The most stunning quotes of %s", author.Mention(), + ), + Color: utils.RandomColor(), + Thumbnail: &discordgo.MessageEmbedThumbnail{ + URL: author.AvatarURL("64x64"), + }, + } + + quoteEmbeds := make([]*discordgo.MessageEmbed, 0, len(quotes)) for _, quote := range quotes { embed := &discordgo.MessageEmbed{ Title: quote.Text, @@ -84,9 +115,24 @@ func QuoteList(quotes []*models.Quote) []*discordgo.MessageEmbed { IconURL: quote.AddedBy.AvatarURL("16x16"), }, } - embeds = append(embeds, embed) + quoteEmbeds = append(quoteEmbeds, embed) + } + + return &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Embeds: append( + []*discordgo.MessageEmbed{headerEmbed}, quoteEmbeds..., + ), + Components: []discordgo.MessageComponent{ + discordgo.ActionsRow{ + Components: []discordgo.MessageComponent{ + prevButton, nextButton, + }, + }, + }, + }, } - return embeds } func QuoteAdded(quote *models.Quote) *discordgo.InteractionResponse { @@ -103,10 +149,6 @@ func QuoteAdded(quote *models.Quote) *discordgo.InteractionResponse { Text: quote.Author.DisplayName(), IconURL: quote.Author.AvatarURL("16x16"), }, - Author: &discordgo.MessageEmbedAuthor{ - Name: quote.AddedBy.DisplayName(), - IconURL: quote.AddedBy.AvatarURL("32x32"), - }, }, }, }, diff --git a/internal/discord/router.go b/internal/discord/router.go index b2931cf..be2ce5d 100644 --- a/internal/discord/router.go +++ b/internal/discord/router.go @@ -2,11 +2,13 @@ package discord import ( "context" + "errors" "fmt" "log/slog" "github.com/bwmarrin/discordgo" - "github.com/bytedance/sonic" + + "github.com/nijeti/cinema-keeper/internal/pkg/discordUtils" ) type Router struct { @@ -113,9 +115,6 @@ func (r *Router) UnsetCommands() error { } func (r *Router) configureSession(token string) (*discordgo.Session, error) { - discordgo.Marshal = sonic.Marshal - discordgo.Unmarshal = sonic.Unmarshal - session, err := discordgo.New("Bot " + token) if err != nil { return nil, fmt.Errorf("failed to create Discord client: %w", err) @@ -140,22 +139,39 @@ func (r *Router) handle(_ *discordgo.Session, i *discordgo.InteractionCreate) { defer func() { if err := recover(); err != nil { r.logger.ErrorContext( - r.ctx, "panic in command handler", "error", err, + r.ctx, "panic in command router", "error", err, ) } }() - cmdName := i.ApplicationCommandData().Name + cmdName, _, err := discordUtils.ParseCommand(i.Interaction) + if err != nil { + if errors.Is(err, discordUtils.ErrUnknownInteractionType) { + r.logger.WarnContext( + r.ctx, "unknown interaction type", "type", i.Type, + ) + } else { + r.logger.WarnContext( + r.ctx, "invalid interaction", "error", err, + ) + } + + return + } + cmd, ok := r.commands[cmdName] if !ok { - r.logger.WarnContext(r.ctx, "unknown command", "command", cmdName) + r.logger.WarnContext( + r.ctx, "unknown command", "command", cmdName, + ) return } - err := cmd.Handler.Handle(r.ctx, i) + err = cmd.Handler.Handle(r.ctx, i) if err != nil { r.logger.ErrorContext( - r.ctx, "failed to handle command", "command", cmdName, "error", err, + r.ctx, "failed to handle command", + "command", cmdName, "error", err, ) } } diff --git a/internal/handlers/quote/deps.go b/internal/handlers/quote/deps.go index eb21304..67e5472 100644 --- a/internal/handlers/quote/deps.go +++ b/internal/handlers/quote/deps.go @@ -14,7 +14,10 @@ type printRandomQuote interface { type listUserQuotes interface { Exec( - ctx context.Context, i *discordgo.Interaction, authorID models.ID, + ctx context.Context, + i *discordgo.Interaction, + authorID models.ID, + page int, ) error } diff --git a/internal/handlers/quote/handler.go b/internal/handlers/quote/handler.go index bcdd4b4..22cd360 100644 --- a/internal/handlers/quote/handler.go +++ b/internal/handlers/quote/handler.go @@ -33,13 +33,39 @@ func New( func (h *Handler) Handle( ctx context.Context, i *discordgo.InteractionCreate, ) error { - subCommandsMap := discordUtils.OptionsMap(i.Interaction) + switch i.Interaction.Type { + case discordgo.InteractionApplicationCommand: + return h.handlerNew(ctx, i.Interaction) + case discordgo.InteractionMessageComponent: + return h.handleContinue(ctx, i.Interaction) + } + + return nil +} - if sc, ok := subCommandsMap[commands.QuoteSubCommandGet]; ok { - return h.getSubCommand(ctx, i, sc) +func (h *Handler) handlerNew( + ctx context.Context, i *discordgo.Interaction, +) error { + optionsMap := discordUtils.OptionsMap(i) + + if opt, ok := optionsMap[commands.QuoteSubCommandGet]; ok { + return h.getSubCommand(ctx, i, opt) + } + if opt, ok := optionsMap[commands.QuoteSubCommandAdd]; ok { + return h.addSubCommand(ctx, i, opt) } - if sc, ok := subCommandsMap[commands.QuoteSubCommandAdd]; ok { - return h.addSubCommand(ctx, i, sc) + + return nil +} + +func (h *Handler) handleContinue( + ctx context.Context, i *discordgo.Interaction, +) error { + _, subCommand := discordUtils.MustParseCommand(i) + + switch subCommand { + case commands.QuoteSubCommandGet: + return h.getSubCommandContinue(ctx, i) } return nil @@ -47,44 +73,56 @@ func (h *Handler) Handle( func (h *Handler) getSubCommand( ctx context.Context, - i *discordgo.InteractionCreate, - subCommand *discordgo.ApplicationCommandInteractionDataOption, + i *discordgo.Interaction, + opt *discordgo.ApplicationCommandInteractionDataOption, ) error { - optionsMap := discordUtils.SubOptionsMap(subCommand) + subOptionsMap := discordUtils.SubOptionsMap(opt) var authorID *models.ID - if opt, ok := optionsMap[commands.QuoteOptionAuthor]; ok { + if opt, ok := subOptionsMap[commands.QuoteOptionAuthor]; ok { authorID = ptr.To(models.ID(opt.UserValue(nil).ID)) } - var err error - if authorID != nil { - err = h.listUserQuotes.Exec(ctx, i.Interaction, *authorID) - } else { - err = h.printRandomQuote.Exec(ctx, i.Interaction) - } - if err != nil { - return fmt.Errorf("failed to execute service: %w", err) + if authorID == nil { + if err := h.printRandomQuote.Exec(ctx, i); err != nil { + return fmt.Errorf("failed to print random quote: %w", err) + } + return nil } + if err := h.listUserQuotes.Exec(ctx, i, *authorID, 0); err != nil { + return fmt.Errorf("failed to list user quotes: %w", err) + } return nil } func (h *Handler) addSubCommand( ctx context.Context, - i *discordgo.InteractionCreate, + i *discordgo.Interaction, subCommand *discordgo.ApplicationCommandInteractionDataOption, ) error { optionsMap := discordUtils.SubOptionsMap(subCommand) - authorID := models.ID( - optionsMap[commands.QuoteOptionAuthor].UserValue(nil).ID, - ) + authorID := models.ID(optionsMap[commands.QuoteOptionAuthor].UserValue(nil).ID) text := optionsMap[commands.QuoteOptionText].StringValue() - if err := h.addQuote.Exec(ctx, i.Interaction, authorID, text); err != nil { + if err := h.addQuote.Exec(ctx, i, authorID, text); err != nil { return fmt.Errorf("failed to execute service: %w", err) } return nil } + +func (h *Handler) getSubCommandContinue( + ctx context.Context, i *discordgo.Interaction, +) error { + authorID, page := commands.QuoteParseButtonCustomID( + i.MessageComponentData().CustomID, + ) + + if err := h.listUserQuotes.Exec(ctx, i, authorID, page); err != nil { + return fmt.Errorf("failed to update user quotes list: %w", err) + } + + return nil +} diff --git a/internal/pkg/discordUtils/commands.go b/internal/pkg/discordUtils/commands.go new file mode 100644 index 0000000..8817611 --- /dev/null +++ b/internal/pkg/discordUtils/commands.go @@ -0,0 +1,54 @@ +package discordUtils + +import ( + "errors" + + "github.com/bwmarrin/discordgo" +) + +var ErrUnknownInteractionType = errors.New("unknown interaction type") + +func ParseCommand( + i *discordgo.Interaction, +) (command string, subCommand string, err error) { + switch i.Type { //nolint:exhaustive // general return instead of default + case discordgo.InteractionApplicationCommand: + return parseInteraction(i.ApplicationCommandData()) + case discordgo.InteractionMessageComponent: + return parseCombinedCommand(i.MessageComponentData()) + } + + return "", "", ErrUnknownInteractionType +} + +func MustParseCommand( + i *discordgo.Interaction, +) (command string, subCommand string) { + cmd, subCmd, err := ParseCommand(i) + if err != nil { + panic(err) + } + + return cmd, subCmd +} + +func parseInteraction( + i discordgo.ApplicationCommandInteractionData, +) (command string, subCommand string, err error) { + command = i.Name + + for _, opt := range i.Options { + if opt.Type == discordgo.ApplicationCommandOptionSubCommand { + subCommand = opt.Name + break + } + } + + return command, subCommand, nil +} + +func parseCombinedCommand( + i discordgo.MessageComponentInteractionData, +) (command string, subCommand string, err error) { + return ParseCustomID(i.CustomID) +} diff --git a/internal/pkg/discordUtils/customID.go b/internal/pkg/discordUtils/customID.go new file mode 100644 index 0000000..4c68f45 --- /dev/null +++ b/internal/pkg/discordUtils/customID.go @@ -0,0 +1,32 @@ +package discordUtils + +import ( + "errors" + "regexp" +) + +var ErrInvalidCustomID = errors.New("invalid custom id") + +var customIDRegex = regexp.MustCompile(`^([a-z]+)(_([a-z]+))?_(.*)$`) + +const ( + customIDCommandIndex = 1 + customIDSubCommandIndex = 3 +) + +func ParseCustomID( + id string, +) (command string, subCommand string, err error) { + matches := customIDRegex.FindStringSubmatch(id) + if len(matches) < customIDRegex.NumSubexp() { + return "", "", ErrInvalidCustomID + } + + command = matches[customIDCommandIndex] + + if len(matches) != 2 { + subCommand = matches[customIDSubCommandIndex] + } + + return command, matches[3], nil +} diff --git a/internal/pkg/paginator/paginator.go b/internal/pkg/paginator/paginator.go index d8bf283..13a7d9a 100644 --- a/internal/pkg/paginator/paginator.go +++ b/internal/pkg/paginator/paginator.go @@ -1,17 +1,35 @@ package paginator +type PageInfo struct { + Page int + LastPage int + Offset int + Limit int +} + +func Info(elemsCount int, pageSize int, offset int) PageInfo { + limit := offset + pageSize + if limit > elemsCount { + limit = elemsCount + } + + return PageInfo{ + Page: offset / pageSize, + LastPage: (elemsCount - 1) / pageSize, + Offset: offset, + Limit: limit - 1, + } +} + func Paginate[T any]( elems []T, pageSize int, - iterFunc func(page []T) error, + iterFunc func(page []T, info PageInfo) error, ) error { for offset := 0; offset < len(elems); offset += pageSize { - limit := offset + pageSize - if limit > len(elems) { - limit = len(elems) - } - - if err := iterFunc(elems[offset:limit]); err != nil { + info := Info(len(elems), pageSize, offset) + page := elems[offset : info.Limit+1] + if err := iterFunc(page, info); err != nil { return err } } diff --git a/internal/pkg/paginator/paginator_test.go b/internal/pkg/paginator/paginator_test.go index e78f3c6..af5c77a 100644 --- a/internal/pkg/paginator/paginator_test.go +++ b/internal/pkg/paginator/paginator_test.go @@ -9,32 +9,107 @@ import ( "github.com/nijeti/cinema-keeper/internal/pkg/paginator" ) +func TestInfo(t *testing.T) { + t.Parallel() + + tests := map[string]func(t *testing.T){ + "first_page": func(t *testing.T) { + info := paginator.Info(100, 10, 0) + assert.Equal( + t, paginator.PageInfo{ + Page: 0, + LastPage: 9, + Offset: 0, + Limit: 9, + }, + info, + ) + }, + "last_page": func(t *testing.T) { + info := paginator.Info(100, 10, 90) + assert.Equal( + t, paginator.PageInfo{ + Page: 9, + LastPage: 9, + Offset: 90, + Limit: 99, + }, + info, + ) + }, + "uneven_elems": func(t *testing.T) { + info := paginator.Info(11, 10, 10) + assert.Equal( + t, paginator.PageInfo{ + Page: 1, + LastPage: 1, + Offset: 10, + Limit: 10, + }, + info, + ) + }, + } + + for name, test := range tests { + t.Run(name, test) + } +} + func TestPaginate(t *testing.T) { t.Parallel() tests := map[string]func(t *testing.T){ "success": func(t *testing.T) { pages := make([][]int, 0, 3) + infos := make([]paginator.PageInfo, 0, 3) err := paginator.Paginate( - []int{1, 2, 3, 4, 5}, 2, func(page []int) error { + []int{1, 2, 3, 4, 5}, + 2, + func(page []int, info paginator.PageInfo) error { pages = append(pages, page) + infos = append(infos, info) return nil }, ) assert.NoError(t, err) + assert.Len(t, pages, 3) + assert.Len(t, infos, 3) + assert.Equal(t, []int{1, 2}, pages[0]) + assert.Equal( + t, paginator.PageInfo{ + Page: 0, LastPage: 2, Offset: 0, Limit: 2, + }, + infos[0], + ) + assert.Equal(t, []int{3, 4}, pages[1]) + assert.Equal( + t, paginator.PageInfo{ + Page: 1, LastPage: 2, Offset: 2, Limit: 4, + }, + infos[1], + ) + assert.Equal(t, []int{5}, pages[2]) + assert.Equal( + t, paginator.PageInfo{ + Page: 2, LastPage: 2, Offset: 4, Limit: 5, + }, + infos[2], + ) }, "error": func(t *testing.T) { wantErr := errors.New("error") pageCount := 0 err := paginator.Paginate( - []int{1, 2, 3, 4, 5}, 2, func(_ []int) error { + []int{1, 2, 3, 4, 5}, 2, + func(_ []int, _ paginator.PageInfo) error { pageCount++ return wantErr }, diff --git a/internal/services/listUserQuotes/deps.go b/internal/services/listUserQuotes/deps.go index 971d537..f7b4aa4 100644 --- a/internal/services/listUserQuotes/deps.go +++ b/internal/services/listUserQuotes/deps.go @@ -15,19 +15,20 @@ type discord interface { response *discordgo.InteractionResponse, ) error - SendEmbeds( - ctx context.Context, - channelID models.ID, - embeds []*discordgo.MessageEmbed, - ) error - GuildMember( ctx context.Context, guildID models.ID, userID models.ID, ) (*discordgo.Member, error) } type db interface { - GetUserQuotesInGuild( + CountUserQuotesInGuild( ctx context.Context, guildID models.ID, authorID models.ID, + ) (int, error) + + GetUserQuotesInGuild( + ctx context.Context, + guildID models.ID, + authorID models.ID, + offset, limit int, ) ([]*models.Quote, error) } diff --git a/internal/services/listUserQuotes/service.go b/internal/services/listUserQuotes/service.go index 7aed9df..e697d22 100644 --- a/internal/services/listUserQuotes/service.go +++ b/internal/services/listUserQuotes/service.go @@ -28,25 +28,25 @@ func New( } func (s *Service) Exec( - ctx context.Context, i *discordgo.Interaction, authorID models.ID, + ctx context.Context, i *discordgo.Interaction, authorID models.ID, page int, ) error { author, err := s.discord.GuildMember(ctx, models.ID(i.GuildID), authorID) if err != nil { return fmt.Errorf("failed to get author: %w", err) } - quotes, err := s.db.GetUserQuotesInGuild( + quoteCount, err := s.db.CountUserQuotesInGuild( ctx, models.ID(i.GuildID), authorID, ) if err != nil { - return fmt.Errorf("failed to get quotes: %w", err) + return fmt.Errorf("failed to count quotes: %w", err) } - if len(quotes) == 0 { + if quoteCount == 0 { return s.respondEmpty(ctx, i, author) } - return s.respondList(ctx, i, author, quotes) + return s.respondList(ctx, i, author, quoteCount, page) } func (s *Service) respondEmpty( @@ -54,7 +54,7 @@ func (s *Service) respondEmpty( ) error { err := s.discord.Respond(ctx, i, responses.QuoteUserNoQuotes(author)) if err != nil { - return fmt.Errorf("failed to respond: %w", err) + return fmt.Errorf("failed to send empty response: %w", err) } return nil @@ -64,8 +64,22 @@ func (s *Service) respondList( ctx context.Context, i *discordgo.Interaction, author *discordgo.Member, - quotes []*models.Quote, + quoteCount int, + page int, ) error { + offset := page * commands.QuoteMaxQuotesPerPage + + quotes, err := s.db.GetUserQuotesInGuild( + ctx, + models.ID(i.GuildID), + models.ID(author.User.ID), + offset, + commands.QuoteMaxQuotesPerPage, + ) + if err != nil { + return fmt.Errorf("failed to get quotes: %w", err) + } + for _, q := range quotes { addedBy, err := s.discord.GuildMember( ctx, models.ID(i.GuildID), models.ID(q.AddedBy.User.ID), @@ -78,26 +92,14 @@ func (s *Service) respondList( q.AddedBy = addedBy } - err := s.discord.Respond(ctx, i, responses.QuoteListHeader(author)) - if err != nil { - return fmt.Errorf("failed to respond: %w", err) - } - - err = paginator.Paginate( - quotes, commands.QuoteMaxQuotesPerPage, - func(page []*models.Quote) error { - err = s.discord.SendEmbeds( - ctx, models.ID(i.ChannelID), responses.QuoteList(page), - ) - if err != nil { - return fmt.Errorf("failed to send quotes page: %w", err) - } - - return nil - }, + pageInfo := paginator.Info( + quoteCount, commands.QuoteMaxQuotesPerPage, offset, + ) + err = s.discord.Respond( + ctx, i, responses.QuoteList(quotes, pageInfo.Page, pageInfo.LastPage), ) if err != nil { - return err //nolint:wrapcheck // error passthrough + return fmt.Errorf("failed to send quotes page: %w", err) } return nil