mirror of
https://github.com/superseriousbusiness/gotosocial.git
synced 2025-10-29 06:22:25 -05:00
[feature] Add experimental instance-federation-spam-filter option (#2685)
* [chore] Move `visibility` to `filter/visibility` * [feature] Add experimental instance-federation-spam-filter option
This commit is contained in:
parent
87e8cca3ae
commit
9cadc764b3
80 changed files with 1638 additions and 137 deletions
|
|
@ -33,6 +33,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// ExtractObjects will extract object vocab.Types from given implementing interface.
|
||||
|
|
@ -776,11 +777,21 @@ func extractHashtag(i Hashtaggable) (*gtsmodel.Tag, error) {
|
|||
}
|
||||
tagName := strings.TrimPrefix(name, "#")
|
||||
|
||||
yeah := func() *bool { t := true; return &t }
|
||||
// Extract href for the tag, if set.
|
||||
//
|
||||
// Fine if not, it's only used for spam
|
||||
// checking anyway so not critical.
|
||||
var href string
|
||||
hrefProp := i.GetActivityStreamsHref()
|
||||
if hrefProp != nil && hrefProp.IsIRI() {
|
||||
href = hrefProp.GetIRI().String()
|
||||
}
|
||||
|
||||
return >smodel.Tag{
|
||||
Name: tagName,
|
||||
Useable: yeah(), // Assume true by default.
|
||||
Listable: yeah(), // Assume true by default.
|
||||
Useable: util.Ptr(true), // Assume true by default.
|
||||
Listable: util.Ptr(true), // Assume true by default.
|
||||
Href: href,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
|
|
@ -36,7 +37,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/middleware"
|
||||
|
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -37,7 +38,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -37,7 +38,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -42,7 +43,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -36,7 +37,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -36,7 +37,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
|
|
@ -45,7 +46,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
|
|
@ -42,7 +43,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -36,7 +37,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -42,7 +43,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
|
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -30,7 +31,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
|
|
@ -35,7 +36,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ type Configuration struct {
|
|||
WebAssetBaseDir string `name:"web-asset-base-dir" usage:"Directory to serve static assets from, accessible at example.org/assets/"`
|
||||
|
||||
InstanceFederationMode string `name:"instance-federation-mode" usage:"Set instance federation mode."`
|
||||
InstanceFederationSpamFilter bool `name:"instance-federation-spam-filter" usage:"Enable basic spam filter heuristics for messages coming from other instances, and drop messages identified as spam"`
|
||||
InstanceExposePeers bool `name:"instance-expose-peers" usage:"Allow unauthenticated users to query /api/v1/instance/peers?filter=open"`
|
||||
InstanceExposeSuspended bool `name:"instance-expose-suspended" usage:"Expose suspended instances via web UI, and allow unauthenticated users to query /api/v1/instance/peers?filter=suspended"`
|
||||
InstanceExposeSuspendedWeb bool `name:"instance-expose-suspended-web" usage:"Expose list of suspended instances as webpage on /about/suspended"`
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ var Defaults = Configuration{
|
|||
WebAssetBaseDir: "./web/assets/",
|
||||
|
||||
InstanceFederationMode: InstanceFederationModeDefault,
|
||||
InstanceFederationSpamFilter: false,
|
||||
InstanceExposePeers: false,
|
||||
InstanceExposeSuspended: false,
|
||||
InstanceExposeSuspendedWeb: false,
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ func (s *ConfigState) AddServerFlags(cmd *cobra.Command) {
|
|||
|
||||
// Instance
|
||||
cmd.Flags().String(InstanceFederationModeFlag(), cfg.InstanceFederationMode, fieldtag("InstanceFederationMode", "usage"))
|
||||
cmd.Flags().Bool(InstanceFederationSpamFilterFlag(), cfg.InstanceFederationSpamFilter, fieldtag("InstanceFederationSpamFilter", "usage"))
|
||||
cmd.Flags().Bool(InstanceExposePeersFlag(), cfg.InstanceExposePeers, fieldtag("InstanceExposePeers", "usage"))
|
||||
cmd.Flags().Bool(InstanceExposeSuspendedFlag(), cfg.InstanceExposeSuspended, fieldtag("InstanceExposeSuspended", "usage"))
|
||||
cmd.Flags().Bool(InstanceExposeSuspendedWebFlag(), cfg.InstanceExposeSuspendedWeb, fieldtag("InstanceExposeSuspendedWeb", "usage"))
|
||||
|
|
|
|||
|
|
@ -775,6 +775,31 @@ func GetInstanceFederationMode() string { return global.GetInstanceFederationMod
|
|||
// SetInstanceFederationMode safely sets the value for global configuration 'InstanceFederationMode' field
|
||||
func SetInstanceFederationMode(v string) { global.SetInstanceFederationMode(v) }
|
||||
|
||||
// GetInstanceFederationSpamFilter safely fetches the Configuration value for state's 'InstanceFederationSpamFilter' field
|
||||
func (st *ConfigState) GetInstanceFederationSpamFilter() (v bool) {
|
||||
st.mutex.RLock()
|
||||
v = st.config.InstanceFederationSpamFilter
|
||||
st.mutex.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// SetInstanceFederationSpamFilter safely sets the Configuration value for state's 'InstanceFederationSpamFilter' field
|
||||
func (st *ConfigState) SetInstanceFederationSpamFilter(v bool) {
|
||||
st.mutex.Lock()
|
||||
defer st.mutex.Unlock()
|
||||
st.config.InstanceFederationSpamFilter = v
|
||||
st.reloadToViper()
|
||||
}
|
||||
|
||||
// InstanceFederationSpamFilterFlag returns the flag name for the 'InstanceFederationSpamFilter' field
|
||||
func InstanceFederationSpamFilterFlag() string { return "instance-federation-spam-filter" }
|
||||
|
||||
// GetInstanceFederationSpamFilter safely fetches the value for global configuration 'InstanceFederationSpamFilter' field
|
||||
func GetInstanceFederationSpamFilter() bool { return global.GetInstanceFederationSpamFilter() }
|
||||
|
||||
// SetInstanceFederationSpamFilter safely sets the value for global configuration 'InstanceFederationSpamFilter' field
|
||||
func SetInstanceFederationSpamFilter(v bool) { global.SetInstanceFederationSpamFilter(v) }
|
||||
|
||||
// GetInstanceExposePeers safely fetches the Configuration value for state's 'InstanceExposePeers' field
|
||||
func (st *ConfigState) GetInstanceExposePeers() (v bool) {
|
||||
st.mutex.RLock()
|
||||
|
|
|
|||
|
|
@ -20,10 +20,10 @@ package bundb_test
|
|||
import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,11 @@ import (
|
|||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation/dereferencing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,13 +21,11 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"codeberg.org/gruf/go-logger/v2/level"
|
||||
"github.com/miekg/dns"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
|
|
@ -35,7 +33,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// Create adds a new entry to the database which must be able to be
|
||||
|
|
@ -321,26 +318,45 @@ func (f *federatingDB) createStatusable(
|
|||
statusable ap.Statusable,
|
||||
forwarded bool,
|
||||
) error {
|
||||
|
||||
// Check whether we should accept this new status,
|
||||
// we do this BEFORE even handling forwards to us.
|
||||
accept, err := f.shouldAcceptStatusable(ctx,
|
||||
// Check whether this status is both
|
||||
// relevant, and doesn't look like spam.
|
||||
err := f.spamFilter.StatusableOK(ctx,
|
||||
receiver,
|
||||
requester,
|
||||
statusable,
|
||||
)
|
||||
if err != nil {
|
||||
return gtserror.Newf("error checking status acceptibility: %w", err)
|
||||
}
|
||||
|
||||
if !accept {
|
||||
// This is a status sent with no relation to receiver, i.e.
|
||||
// - receiving account does not follow requesting account
|
||||
// - received status does not mention receiving account
|
||||
switch {
|
||||
case err == nil:
|
||||
// No problem!
|
||||
|
||||
case gtserror.IsNotRelevant(err):
|
||||
// This case is quite common if a remote (Mastodon)
|
||||
// instance forwards a message to us which is a reply
|
||||
// from someone else to a status we've also replied to.
|
||||
//
|
||||
// We just pretend that all is fine (dog with cuppa, flames everywhere)
|
||||
log.Trace(ctx, "status failed acceptability check")
|
||||
// It does this to try to ensure thread completion, but
|
||||
// we have our own thread fetching mechanism anyway.
|
||||
log.Debugf(ctx,
|
||||
"status %s is not relevant to receiver (%v); dropping it",
|
||||
ap.GetJSONLDId(statusable), err,
|
||||
)
|
||||
return nil
|
||||
|
||||
case gtserror.IsSpam(err):
|
||||
// Log this at a higher level so admins can
|
||||
// gauge how much spam is being sent to them.
|
||||
//
|
||||
// TODO: add Prometheus metrics for this.
|
||||
log.Infof(ctx,
|
||||
"status %s looked like spam (%v); dropping it",
|
||||
ap.GetJSONLDId(statusable), err,
|
||||
)
|
||||
return nil
|
||||
|
||||
default:
|
||||
// A real error has occurred.
|
||||
return gtserror.Newf("error checking relevancy/spam: %w", err)
|
||||
}
|
||||
|
||||
// If we do have a forward, we should ignore the content
|
||||
|
|
@ -378,52 +394,6 @@ func (f *federatingDB) createStatusable(
|
|||
return nil
|
||||
}
|
||||
|
||||
func (f *federatingDB) shouldAcceptStatusable(ctx context.Context, receiver *gtsmodel.Account, requester *gtsmodel.Account, statusable ap.Statusable) (bool, error) {
|
||||
host := config.GetHost()
|
||||
accountDomain := config.GetAccountDomain()
|
||||
|
||||
// Check whether status mentions the receiver,
|
||||
// this is the quickest check so perform it first.
|
||||
mentions, _ := ap.ExtractMentions(statusable)
|
||||
for _, mention := range mentions {
|
||||
|
||||
// Extract placeholder mention vars.
|
||||
accURI := mention.TargetAccountURI
|
||||
name := mention.NameString
|
||||
|
||||
switch {
|
||||
case accURI != "" &&
|
||||
accURI == receiver.URI || accURI == receiver.URL:
|
||||
// Mention target is receiver,
|
||||
// they are mentioned in status.
|
||||
return true, nil
|
||||
|
||||
case accURI == "" && name != "":
|
||||
// Only a name was provided, extract the user@domain parts.
|
||||
user, domain, err := util.ExtractNamestringParts(name)
|
||||
if err != nil {
|
||||
return false, gtserror.Newf("error extracting mention name parts: %w", err)
|
||||
}
|
||||
|
||||
// Check if the name points to our receiving local user.
|
||||
isLocal := (domain == host || domain == accountDomain)
|
||||
if isLocal && strings.EqualFold(user, receiver.Username) {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check whether receiving account follows the requesting account.
|
||||
follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, requester.ID)
|
||||
if err != nil {
|
||||
return false, gtserror.Newf("error checking follow status: %w", err)
|
||||
}
|
||||
|
||||
// Status will only be acceptable
|
||||
// if receiver follows requester.
|
||||
return follows, nil
|
||||
}
|
||||
|
||||
/*
|
||||
FOLLOW HANDLERS
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -22,12 +22,14 @@ import (
|
|||
|
||||
"github.com/superseriousbusiness/activity/pub"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/spam"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// DB wraps the pub.Database interface with a couple of custom functions for GoToSocial.
|
||||
// DB wraps the pub.Database interface with
|
||||
// a couple of custom functions for GoToSocial.
|
||||
type DB interface {
|
||||
pub.Database
|
||||
Undo(ctx context.Context, undo vocab.ActivityStreamsUndo) error
|
||||
|
|
@ -36,20 +38,28 @@ type DB interface {
|
|||
Announce(ctx context.Context, announce vocab.ActivityStreamsAnnounce) error
|
||||
}
|
||||
|
||||
// FederatingDB uses the underlying DB interface to implement the go-fed pub.Database interface.
|
||||
// It doesn't care what the underlying implementation of the DB interface is, as long as it works.
|
||||
// FederatingDB uses the given state interface
|
||||
// to implement the go-fed pub.Database interface.
|
||||
type federatingDB struct {
|
||||
state *state.State
|
||||
converter *typeutils.Converter
|
||||
filter *visibility.Filter
|
||||
state *state.State
|
||||
converter *typeutils.Converter
|
||||
visFilter *visibility.Filter
|
||||
spamFilter *spam.Filter
|
||||
}
|
||||
|
||||
// New returns a DB interface using the given database and config
|
||||
func New(state *state.State, converter *typeutils.Converter, filter *visibility.Filter) DB {
|
||||
// New returns a DB that satisfies the pub.Database
|
||||
// interface, using the given state and filters.
|
||||
func New(
|
||||
state *state.State,
|
||||
converter *typeutils.Converter,
|
||||
visFilter *visibility.Filter,
|
||||
spamFilter *spam.Filter,
|
||||
) DB {
|
||||
fdb := federatingDB{
|
||||
state: state,
|
||||
converter: converter,
|
||||
filter: filter,
|
||||
state: state,
|
||||
converter: converter,
|
||||
visFilter: visFilter,
|
||||
spamFilter: spamFilter,
|
||||
}
|
||||
return &fdb
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation/federatingdb"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,12 +23,12 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
32
internal/filter/spam/spam.go
Normal file
32
internal/filter/spam/spam.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package spam
|
||||
|
||||
import "github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
|
||||
// Filter packages logic for checking whether
|
||||
// given statuses should be considered spam.
|
||||
type Filter struct {
|
||||
state *state.State
|
||||
}
|
||||
|
||||
// NewFilter returns a new spam Filter
|
||||
// that will use the provided state.
|
||||
func NewFilter(state *state.State) *Filter {
|
||||
return &Filter{state: state}
|
||||
}
|
||||
59
internal/filter/spam/spam_test.go
Normal file
59
internal/filter/spam/spam_test.go
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package spam_test
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/spam"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
type FilterStandardTestSuite struct {
|
||||
// standard suite interfaces
|
||||
suite.Suite
|
||||
db db.DB
|
||||
state state.State
|
||||
|
||||
// standard suite models
|
||||
testAccounts map[string]*gtsmodel.Account
|
||||
|
||||
filter *spam.Filter
|
||||
}
|
||||
|
||||
func (suite *FilterStandardTestSuite) SetupSuite() {
|
||||
suite.testAccounts = testrig.NewTestAccounts()
|
||||
}
|
||||
|
||||
func (suite *FilterStandardTestSuite) SetupTest() {
|
||||
suite.state.Caches.Init()
|
||||
|
||||
testrig.InitTestConfig()
|
||||
testrig.InitTestLog()
|
||||
|
||||
suite.db = testrig.NewTestDB(&suite.state)
|
||||
suite.filter = spam.NewFilter(&suite.state)
|
||||
|
||||
testrig.StandardDBSetup(suite.db, nil)
|
||||
}
|
||||
|
||||
func (suite *FilterStandardTestSuite) TearDownTest() {
|
||||
testrig.StandardDBTeardown(suite.db)
|
||||
}
|
||||
472
internal/filter/spam/statusable.go
Normal file
472
internal/filter/spam/statusable.go
Normal file
|
|
@ -0,0 +1,472 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package spam
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"net/url"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/miekg/dns"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/config"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/regexes"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
)
|
||||
|
||||
// preppedMention represents a partially-parsed
|
||||
// mention, prepared for spam checking purposes.
|
||||
type preppedMention struct {
|
||||
*gtsmodel.Mention
|
||||
uri *url.URL
|
||||
domain string
|
||||
user string
|
||||
local bool
|
||||
}
|
||||
|
||||
// StatusableOK returns no error if the given statusable looks OK,
|
||||
// ie., relevant to the receiver, and not spam.
|
||||
//
|
||||
// This should only be used for Creates of statusables, NOT Announces!
|
||||
//
|
||||
// If the statusable does not pass relevancy or spam checks, either
|
||||
// a Spam or NotRelevant error will be returned. Callers should use
|
||||
// gtserror.IsSpam() and gtserror.IsNotRelevant() to check for this.
|
||||
//
|
||||
// If the returned error is not nil, but neither Spam or NotRelevant,
|
||||
// then it's an actual database error.
|
||||
//
|
||||
// The decision is made based on the following heuristics, in order:
|
||||
//
|
||||
// 1. Receiver follow requester. Return nil.
|
||||
// 2. Statusable doesn't mention receiver. Return NotRelevant.
|
||||
//
|
||||
// If instance-federation-spam-filter = false, then return nil now.
|
||||
// Otherwise check:
|
||||
//
|
||||
// 3. Receiver is locked and is followed by requester. Return nil.
|
||||
// 4. Five or more people are mentioned. Return Spam.
|
||||
// 5. Receiver follow (requests) a mentioned account. Return nil.
|
||||
// 6. Statusable has a media attachment. Return Spam.
|
||||
// 7. Statusable contains non-mention, non-hashtag links. Return Spam.
|
||||
func (f *Filter) StatusableOK(
|
||||
ctx context.Context,
|
||||
receiver *gtsmodel.Account,
|
||||
requester *gtsmodel.Account,
|
||||
statusable ap.Statusable,
|
||||
) error {
|
||||
// HEURISTIC 1: Check whether receiving account follows the requesting account.
|
||||
// If so, we know it's OK and don't need to do any other checks.
|
||||
follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, requester.ID)
|
||||
if err != nil {
|
||||
return gtserror.Newf("db error checking follow status: %w", err)
|
||||
}
|
||||
|
||||
if follows {
|
||||
// Looks fine.
|
||||
return nil
|
||||
}
|
||||
|
||||
// HEURISTIC 2: Check whether statusable mentions the
|
||||
// receiver. If not, we don't want to process this message.
|
||||
rawMentions, _ := ap.ExtractMentions(statusable)
|
||||
mentions := prepMentions(ctx, rawMentions)
|
||||
mentioned := f.isMentioned(ctx, receiver, mentions)
|
||||
if !mentioned {
|
||||
// This is a random message fired
|
||||
// into our inbox, just drop it.
|
||||
err := errors.New("receiver does not follow requester, and is not mentioned")
|
||||
return gtserror.SetNotRelevant(err)
|
||||
}
|
||||
|
||||
// Receiver is mentioned, but not by someone
|
||||
// they follow. Check if we need to do more
|
||||
// granular spam filtering.
|
||||
if !config.GetInstanceFederationSpamFilter() {
|
||||
// Filter is not enabled, allow it
|
||||
// through without further checks.
|
||||
return nil
|
||||
}
|
||||
|
||||
// More granular spam filtering time!
|
||||
//
|
||||
// HEURISTIC 3: Does requester follow locked receiver?
|
||||
followedBy, err := f.lockedFollowedBy(ctx, receiver, requester)
|
||||
if err != nil {
|
||||
return gtserror.Newf("db error checking follow status: %w", err)
|
||||
}
|
||||
|
||||
// If receiver is locked, and is followed
|
||||
// by requester, this likely means they're
|
||||
// interested in the message. Allow it.
|
||||
if followedBy {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HEURISTIC 4: How many people are mentioned?
|
||||
// If it's 5 or more we can assume this is spam.
|
||||
mentionsLen := len(mentions)
|
||||
if mentionsLen >= 5 {
|
||||
err := errors.New("status mentions 5 or more people")
|
||||
return gtserror.SetSpam(err)
|
||||
}
|
||||
|
||||
// HEURISTIC 5: Four or fewer people are mentioned,
|
||||
// do we follow (request) at least one of them?
|
||||
// If so, we're probably interested in the message.
|
||||
knowsOne := f.knowsOneMentioned(ctx, receiver, mentions)
|
||||
if knowsOne {
|
||||
return nil
|
||||
}
|
||||
|
||||
// HEURISTIC 6: Are there any media attachments?
|
||||
attachments, _ := ap.ExtractAttachments(statusable)
|
||||
hasAttachments := len(attachments) != 0
|
||||
if hasAttachments {
|
||||
err := errors.New("status has attachment(s)")
|
||||
return gtserror.SetSpam(err)
|
||||
}
|
||||
|
||||
// HEURISTIC 7: Are there any links in the post
|
||||
// aside from mentions and hashtags? Include the
|
||||
// summary/content warning when checking.
|
||||
hashtags, _ := ap.ExtractHashtags(statusable)
|
||||
hasErrantLinks := f.errantLinks(ctx, statusable, mentions, hashtags)
|
||||
if hasErrantLinks {
|
||||
err := errors.New("status has one or more non-mention, non-hashtag links")
|
||||
return gtserror.SetSpam(err)
|
||||
}
|
||||
|
||||
// Looks OK.
|
||||
return nil
|
||||
}
|
||||
|
||||
// prepMentions prepares a slice of mentions
|
||||
// for spam checking by parsing out the namestring
|
||||
// and targetAccountURI values, if present.
|
||||
func prepMentions(
|
||||
ctx context.Context,
|
||||
mentions []*gtsmodel.Mention,
|
||||
) []preppedMention {
|
||||
var (
|
||||
host = config.GetHost()
|
||||
accountDomain = config.GetAccountDomain()
|
||||
)
|
||||
|
||||
parsedMentions := make([]preppedMention, 0, len(mentions))
|
||||
for _, mention := range mentions {
|
||||
// Start by just embedding
|
||||
// the original mention.
|
||||
parsedMention := preppedMention{
|
||||
Mention: mention,
|
||||
}
|
||||
|
||||
// Try to parse namestring if present.
|
||||
if mention.NameString != "" {
|
||||
user, domain, err := util.ExtractNamestringParts(mention.NameString)
|
||||
if err != nil {
|
||||
// Malformed mention,
|
||||
// just log + ignore.
|
||||
log.Debugf(ctx,
|
||||
"malformed mention namestring: %v",
|
||||
err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
parsedMention.domain = domain
|
||||
parsedMention.user = user
|
||||
}
|
||||
|
||||
// Try to parse URI if present.
|
||||
if mention.TargetAccountURI != "" {
|
||||
targetURI, err := url.Parse(mention.TargetAccountURI)
|
||||
if err != nil {
|
||||
// Malformed mention,
|
||||
// just log + ignore.
|
||||
log.Debugf(ctx,
|
||||
"malformed mention uri: %v",
|
||||
err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
parsedMention.uri = targetURI
|
||||
|
||||
// Set host from targetURI if
|
||||
// it wasn't set by namestring.
|
||||
if parsedMention.domain == "" {
|
||||
parsedMention.domain = targetURI.Host
|
||||
}
|
||||
}
|
||||
|
||||
// It's a mention of a local account if the target host is us.
|
||||
parsedMention.local = parsedMention.domain == host || parsedMention.domain == accountDomain
|
||||
|
||||
// Done with this one.
|
||||
parsedMentions = append(parsedMentions, parsedMention)
|
||||
}
|
||||
|
||||
return parsedMentions
|
||||
}
|
||||
|
||||
// isMentioned returns true if the
|
||||
// receiver is targeted by at least
|
||||
// one of the given mentions.
|
||||
func (f *Filter) isMentioned(
|
||||
ctx context.Context,
|
||||
receiver *gtsmodel.Account,
|
||||
mentions []preppedMention,
|
||||
) bool {
|
||||
return slices.ContainsFunc(
|
||||
mentions,
|
||||
func(mention preppedMention) bool {
|
||||
// Check if receiver mentioned by URI.
|
||||
if accURI := mention.TargetAccountURI; accURI != "" &&
|
||||
(accURI == receiver.URI || accURI == receiver.URL) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Check if receiver mentioned by namestring.
|
||||
if mention.local && strings.EqualFold(mention.user, receiver.Username) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Mention doesn't
|
||||
// target receiver.
|
||||
return false
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// lockedFollowedBy returns true
|
||||
// if receiver account is locked,
|
||||
// and requester follows receiver.
|
||||
func (f *Filter) lockedFollowedBy(
|
||||
ctx context.Context,
|
||||
receiver *gtsmodel.Account,
|
||||
requester *gtsmodel.Account,
|
||||
) (bool, error) {
|
||||
// If receiver is not locked,
|
||||
// return early to avoid a db call.
|
||||
if !*receiver.Locked {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return f.state.DB.IsFollowing(ctx, requester.ID, receiver.ID)
|
||||
}
|
||||
|
||||
// knowsOneMentioned returns true if the
|
||||
// receiver follows or has follow requested
|
||||
// at least one of the mentioned accounts.
|
||||
func (f *Filter) knowsOneMentioned(
|
||||
ctx context.Context,
|
||||
receiver *gtsmodel.Account,
|
||||
mentions []preppedMention,
|
||||
) bool {
|
||||
return slices.ContainsFunc(
|
||||
mentions,
|
||||
func(mention preppedMention) bool {
|
||||
var (
|
||||
acc *gtsmodel.Account
|
||||
err error
|
||||
)
|
||||
|
||||
// Try to get target account without
|
||||
// dereffing. After all, if they're not
|
||||
// in our db we definitely don't know them.
|
||||
if mention.TargetAccountURI != "" {
|
||||
acc, err = f.state.DB.GetAccountByURI(
|
||||
gtscontext.SetBarebones(ctx),
|
||||
mention.TargetAccountURI,
|
||||
)
|
||||
} else if mention.user != "" {
|
||||
acc, err = f.state.DB.GetAccountByUsernameDomain(
|
||||
gtscontext.SetBarebones(ctx),
|
||||
mention.user,
|
||||
mention.domain,
|
||||
)
|
||||
}
|
||||
|
||||
if err != nil && !errors.Is(err, db.ErrNoEntries) {
|
||||
// Proper error.
|
||||
log.Errorf(ctx, "db error getting mentioned account: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if acc == nil {
|
||||
// We don't know this nerd!
|
||||
return false
|
||||
}
|
||||
|
||||
if acc.ID == receiver.ID {
|
||||
// This is us, doesn't count.
|
||||
return false
|
||||
}
|
||||
|
||||
follows, err := f.state.DB.IsFollowing(ctx, receiver.ID, acc.ID)
|
||||
if err != nil {
|
||||
// Proper error.
|
||||
log.Errorf(ctx, "db error checking follow status: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if follows {
|
||||
// We follow this nerd.
|
||||
return true
|
||||
}
|
||||
|
||||
// We don't follow this nerd, but
|
||||
// have we requested to follow them?
|
||||
followRequested, err := f.state.DB.IsFollowRequested(ctx, receiver.ID, acc.ID)
|
||||
if err != nil {
|
||||
// Proper error.
|
||||
log.Errorf(ctx, "db error checking follow req status: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
return followRequested
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// errantLinks returns true if any http/https
|
||||
// link discovered in the statusable content + cw
|
||||
// is not either a mention link, or a hashtag link.
|
||||
func (f *Filter) errantLinks(
|
||||
ctx context.Context,
|
||||
statusable ap.Statusable,
|
||||
mentions []preppedMention,
|
||||
hashtags []*gtsmodel.Tag,
|
||||
) bool {
|
||||
// Concatenate the cw with the
|
||||
// content to check for links in both.
|
||||
cw := ap.ExtractSummary(statusable)
|
||||
content := ap.ExtractContent(statusable)
|
||||
concat := cw + " " + content.Content
|
||||
|
||||
// Store link string alongside link
|
||||
// URI to avoid stringifying twice.
|
||||
type preppedLink struct {
|
||||
*url.URL
|
||||
str string
|
||||
}
|
||||
|
||||
// Find + parse every http/https link in the status.
|
||||
rawLinks := regexes.LinkScheme.FindAllString(concat, -1)
|
||||
links := make([]preppedLink, 0, len(rawLinks))
|
||||
for _, rawLink := range rawLinks {
|
||||
linkURI, err := url.Parse(rawLink)
|
||||
if err != nil {
|
||||
log.Debugf(ctx,
|
||||
"malformed link in status: %v",
|
||||
err,
|
||||
)
|
||||
// Ignore bad links
|
||||
// for spam checking.
|
||||
continue
|
||||
}
|
||||
|
||||
links = append(links, preppedLink{
|
||||
URL: linkURI,
|
||||
str: rawLink,
|
||||
})
|
||||
}
|
||||
|
||||
// For each link in the status, try to
|
||||
// match it to a hashtag or a mention.
|
||||
// If we can't, we have an errant link.
|
||||
for _, link := range links {
|
||||
hashtagLink := slices.ContainsFunc(
|
||||
hashtags,
|
||||
func(hashtag *gtsmodel.Tag) bool {
|
||||
// If a link is to the href
|
||||
// of a hashtag, it's fine.
|
||||
return strings.EqualFold(
|
||||
link.str,
|
||||
hashtag.Href,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
if hashtagLink {
|
||||
// This link is accounted for.
|
||||
// Move to the next one.
|
||||
continue
|
||||
}
|
||||
|
||||
mentionLink := slices.ContainsFunc(
|
||||
mentions,
|
||||
func(mention preppedMention) bool {
|
||||
// If link is straight up to the URI
|
||||
// of a mentioned account, it's fine.
|
||||
if strings.EqualFold(
|
||||
link.str,
|
||||
mention.TargetAccountURI,
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Link might be to an account URL rather
|
||||
// than URI. This is a bit trickier because
|
||||
// we can't predict the format of such URLs,
|
||||
// and it's difficult to reconstruct them
|
||||
// while also taking account of different
|
||||
// host + account-domain values.
|
||||
//
|
||||
// So, just check if this link is on the same
|
||||
// host as the mentioned account, or at least
|
||||
// shares a host with it.
|
||||
if link.Host == mention.domain {
|
||||
// Same host.
|
||||
return true
|
||||
}
|
||||
|
||||
// Shares a host if it has at least two
|
||||
// components from the right in common.
|
||||
common := dns.CompareDomainName(
|
||||
link.Host,
|
||||
mention.domain,
|
||||
)
|
||||
return common >= 2
|
||||
},
|
||||
)
|
||||
|
||||
if mentionLink {
|
||||
// This link is accounted for.
|
||||
// Move to the next one.
|
||||
continue
|
||||
}
|
||||
|
||||
// Not a hashtag link
|
||||
// or a mention link,
|
||||
// so it's errant.
|
||||
return true
|
||||
}
|
||||
|
||||
// All links OK, or
|
||||
// no links found.
|
||||
return false
|
||||
}
|
||||
780
internal/filter/spam/statusable_test.go
Normal file
780
internal/filter/spam/statusable_test.go
Normal file
|
|
@ -0,0 +1,780 @@
|
|||
// GoToSocial
|
||||
// Copyright (C) GoToSocial Authors admin@gotosocial.org
|
||||
// SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package spam_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/ap"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/id"
|
||||
)
|
||||
|
||||
type StatusableTestSuite struct {
|
||||
FilterStandardTestSuite
|
||||
}
|
||||
|
||||
const (
|
||||
// Message that mentions 5 people (including receiver),
|
||||
// and contains a errant link.
|
||||
spam1 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.org/users/9gol6f8zff",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.org/@Nao_ya_ia22\" class=\"u-url mention\">@<span>Nao_ya_ia22</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.org/users/9gol6f8zff",
|
||||
"name": "@Nao_ya_ia22@example.org"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"name": "@mendako@another.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Message that mentions 4 people (including receiver),
|
||||
// and contains a errant link.
|
||||
spam2 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"name": "@mendako@another.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Message that mentions 4 people (including receiver),
|
||||
// but contains no errant links.
|
||||
spam3 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"name": "@mendako@another.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Message that mentions 4 people (including receiver),
|
||||
// contains no errant links, but 1 attachment.
|
||||
spam4 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [
|
||||
{
|
||||
"blurhash": "LNJRdVM{00Rj%Mayt7j[4nWBofRj",
|
||||
"mediaType": "image/jpeg",
|
||||
"name": "",
|
||||
"type": "Document",
|
||||
"url": "http://fossbros-anonymous.io/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpg"
|
||||
}
|
||||
],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"name": "@mendako@another.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Message that mentions 4 people (including receiver),
|
||||
// and contains a errant link, and receiver follows
|
||||
// another mentioned account.
|
||||
spam5 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"http://localhost:8080/users/admin",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\">@<span>admin</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/admin",
|
||||
"name": "@admin@localhost:8080"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Message that mentions 3 people, contains a
|
||||
// errant link, and receiver follows another
|
||||
// mentioned account. However, receiver is not mentioned.
|
||||
spam6 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"http://localhost:8080/users/admin"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><a href=\"https://spammylink.org/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\" translate=\"no\"><span class=\"invisible\">https://</span><span class=\"\">spammylink.org/</span><span class=\"invisible\"></span></a></p><p><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@admin\" class=\"u-url mention\">@<span>admin</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/admin",
|
||||
"name": "@admin@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Message that mentions 4 people (including receiver),
|
||||
// and hash a hashtag, but contains no errant links.
|
||||
spam7 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><a href=\"https://fossbros-anonymous.io/tags/gotosocial\" class=\"mention hashtag\" rel=\"tag\">#<span>gotosocial</span></a> smells<br/><br/><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"name": "@mendako@another.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
},
|
||||
{
|
||||
"type": "Hashtag",
|
||||
"href": "https://fossbros-anonymous.io/tags/gotosocial",
|
||||
"name": "#gotosocial"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
|
||||
// Same as spam7, except message doesn't
|
||||
// have a hashtag in the tags array.
|
||||
spam8 = `{
|
||||
"@context": [
|
||||
"https://www.w3.org/ns/activitystreams",
|
||||
{
|
||||
"ostatus": "http://ostatus.org#",
|
||||
"atomUri": "ostatus:atomUri",
|
||||
"inReplyToAtomUri": "ostatus:inReplyToAtomUri",
|
||||
"conversation": "ostatus:conversation",
|
||||
"sensitive": "as:sensitive",
|
||||
"toot": "http://joinmastodon.org/ns#",
|
||||
"votersCount": "toot:votersCount"
|
||||
}
|
||||
],
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"type": "Note",
|
||||
"summary": null,
|
||||
"inReplyTo": null,
|
||||
"published": "2024-02-24T07:06:14Z",
|
||||
"url": "http://fossbros-anonymous.io/@foss_satan/111985188827079562",
|
||||
"attributedTo": "http://fossbros-anonymous.io/users/foss_satan",
|
||||
"to": [
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [
|
||||
"http://fossbros-anonymous.io/users/foss_satan/followers",
|
||||
"https://example.net/users/nityosan",
|
||||
"https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"http://localhost:8080/users/the_mighty_zork"
|
||||
],
|
||||
"sensitive": false,
|
||||
"atomUri": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562",
|
||||
"inReplyToAtomUri": null,
|
||||
"content": "<p><a href=\"https://fossbros-anonymous.io/tags/gotosocial\" class=\"mention hashtag\" rel=\"tag\">#<span>gotosocial</span></a> smells<br/><br/><span class=\"h-card\" translate=\"no\"><a href=\"https://example.net/@nityosan\" class=\"u-url mention\">@<span>nityosan</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://a.misskey.instance.com/@FIzxive\" class=\"u-url mention\">@<span>FIzxive</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"https://another.misskey.instance.com/@mendako\" class=\"u-url mention\">@<span>mendako</span></a></span><br /><span class=\"h-card\" translate=\"no\"><a href=\"http://localhost:8080/@the_mighty_zork\" class=\"u-url mention\">@<span>the_mighty_zork</span></a></span></p>",
|
||||
"attachment": [],
|
||||
"tag": [
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://example.net/users/nityosan",
|
||||
"name": "@nityosan@example.net"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://a.misskey.instance.com/users/9c06ylkgsx",
|
||||
"name": "@FIzxive@a.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "https://another.misskey.instance.com/users/9eklgce5yk",
|
||||
"name": "@mendako@another.misskey.instance.com"
|
||||
},
|
||||
{
|
||||
"type": "Mention",
|
||||
"href": "http://localhost:8080/users/the_mighty_zork",
|
||||
"name": "@the_mighty_zork@localhost:8080"
|
||||
}
|
||||
],
|
||||
"replies": {
|
||||
"id": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"type": "Collection",
|
||||
"first": {
|
||||
"type": "CollectionPage",
|
||||
"next": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies?only_other_accounts=true&page=true",
|
||||
"partOf": "http://fossbros-anonymous.io/users/foss_satan/statuses/111985188827079562/replies",
|
||||
"items": []
|
||||
}
|
||||
}
|
||||
}`
|
||||
)
|
||||
|
||||
func (suite *StatusableTestSuite) TestStatusableOK() {
|
||||
var (
|
||||
ctx = context.Background()
|
||||
receiver = suite.testAccounts["local_account_1"]
|
||||
requester = suite.testAccounts["remote_account_1"]
|
||||
)
|
||||
|
||||
type testStruct struct {
|
||||
message string
|
||||
check func(error)
|
||||
}
|
||||
|
||||
for _, test := range []testStruct{
|
||||
{
|
||||
// SPAM: status mentions 5 or more people
|
||||
message: spam1,
|
||||
check: func(err error) {
|
||||
suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// SPAM: receiver doesn't know a mentioned account, and status has attachments or errant links
|
||||
message: spam2,
|
||||
check: func(err error) {
|
||||
suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// NOT SPAM: receiver doesn't know a mentioned account, but status has no attachments or errant links
|
||||
message: spam3,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// SPAM: receiver doesn't know a mentioned account, and status has attachments or errant links
|
||||
message: spam4,
|
||||
check: func(err error) {
|
||||
suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// NOT SPAM: receiver knows a mentioned account
|
||||
message: spam5,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// SPAM: receiver does not follow requester, and is not mentioned
|
||||
message: spam6,
|
||||
check: func(err error) {
|
||||
suite.True(gtserror.IsNotRelevant(err), "expected NotRelevant, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// NOT SPAM: receiver doesn't know a mentioned account, but status has no attachments or errant links
|
||||
message: spam7,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
// SPAM: receiver doesn't know a mentioned account, and status has attachments or errant links
|
||||
message: spam8,
|
||||
check: func(err error) {
|
||||
suite.True(gtserror.IsSpam(err), "expected Spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
} {
|
||||
rc := io.NopCloser(bytes.NewReader([]byte(test.message)))
|
||||
|
||||
statusable, err := ap.ResolveStatusable(ctx, rc)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
err = suite.filter.StatusableOK(ctx, receiver, requester, statusable)
|
||||
test.check(err)
|
||||
}
|
||||
|
||||
// Put a follow in place from receiver to requester.
|
||||
fID := id.NewULID()
|
||||
if err := suite.state.DB.PutFollow(ctx, >smodel.Follow{
|
||||
ID: fID,
|
||||
URI: "http://localhost:8080/users/the_mighty_zork/follows/" + fID,
|
||||
AccountID: receiver.ID,
|
||||
TargetAccountID: requester.ID,
|
||||
}); err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
// Run all the tests again. They should all
|
||||
// be OK since receiver now follows requester.
|
||||
for _, test := range []testStruct{
|
||||
{
|
||||
message: spam1,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam2,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam3,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam4,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam5,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam6,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam7,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
{
|
||||
message: spam8,
|
||||
check: func(err error) {
|
||||
suite.NoError(err, "expected not spam, got %+v", err)
|
||||
},
|
||||
},
|
||||
} {
|
||||
rc := io.NopCloser(bytes.NewReader([]byte(test.message)))
|
||||
|
||||
statusable, err := ap.ResolveStatusable(ctx, rc)
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
err = suite.filter.StatusableOK(ctx, receiver, requester, statusable)
|
||||
test.check(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusableTestSuite(t *testing.T) {
|
||||
suite.Run(t, &StatusableTestSuite{})
|
||||
}
|
||||
|
|
@ -20,9 +20,9 @@ package visibility_test
|
|||
import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
@ -37,6 +37,8 @@ const (
|
|||
wrongTypeKey
|
||||
smtpKey
|
||||
malformedKey
|
||||
notRelevantKey
|
||||
spamKey
|
||||
)
|
||||
|
||||
// IsUnretrievable indicates that a call to retrieve a resource
|
||||
|
|
@ -127,3 +129,30 @@ func IsMalformed(err error) bool {
|
|||
func SetMalformed(err error) error {
|
||||
return errors.WithValue(err, malformedKey, struct{}{})
|
||||
}
|
||||
|
||||
// IsNotRelevant checks error for a stored "notRelevant" flag.
|
||||
// This error is used when determining whether or not to store
|
||||
// + process an incoming AP message.
|
||||
func IsNotRelevant(err error) bool {
|
||||
_, ok := errors.Value(err, notRelevantKey).(struct{})
|
||||
return ok
|
||||
}
|
||||
|
||||
// SetNotRelevant will wrap the given error to store a "notRelevant" flag,
|
||||
// returning wrapped error. See IsNotRelevant() for example use-cases.
|
||||
func SetNotRelevant(err error) error {
|
||||
return errors.WithValue(err, notRelevantKey, struct{}{})
|
||||
}
|
||||
|
||||
// IsSpam checks error for a stored "spam" flag. This error is used when
|
||||
// determining whether or not to store + process an incoming AP message.
|
||||
func IsSpam(err error) bool {
|
||||
_, ok := errors.Value(err, spamKey).(struct{})
|
||||
return ok
|
||||
}
|
||||
|
||||
// SetSpam will wrap the given error to store a "spam" flag,
|
||||
// returning wrapped error. See IsSpam() for example use-cases.
|
||||
func SetSpam(err error) error {
|
||||
return errors.WithValue(err, spamKey, struct{}{})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,4 +27,5 @@ type Tag struct {
|
|||
Name string `bun:",unique,nullzero,notnull"` // (lowercase) name of the tag without the hash prefix
|
||||
Useable *bool `bun:",nullzero,notnull,default:true"` // Tag is useable on this instance.
|
||||
Listable *bool `bun:",nullzero,notnull,default:true"` // Tagged statuses can be listed on this instance.
|
||||
Href string `bun:"-"` // Href of the hashtag. Will only be set on freshly-extracted hashtags from remote AP messages. Not stored in the database.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,13 +20,13 @@ package media_test
|
|||
import (
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
gtsmodel "github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ package account
|
|||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -26,7 +27,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// Processor wraps functionality for updating, creating, and deleting accounts in response to API requests.
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
|
|
@ -35,7 +36,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/messages"
|
||||
|
|
@ -33,7 +34,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package common
|
|||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// Processor provides a processor with logic
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package fedi
|
|||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/common"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
type Processor struct {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/stretchr/testify/suite"
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
|
|
@ -32,7 +33,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/processing/polls"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/cleaner"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
mm "github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -42,7 +43,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// Processor groups together processing functions and
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -34,7 +35,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ package search
|
|||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
type Processor struct {
|
||||
|
|
|
|||
|
|
@ -19,13 +19,13 @@ package status
|
|||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/common"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/polls"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/text"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
type Processor struct {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
|
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -30,7 +31,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// HomeTimelineGrab returns a function that satisfies GrabFunction for home timelines.
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import (
|
|||
|
||||
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -30,7 +31,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/timeline"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// ListTimelineGrab returns a function that satisfies GrabFunction for list timelines.
|
||||
|
|
|
|||
|
|
@ -18,9 +18,9 @@
|
|||
package timeline
|
||||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
type Processor struct {
|
||||
|
|
|
|||
|
|
@ -19,10 +19,10 @@ package workers
|
|||
|
||||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/stream"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
)
|
||||
|
||||
// surface wraps functions for 'surfacing' the result
|
||||
|
|
|
|||
|
|
@ -20,12 +20,12 @@ package workers
|
|||
import (
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/account"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing/stream"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/workers"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
|
|
@ -34,7 +35,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/stream"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -22,10 +22,10 @@ import (
|
|||
"sort"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/email"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/federation"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/media"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
|
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/storage"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/transport"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -21,11 +21,11 @@ import (
|
|||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/activity/streams/vocab"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/filter/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/processing"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/typeutils"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/visibility"
|
||||
"github.com/superseriousbusiness/gotosocial/testrig"
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -2000,8 +2000,8 @@ func (suite *InternalToFrontendTestSuite) TestRelationshipFollowRequested() {
|
|||
|
||||
b, err := json.MarshalIndent(relationship, "", " ")
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
suite.Equal(`{
|
||||
"id": "01F8MH5NBDF2MV7CTC4Q5128HF",
|
||||
|
|
@ -2034,10 +2034,10 @@ func (suite *InternalToFrontendTestSuite) TestRelationshipFollowRequested() {
|
|||
|
||||
b, err = json.MarshalIndent(relationship, "", " ")
|
||||
if err != nil {
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
suite.FailNow(err.Error())
|
||||
}
|
||||
|
||||
suite.Equal(`{
|
||||
suite.Equal(`{
|
||||
"id": "01F8MH17FWEB39HZJ76B6VXSKF",
|
||||
"following": false,
|
||||
"showing_reblogs": false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue