diff --git a/docs/api/swagger.yaml b/docs/api/swagger.yaml index e55011a41..0a8b94ff2 100644 --- a/docs/api/swagger.yaml +++ b/docs/api/swagger.yaml @@ -15965,6 +15965,7 @@ paths: `user:notification`: receive notifications for the account. `public`: receive updates for the public timeline. `public:local`: receive updates for the local timeline. + `public:remote`: receive updates for the public remote-only timeline `hashtag`: receive updates for a given hashtag. `hashtag:local`: receive local updates for a given hashtag. `list`: receive updates for a certain list of accounts. @@ -15974,6 +15975,7 @@ paths: - user:notification - public - public:local + - public:remote - hashtag - hashtag:local - list @@ -16035,6 +16037,7 @@ paths: - user:notification - public - public:local + - public:remote - hashtag - hashtag:local - list diff --git a/internal/api/client/streaming/stream.go b/internal/api/client/streaming/stream.go index 7db2e5878..892697673 100644 --- a/internal/api/client/streaming/stream.go +++ b/internal/api/client/streaming/stream.go @@ -76,6 +76,7 @@ var pingMsg = []byte("ping!") // - user:notification // - public // - public:local +// - public:remote // - hashtag // - hashtag:local // - list @@ -89,6 +90,7 @@ var pingMsg = []byte("ping!") // `user:notification`: receive notifications for the account. // `public`: receive updates for the public timeline. // `public:local`: receive updates for the local timeline. +// `public:remote`: receive updates for the public remote-only timeline // `hashtag`: receive updates for a given hashtag. // `hashtag:local`: receive local updates for a given hashtag. // `list`: receive updates for a certain list of accounts. @@ -128,6 +130,7 @@ var pingMsg = []byte("ping!") // - user:notification // - public // - public:local +// - public:remote // - hashtag // - hashtag:local // - list @@ -218,6 +221,12 @@ func (m *Module) StreamGETHandler(c *gin.Context) { // Get the initial requested stream type, if there is one. streamType := c.Query(StreamQueryKey) + // Rewrite "allow_local_only" type as we always + // allow local-only posts in the public timeline. + if streamType == "public:allow_local_only" { + streamType = "public" + } + // By appending other query params to the streamType, we // can allow streaming for specific list IDs or hashtags. // The streamType in this case will end up looking like @@ -292,7 +301,7 @@ func (m *Module) handleWSConn(l *log.Entry, wsConn *websocket.Conn, stream *stre defer cncl() // Read messages from websocket to server. - m.readFromWSConn(ctx, wsConn, stream, l) + m.readFromWSConn(wsConn, stream, l) }() go func() { @@ -325,7 +334,6 @@ func (m *Module) handleWSConn(l *log.Entry, wsConn *websocket.Conn, stream *stre // This is a blocking function; will return only on read error or // if the given context is canceled. func (m *Module) readFromWSConn( - ctx context.Context, wsConn *websocket.Conn, stream *streampkg.Stream, l *log.Entry, @@ -359,6 +367,12 @@ func (m *Module) readFromWSConn( // and usually interesting, so log this at info. l.Infof("received websocket message: %+v", msg) + // Rewrite "allow_local_only" type as we always + // allow local-only posts in the public timeline. + if msg.Stream == "public:allow_local_only" { + msg.Stream = "public" + } + // Ignore if the updateStreamType is unknown (or missing), // so a bad client can't cause extra memory allocations if !slices.Contains(streampkg.AllStatusTimelines, msg.Stream) { diff --git a/internal/stream/stream.go b/internal/stream/stream.go index 0a352133a..05495dce0 100644 --- a/internal/stream/stream.go +++ b/internal/stream/stream.go @@ -59,10 +59,16 @@ const ( TimelineLocal = "public:local" // TimelinePublic: - // All public posts known to the server. + // All public posts known to the server, + // including local ones too. // Analogous to the federated timeline. TimelinePublic = "public" + // TimelinePublicRemote: + // All *remote* public posts known to + // the server, excluding local posts. + TimelinePublicRemote = "public:remote" + // TimelineHome: // Events related to the current user, such // as home feed updates and notifications. @@ -87,6 +93,7 @@ const ( var AllStatusTimelines = []string{ TimelineLocal, TimelinePublic, + TimelinePublicRemote, TimelineHome, TimelineDirect, TimelineList, diff --git a/internal/surfacing/surfacetimeline.go b/internal/surfacing/surfacetimeline.go index 0635b8d54..066ac0717 100644 --- a/internal/surfacing/surfacetimeline.go +++ b/internal/surfacing/surfacetimeline.go @@ -77,6 +77,12 @@ func (s *Surfacer) TimelineAndNotifyStatus(ctx context.Context, status *gtsmodel // Stream the status model as public timeline update event. s.stream.Update(ctx, account, apiStatus, stream.TimelinePublic) + + // If this is a remote status, stream it to + // the public remote-only timeline as well. + if status.Account.IsRemote() { + s.stream.Update(ctx, account, apiStatus, stream.TimelinePublicRemote) + } }, )