Compare commits

..

23 Commits
3.19.0 ... main

Author SHA1 Message Date
cea9a47de4 Fix filtering to use queries rather than ad-hoc code 2025-12-23 05:35:20 -05:00
6a24cd6546 Add confirmation menu to removing cards as well 2025-12-22 20:10:11 -05:00
b58718b537 Adding cards now requires confirmation 2025-12-21 11:02:09 -05:00
c4b4967777 Slightly increase image size 2025-12-20 23:27:50 -05:00
3e4a977bef Add basic in-deck tracking 2025-12-20 23:09:45 -05:00
bfa34d5c06 Add initial image support via kitty terminal protocol 2025-12-20 22:00:11 -05:00
Florian Baumann
f75888a1a9 Adjustments for docker image to comply with update step 2025-12-03 08:53:02 +01:00
Florian Baumann
806137de61 Update to go 1.25 2025-12-03 08:11:28 +01:00
Florian Baumann
34686494dd Quit update on err 2025-12-03 08:06:53 +01:00
Florian Baumann
507eef148a Fetch updates from scryfall bulk file
* Switch update to bulkupdate
* Remove tmpfile
* Better output for update
2025-12-03 08:03:59 +01:00
Florian Baumann
a9d1fbc2cd Rename Id to ID 2025-10-17 11:31:34 +02:00
Florian Baumann
3c593f5fdc Remove unused unique flag from removeInteractive 2025-10-17 11:31:18 +02:00
Florian Baumann
7d23bca7f1 Rename count_reserved to countReserved 2025-10-17 11:29:19 +02:00
Florian Baumann
956ae53b59 Rename exportJson to exportJSON 2025-10-17 11:27:58 +02:00
Florian Baumann
7a1803df3a Rename count_all to countAll in stats.go 2025-10-17 11:27:48 +02:00
Florian Baumann
6e961a708c Add package comment 2025-10-17 11:25:32 +02:00
Florian Baumann
dab28a044a Fix export of foils 2025-04-04 09:15:04 +02:00
Florian Baumann
e2c7e54c16 New missing view 2025-03-23 12:05:37 +01:00
Florian Baumann
d2728b455b Add colored output to values 2024-12-10 14:03:44 +01:00
Florian Baumann
cdc38ce2a0 update deps 2024-10-21 11:24:48 +02:00
Florian Baumann
7570d240ab fix fish prompt 2024-10-14 09:46:31 +02:00
Florian Baumann
7143eb84d2 bulk remove script 2024-10-11 09:57:14 +02:00
Florian Baumann
541a4bbd19 Fix missing foil count in web view 2024-09-14 20:23:49 +02:00
19 changed files with 601 additions and 182 deletions

View File

@ -1,6 +1,6 @@
FROM golang:1.21-alpine AS builder FROM golang:1.25-alpine AS build
RUN apk update && apk add --no-cache git RUN apk update && apk add --no-cache git ca-certificates curl
WORKDIR /go/src/app WORKDIR /go/src/app
COPY pkg /go/src/app/pkg COPY pkg /go/src/app/pkg
@ -16,7 +16,9 @@ RUN go build -ldflags "-X github.com/noqqe/serra/pkg/serra.Version=`git describe
# copy # copy
FROM scratch FROM scratch
WORKDIR /go/src/app WORKDIR /go/src/app
COPY --from=builder /go/src/app/serra /go/src/app/serra COPY --from=build /go/src/app/serra /go/src/app/serra
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
COPY --from=build /tmp /tmp
COPY templates /go/src/app/templates COPY templates /go/src/app/templates
# run # run

68
go.mod
View File

