chore: refresh go tooling and dependencies
This commit is contained in:
parent
d83d1c2f6f
commit
ad609bcc6b
@ -6,11 +6,17 @@ linters:
|
||||
- copyloopvar
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- errorlint
|
||||
- govet
|
||||
- intrange
|
||||
- ineffassign
|
||||
- misspell
|
||||
- modernize
|
||||
- nilerr
|
||||
- nilnesserr
|
||||
- nolintlint
|
||||
- perfsprint
|
||||
- rowserrcheck
|
||||
- sloglint
|
||||
- sqlclosecheck
|
||||
@ -18,6 +24,7 @@ linters:
|
||||
- testifylint
|
||||
- unconvert
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
|
||||
formatters:
|
||||
|
||||
@ -8,6 +8,7 @@ All notable changes to `discrawl` will be documented in this file.
|
||||
|
||||
- `dms` now lists local wiretap DM conversations and can read or search one DM thread with `--with`, `--last`, and `--search`, so common DM queries no longer require raw SQL.
|
||||
- `search --dm` and `messages --dm` now target the local-only `@me` archive directly and skip Git snapshot auto-update, since DMs are never imported from the shared mirror.
|
||||
- Go module dependencies and lint rules were refreshed for the current Go toolchain, including stricter JSON marshal checks and modern simplification rules.
|
||||
|
||||
### Fixes
|
||||
|
||||
|
||||
10
go.mod
10
go.mod
@ -7,7 +7,7 @@ require (
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/pelletier/go-toml/v2 v2.3.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
golang.org/x/text v0.35.0
|
||||
golang.org/x/text v0.36.0
|
||||
modernc.org/sqlite v1.49.1
|
||||
)
|
||||
|
||||
@ -15,14 +15,14 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-isatty v0.0.21 // indirect
|
||||
github.com/ncruces/go-strftime v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
modernc.org/libc v1.72.0 // indirect
|
||||
modernc.org/libc v1.72.1 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
||||
41
go.sum
41
go.sum
@ -13,8 +13,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
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/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.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/ncruces/go-strftime v1.0.0 h1:HMFp8mLCTPp341M/ZnA4qaf7ZlsbTc+miZjCLOFAw7w=
|
||||
github.com/ncruces/go-strftime v1.0.0/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
@ -26,32 +26,31 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
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/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
modernc.org/cc/v4 v4.27.3 h1:uNCgn37E5U09mTv1XgskEVUJ8ADKpmFMPxzGJ0TSo+U=
|
||||
modernc.org/cc/v4 v4.27.3/go.mod h1:3YjcbCqhoTTHPycJDRl2WZKKFj0nwcOIPBfEZK0Hdk8=
|
||||
modernc.org/ccgo/v4 v4.32.4 h1:L5OB8rpEX4ZsXEQwGozRfJyJSFHbbNVOoQ59DU9/KuU=
|
||||
modernc.org/ccgo/v4 v4.32.4/go.mod h1:lY7f+fiTDHfcv6YlRgSkxYfhs+UvOEEzj49jAn2TOx0=
|
||||
modernc.org/cc/v4 v4.28.1 h1:XpLbkYVQ24E8tX5u8+yWGvaxerxkR/S4zqxI8ZoSBuc=
|
||||
modernc.org/cc/v4 v4.28.1/go.mod h1:OnovgIhbbMXMu1aISnJ0wvVD1KnW+cAUJkIrAWh+kVI=
|
||||
modernc.org/ccgo/v4 v4.33.0 h1:dspBCm75jsj8Y/ufwAMVfe375L2iYdMyQ2QG/v3hL54=
|
||||
modernc.org/ccgo/v4 v4.33.0/go.mod h1:+RhXBoRYzRwaH21mV/aj6XvQRDtfjcZfAlPMsQo8CR0=
|
||||
modernc.org/fileutil v1.4.0 h1:j6ZzNTftVS054gi281TyLjHPp6CPHr2KCxEXjEbD6SM=
|
||||
modernc.org/fileutil v1.4.0/go.mod h1:EqdKFDxiByqxLk8ozOxObDSfcVOv/54xDs/DUHdvCUU=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
@ -60,14 +59,14 @@ modernc.org/gc/v3 v3.1.2 h1:ZtDCnhonXSZexk/AYsegNRV1lJGgaNZJuKjJSWKyEqo=
|
||||
modernc.org/gc/v3 v3.1.2/go.mod h1:HFK/6AGESC7Ex+EZJhJ2Gni6cTaYpSMmU/cT9RmlfYY=
|
||||
modernc.org/goabi0 v0.2.0 h1:HvEowk7LxcPd0eq6mVOAEMai46V+i7Jrj13t4AzuNks=
|
||||
modernc.org/goabi0 v0.2.0/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.72.0 h1:IEu559v9a0XWjw0DPoVKtXpO2qt5NVLAnFaBbjq+n8c=
|
||||
modernc.org/libc v1.72.0/go.mod h1:tTU8DL8A+XLVkEY3x5E/tO7s2Q/q42EtnNWda/L5QhQ=
|
||||
modernc.org/libc v1.72.1 h1:db1xwJ6u1kE3KHTFTTbe2GCrczHPKzlURP0aDC4NGD0=
|
||||
modernc.org/libc v1.72.1/go.mod h1:HRMiC/PhPGLIPM7GzAFCbI+oSgE3dhZ8FWftmRrHVlY=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/opt v0.2.0 h1:tGyef5ApycA7FSEOMraay9SaTk5zmbx7Tu+cJs4QKZg=
|
||||
modernc.org/opt v0.2.0/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.49.1 h1:dYGHTKcX1sJ+EQDnUzvz4TJ5GbuvhNJa8Fg6ElGx73U=
|
||||
|
||||
@ -2,6 +2,7 @@ package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -250,10 +251,10 @@ func (r *runtime) runWiretap(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("wiretap takes flags only"))
|
||||
return usageErr(errors.New("wiretap takes flags only"))
|
||||
}
|
||||
if *maxFileBytes <= 0 {
|
||||
return usageErr(fmt.Errorf("--max-file-bytes must be positive"))
|
||||
return usageErr(errors.New("--max-file-bytes must be positive"))
|
||||
}
|
||||
runOnce := func(ctx context.Context) error {
|
||||
stats, err := discorddesktop.Import(ctx, r.store, discorddesktop.Options{
|
||||
@ -271,7 +272,7 @@ func (r *runtime) runWiretap(args []string) error {
|
||||
return runOnce(r.ctx)
|
||||
}
|
||||
if *watchEvery < time.Second {
|
||||
return usageErr(fmt.Errorf("--watch-every must be at least 1s"))
|
||||
return usageErr(errors.New("--watch-every must be at least 1s"))
|
||||
}
|
||||
ctx, stop := signal.NotifyContext(r.ctx, os.Interrupt, syscall.SIGTERM)
|
||||
defer stop()
|
||||
@ -294,7 +295,7 @@ func (r *runtime) runWiretap(args []string) error {
|
||||
|
||||
func (r *runtime) runStatus(args []string) error {
|
||||
if len(args) != 0 {
|
||||
return usageErr(fmt.Errorf("status takes no arguments"))
|
||||
return usageErr(errors.New("status takes no arguments"))
|
||||
}
|
||||
dbPath, err := config.ExpandPath(r.cfg.DBPath)
|
||||
if err != nil {
|
||||
@ -317,16 +318,16 @@ func (r *runtime) runEmbed(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("embed takes no positional arguments"))
|
||||
return usageErr(errors.New("embed takes no positional arguments"))
|
||||
}
|
||||
if *limit <= 0 {
|
||||
return usageErr(fmt.Errorf("--limit must be positive"))
|
||||
return usageErr(errors.New("--limit must be positive"))
|
||||
}
|
||||
if *batchSize <= 0 {
|
||||
return usageErr(fmt.Errorf("--batch-size must be positive"))
|
||||
return usageErr(errors.New("--batch-size must be positive"))
|
||||
}
|
||||
if !r.cfg.Search.Embeddings.Enabled {
|
||||
return usageErr(fmt.Errorf("embeddings are disabled in config"))
|
||||
return usageErr(errors.New("embeddings are disabled in config"))
|
||||
}
|
||||
providerFactory := r.newEmbed
|
||||
if providerFactory == nil {
|
||||
@ -364,7 +365,7 @@ func (r *runtime) runEmbed(args []string) error {
|
||||
|
||||
func (r *runtime) runDoctor(args []string) error {
|
||||
if len(args) != 0 {
|
||||
return usageErr(fmt.Errorf("doctor takes no arguments"))
|
||||
return usageErr(errors.New("doctor takes no arguments"))
|
||||
}
|
||||
report := map[string]any{
|
||||
"config_path": r.configPath,
|
||||
|
||||
@ -1365,7 +1365,7 @@ func TestRuntimeConfiguresAttachmentTextOnSyncer(t *testing.T) {
|
||||
require.NoError(t, rt.withServices(true, func() error { return nil }))
|
||||
require.True(t, fakeSync.attachmentTextEnabled)
|
||||
|
||||
cfg.Sync.AttachmentText = ptrBool(false)
|
||||
cfg.Sync.AttachmentText = new(false)
|
||||
require.NoError(t, config.Write(cfgPath, cfg))
|
||||
require.NoError(t, rt.withServices(true, func() error { return nil }))
|
||||
require.False(t, fakeSync.attachmentTextEnabled)
|
||||
@ -1498,10 +1498,6 @@ func discardLogger() *slog.Logger {
|
||||
return slog.New(slog.DiscardHandler)
|
||||
}
|
||||
|
||||
func ptrBool(value bool) *bool {
|
||||
return &value
|
||||
}
|
||||
|
||||
func TestRuntimeHelpersAndSubcommands(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
dir := t.TempDir()
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -30,28 +31,28 @@ func (r *runtime) runDirectMessages(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("dms takes flags only"))
|
||||
return usageErr(errors.New("dms takes flags only"))
|
||||
}
|
||||
if *hours < 0 {
|
||||
return usageErr(fmt.Errorf("--hours must be >= 0"))
|
||||
return usageErr(errors.New("--hours must be >= 0"))
|
||||
}
|
||||
if *days < 0 {
|
||||
return usageErr(fmt.Errorf("--days must be >= 0"))
|
||||
return usageErr(errors.New("--days must be >= 0"))
|
||||
}
|
||||
if countNonZero(*hours > 0, *days > 0, strings.TrimSpace(*since) != "") > 1 {
|
||||
return usageErr(fmt.Errorf("use only one of --hours, --days, or --since"))
|
||||
return usageErr(errors.New("use only one of --hours, --days, or --since"))
|
||||
}
|
||||
if *limit < 0 {
|
||||
return usageErr(fmt.Errorf("--limit must be >= 0"))
|
||||
return usageErr(errors.New("--limit must be >= 0"))
|
||||
}
|
||||
if *last < 0 {
|
||||
return usageErr(fmt.Errorf("--last must be >= 0"))
|
||||
return usageErr(errors.New("--last must be >= 0"))
|
||||
}
|
||||
if *all && *last > 0 && flagPassed(fs, "last") {
|
||||
return usageErr(fmt.Errorf("use either --all or --last"))
|
||||
return usageErr(errors.New("use either --all or --last"))
|
||||
}
|
||||
if flagPassed(fs, "limit") && flagPassed(fs, "last") {
|
||||
return usageErr(fmt.Errorf("use either --limit or --last"))
|
||||
return usageErr(errors.New("use either --limit or --last"))
|
||||
}
|
||||
|
||||
if *list || (strings.TrimSpace(*with) == "" && strings.TrimSpace(*search) == "" && noDMMessageTimeFilter(*hours, *days, *since, *before)) {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@ -26,7 +26,7 @@ func (r *runtime) resolveSyncGuildsAll(guild, guilds string, all bool) ([]string
|
||||
return r.resolveSyncGuilds(guild, guilds), nil
|
||||
}
|
||||
if len(csvList(guilds)) > 0 || strings.TrimSpace(guild) != "" {
|
||||
return nil, fmt.Errorf("use either --all or --guild/--guilds")
|
||||
return nil, errors.New("use either --all or --guild/--guilds")
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@ -42,7 +42,7 @@ func directMessageGuildScope(dm bool, guild, guilds string) ([]string, error) {
|
||||
return csvList(strings.Join(requested, ",")), nil
|
||||
}
|
||||
if len(csvList(guilds)) > 0 || strings.TrimSpace(guild) != "" {
|
||||
return nil, fmt.Errorf("use either --dm or --guild/--guilds")
|
||||
return nil, errors.New("use either --dm or --guild/--guilds")
|
||||
}
|
||||
return []string{store.DirectMessageGuildID}, nil
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -27,19 +28,19 @@ func (r *runtime) runMentions(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("mentions takes flags only"))
|
||||
return usageErr(errors.New("mentions takes flags only"))
|
||||
}
|
||||
if *days < 0 {
|
||||
return usageErr(fmt.Errorf("--days must be >= 0"))
|
||||
return usageErr(errors.New("--days must be >= 0"))
|
||||
}
|
||||
if *days > 0 && strings.TrimSpace(*since) != "" {
|
||||
return usageErr(fmt.Errorf("use either --days or --since"))
|
||||
return usageErr(errors.New("use either --days or --since"))
|
||||
}
|
||||
if *limit < 0 {
|
||||
return usageErr(fmt.Errorf("--limit must be >= 0"))
|
||||
return usageErr(errors.New("--limit must be >= 0"))
|
||||
}
|
||||
if targetTypeValue := strings.TrimSpace(*targetType); targetTypeValue != "" && targetTypeValue != "user" && targetTypeValue != "role" {
|
||||
return usageErr(fmt.Errorf("--type must be user or role"))
|
||||
return usageErr(errors.New("--type must be user or role"))
|
||||
}
|
||||
|
||||
var sinceTime time.Time
|
||||
@ -73,7 +74,7 @@ func (r *runtime) runMentions(args []string) error {
|
||||
sinceTime.IsZero() &&
|
||||
beforeTime.IsZero() &&
|
||||
len(guildIDs) == 0 {
|
||||
return usageErr(fmt.Errorf("mentions needs at least one filter"))
|
||||
return usageErr(errors.New("mentions needs at least one filter"))
|
||||
}
|
||||
|
||||
rows, err := r.store.ListMentions(r.ctx, store.MentionListOptions{
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -33,29 +34,29 @@ func (r *runtime) runMessages(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("messages takes flags only"))
|
||||
return usageErr(errors.New("messages takes flags only"))
|
||||
}
|
||||
if *hours < 0 {
|
||||
return usageErr(fmt.Errorf("--hours must be >= 0"))
|
||||
return usageErr(errors.New("--hours must be >= 0"))
|
||||
}
|
||||
if *days < 0 {
|
||||
return usageErr(fmt.Errorf("--days must be >= 0"))
|
||||
return usageErr(errors.New("--days must be >= 0"))
|
||||
}
|
||||
if countNonZero(*hours > 0, *days > 0, strings.TrimSpace(*since) != "") > 1 {
|
||||
return usageErr(fmt.Errorf("use only one of --hours, --days, or --since"))
|
||||
return usageErr(errors.New("use only one of --hours, --days, or --since"))
|
||||
}
|
||||
if *limit < 0 {
|
||||
return usageErr(fmt.Errorf("--limit must be >= 0"))
|
||||
return usageErr(errors.New("--limit must be >= 0"))
|
||||
}
|
||||
if *last < 0 {
|
||||
return usageErr(fmt.Errorf("--last must be >= 0"))
|
||||
return usageErr(errors.New("--last must be >= 0"))
|
||||
}
|
||||
limitSet := flagPassed(fs, "limit")
|
||||
if *all && *last > 0 {
|
||||
return usageErr(fmt.Errorf("use either --all or --last"))
|
||||
return usageErr(errors.New("use either --all or --last"))
|
||||
}
|
||||
if limitSet && *last > 0 {
|
||||
return usageErr(fmt.Errorf("use either --limit or --last"))
|
||||
return usageErr(errors.New("use either --limit or --last"))
|
||||
}
|
||||
if *last > 0 {
|
||||
*limit = 0
|
||||
@ -96,10 +97,10 @@ func (r *runtime) runMessages(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if *dm && *syncNow {
|
||||
return usageErr(fmt.Errorf("messages --sync is not supported with --dm; run wiretap or sync --source wiretap first"))
|
||||
return usageErr(errors.New("messages --sync is not supported with --dm; run wiretap or sync --source wiretap first"))
|
||||
}
|
||||
if strings.TrimSpace(*channel) == "" && strings.TrimSpace(*author) == "" && sinceTime.IsZero() && beforeTime.IsZero() && len(guildIDs) == 0 {
|
||||
return usageErr(fmt.Errorf("messages needs at least one filter"))
|
||||
return usageErr(errors.New("messages needs at least one filter"))
|
||||
}
|
||||
if *all {
|
||||
*limit = 0
|
||||
|
||||
@ -2,6 +2,7 @@ package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"sort"
|
||||
@ -69,7 +70,7 @@ func printPlain(w io.Writer, value any) error {
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("no plain printer")
|
||||
return errors.New("no plain printer")
|
||||
}
|
||||
}
|
||||
|
||||
@ -286,7 +287,7 @@ func printHuman(w io.Writer, value any) error {
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("no human printer")
|
||||
return errors.New("no human printer")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ func (r *runtime) runSearch(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 1 {
|
||||
return usageErr(fmt.Errorf("search requires a query"))
|
||||
return usageErr(errors.New("search requires a query"))
|
||||
}
|
||||
guildIDs, err := directMessageGuildScope(*dm, *guildFlag, *guildsFlag)
|
||||
if err != nil {
|
||||
@ -107,7 +107,7 @@ func (r *runtime) searchMessagesHybrid(opts store.SearchOptions) ([]store.Search
|
||||
|
||||
func (r *runtime) semanticSearchOptions(opts store.SearchOptions) (store.SemanticSearchOptions, error) {
|
||||
if !r.cfg.Search.Embeddings.Enabled {
|
||||
return store.SemanticSearchOptions{}, fmt.Errorf("embeddings are disabled; enable [search.embeddings] first")
|
||||
return store.SemanticSearchOptions{}, errors.New("embeddings are disabled; enable [search.embeddings] first")
|
||||
}
|
||||
providerFactory := r.newEmbed
|
||||
if providerFactory == nil {
|
||||
@ -158,7 +158,7 @@ func (r *runtime) runSQL(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if *confirm && !*unsafe {
|
||||
return usageErr(fmt.Errorf("--confirm requires --unsafe"))
|
||||
return usageErr(errors.New("--confirm requires --unsafe"))
|
||||
}
|
||||
|
||||
var query string
|
||||
@ -184,7 +184,7 @@ func (r *runtime) runSQL(args []string) error {
|
||||
return printRows(r.stdout, cols, rows)
|
||||
}
|
||||
if !*confirm {
|
||||
return usageErr(fmt.Errorf("--unsafe requires --confirm"))
|
||||
return usageErr(errors.New("--unsafe requires --confirm"))
|
||||
}
|
||||
|
||||
if store.IsReadOnlySQL(query) {
|
||||
@ -207,7 +207,7 @@ func (r *runtime) runSQL(args []string) error {
|
||||
|
||||
func (r *runtime) runMembers(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return usageErr(fmt.Errorf("members requires a subcommand"))
|
||||
return usageErr(errors.New("members requires a subcommand"))
|
||||
}
|
||||
switch args[0] {
|
||||
case "list":
|
||||
@ -220,7 +220,7 @@ func (r *runtime) runMembers(args []string) error {
|
||||
return r.runMembersShow(args[1:])
|
||||
case "search":
|
||||
if len(args) < 2 {
|
||||
return usageErr(fmt.Errorf("members search requires a query"))
|
||||
return usageErr(errors.New("members search requires a query"))
|
||||
}
|
||||
rows, err := r.store.Members(r.ctx, "", strings.Join(args[1:], " "), 100)
|
||||
if err != nil {
|
||||
@ -240,7 +240,7 @@ func (r *runtime) runMembersShow(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() < 1 {
|
||||
return usageErr(fmt.Errorf("members show requires a user id or query"))
|
||||
return usageErr(errors.New("members show requires a user id or query"))
|
||||
}
|
||||
query := strings.Join(fs.Args(), " ")
|
||||
|
||||
@ -282,7 +282,7 @@ func (r *runtime) runMembersShow(args []string) error {
|
||||
|
||||
func (r *runtime) runChannels(args []string) error {
|
||||
if len(args) == 0 {
|
||||
return usageErr(fmt.Errorf("channels requires a subcommand"))
|
||||
return usageErr(errors.New("channels requires a subcommand"))
|
||||
}
|
||||
rows, err := r.store.Channels(r.ctx, "")
|
||||
if err != nil {
|
||||
@ -293,7 +293,7 @@ func (r *runtime) runChannels(args []string) error {
|
||||
return r.print(rows)
|
||||
case "show":
|
||||
if len(args) < 2 {
|
||||
return usageErr(fmt.Errorf("channels show requires a channel id"))
|
||||
return usageErr(errors.New("channels show requires a channel id"))
|
||||
}
|
||||
filtered := make([]store.ChannelRow, 0, 1)
|
||||
for _, row := range rows {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
@ -10,7 +11,7 @@ import (
|
||||
|
||||
func (r *runtime) syncMessagesQuery(channel, guild, guilds string) error {
|
||||
if r.syncer == nil {
|
||||
return usageErr(fmt.Errorf("messages --sync requires Discord access"))
|
||||
return usageErr(errors.New("messages --sync requires Discord access"))
|
||||
}
|
||||
opts, err := r.messageSyncOptions(channel, guild, guilds)
|
||||
if err != nil {
|
||||
@ -30,7 +31,7 @@ func (r *runtime) messageSyncOptions(channel, guild, guilds string) (syncer.Sync
|
||||
channelFilter := strings.TrimSpace(strings.TrimPrefix(strings.TrimSpace(channel), "#"))
|
||||
if channelFilter == "" {
|
||||
if len(opts.GuildIDs) == 0 {
|
||||
return opts, fmt.Errorf("messages --sync needs --channel or --guild")
|
||||
return opts, errors.New("messages --sync needs --channel or --guild")
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/steipete/discrawl/internal/report"
|
||||
@ -16,7 +16,7 @@ func (r *runtime) runReport(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("report takes no positional arguments"))
|
||||
return usageErr(errors.New("report takes no positional arguments"))
|
||||
}
|
||||
activity, err := report.Build(r.ctx, r.store, report.Options{})
|
||||
if err != nil {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package cli
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
@ -29,7 +29,7 @@ func (r *runtime) runPublish(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("publish takes no positional arguments"))
|
||||
return usageErr(errors.New("publish takes no positional arguments"))
|
||||
}
|
||||
opts, err := shareOptionsFromFlags(*repoPath, *remote, *branch)
|
||||
if err != nil {
|
||||
@ -100,7 +100,7 @@ func (r *runtime) runSubscribe(args []string) error {
|
||||
}
|
||||
remote := defaultShareRemote
|
||||
if fs.NArg() > 1 {
|
||||
return usageErr(fmt.Errorf("subscribe takes at most one remote"))
|
||||
return usageErr(errors.New("subscribe takes at most one remote"))
|
||||
}
|
||||
if fs.NArg() == 1 {
|
||||
remote = fs.Arg(0)
|
||||
@ -172,7 +172,7 @@ func (r *runtime) runUpdate(args []string) error {
|
||||
return usageErr(err)
|
||||
}
|
||||
if fs.NArg() != 0 {
|
||||
return usageErr(fmt.Errorf("update takes no positional arguments"))
|
||||
return usageErr(errors.New("update takes no positional arguments"))
|
||||
}
|
||||
opts, err := shareOptionsFromFlags(*repoPath, *remote, *branch)
|
||||
if err != nil {
|
||||
|
||||
@ -131,7 +131,7 @@ func Default() Config {
|
||||
Concurrency: defaultSyncConcurrency(),
|
||||
RepairEvery: "6h",
|
||||
FullHistory: true,
|
||||
AttachmentText: boolPtr(true),
|
||||
AttachmentText: new(true),
|
||||
},
|
||||
Search: SearchConfig{
|
||||
DefaultMode: "fts",
|
||||
@ -262,7 +262,7 @@ func (c *Config) Normalize() error {
|
||||
c.Sync.RepairEvery = "6h"
|
||||
}
|
||||
if c.Sync.AttachmentText == nil {
|
||||
c.Sync.AttachmentText = boolPtr(true)
|
||||
c.Sync.AttachmentText = new(true)
|
||||
}
|
||||
if c.Search.DefaultMode == "" {
|
||||
c.Search.DefaultMode = "fts"
|
||||
@ -497,10 +497,6 @@ func normalizeAccount(account string) string {
|
||||
return account
|
||||
}
|
||||
|
||||
func boolPtr(value bool) *bool {
|
||||
return &value
|
||||
}
|
||||
|
||||
func mapKeys[V any](m map[string]V) []string {
|
||||
keys := make([]string, 0, len(m))
|
||||
for key := range m {
|
||||
|
||||
@ -249,7 +249,7 @@ func TestAttachmentTextExplicitFalseSurvivesNormalize(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
cfg := Default()
|
||||
cfg.Sync.AttachmentText = boolPtr(false)
|
||||
cfg.Sync.AttachmentText = new(false)
|
||||
require.NoError(t, cfg.Normalize())
|
||||
require.False(t, cfg.AttachmentTextEnabled())
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@ package discord
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"slices"
|
||||
@ -179,7 +180,7 @@ func (c *Client) ChannelMessage(ctx context.Context, channelID, messageID string
|
||||
|
||||
func (c *Client) Tail(ctx context.Context, handler EventHandler) error {
|
||||
if handler == nil {
|
||||
return fmt.Errorf("missing event handler")
|
||||
return errors.New("missing event handler")
|
||||
}
|
||||
tailCtx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
@ -187,10 +188,8 @@ func (c *Client) Tail(ctx context.Context, handler EventHandler) error {
|
||||
errCh := make(chan error, 1)
|
||||
workCh := make(chan func(context.Context) error, c.tailQueueSize)
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < c.tailWorkerCount; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for range c.tailWorkerCount {
|
||||
wg.Go(func() {
|
||||
for {
|
||||
select {
|
||||
case <-tailCtx.Done():
|
||||
@ -207,7 +206,7 @@ func (c *Client) Tail(ctx context.Context, handler EventHandler) error {
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
c.session.AddHandler(func(_ *discordgo.Session, evt *discordgo.MessageCreate) {
|
||||
@ -299,7 +298,7 @@ func (c *Client) enqueueTailTask(
|
||||
case workCh <- task:
|
||||
default:
|
||||
select {
|
||||
case errCh <- fmt.Errorf("tail worker queue full"):
|
||||
case errCh <- errors.New("tail worker queue full"):
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
@ -400,7 +400,7 @@ func TestTailFailsFastWhenWorkerQueueFills(t *testing.T) {
|
||||
return
|
||||
}
|
||||
now := time.Now().UTC().Format(time.RFC3339)
|
||||
for i := 0; i < 4; i++ {
|
||||
for i := range 4 {
|
||||
if err := conn.WriteJSON(map[string]any{
|
||||
"op": 0,
|
||||
"t": "MESSAGE_CREATE",
|
||||
|
||||
@ -467,7 +467,7 @@ func withRawGuildID(rawJSON, guildID string) string {
|
||||
|
||||
func extractGzipPayloads(data []byte, maxBytes int64) [][]byte {
|
||||
var out [][]byte
|
||||
for offset := 0; offset < len(data)-1; offset++ {
|
||||
for offset := range len(data) - 1 {
|
||||
if data[offset] != 0x1f || data[offset+1] != 0x8b {
|
||||
continue
|
||||
}
|
||||
@ -688,24 +688,24 @@ func messageReferenceID(raw map[string]any) string {
|
||||
}
|
||||
|
||||
func syntheticGuild(id, name string) store.GuildRecord {
|
||||
raw, _ := json.Marshal(map[string]any{
|
||||
raw := marshalJSONString(map[string]any{
|
||||
"id": id,
|
||||
"name": name,
|
||||
"source": "discord_desktop",
|
||||
})
|
||||
return store.GuildRecord{ID: id, Name: name, RawJSON: string(raw)}
|
||||
}, "{}")
|
||||
return store.GuildRecord{ID: id, Name: name, RawJSON: raw}
|
||||
}
|
||||
|
||||
func syntheticChannel(id, guildID, name string) store.ChannelRecord {
|
||||
if name == "" {
|
||||
name = "channel-" + shortID(id)
|
||||
}
|
||||
raw, _ := json.Marshal(map[string]any{
|
||||
raw := marshalJSONString(map[string]any{
|
||||
"id": id,
|
||||
"guild_id": guildID,
|
||||
"name": name,
|
||||
"source": "discord_desktop",
|
||||
})
|
||||
}, "{}")
|
||||
kind := "text"
|
||||
if guildID == DirectMessageGuildID {
|
||||
kind = "dm"
|
||||
@ -713,7 +713,7 @@ func syntheticChannel(id, guildID, name string) store.ChannelRecord {
|
||||
kind = "group_dm"
|
||||
}
|
||||
}
|
||||
return store.ChannelRecord{ID: id, GuildID: guildID, Kind: kind, Name: name, RawJSON: string(raw)}
|
||||
return store.ChannelRecord{ID: id, GuildID: guildID, Kind: kind, Name: name, RawJSON: raw}
|
||||
}
|
||||
|
||||
func guildName(id string) string {
|
||||
@ -751,15 +751,14 @@ func kindForChannelType(typeValue int, dm bool) string {
|
||||
}
|
||||
|
||||
func channelRawJSON(raw map[string]any, id, guildID, name, kind string) string {
|
||||
body, _ := json.Marshal(map[string]any{
|
||||
return marshalJSONString(map[string]any{
|
||||
"id": id,
|
||||
"guild_id": guildID,
|
||||
"name": name,
|
||||
"kind": kind,
|
||||
"source": "discord_desktop",
|
||||
"type": raw["type"],
|
||||
})
|
||||
return string(body)
|
||||
}, "{}")
|
||||
}
|
||||
|
||||
func messageRawJSON(raw map[string]any, id, guildID, channelID, authorID string) string {
|
||||
@ -780,8 +779,7 @@ func messageRawJSON(raw map[string]any, id, guildID, channelID, authorID string)
|
||||
if author := sanitizedRawAuthor(raw, authorID); len(author) > 0 {
|
||||
payload["author"] = author
|
||||
}
|
||||
body, _ := json.Marshal(payload)
|
||||
return string(body)
|
||||
return marshalJSONString(payload, "{}")
|
||||
}
|
||||
|
||||
func recipientLabel(items []any) string {
|
||||
@ -929,3 +927,11 @@ func mapValues[M ~map[string]T, T any](m M) []T {
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func marshalJSONString(value any, fallback string) string {
|
||||
raw, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
@ -80,7 +80,7 @@ type EmbeddingManifest struct {
|
||||
|
||||
func EnsureRepo(ctx context.Context, opts Options) error {
|
||||
if strings.TrimSpace(opts.RepoPath) == "" {
|
||||
return fmt.Errorf("share repo path is empty")
|
||||
return errors.New("share repo path is empty")
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(opts.RepoPath, ".git")); err == nil {
|
||||
return nil
|
||||
@ -477,7 +477,7 @@ func exportEmbeddings(ctx context.Context, db *sql.DB, opts Options) (EmbeddingM
|
||||
inputVersion = store.EmbeddingInputVersion
|
||||
}
|
||||
if provider == "" || model == "" {
|
||||
return EmbeddingManifest{}, fmt.Errorf("embedding provider and model are required")
|
||||
return EmbeddingManifest{}, errors.New("embedding provider and model are required")
|
||||
}
|
||||
relDir := filepath.ToSlash(filepath.Join("embeddings", safePathSegment(provider), safePathSegment(model), safePathSegment(inputVersion)))
|
||||
if err := os.RemoveAll(filepath.Join(opts.RepoPath, "embeddings")); err != nil {
|
||||
|
||||
@ -18,8 +18,8 @@ type DirectMessageConversationRow struct {
|
||||
Name string `json:"name"`
|
||||
MessageCount int `json:"message_count"`
|
||||
AuthorCount int `json:"author_count"`
|
||||
FirstMessageAt time.Time `json:"first_message_at,omitempty"`
|
||||
LastMessageAt time.Time `json:"last_message_at,omitempty"`
|
||||
FirstMessageAt time.Time `json:"first_message_at,omitzero"`
|
||||
LastMessageAt time.Time `json:"last_message_at,omitzero"`
|
||||
}
|
||||
|
||||
func (s *Store) DirectMessageConversations(ctx context.Context, opts DirectMessageConversationOptions) ([]DirectMessageConversationRow, error) {
|
||||
|
||||
@ -3,6 +3,7 @@ package store
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/url"
|
||||
"slices"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -147,10 +148,8 @@ func shouldIgnoreProfileValue(key, value string) bool {
|
||||
}
|
||||
|
||||
func appendUnique(items []string, value string) []string {
|
||||
for _, item := range items {
|
||||
if item == value {
|
||||
return items
|
||||
}
|
||||
if slices.Contains(items, value) {
|
||||
return items
|
||||
}
|
||||
return append(items, value)
|
||||
}
|
||||
|
||||
@ -13,8 +13,8 @@ type MemberProfile struct {
|
||||
Member MemberRow `json:"member"`
|
||||
RawJSON string `json:"raw_json,omitempty"`
|
||||
MessageCount int `json:"message_count"`
|
||||
FirstMessageAt time.Time `json:"first_message_at,omitempty"`
|
||||
LastMessageAt time.Time `json:"last_message_at,omitempty"`
|
||||
FirstMessageAt time.Time `json:"first_message_at,omitzero"`
|
||||
LastMessageAt time.Time `json:"last_message_at,omitzero"`
|
||||
RecentMessages []MessageRow `json:"recent_messages,omitempty"`
|
||||
}
|
||||
|
||||
|
||||
@ -705,10 +705,10 @@ func (s *Store) Status(ctx context.Context, dbPath, defaultGuildID string) (Stat
|
||||
func (s *Store) ReadOnlyQuery(ctx context.Context, query string) ([]string, [][]string, error) {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
return nil, nil, fmt.Errorf("empty query")
|
||||
return nil, nil, errors.New("empty query")
|
||||
}
|
||||
if !IsReadOnlySQL(query) {
|
||||
return nil, nil, fmt.Errorf("only read-only sql is allowed")
|
||||
return nil, nil, errors.New("only read-only sql is allowed")
|
||||
}
|
||||
db, closeFn, err := s.openReadOnlyDB()
|
||||
if err != nil {
|
||||
@ -723,7 +723,7 @@ func (s *Store) ReadOnlyQuery(ctx context.Context, query string) ([]string, [][]
|
||||
func (s *Store) Query(ctx context.Context, query string) ([]string, [][]string, error) {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
return nil, nil, fmt.Errorf("empty query")
|
||||
return nil, nil, errors.New("empty query")
|
||||
}
|
||||
return queryRows(ctx, s.db, query)
|
||||
}
|
||||
@ -731,7 +731,7 @@ func (s *Store) Query(ctx context.Context, query string) ([]string, [][]string,
|
||||
func (s *Store) Exec(ctx context.Context, query string) (int64, error) {
|
||||
query = strings.TrimSpace(query)
|
||||
if query == "" {
|
||||
return 0, fmt.Errorf("empty query")
|
||||
return 0, errors.New("empty query")
|
||||
}
|
||||
queryCtx, cancel := withQueryTimeout(ctx)
|
||||
defer cancel()
|
||||
@ -762,7 +762,7 @@ func queryRows(ctx context.Context, db *sql.DB, query string) ([]string, [][]str
|
||||
return nil, nil, err
|
||||
}
|
||||
if len(cols) == 0 {
|
||||
return nil, nil, fmt.Errorf("query returned no columns")
|
||||
return nil, nil, errors.New("query returned no columns")
|
||||
}
|
||||
|
||||
var out [][]string
|
||||
|
||||
@ -34,8 +34,8 @@ type Status struct {
|
||||
MessageCount int `json:"message_count"`
|
||||
MemberCount int `json:"member_count"`
|
||||
EmbeddingBacklog int `json:"embedding_backlog"`
|
||||
LastSyncAt time.Time `json:"last_sync_at,omitempty"`
|
||||
LastTailEventAt time.Time `json:"last_tail_event_at,omitempty"`
|
||||
LastSyncAt time.Time `json:"last_sync_at,omitzero"`
|
||||
LastTailEventAt time.Time `json:"last_tail_event_at,omitzero"`
|
||||
DefaultGuildID string `json:"default_guild_id,omitempty"`
|
||||
DefaultGuildName string `json:"default_guild_name,omitempty"`
|
||||
AccessibleGuildIDs []string `json:"accessible_guild_ids,omitempty"`
|
||||
@ -86,7 +86,7 @@ type MemberRow struct {
|
||||
Avatar string `json:"avatar,omitempty"`
|
||||
RoleIDsJSON string `json:"role_ids_json"`
|
||||
Bot bool `json:"bot"`
|
||||
JoinedAt time.Time `json:"joined_at,omitempty"`
|
||||
JoinedAt time.Time `json:"joined_at,omitzero"`
|
||||
Bio string `json:"bio,omitempty"`
|
||||
Pronouns string `json:"pronouns,omitempty"`
|
||||
Location string `json:"location,omitempty"`
|
||||
@ -110,7 +110,7 @@ type ChannelRow struct {
|
||||
IsLocked bool `json:"is_locked"`
|
||||
IsPrivateThread bool `json:"is_private_thread"`
|
||||
ThreadParentID string `json:"thread_parent_id,omitempty"`
|
||||
ArchiveTimestamp time.Time `json:"archive_timestamp,omitempty"`
|
||||
ArchiveTimestamp time.Time `json:"archive_timestamp,omitzero"`
|
||||
}
|
||||
|
||||
func Open(ctx context.Context, path string) (*Store, error) {
|
||||
|
||||
@ -603,7 +603,7 @@ func TestSearchMessagesSemanticScoresOlderMatchesBeyondRecentWindow(t *testing.T
|
||||
}))
|
||||
require.NoError(t, insertTestEmbedding(ctx, s, "old-best", "ollama", "nomic-embed-text", []float32{1, 0}))
|
||||
|
||||
for i := 0; i < searchCandidateFloor+10; i++ {
|
||||
for i := range searchCandidateFloor + 10 {
|
||||
messageID := "newer-weak-" + strconv.Itoa(i)
|
||||
require.NoError(t, s.UpsertMessage(ctx, MessageRecord{
|
||||
ID: messageID,
|
||||
|
||||
@ -586,7 +586,7 @@ func TestConcurrentMessageUpsertsShareSingleWriter(t *testing.T) {
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errCh := make(chan error, 8)
|
||||
for i := 0; i < 8; i++ {
|
||||
for i := range 8 {
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
defer wg.Done()
|
||||
|
||||
@ -2,7 +2,7 @@ package syncer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
@ -14,7 +14,7 @@ import (
|
||||
)
|
||||
|
||||
func errMissingAccess() error {
|
||||
return fmt.Errorf("HTTP 403 Forbidden, {\"message\": \"Missing Access\", \"code\": 50001}")
|
||||
return errors.New("HTTP 403 Forbidden, {\"message\": \"Missing Access\", \"code\": 50001}")
|
||||
}
|
||||
|
||||
func TestActiveThreadCatalogEdges(t *testing.T) {
|
||||
@ -34,7 +34,7 @@ func TestActiveThreadCatalogEdges(t *testing.T) {
|
||||
require.NoError(t, svc.appendActiveThreadCatalog(ctx, allChannels, "g1", []string{"forum"}))
|
||||
require.Equal(t, 1, client.guildThreadCalls)
|
||||
|
||||
client.guildThreadErrs = map[string]error{"g1": fmt.Errorf("boom")}
|
||||
client.guildThreadErrs = map[string]error{"g1": errors.New("boom")}
|
||||
require.ErrorContains(t, svc.appendActiveThreadCatalog(ctx, allChannels, "g1", []string{"forum"}), "boom")
|
||||
|
||||
client.guildThreadErrs = nil
|
||||
|
||||
@ -121,9 +121,7 @@ func (s *Syncer) syncMessageChannelsConcurrent(
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for range workers {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
wg.Go(func() {
|
||||
for channel := range jobs {
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
@ -151,7 +149,7 @@ func (s *Syncer) syncMessageChannelsConcurrent(
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
})
|
||||
}
|
||||
|
||||
go func() {
|
||||
|
||||
@ -3,7 +3,7 @@ package syncer
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"io"
|
||||
"log/slog"
|
||||
"strings"
|
||||
@ -63,9 +63,9 @@ func TestMessageSyncProgressFinishReportsSummaryCounts(t *testing.T) {
|
||||
third := &discordgo.Channel{ID: "c3", Name: "ok"}
|
||||
|
||||
progress.start(first)
|
||||
progress.recordSkip(first, fmt.Errorf(`HTTP 403 Forbidden, {"message": "Missing Access", "code": 50001}`))
|
||||
progress.recordSkip(first, errors.New(`HTTP 403 Forbidden, {"message": "Missing Access", "code": 50001}`))
|
||||
progress.start(second)
|
||||
progress.recordSkip(second, fmt.Errorf(`HTTP 404 Not Found, {"message": "Unknown Channel", "code": 10003}`))
|
||||
progress.recordSkip(second, errors.New(`HTTP 404 Not Found, {"message": "Unknown Channel", "code": 10003}`))
|
||||
progress.start(third)
|
||||
progress.record(third, 42)
|
||||
progress.finish(nil)
|
||||
|
||||
@ -13,8 +13,8 @@ import (
|
||||
)
|
||||
|
||||
func toMemberRecord(guildID string, member *discordgo.Member) store.MemberRecord {
|
||||
raw, _ := json.Marshal(member)
|
||||
roles, _ := json.Marshal(member.Roles)
|
||||
raw := marshalJSONString(member, "{}")
|
||||
roles := marshalJSONString(member.Roles, "[]")
|
||||
return store.MemberRecord{
|
||||
GuildID: guildID,
|
||||
UserID: member.User.ID,
|
||||
@ -26,13 +26,13 @@ func toMemberRecord(guildID string, member *discordgo.Member) store.MemberRecord
|
||||
Avatar: member.Avatar,
|
||||
Bot: member.User.Bot,
|
||||
JoinedAt: member.JoinedAt.Format(time.RFC3339Nano),
|
||||
RoleIDsJSON: string(roles),
|
||||
RawJSON: string(raw),
|
||||
RoleIDsJSON: roles,
|
||||
RawJSON: raw,
|
||||
}
|
||||
}
|
||||
|
||||
func toMessageRecord(message *discordgo.Message, channelName, normalizedContent string) store.MessageRecord {
|
||||
raw, _ := json.Marshal(message)
|
||||
raw := marshalJSONString(message, "{}")
|
||||
authorID := ""
|
||||
authorName := ""
|
||||
if message.Author != nil {
|
||||
@ -65,10 +65,18 @@ func toMessageRecord(message *discordgo.Message, channelName, normalizedContent
|
||||
ReplyToMessageID: replyTo,
|
||||
Pinned: message.Pinned,
|
||||
HasAttachments: len(message.Attachments) > 0,
|
||||
RawJSON: string(raw),
|
||||
RawJSON: raw,
|
||||
}
|
||||
}
|
||||
|
||||
func marshalJSONString(value any, fallback string) string {
|
||||
raw, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
return string(raw)
|
||||
}
|
||||
|
||||
func normalizeMessage(message *discordgo.Message) string {
|
||||
return normalizeMessageParts(message, nil)
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package syncer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
@ -124,12 +123,11 @@ func (s *Syncer) syncGuild(ctx context.Context, guildID string, opts SyncOptions
|
||||
if err != nil {
|
||||
return SyncStats{}, fmt.Errorf("fetch guild %s: %w", guildID, err)
|
||||
}
|
||||
rawGuild, _ := json.Marshal(guild)
|
||||
if err := s.store.UpsertGuild(ctx, store.GuildRecord{
|
||||
ID: guild.ID,
|
||||
Name: guild.Name,
|
||||
Icon: guild.Icon,
|
||||
RawJSON: string(rawGuild),
|
||||
RawJSON: marshalJSONString(guild, "{}"),
|
||||
}); err != nil {
|
||||
return SyncStats{}, err
|
||||
}
|
||||
@ -160,8 +158,7 @@ func (s *Syncer) syncGuild(ctx context.Context, guildID string, opts SyncOptions
|
||||
return stats, err
|
||||
}
|
||||
for _, channel := range channelList {
|
||||
raw, _ := json.Marshal(channel)
|
||||
record := toChannelRecord(channel, string(raw))
|
||||
record := toChannelRecord(channel, marshalJSONString(channel, "{}"))
|
||||
if err := s.store.UpsertChannel(ctx, record); err != nil {
|
||||
return stats, err
|
||||
}
|
||||
@ -204,10 +201,7 @@ func (s *Syncer) syncGuildIncompleteBatches(ctx context.Context, guildID string,
|
||||
}
|
||||
stats := SyncStats{}
|
||||
for start := 0; start < len(incomplete); start += fullSyncBatchSize {
|
||||
end := start + fullSyncBatchSize
|
||||
if end > len(incomplete) {
|
||||
end = len(incomplete)
|
||||
}
|
||||
end := min(start+fullSyncBatchSize, len(incomplete))
|
||||
batchOpts := opts
|
||||
batchOpts.ChannelIDs = incomplete[start:end]
|
||||
one, err := s.syncGuild(ctx, guildID, batchOpts)
|
||||
|
||||
@ -3,7 +3,6 @@ package syncer
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
@ -186,16 +185,16 @@ func TestHelpers(t *testing.T) {
|
||||
require.Equal(t, "Nick", displayName(&discordgo.Member{Nick: "Nick", User: &discordgo.User{Username: "user"}}))
|
||||
require.Equal(t, "Global", displayName(&discordgo.Member{User: &discordgo.User{GlobalName: "Global", Username: "user"}}))
|
||||
require.Equal(t, "user", displayName(&discordgo.Member{User: &discordgo.User{Username: "user"}}))
|
||||
require.True(t, isMissingAccess(fmt.Errorf("HTTP 403 Forbidden")))
|
||||
require.True(t, isMissingAccess(fmt.Errorf("Missing Access")))
|
||||
require.False(t, isMissingAccess(fmt.Errorf("boom")))
|
||||
require.Equal(t, "missing_access", unavailableReason(fmt.Errorf("HTTP 403 Forbidden")))
|
||||
require.Equal(t, "unknown_channel", unavailableReason(fmt.Errorf("HTTP 404 Not Found, {\"message\": \"Unknown Channel\", \"code\": 10003}")))
|
||||
require.True(t, isUnknownChannel(fmt.Errorf("Unknown Channel")))
|
||||
require.False(t, isUnknownChannel(fmt.Errorf("boom")))
|
||||
require.True(t, isMissingAccess(errors.New("HTTP 403 Forbidden")))
|
||||
require.True(t, isMissingAccess(errors.New("Missing Access")))
|
||||
require.False(t, isMissingAccess(errors.New("boom")))
|
||||
require.Equal(t, "missing_access", unavailableReason(errors.New("HTTP 403 Forbidden")))
|
||||
require.Equal(t, "unknown_channel", unavailableReason(errors.New("HTTP 404 Not Found, {\"message\": \"Unknown Channel\", \"code\": 10003}")))
|
||||
require.True(t, isUnknownChannel(errors.New("Unknown Channel")))
|
||||
require.False(t, isUnknownChannel(errors.New("boom")))
|
||||
require.True(t, isRetryableSyncError(context.Background(), context.DeadlineExceeded))
|
||||
require.True(t, isRetryableSyncError(context.Background(), fmt.Errorf("HTTP 503 Service Unavailable")))
|
||||
require.True(t, isRetryableSyncError(context.Background(), fmt.Errorf("stream error: stream ID 1; INTERNAL_ERROR")))
|
||||
require.True(t, isRetryableSyncError(context.Background(), errors.New("HTTP 503 Service Unavailable")))
|
||||
require.True(t, isRetryableSyncError(context.Background(), errors.New("stream error: stream ID 1; INTERNAL_ERROR")))
|
||||
require.False(t, isRetryableSyncError(context.Background(), context.Canceled))
|
||||
canceledCtx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
@ -2,7 +2,7 @@ package syncer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
@ -777,7 +777,7 @@ func TestSyncSkipsMissingAccessChannels(t *testing.T) {
|
||||
"c1": {{ID: "10", GuildID: "g1", ChannelID: "c1", Content: "ok", Timestamp: time.Now().UTC(), Author: &discordgo.User{ID: "u1", Username: "user"}}},
|
||||
},
|
||||
messageErrors: map[string]error{
|
||||
"c2": fmt.Errorf("HTTP 403 Forbidden, {\"message\": \"Missing Access\", \"code\": 50001}"),
|
||||
"c2": errors.New("HTTP 403 Forbidden, {\"message\": \"Missing Access\", \"code\": 50001}"),
|
||||
},
|
||||
}
|
||||
|
||||
@ -814,7 +814,7 @@ func TestSyncSkipsUnknownChannels(t *testing.T) {
|
||||
"c1": {{ID: "10", GuildID: "g1", ChannelID: "c1", Content: "ok", Timestamp: time.Now().UTC(), Author: &discordgo.User{ID: "u1", Username: "user"}}},
|
||||
},
|
||||
messageErrors: map[string]error{
|
||||
"c2": fmt.Errorf("HTTP 404 Not Found, {\"message\": \"Unknown Channel\", \"code\": 10003}"),
|
||||
"c2": errors.New("HTTP 404 Not Found, {\"message\": \"Unknown Channel\", \"code\": 10003}"),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,6 @@ package syncer
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
@ -97,8 +96,7 @@ func (t *tailHandler) OnChannelUpsert(ctx context.Context, channel *discordgo.Ch
|
||||
if !t.allowGuild(channel.GuildID) {
|
||||
return nil
|
||||
}
|
||||
raw, _ := json.Marshal(channel)
|
||||
return t.store.UpsertChannel(ctx, toChannelRecord(channel, string(raw)))
|
||||
return t.store.UpsertChannel(ctx, toChannelRecord(channel, marshalJSONString(channel, "{}")))
|
||||
}
|
||||
|
||||
func (t *tailHandler) OnMemberUpsert(ctx context.Context, guildID string, member *discordgo.Member) error {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user