@ -1,61 +1,67 @@
module github.com/noqqe/serra module github.com/noqqe/serra
go 1.21.3 go 1.25.0
toolchain go1.25.4
require ( require (
github.com/charmbracelet/log v0.3.1 github.com/charmbracelet/log v0.4.0
github.com/chzyer/readline v1.5.1 github.com/chzyer/readline v1.5.1
github.com/gin-gonic/gin v1.9.1 github.com/gin-gonic/gin v1.10.0
github.com/mitchellh/mapstructure v1.5.0 github.com/mitchellh/mapstructure v1.5.0
github.com/schollz/progressbar/v3 v3.14.2 github.com/schollz/progressbar/v3 v3.16.1
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.1
go.mongodb.org/mongo-driver v1.14.0 go.mongodb.org/mongo-driver v1.17.1
) )
require ( require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.12.3 // indirect
github.com/charmbracelet/lipgloss v0.9.1 // indirect github.com/bytedance/sonic/loader v0.2.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/charmbracelet/lipgloss v0.13.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/charmbracelet/x/ansi v0.3.2 // indirect
github.com/cloudwego/base64x v0.1.4 // indirect
github.com/cloudwego/iasm v0.2.0 // indirect
github.com/dolmen-go/kittyimg v0.0.0-20250610224728-874967bd8ea4 // indirect
github.com/gabriel-vasile/mimetype v1.4.6 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-playground/validator/v10 v10.22.1 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.3 // indirect
github.com/golang/snappy v0.0.1 // indirect github.com/golang/snappy v0.0.4 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.13.6 // indirect github.com/klauspost/compress v1.17.11 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/klauspost/cpuid/v2 v2.2.8 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.4.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe // indirect github.com/montanaflynn/stats v0.7.1 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect github.com/muesli/termenv v0.15.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect
github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/pflag v1.0.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.12 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect
github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/scram v1.1.2 // indirect
github.com/xdg-go/stringprep v1.0.4 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.11.0 // indirect
golang.org/x/crypto v0.17.0 // indirect golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/net v0.10.0 // indirect golang.org/x/net v0.30.0 // indirect
golang.org/x/sync v0.1.0 // indirect golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sys v0.30.0 // indirect
golang.org/x/term v0.17.0 // indirect golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.19.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

162
go.sum
View File

@ -1,31 +1,40 @@
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/charmbracelet/lipgloss v0.9.1 h1:PNyd3jvaJbg4jRHKWXnCj1akQm4rh8dbEzN1p/u1KWg= github.com/bytedance/sonic/loader v0.2.1 h1:1GgorWTqf12TA8mma4DDSbaQigE2wOgQo7iCjjJv3+E=
github.com/charmbracelet/lipgloss v0.9.1/go.mod h1:1mPmG4cxScwUQALAAnacHaigiiHB9Pmr+v1VEawJl6I= github.com/bytedance/sonic/loader v0.2.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw= github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g= github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY=
github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
github.com/chengxilo/virtualterm v1.0.4 h1:Z6IpERbRVlfB8WkOmtbHiDbBANU7cimRIof7mk9/PwM=
github.com/chengxilo/virtualterm v1.0.4/go.mod h1:DyxxBZz/x1iqJjFxTFcr6/x+jSpqN0iwWCOK1q10rlY=
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM= github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ= github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI= github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04= github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 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/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/dolmen-go/kittyimg v0.0.0-20250610224728-874967bd8ea4 h1:KGRb+vxMx5pGsfDjDSW2Th+b2OEflb0yC3s0daCmiYU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/dolmen-go/kittyimg v0.0.0-20250610224728-874967bd8ea4/go.mod h1:2vk7ATPVcI7uW4Sh6PrSQvtO+Czmq8509xcg/y8Osd0=
github.com/gabriel-vasile/mimetype v1.4.6 h1:3+PzJTKLkvgjeTbts6msPJt4DixhT4YtFNf1gtGe3zc=
github.com/gabriel-vasile/mimetype v1.4.6/go.mod h1:JX1qVKqZd40hUPpAfiNTe0Sne7hdfKSbOqqmkq8GCXc=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y=
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
@ -34,36 +43,33 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213/go.mod h1:vNUNkEQ1e29fT/6vq2aBdFsgNPmy8qMdSay1npru+Sw= github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 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-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db h1:62I3jR2EmQ4l5rM/4FEfDWcRD+abF5XlKShorW5LRoQ=
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw= github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db/go.mod h1:l0dey0ia/Uv7NcFFVbCLtqEBQbrT4OCwCSKTEv6enCw=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
@ -73,25 +79,24 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/schollz/progressbar/v3 v3.14.2 h1:EducH6uNLIWsr560zSV1KrTeUb/wZGAHqyMFIEa99ks= github.com/schollz/progressbar/v3 v3.16.1 h1:RnF1neWZFzLCoGx8yp1yF7SDl4AzNDI5y4I0aUJRrZQ=
github.com/schollz/progressbar/v3 v3.14.2/go.mod h1:aQAZQnhF4JGFtRJiw/eobaXpsqpVQAftEQ+hLGXaRc4= github.com/schollz/progressbar/v3 v3.16.1/go.mod h1:I2ILR76gz5VXqYMIY/LdLecvMHDPVcQm3W/MSKi1TME=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@ -102,75 +107,74 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.1/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.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.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.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 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/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY= github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.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-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/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-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/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-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/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.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
golang.org/x/sys v0.30.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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=
golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 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.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.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 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-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50=

View File

@ -134,17 +134,22 @@ func addCards(cards []string, unique bool, count int64) error {
if len(co) >= 1 { if len(co) >= 1 {
c := co[0] c := co[0]
outputColor := coloredValue(c.getValue(foil))
if unique { if unique {
l.Warnf("%dx \"%s\" (%s, %.2f%s) not added, because it already exists", count, c.Name, c.Rarity, c.getValue(foil), getCurrency()) l.Warnf("%dx \"%s\" (%s, %s%.2f%s%s) not added, because it already exists", count, c.Name, c.Rarity, outputColor, c.getValue(foil), getCurrency(), Reset)
continue continue
} }
modifyCardCount(coll, &c, count, foil) if askConfirmation(&c) {
modifyCardCount(coll, &c, count, foil)
}
} else { } else {
// Fetch card from scryfall // Fetch card from scryfall
c, err := fetchCard(setName, collectorNumber) c, err := fetchCard(setName, collectorNumber)
outputColor := coloredValue(c.getValue(foil))
if err != nil { if err != nil {
l.Warn(err) l.Warn(err)
continue continue
@ -154,11 +159,18 @@ func addCards(cards []string, unique bool, count int64) error {
var total int64 = 0 var total int64 = 0
if foil { if foil {
c.SerraCountFoil = count c.SerraCountFoil = count
c.SerraCountFoilDeck = 0
total = c.SerraCountFoil total = c.SerraCountFoil
} else { } else {
c.SerraCount = count c.SerraCount = count
c.SerraCountDeck = 0
total = c.SerraCount total = c.SerraCount
} }
if !askConfirmation(c) {
continue
}
err = coll.storageAdd(c) err = coll.storageAdd(c)
if err != nil { if err != nil {
l.Warn(err) l.Warn(err)
@ -167,9 +179,9 @@ func addCards(cards []string, unique bool, count int64) error {
// Give feedback of successfully added card // Give feedback of successfully added card
if foil { if foil {
l.Infof("%dx \"%s\" (%s, %.2f%s, foil) added", total, c.Name, c.Rarity, c.getValue(foil), getCurrency()) l.Infof("%dx \"%s\" (%s, %s%.2f%s%s, foil) added", total, c.Name, c.Rarity, outputColor, c.getValue(foil), getCurrency(), Reset)
} else { } else {
l.Infof("%dx \"%s\" (%s, %.2f%s) added", total, c.Name, c.Rarity, c.getValue(foil), getCurrency()) l.Infof("%dx \"%s\" (%s, %s%.2f%s%s) added", total, c.Name, c.Rarity, outputColor, c.getValue(foil), getCurrency(), Reset)
} }
} }
} }

View File

@ -1,10 +1,16 @@
package serra package serra
import ( import (
"bytes"
"encoding/base64"
"fmt" "fmt"
"image/png"
"os"
"sort" "sort"
"strings" "strings"
"github.com/dolmen-go/kittyimg"
"github.com/nfnt/resize"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson"
) )
@ -23,6 +29,8 @@ func init() {
cardCmd.Flags().BoolVarP(&detail, "detail", "d", false, "Show details for cards (url)") cardCmd.Flags().BoolVarP(&detail, "detail", "d", false, "Show details for cards (url)")
cardCmd.Flags().BoolVarP(&reserved, "reserved", "w", false, "If card is on reserved list") cardCmd.Flags().BoolVarP(&reserved, "reserved", "w", false, "If card is on reserved list")
cardCmd.Flags().BoolVarP(&foil, "foil", "f", false, "If card is foil list") cardCmd.Flags().BoolVarP(&foil, "foil", "f", false, "If card is foil list")
cardCmd.Flags().BoolVarP(&drawImg, "image", "g", false, "Draw card image using kitty format")
cardCmd.Flags().BoolVarP(&omitInDeck, "omit-in-deck", "q", false, "Omit cards that are in decks")
rootCmd.AddCommand(cardCmd) rootCmd.AddCommand(cardCmd)
} }
@ -36,7 +44,7 @@ otherwise you'll get a list of cards as a search result.`,
SilenceErrors: true, SilenceErrors: true,
RunE: func(cmd *cobra.Command, cards []string) error { RunE: func(cmd *cobra.Command, cards []string) error {
if len(cards) == 0 { if len(cards) == 0 {
cardList := Cards(rarity, set, sortby, name, oracle, cardType, reserved, foil, 0, 0) cardList := Cards(rarity, set, sortby, name, oracle, cardType, reserved, foil, 0, 0, omitInDeck)
showCardList(cardList, detail) showCardList(cardList, detail)
} else { } else {
ShowCard(cards) ShowCard(cards)
@ -65,7 +73,7 @@ func ShowCard(cardids []string) {
} }
} }
func Cards(rarity, set, sortby, name, oracle, cardType string, reserved, foil bool, skip, limit int64) []Card { func Cards(rarity, set, sortby, name, oracle, cardType string, reserved, foil bool, skip, limit int64, omitInDeck bool) []Card {
client := storageConnect() client := storageConnect()
coll := &Collection{client.Database("serra").Collection("cards")} coll := &Collection{client.Database("serra").Collection("cards")}
defer storageDisconnect(client) defer storageDisconnect(client)
@ -138,6 +146,13 @@ func Cards(rarity, set, sortby, name, oracle, cardType string, reserved, foil bo
filter = append(filter, bson.E{"serra_count_foil", bson.D{{"$gt", 0}}}) filter = append(filter, bson.E{"serra_count_foil", bson.D{{"$gt", 0}}})
} }
if omitInDeck {
filter = append(filter, bson.E{ "$expr", bson.D{{"$or",bson.A{
bson.D{{"$ne", bson.A{"$serra_count", "$serra_count_deck"}}},
bson.D{{"$ne", bson.A{"$serra_count_foil", "$serra_count_foil_deck"}}},
}}}})
}
cards, _ := coll.storageFind(filter, sortStage, skip, limit) cards, _ := coll.storageFind(filter, sortStage, skip, limit)
// This is needed because collectornumbers are strings (ie. "23a") but still we // This is needed because collectornumbers are strings (ie. "23a") but still we
@ -165,36 +180,64 @@ func Cards(rarity, set, sortby, name, oracle, cardType string, reserved, foil bo
func showCardList(cards []Card, detail bool) { func showCardList(cards []Card, detail bool) {
var total float64 var total float64
if detail { if drawImg {
for _, card := range cards {
drawImage(&card)
}
} else if detail {
for _, card := range cards { for _, card := range cards {
fmt.Printf("* %dx %s%s%s (%s/%s) %s%.2f%s %s %s %s\n", card.SerraCount+card.SerraCountFoil+card.SerraCountEtched, Purple, card.Name, Reset, card.Set, card.CollectorNumber, Yellow, card.getValue(false), getCurrency(), Background, strings.Replace(card.ScryfallURI, "?utm_source=api", "", 1), Reset) fmt.Printf("* %dx %s%s%s (%s/%s) %s%.2f%s %s %s %s\n", card.SerraCount+card.SerraCountFoil+card.SerraCountEtched, Purple, card.Name, Reset, card.Set, card.CollectorNumber, Yellow, card.getValue(false), getCurrency(), Background, strings.Replace(card.ScryfallURI, "?utm_source=api", "", 1), Reset)
total = total + card.getValue(false)*float64(card.SerraCount) + card.getValue(true)*float64(card.SerraCountFoil) total = total + card.getValue(false)*float64(card.SerraCount) + card.getValue(true)*float64(card.SerraCountFoil)
} }
} else { } else {
for _, card := range cards { for _, card := range cards {
fmt.Printf("* %dx %s%s%s (%s/%s) %s%.2f%s%s\n", card.SerraCount+card.SerraCountFoil+card.SerraCountEtched, Purple, card.Name, Reset, card.Set, card.CollectorNumber, Yellow, card.getValue(false), getCurrency(), Reset) fmt.Printf("* %dx (%dx) %s%s%s (%s/%s) %s%.2f%s%s\n",
card.SerraCount+card.SerraCountFoil+card.SerraCountEtched-card.SerraCountDeck-card.SerraCountFoilDeck-card.SerraCountEtchedDeck,
card.SerraCount+card.SerraCountFoil+card.SerraCountEtched,
Purple, card.Name, Reset, card.Set, card.CollectorNumber, Yellow, card.getValue(false), getCurrency(), Reset)
total = total + card.getValue(false)*float64(card.SerraCount) + card.getValue(true)*float64(card.SerraCountFoil) total = total + card.getValue(false)*float64(card.SerraCount) + card.getValue(true)*float64(card.SerraCountFoil)
} }
} }
fmt.Printf("\nTotal Value: %s%.2f%s%s\n", Yellow, total, getCurrency(), Reset) if !drawImg {
fmt.Printf("\nTotal Value: %s%.2f%s%s\n", Yellow, total, getCurrency(), Reset)
}
} }
func showCardDetails(card *Card) error { func showCardDetails(card *Card) error {
fmt.Printf("%s%s%s (%s/%s)\n", Purple, card.Name, Reset, card.Set, card.CollectorNumber) if drawImg {
fmt.Printf("Added: %s\n", stringToTime(card.SerraCreated)) drawImage(card)
fmt.Printf("Rarity: %s\n", card.Rarity) } else {
fmt.Printf("Scryfall: %s\n", strings.Replace(card.ScryfallURI, "?utm_source=api", "", 1)) fmt.Printf("%s%s%s (%s/%s)\n", Purple, card.Name, Reset, card.Set, card.CollectorNumber)
fmt.Printf("Available: %d / %d\n", card.SerraCount + card.SerraCountFoil - card.SerraCountDeck - card.SerraCountFoilDeck, card.SerraCount + card.SerraCountFoil)
fmt.Printf("Added: %s\n", stringToTime(card.SerraCreated))
fmt.Printf("Rarity: %s\n", card.Rarity)
fmt.Printf("Scryfall: %s\n", strings.Replace(card.ScryfallURI, "?utm_source=api", "", 1))
fmt.Printf("\n%sCurrent Value%s\n", Green, Reset) fmt.Printf("\n%sCurrent Value%s\n", Green, Reset)
fmt.Printf("* Normal: %dx %s%.2f%s%s\n", card.SerraCount, Yellow, card.getValue(false), getCurrency(), Reset) fmt.Printf("* Normal: %dx %s%.2f%s%s\n", card.SerraCount, Yellow, card.getValue(false), getCurrency(), Reset)
if card.SerraCountFoil > 0 { if card.SerraCountFoil > 0 {
fmt.Printf("* Foil: %dx %s%.2f%s%s\n", card.SerraCountFoil, Yellow, card.getValue(true), getCurrency(), Reset) fmt.Printf("* Foil: %dx %s%.2f%s%s\n", card.SerraCountFoil, Yellow, card.getValue(true), getCurrency(), Reset)
}
fmt.Printf("\n%sValue History%s\n", Green, Reset)
showPriceHistory(card.SerraPrices, "* ", false)
fmt.Println()
} }
fmt.Printf("\n%sValue History%s\n", Green, Reset)
showPriceHistory(card.SerraPrices, "* ", false)
fmt.Println()
return nil return nil
} }
func drawImage(card *Card) {
fmt.Printf("%s - %s (%s/%s)\n", card.Name, card.SetName, card.Set, card.CollectorNumber)
data, err := base64.StdEncoding.DecodeString(card.SerraImage64)
if err == nil {
reader := bytes.NewReader(data)
img, err := png.Decode(reader)
img = resize.Resize(305, 425, img, resize.Bicubic)
if err == nil {
kittyimg.Fprintln(os.Stdout, img)
}
}
fmt.Println()
}

67
pkg/serra/deck.go Normal file
View File

@ -0,0 +1,67 @@
package serra
import (
"strings"
"github.com/spf13/cobra"
"go.mongodb.org/mongo-driver/bson"
)
func init() {
deckCmd.Flags().Int64VarP(&count, "count", "c", 1, "Amount of cards to add")
deckCmd.Flags().BoolVarP(&foil, "foil", "f", false, "Add foil variant of card")
rootCmd.AddCommand(deckCmd)
}
var deckCmd = &cobra.Command{
Aliases: []string{"d"},
Use: "deck",
Short: "Mark a card as in a deck",
Long: "Mark a card as in a deck",
SilenceErrors: true,
RunE: func(cmd *cobra.Command, cards []string) error {
deckCards(cards, count)
return nil
},
}
func deckCards(cards []string, count int64) error {
client := storageConnect()
coll := &Collection{client.Database("serra").Collection("cards")}
l := Logger()
defer storageDisconnect(client)
// Loop over different cards
for _, card := range cards {
// Extract collector number and set name from card input & trim any leading 0 from collector number
if !strings.Contains(card, "/") {
l.Errorf("Invalid card format %s. Needs to be set/collector number i.e. \"usg/13\"", card)
continue
}
setName := strings.ToLower(strings.Split(card, "/")[0])
collectorNumber := strings.TrimLeft(strings.Split(card, "/")[1], "0")
if collectorNumber == "" {
l.Errorf("Invalid card format %s. Needs to be set/collector number i.e. \"usg/13\"", card)
continue
}
// Check if card is already in collection
co, err := coll.storageFind(bson.D{{"set", setName}, {"collectornumber", collectorNumber}}, bson.D{}, 0, 0)
if err != nil {
l.Error(err)
continue
}
if len(co) >= 1 {
modifyCardDeckCount(coll, &co[0], count, foil)
} else {
l.Errorf("Card not in collection: %s", card)
continue
}
}
storageDisconnect(client)
return nil
}

View File

@ -25,7 +25,7 @@ var exportCmd = &cobra.Command{
Supports multiple output formats depending on where you want to export your collection.`, Supports multiple output formats depending on where you want to export your collection.`,
SilenceErrors: true, SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
cardList := Cards(rarity, set, sortby, name, oracle, cardType, reserved, foil, 0, 0) cardList := Cards(rarity, set, sortby, name, oracle, cardType, reserved, foil, 0, 0, false)
// filter out cards that do not reach the minimum amount (--min-count) // filter out cards that do not reach the minimum amount (--min-count)
// this is done after query result because find query constructed does not support // this is done after query result because find query constructed does not support
@ -46,7 +46,7 @@ var exportCmd = &cobra.Command{
case "moxfield": case "moxfield":
exportMoxfield(cardList) exportMoxfield(cardList)
case "json": case "json":
exportJson(cardList) exportJSON(cardList)
} }
return nil return nil
}, },
@ -61,7 +61,7 @@ func exportTCGPowertools(cards []Card) {
fmt.Println("quantity,cardmarketId,name,set,condition,language,isFoil,isPlayset,price,comment") fmt.Println("quantity,cardmarketId,name,set,condition,language,isFoil,isPlayset,price,comment")
for _, card := range cards { for _, card := range cards {
fmt.Printf("%d,%.0f,%s,%s,EX,German,false,false,%.2f,\n", card.SerraCount, card.CardmarketID, card.Name, card.SetName, card.getValue(false)) fmt.Printf("%d,%.0f,%s,%s,EX,German,false,false,%.2f,\n", card.SerraCount+card.SerraCountFoil, card.CardmarketID, card.Name, card.SetName, card.getValue(false))
} }
} }
@ -77,7 +77,7 @@ func exportMoxfield(cards []Card) {
for _, card := range cards { for _, card := range cards {
records = append(records, records = append(records,
[]string{fmt.Sprintf("%d", card.SerraCount), card.Name, card.Set, "NM", "English", "FALSE", card.CollectorNumber, "FALSE", "FALSE", ""}) []string{fmt.Sprintf("%d", card.SerraCount+card.SerraCountFoil), card.Name, card.Set, "NM", "English", "FALSE", card.CollectorNumber, "FALSE", "FALSE", ""})
} }
for _, record := range records { for _, record := range records {
@ -105,7 +105,7 @@ func exportTCGHome(cards []Card) {
for _, card := range cards { for _, card := range cards {
records = append(records, records = append(records,
[]string{fmt.Sprintf("%d", card.SerraCount), card.Name, "", card.Set, card.CollectorNumber, "English", "EX", card.ID, ""}) []string{fmt.Sprintf("%d", card.SerraCount+card.SerraCountFoil), card.Name, "", card.Set, card.CollectorNumber, "English", "EX", card.ID, ""})
} }
for _, record := range records { for _, record := range records {
@ -121,7 +121,7 @@ func exportTCGHome(cards []Card) {
} }
} }
func exportJson(cards []Card) { func exportJSON(cards []Card) {
ehj, _ := json.MarshalIndent(cards, "", " ") ehj, _ := json.MarshalIndent(cards, "", " ")
fmt.Println(string(ehj)) fmt.Println(string(ehj))
} }

View File

@ -41,17 +41,25 @@ func Logger() *log.Logger {
return l return l
} }
func modifyCardCount(coll *Collection, c *Card, amount int64, foil bool) error { func getStoredCard(coll *Collection, c *Card) (Card, error) {
// find already existing card // find already existing card
sort := bson.D{{"_id", 1}} sort := bson.D{{"_id", 1}}
searchFilter := bson.D{{"_id", c.ID}} searchFilter := bson.D{{"_id", c.ID}}
l := Logger()
storedCards, err := coll.storageFind(searchFilter, sort, 0, 0) storedCards, err := coll.storageFind(searchFilter, sort, 0, 0)
if err != nil {
return Card{}, err
}
return storedCards[0], nil
}
func modifyCardCount(coll *Collection, c *Card, amount int64, foil bool) error {
l := Logger()
storedCard, err := getStoredCard(coll, c)
if err != nil { if err != nil {
return err return err
} }
storedCard := storedCards[0]
// update card amount // update card amount
var update bson.M var update bson.M
@ -87,6 +95,35 @@ func modifyCardCount(coll *Collection, c *Card, amount int64, foil bool) error {
return nil return nil
} }
func modifyCardDeckCount(coll *Collection, c *Card, amount int64, foil bool) error {
l := Logger()
storedCard, err := getStoredCard(coll, c)
if err != nil {
return err
}
// update card amount
var update bson.M
if foil {
newAmount := min(max(storedCard.SerraCountFoilDeck + amount, 0), storedCard.SerraCountFoil)
l.Infof("%d / %d available", storedCard.SerraCountFoil - newAmount, storedCard.SerraCountFoil)
update = bson.M{
"$set": bson.M{"serra_count_foil_deck": newAmount},
}
} else {
newAmount := min(max(storedCard.SerraCountDeck + amount, 0), storedCard.SerraCount)
l.Infof("%d / %d available", storedCard.SerraCount - newAmount, storedCard.SerraCount)
update = bson.M{
"$set": bson.M{"serra_count_deck": newAmount},
}
}
coll.storageUpdate(bson.M{"_id": bson.M{"$eq": c.ID}}, update)
return nil
}
func findCardByCollectorNumber(coll *Collection, setCode string, collectorNumber string) (*Card, error) { func findCardByCollectorNumber(coll *Collection, setCode string, collectorNumber string) (*Card, error) {
sort := bson.D{{"_id", 1}} sort := bson.D{{"_id", 1}}
searchFilter := bson.D{{"set", setCode}, {"collectornumber", collectorNumber}} searchFilter := bson.D{{"set", setCode}, {"collectornumber", collectorNumber}}
@ -254,3 +291,33 @@ func getFloat64(unknown interface{}) (float64, error) {
return math.NaN(), errors.New("non-numeric type could not be converted to float") return math.NaN(), errors.New("non-numeric type could not be converted to float")
} }
} }
func coloredValue(value float64) string {
outputColor := Reset
if value > 1 {
outputColor = Green
}
if value > 5 {
outputColor = Yellow
}
if value > 10 {
outputColor = Red
}
return outputColor
}
func askConfirmation(card *Card) bool {
drawImage(card)
fmt.Println("Is this correct (y/n)?")
var char = 'x'
for char != 'y' && char != 'n' {
fmt.Scanf("%c\n", &char)
}
return char == 'y'
}

View File

@ -76,7 +76,7 @@ cards you dont own (yet) :)`,
}) })
for _, card := range missingCards { for _, card := range missingCards {
fmt.Printf("%s%s/%s%s %s%.02f%s%s\t%s (%s)\n", Purple, card.Set, card.CollectorNumber, Reset, Green, card.getValue(false), Reset, getCurrency(), card.Name, card.SetName) fmt.Printf("%s%s/%s\t%s(%s, %shttps://scryfall.com/card/%s/%s%s)\t%s%.02f%s%s\t%s (%s)\n", Purple, card.Set, card.CollectorNumber, Reset, string([]rune(card.Rarity)[0]), Background, card.Set, card.CollectorNumber, Reset, Green, card.getValue(false), Reset, getCurrency(), card.Name, card.SetName)
} }
return nil return nil

View File

@ -26,7 +26,7 @@ var removeCmd = &cobra.Command{
RunE: func(cmd *cobra.Command, cards []string) error { RunE: func(cmd *cobra.Command, cards []string) error {
if interactive { if interactive {
removeCardsInteractive(unique, set) removeCardsInteractive(set)
} else { } else {
removeCards(cards, count) removeCards(cards, count)
} }
@ -34,7 +34,7 @@ var removeCmd = &cobra.Command{
}, },
} }
func removeCardsInteractive(unique bool, set string) { func removeCardsInteractive(set string) {
l := Logger() l := Logger()
if len(set) == 0 { if len(set) == 0 {
@ -103,6 +103,10 @@ func removeCards(cards []string, count int64) error {
continue continue
} }
if !askConfirmation(c) {
continue
}
if foil && c.SerraCountFoil == 1 && c.SerraCount == 0 || !foil && c.SerraCount == 1 && c.SerraCountFoil == 0 { if foil && c.SerraCountFoil == 1 && c.SerraCount == 0 || !foil && c.SerraCount == 1 && c.SerraCountFoil == 0 {
coll.storageRemove(bson.M{"_id": c.ID}) coll.storageRemove(bson.M{"_id": c.ID})
l.Infof("\"%s\" (%.2f%s) removed", c.Name, c.getValue(foil), getCurrency()) l.Infof("\"%s\" (%.2f%s) removed", c.Name, c.getValue(foil), getCurrency())

View File

@ -1,3 +1,7 @@
// Package serra
//
// It implements base functions and also cli wrappers
// The entire tool consists only of this one package.
package serra package serra
import ( import (
@ -13,11 +17,13 @@ var (
cmc int64 cmc int64
count int64 count int64
detail bool detail bool
drawImg bool
foil bool foil bool
format string format string
interactive bool interactive bool
limit float64 limit float64
name string name string
omitInDeck bool
oracle string oracle string
port uint64 port uint64
rarity string rarity string

View File

@ -2,11 +2,14 @@ package serra
import ( import (
"bytes" "bytes"
"encoding/base64"
"encoding/json" "encoding/json"
"fmt" "fmt"
"io" "io"
"io/ioutil"
"log" "log"
"net/http" "net/http"
"os"
"time" "time"
"go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/bson/primitive"
@ -14,12 +17,16 @@ import (
type Card struct { type Card struct {
// Added by Serra // Added by Serra
SerraCount int64 `bson:"serra_count"` SerraCount int64 `bson:"serra_count"`
SerraCountFoil int64 `bson:"serra_count_foil"` SerraCountFoil int64 `bson:"serra_count_foil"`
SerraCountEtched int64 `bson:"serra_count_etched"` SerraCountEtched int64 `bson:"serra_count_etched"`
SerraPrices []PriceEntry `bson:"serra_prices"` SerraCountDeck int64 `bson:"serra_count_deck"`
SerraCreated primitive.DateTime `bson:"serra_created"` SerraCountFoilDeck int64 `bson:"serra_count_foil_deck"`
SerraUpdated primitive.DateTime `bson:"serra_updated"` SerraCountEtchedDeck int64 `bson:"serra_count_etched_deck"`
SerraPrices []PriceEntry `bson:"serra_prices"`
SerraCreated primitive.DateTime `bson:"serra_created"`
SerraUpdated primitive.DateTime `bson:"serra_updated"`
SerraImage64 string `bson:"serra_image"`
Artist string `json:"artist"` Artist string `json:"artist"`
ArtistIds []string `json:"artist_ids"` ArtistIds []string `json:"artist_ids"`
@ -119,18 +126,119 @@ type Card struct {
Variation bool `json:"variation"` Variation bool `json:"variation"`
} }
// Getter for currency specific value type BulkIndex struct {
func (c Card) getValue(foil bool) float64 { Object string `json:"object"`
if getCurrency() == EUR { HasMore bool `json:"has_more"`
if foil { Data []struct {
return c.Prices.EurFoil Object string `json:"object"`
ID string `json:"id"`
Type string `json:"type"`
UpdatedAt time.Time `json:"updated_at"`
URI string `json:"uri"`
Name string `json:"name"`
Description string `json:"description"`
Size int `json:"size"`
DownloadURI string `json:"download_uri"`
ContentType string `json:"content_type"`
ContentEncoding string `json:"content_encoding"`
} `json:"data"`
}
func fetchBulkDownloadURL() (string, error) {
url := "https://api.scryfall.com/bulk-data"
downloadURL := ""
// Make an HTTP GET request
resp, err := http.Get(url)
if err != nil {
log.Fatalf("Error fetching data: %v", err)
}
defer resp.Body.Close()
// Read the response body
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Fatalf("Error reading response body: %v", err)
}
// Unmarshal the JSON response
var bulkData BulkIndex
if err := json.Unmarshal(body, &bulkData); err != nil {
log.Fatalf("Error unmarshaling JSON: %v", err)
}
// Find and print the unique cards URL
for _, item := range bulkData.Data {
if item.Type == "default_cards" {
downloadURL = item.DownloadURI
} }
return c.Prices.Eur
} }
if foil {
return c.Prices.UsdFoil return downloadURL, nil
}
func downloadBulkData(downloadURL string) (string, error) {
// Create a temporary directory
tempDir, err := os.MkdirTemp("", "download")
if err != nil {
log.Fatalf("Error creating temporary directory: %v", err)
} }
return c.Prices.Usd // defer os.RemoveAll(tempDir) // Clean up the directory when done
// Create a temporary file in the temporary directory
tempFile, err := os.CreateTemp(tempDir, "downloaded-*.json") // Adjust the extension if necessary
if err != nil {
log.Fatalf("Error creating temporary file: %v", err)
}
// defer tempFile.Close() // Ensure we close the file when we're done
// Download the file
resp, err := http.Get(downloadURL)
if err != nil {
log.Fatalf("Error downloading file: %v", err)
}
defer resp.Body.Close() // Make sure to close the response body
// Check for a successful response
if resp.StatusCode != http.StatusOK {
log.Fatalf("Error: received status code %d", resp.StatusCode)
}
// Copy the response body to the temporary file
_, err = io.Copy(tempFile, resp.Body)
if err != nil {
log.Fatalf("Error saving file: %v", err)
}
return tempFile.Name(), nil
}
func loadBulkFile(bulkFilePath string) ([]Card, error) {
var cards []Card
fileBytes, _ := os.ReadFile(bulkFilePath)
defer os.Remove(bulkFilePath)
err := json.Unmarshal(fileBytes, &cards)
if err != nil {
fmt.Println("Error unmarshalling bulk file:", err)
return cards, nil
}
return cards, nil
}
func getCardFromBulk(cards []Card, setName, collectorNumber string) (*Card, error) {
var foundCard Card
for _, v := range cards {
if v.CollectorNumber == collectorNumber && v.Set == setName {
foundCard = v
return &foundCard, nil
}
}
return &Card{}, fmt.Errorf("Card %s/%s not found in bulk data", setName, collectorNumber)
} }
type PriceEntry struct { type PriceEntry struct {
@ -170,6 +278,20 @@ type Set struct {
URI string `json:"uri"` URI string `json:"uri"`
} }
// Getter for currency specific value
func (c Card) getValue(foil bool) float64 {
if getCurrency() == EUR {
if foil {
return c.Prices.EurFoil
}
return c.Prices.Eur
}
if foil {
return c.Prices.UsdFoil
}
return c.Prices.Usd
}
func fetchCard(setName, collectorNumber string) (*Card, error) { func fetchCard(setName, collectorNumber string) (*Card, error) {
resp, err := http.Get(fmt.Sprintf("https://api.scryfall.com/cards/%s/%s/", setName, collectorNumber)) resp, err := http.Get(fmt.Sprintf("https://api.scryfall.com/cards/%s/%s/", setName, collectorNumber))
if err != nil { if err != nil {
@ -181,7 +303,7 @@ func fetchCard(setName, collectorNumber string) (*Card, error) {
return &Card{}, fmt.Errorf("Card %s/%s not found", setName, collectorNumber) return &Card{}, fmt.Errorf("Card %s/%s not found", setName, collectorNumber)
} }
//We Read the response body on the line below. //we read the response body on the line below.
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
log.Fatalf("%s", err) log.Fatalf("%s", err)
@ -191,7 +313,6 @@ func fetchCard(setName, collectorNumber string) (*Card, error) {
r := bytes.NewReader(body) r := bytes.NewReader(body)
decoder := json.NewDecoder(r) decoder := json.NewDecoder(r)
val := &Card{} val := &Card{}
err = decoder.Decode(val) err = decoder.Decode(val)
if err != nil { if err != nil {
log.Fatalf("%s", err) log.Fatalf("%s", err)
@ -204,11 +325,24 @@ func fetchCard(setName, collectorNumber string) (*Card, error) {
val.Prices.Date = primitive.NewDateTimeFromTime(time.Now()) val.Prices.Date = primitive.NewDateTimeFromTime(time.Now())
val.SerraPrices = append(val.SerraPrices, val.Prices) val.SerraPrices = append(val.SerraPrices, val.Prices)
imgResp, imgErr := http.Get(val.ImageUris.Png)
if imgErr != nil {
log.Fatalln(err)
return &Card{}, err
}
bytes, readErr := io.ReadAll(imgResp.Body)
if readErr != nil {
log.Fatalln(err)
return &Card{}, err
}
val.SerraImage64 = base64.StdEncoding.EncodeToString(bytes)
return val, nil return val, nil
} }
func fetchSets() (*SetList, error) { func fetchSets() (*SetList, error) {
// TODO better URL Building... // TODO: better URL Building...
resp, err := http.Get("https://api.scryfall.com/sets") resp, err := http.Get("https://api.scryfall.com/sets")
if err != nil { if err != nil {
log.Fatalln(err) log.Fatalln(err)

View File

@ -92,7 +92,7 @@ func showValueStats(coll *Collection, totalcoll *Collection) {
l.Error(err) l.Error(err)
foilValue = 0 foilValue = 0
} }
count_all, err := getFloat64(stats[0]["count_all"]) countAll, err := getFloat64(stats[0]["count_all"])
if err != nil { if err != nil {
l.Error(err) l.Error(err)
foilValue = 0 foilValue = 0
@ -101,7 +101,7 @@ func showValueStats(coll *Collection, totalcoll *Collection) {
fmt.Printf("Total: %s%.2f%s%s\n", Pink, totalValue, getCurrency(), Reset) fmt.Printf("Total: %s%.2f%s%s\n", Pink, totalValue, getCurrency(), Reset)
fmt.Printf("Normal: %s%.2f%s%s\n", Pink, normalValue, getCurrency(), Reset) fmt.Printf("Normal: %s%.2f%s%s\n", Pink, normalValue, getCurrency(), Reset)
fmt.Printf("Foils: %s%.2f%s%s\n", Pink, foilValue, getCurrency(), Reset) fmt.Printf("Foils: %s%.2f%s%s\n", Pink, foilValue, getCurrency(), Reset)
fmt.Printf("Average Card: %s%.2f%s%s\n", Pink, totalValue/count_all, getCurrency(), Reset) fmt.Printf("Average Card: %s%.2f%s%s\n", Pink, totalValue/countAll, getCurrency(), Reset)
total, _ := totalcoll.storageFindTotal() total, _ := totalcoll.storageFindTotal()
fmt.Printf("History: \n") fmt.Printf("History: \n")
@ -120,11 +120,11 @@ func showReservedListStats(coll *Collection) {
}}}, }}},
}) })
var count_reserved int32 var countReserved int32
if len(reserved) > 0 { if len(reserved) > 0 {
count_reserved = reserved[0]["count"].(int32) countReserved = reserved[0]["count"].(int32)
} }
fmt.Printf("Reserved List: %s%d%s\n", Yellow, count_reserved, Reset) fmt.Printf("Reserved List: %s%d%s\n", Yellow, countReserved, Reset)
} }
func showRarityStats(coll *Collection) { func showRarityStats(coll *Collection) {
@ -150,7 +150,7 @@ func showRarityStats(coll *Collection) {
func showCardsAddedPerMonth(coll *Collection) { func showCardsAddedPerMonth(coll *Collection) {
fmt.Printf("\n%sCards added over time%s\n", Green, Reset) fmt.Printf("\n%sCards added over time%s\n", Green, Reset)
type Caot struct { type Caot struct {
Id struct { ID struct {
Year int32 `mapstructure:"year"` Year int32 `mapstructure:"year"`
Month int32 `mapstructure:"month"` Month int32 `mapstructure:"month"`
} `mapstructure:"_id"` } `mapstructure:"_id"`
@ -178,7 +178,7 @@ func showCardsAddedPerMonth(coll *Collection) {
for _, mo := range caot { for _, mo := range caot {
moo := new(Caot) moo := new(Caot)
mapstructure.Decode(mo, moo) mapstructure.Decode(mo, moo)
fmt.Printf("%d-%02d: %s%d%s\n", moo.Id.Year, moo.Id.Month, Purple, moo.Count, Reset) fmt.Printf("%d-%02d: %s%d%s\n", moo.ID.Year, moo.ID.Month, Purple, moo.Count, Reset)
} }
} }

View File

@ -17,7 +17,8 @@ type Total struct {
Value []PriceEntry `bson:"value"` Value []PriceEntry `bson:"value"`
} }
// https://siongui.github.io/2017/02/11/go-add-method-function-to-type-in-external-package/ // Collection Struct
// reason: https://siongui.github.io/2017/02/11/go-add-method-function-to-type-in-external-package/
type Collection struct { type Collection struct {
*mongo.Collection *mongo.Collection
} }

23
pkg/serra/undeck.go Normal file
View File

@ -0,0 +1,23 @@
package serra
import (
"github.com/spf13/cobra"
)
func init() {
undeckCmd.Flags().Int64VarP(&count, "count", "c", 1, "Amount of cards to add")
undeckCmd.Flags().BoolVarP(&foil, "foil", "f", false, "Add foil variant of card")
rootCmd.AddCommand(undeckCmd)
}
var undeckCmd = &cobra.Command{
Aliases: []string{"u"},
Use: "undeck",
Short: "Unmark a card as in a deck",
Long: "Unmark a card as in a deck",
SilenceErrors: true,
RunE: func(cmd *cobra.Command, cards []string) error {
deckCards(cards, -count)
return nil
},
}

View File

@ -19,8 +19,8 @@ func init() {
var updateCmd = &cobra.Command{ var updateCmd = &cobra.Command{
Aliases: []string{"u"}, Aliases: []string{"u"},
Use: "update", Use: "update",
Short: "Update card values from scryfall", Short: "update card values from scryfall",
Long: `The update mechanism iterates over each card in your collection and fetches its price. After all cards you own in a set are updated, the set value will update. After all Sets are updated, the whole collection value is updated.`, Long: `the update mechanism iterates over each card in your collection and fetches its price. after all cards you own in a set are updated, the set value will update. after all sets are updated, the whole collection value is updated.`,
SilenceErrors: true, SilenceErrors: true,
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
@ -49,11 +49,35 @@ var updateCmd = &cobra.Command{
{"usdfoil", bson.D{{"$sum", bson.D{{"$multiply", bson.A{"$last_price.usd_foil", "$serra_count_foil"}}}}}}, {"usdfoil", bson.D{{"$sum", bson.D{{"$multiply", bson.A{"$last_price.usd_foil", "$serra_count_foil"}}}}}},
}}} }}}
l.Info("Fetching bulk data from scryfall...")
downloadURL, err := fetchBulkDownloadURL()
if err != nil {
l.Error("Could not extract bulk download URL:", err)
return err
}
l.Infof("Found latest bulkfile url: %s", downloadURL)
l.Info("Downloading bulk data file...")
bulkFilePath, err := downloadBulkData(downloadURL)
if err != nil {
l.Error("Could not fetch bulk json from scryfall", err)
return err
}
l.Info("Loading bulk data file...")
updatedCards, err := loadBulkFile(bulkFilePath)
if err != nil {
l.Error("Could not load bulk file:", err)
return err
}
l.Infof("Successfully loaded %d cards. Starting Update.", len(updatedCards))
sets, _ := fetchSets() sets, _ := fetchSets()
for _, set := range sets.Data { for _, set := range sets.Data {
// When downloading new sets, PriceList needs to be initialized // When downloading new sets, PriceList needs to be initialized
// This query silently fails if set was already downloaded. Not nice but ok for now. // This query silently fails if set was already downloaded. Not nice but ok for now.
// TODO: make this not fail silently
set.SerraPrices = []PriceEntry{} set.SerraPrices = []PriceEntry{}
setscoll.storageAddSet(&set) setscoll.storageAddSet(&set)
@ -74,13 +98,13 @@ var updateCmd = &cobra.Command{
SaucerHead: "[green]>[reset]", SaucerHead: "[green]>[reset]",
SaucerPadding: " ", SaucerPadding: " ",
BarStart: "|", BarStart: "|",
BarEnd: set.Name, BarEnd: "| " + set.Name,
}), }),
) )
for _, card := range cards { for _, card := range cards {
bar.Add(1) bar.Add(1)
updatedCard, err := fetchCard(card.Set, card.CollectorNumber) updatedCard, err := getCardFromBulk(updatedCards, card.Set, card.CollectorNumber)
if err != nil { if err != nil {
l.Error(err) l.Error(err)
continue continue
@ -115,7 +139,6 @@ var updateCmd = &cobra.Command{
"$set": bson.M{"serra_updated": p.Date, "cardcount": set.CardCount}, "$set": bson.M{"serra_updated": p.Date, "cardcount": set.CardCount},
"$push": bson.M{"serra_prices": p}, "$push": bson.M{"serra_prices": p},
} }
// fmt.Printf("Set %s%s%s (%s) is now worth %s%.02f EUR%s\n", Pink, set.Name, Reset, set.Code, Yellow, setvalue[0]["value"], Reset)
setscoll.storageUpdate(bson.M{"code": bson.M{"$eq": set.Code}}, setUpdate) setscoll.storageUpdate(bson.M{"code": bson.M{"$eq": set.Code}}, setUpdate)
} }
@ -130,7 +153,7 @@ var updateCmd = &cobra.Command{
tmpCard := Card{} tmpCard := Card{}
tmpCard.Prices = t tmpCard.Prices = t
fmt.Printf("\n%sUpdating total value of collection to: %s%.02f%s%s\n", Green, Yellow, tmpCard.getValue(false)+tmpCard.getValue(true), getCurrency(), Reset) l.Info("\n%sUpdating total value of collection to: %s%.02f%s%s\n", Green, Yellow, tmpCard.getValue(false)+tmpCard.getValue(true), getCurrency(), Reset)
totalcoll.storageAddTotal(t) totalcoll.storageAddTotal(t)
return nil return nil

View File

@ -3,6 +3,7 @@ package serra
import ( import (
"net/http" "net/http"
"strconv" "strconv"
"text/template"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -16,6 +17,10 @@ func init() {
rootCmd.AddCommand(webCmd) rootCmd.AddCommand(webCmd)
} }
func add(a, b int64) int64 {
return a + b
}
var webCmd = &cobra.Command{ var webCmd = &cobra.Command{
Aliases: []string{"a"}, Aliases: []string{"a"},
Use: "web", Use: "web",
@ -38,6 +43,9 @@ type Query struct {
func startWeb() error { func startWeb() error {
router := gin.Default() router := gin.Default()
router.SetFuncMap(template.FuncMap{
"add": add,
})
router.LoadHTMLGlob("templates/*.tmpl") router.LoadHTMLGlob("templates/*.tmpl")
router.Static("/assets", "./assets") router.Static("/assets", "./assets")
@ -63,7 +71,7 @@ func landingPage(c *gin.Context) {
sets := Sets("release") sets := Sets("release")
// Fetch all results based on filter criteria // Fetch all results based on filter criteria
cards := Cards("", query.Set, query.Sort, query.Name, "", "", false, false, query.Page*int64(limit), limit) cards := Cards("", query.Set, query.Sort, query.Name, "", "", false, false, query.Page*int64(limit), limit, false)
// Construct quick way for counting results // Construct quick way for counting results
filter := bson.D{} filter := bson.D{}

19
scripts/removebulk.fish Executable file
View File

@ -0,0 +1,19 @@
#!/usr/bin/env fish
set SET $argv[1]
# export and remove colors
serra cards --set $SET --min-count 2 --sort value | gsed 's/\x1B[@A-Z\\\]^_]\|\x1B\[[0-9:;<=>?]*[-!"#$%&'"'"'()*+,.\/]*[][\\@A-Z^_`a-z{|}~]//g' > $SET
# edit
nvim $SET
# remove formatting
cat $SET | gsed 's/^\* //' | gsed 's/x.*(/ /' | gsed 's/).*//' | grep -v "Total Value" > {$SET}.txt
# delete everything from serra
for x in (cat {$SET}.txt) ; echo serra remove -c $x ; end
# cleanup
rm $SET
rm {$SET}.txt

View File

@ -168,7 +168,7 @@
<tbody> <tbody>
{{range .cards}} {{range .cards}}
<tr> <tr>
<td>{{.SerraCount}}</td> <td>{{ add .SerraCount .SerraCountFoil }}</td>
<td> <td>
<div class="cardpreview"><strong>{{.Name }}</strong> <div class="cardpreview"><strong>{{.Name }}</strong>
<span class="cardpreviewtext"> <span class="cardpreviewtext">