mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 02:02:25 -05:00 
			
		
		
		
	Home timeline (#28)
* v. basic implementation of home timeline * Go fmt ./...
This commit is contained in:
		
					parent
					
						
							
								d839f27c30
							
						
					
				
			
			
				commit
				
					
						0df2e18cc0
					
				
			
		
					 14 changed files with 317 additions and 52 deletions
				
			
		|  | @ -69,7 +69,7 @@ func (m *Module) OauthTokenMiddleware(c *gin.Context) { | ||||||
| 	if cid := ti.GetClientID(); cid != "" { | 	if cid := ti.GetClientID(); cid != "" { | ||||||
| 		l.Tracef("authenticated client %s with bearer token, scope is %s", cid, ti.GetScope()) | 		l.Tracef("authenticated client %s with bearer token, scope is %s", cid, ti.GetScope()) | ||||||
| 		app := >smodel.Application{} | 		app := >smodel.Application{} | ||||||
| 		if err := m.db.GetWhere([]db.Where{{Key: "client_id",Value: cid}}, app); err != nil { | 		if err := m.db.GetWhere([]db.Where{{Key: "client_id", Value: cid}}, app); err != nil { | ||||||
| 			l.Tracef("no app found for client %s", cid) | 			l.Tracef("no app found for client %s", cid) | ||||||
| 		} | 		} | ||||||
| 		c.Set(oauth.SessionAuthorizedApplication, app) | 		c.Set(oauth.SessionAuthorizedApplication, app) | ||||||
|  |  | ||||||
							
								
								
									
										98
									
								
								internal/api/client/timeline/home.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								internal/api/client/timeline/home.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,98 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    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 timeline | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // HomeTimelineGETHandler serves status from the HOME timeline. | ||||||
|  | // | ||||||
|  | // Several different filters might be passed into this function in the query: | ||||||
|  | // | ||||||
|  | // 	max_id -- the maximum ID of the status to show | ||||||
|  | //  since_id -- Return results newer than id | ||||||
|  | // 	min_id -- Return results immediately newer than id | ||||||
|  | // 	limit -- show only limit number of statuses | ||||||
|  | // 	local -- Return only local statuses? | ||||||
|  | func (m *Module) HomeTimelineGETHandler(c *gin.Context) { | ||||||
|  | 	l := m.log.WithField("func", "AccountStatusesGETHandler") | ||||||
|  | 
 | ||||||
|  | 	authed, err := oauth.Authed(c, true, true, true, true) | ||||||
|  | 	if err != nil { | ||||||
|  | 		l.Debugf("error authing: %s", err) | ||||||
|  | 		c.JSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	maxID := "" | ||||||
|  | 	maxIDString := c.Query(MaxIDKey) | ||||||
|  | 	if maxIDString != "" { | ||||||
|  | 		maxID = maxIDString | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	sinceID := "" | ||||||
|  | 	sinceIDString := c.Query(SinceIDKey) | ||||||
|  | 	if sinceIDString != "" { | ||||||
|  | 		sinceID = sinceIDString | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	minID := "" | ||||||
|  | 	minIDString := c.Query(MinIDKey) | ||||||
|  | 	if minIDString != "" { | ||||||
|  | 		minID = minIDString | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	limit := 20 | ||||||
|  | 	limitString := c.Query(LimitKey) | ||||||
|  | 	if limitString != "" { | ||||||
|  | 		i, err := strconv.ParseInt(limitString, 10, 64) | ||||||
|  | 		if err != nil { | ||||||
|  | 			l.Debugf("error parsing limit string: %s", err) | ||||||
|  | 			c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse limit query param"}) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		limit = int(i) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	local := false | ||||||
|  | 	localString := c.Query(LocalKey) | ||||||
|  | 	if localString != "" { | ||||||
|  | 		i, err := strconv.ParseBool(localString) | ||||||
|  | 		if err != nil { | ||||||
|  | 			l.Debugf("error parsing local string: %s", err) | ||||||
|  | 			c.JSON(http.StatusBadRequest, gin.H{"error": "couldn't parse local query param"}) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		local = i | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	statuses, errWithCode := m.processor.HomeTimelineGet(authed, maxID, sinceID, minID, limit, local) | ||||||
|  | 	if errWithCode != nil { | ||||||
|  | 		l.Debugf("error from processor account statuses get: %s", errWithCode) | ||||||
|  | 		c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c.JSON(http.StatusOK, statuses) | ||||||
|  | } | ||||||
							
								
								
									
										68
									
								
								internal/api/client/timeline/timeline.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								internal/api/client/timeline/timeline.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,68 @@ | ||||||
|  | package timeline | ||||||
|  | 
 | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    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/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  | 
 | ||||||
|  | 	"github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/api" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/message" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	// BasePath is the base URI path for serving timelines | ||||||
|  | 	BasePath = "/api/v1/timelines" | ||||||
|  | 	// HomeTimeline is the path for the home timeline | ||||||
|  | 	HomeTimeline = BasePath + "/home" | ||||||
|  | 	// MaxIDKey is the url query for setting a max status ID to return | ||||||
|  | 	MaxIDKey = "max_id" | ||||||
|  | 	// SinceIDKey is the url query for returning results newer than the given ID | ||||||
|  | 	SinceIDKey = "since_id" | ||||||
|  | 	// MinIDKey is the url query for returning results immediately newer than the given ID | ||||||
|  | 	MinIDKey = "min_id" | ||||||
|  | 	// Limit key is for specifying maximum number of results to return. | ||||||
|  | 	LimitKey = "limit" | ||||||
|  | 	// LocalKey is for specifying whether only local statuses should be returned | ||||||
|  | 	LocalKey = "local" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Module implements the ClientAPIModule interface for everything relating to viewing timelines | ||||||
|  | type Module struct { | ||||||
|  | 	config    *config.Config | ||||||
|  | 	processor message.Processor | ||||||
|  | 	log       *logrus.Logger | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // New returns a new timeline module | ||||||
|  | func New(config *config.Config, processor message.Processor, log *logrus.Logger) api.ClientModule { | ||||||
|  | 	return &Module{ | ||||||
|  | 		config:    config, | ||||||
|  | 		processor: processor, | ||||||
|  | 		log:       log, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Route attaches all routes from this module to the given router | ||||||
|  | func (m *Module) Route(r router.Router) error { | ||||||
|  | 	r.AttachHandler(http.MethodGet, HomeTimeline, m.HomeTimelineGETHandler) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | @ -32,18 +32,20 @@ const ( | ||||||
| 
 | 
 | ||||||
| // ErrNoEntries is to be returned from the DB interface when no entries are found for a given query. | // ErrNoEntries is to be returned from the DB interface when no entries are found for a given query. | ||||||
| type ErrNoEntries struct{} | type ErrNoEntries struct{} | ||||||
|  | 
 | ||||||
| func (e ErrNoEntries) Error() string { | func (e ErrNoEntries) Error() string { | ||||||
| 	return "no entries" | 	return "no entries" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // ErrAlreadyExists is to be returned from the DB interface when an entry already exists for a given query or its constraints. | // ErrAlreadyExists is to be returned from the DB interface when an entry already exists for a given query or its constraints. | ||||||
| type ErrAlreadyExists struct{} | type ErrAlreadyExists struct{} | ||||||
|  | 
 | ||||||
| func (e ErrAlreadyExists) Error() string { | func (e ErrAlreadyExists) Error() string { | ||||||
| 	return "already exists" | 	return "already exists" | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| type Where struct { | type Where struct { | ||||||
| 	Key string | 	Key   string | ||||||
| 	Value interface{} | 	Value interface{} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -278,6 +280,10 @@ type DB interface { | ||||||
| 	// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user. | 	// This slice will be unfiltered, not taking account of blocks and whatnot, so filter it before serving it back to a user. | ||||||
| 	WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, error) | 	WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel.Account, error) | ||||||
| 
 | 
 | ||||||
|  | 	// GetHomeTimelineForAccount fetches the account's HOME timeline -- ie., posts and replies from people they *follow*. | ||||||
|  | 	// It will use the given filters and try to return as many statuses up to the limit as possible. | ||||||
|  | 	GetHomeTimelineForAccount(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, error) | ||||||
|  | 
 | ||||||
| 	/* | 	/* | ||||||
| 		USEFUL CONVERSION FUNCTIONS | 		USEFUL CONVERSION FUNCTIONS | ||||||
| 	*/ | 	*/ | ||||||
|  |  | ||||||
|  | @ -1103,6 +1103,26 @@ func (ps *postgresService) WhoFavedStatus(status *gtsmodel.Status) ([]*gtsmodel. | ||||||
| 	return accounts, nil | 	return accounts, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (ps *postgresService) GetHomeTimelineForAccount(accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]*gtsmodel.Status, error) { | ||||||
|  | 	statuses := []*gtsmodel.Status{} | ||||||
|  | 
 | ||||||
|  | 	q := ps.conn.Model(&statuses). | ||||||
|  | 		ColumnExpr("status.*"). | ||||||
|  | 		Join("JOIN follows AS f ON f.target_account_id = status.account_id"). | ||||||
|  | 		Where("f.account_id = ?", accountID). | ||||||
|  | 		Limit(limit). | ||||||
|  | 		Order("status.created_at DESC") | ||||||
|  | 
 | ||||||
|  | 	err := q.Select() | ||||||
|  | 	if err != nil { | ||||||
|  | 		if err != pg.ErrNoRows { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return statuses, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* | /* | ||||||
| 	CONVERSION FUNCTIONS | 	CONVERSION FUNCTIONS | ||||||
| */ | */ | ||||||
|  |  | ||||||
|  | @ -38,6 +38,7 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/api/client/instance" | 	"github.com/superseriousbusiness/gotosocial/internal/api/client/instance" | ||||||
| 	mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" | 	mediaModule "github.com/superseriousbusiness/gotosocial/internal/api/client/media" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/api/client/status" | 	"github.com/superseriousbusiness/gotosocial/internal/api/client/status" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/api/client/timeline" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user" | 	"github.com/superseriousbusiness/gotosocial/internal/api/s2s/user" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger" | 	"github.com/superseriousbusiness/gotosocial/internal/api/s2s/webfinger" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/api/security" | 	"github.com/superseriousbusiness/gotosocial/internal/api/security" | ||||||
|  | @ -116,6 +117,7 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr | ||||||
| 	followRequestsModule := followrequest.New(c, processor, log) | 	followRequestsModule := followrequest.New(c, processor, log) | ||||||
| 	webfingerModule := webfinger.New(c, processor, log) | 	webfingerModule := webfinger.New(c, processor, log) | ||||||
| 	usersModule := user.New(c, processor, log) | 	usersModule := user.New(c, processor, log) | ||||||
|  | 	timelineModule := timeline.New(c, processor, log) | ||||||
| 	mm := mediaModule.New(c, processor, log) | 	mm := mediaModule.New(c, processor, log) | ||||||
| 	fileServerModule := fileserver.New(c, processor, log) | 	fileServerModule := fileserver.New(c, processor, log) | ||||||
| 	adminModule := admin.New(c, processor, log) | 	adminModule := admin.New(c, processor, log) | ||||||
|  | @ -138,6 +140,7 @@ var Run action.GTSAction = func(ctx context.Context, c *config.Config, log *logr | ||||||
| 		statusModule, | 		statusModule, | ||||||
| 		webfingerModule, | 		webfingerModule, | ||||||
| 		usersModule, | 		usersModule, | ||||||
|  | 		timelineModule, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for _, m := range apis { | 	for _, m := range apis { | ||||||
|  |  | ||||||
|  | @ -164,46 +164,46 @@ func (p *processor) GetFediFollowers(requestedUsername string, request *http.Req | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *processor) GetFediStatus(requestedUsername string, requestedStatusID string, request *http.Request) (interface{}, ErrorWithCode) { | func (p *processor) GetFediStatus(requestedUsername string, requestedStatusID string, request *http.Request) (interface{}, ErrorWithCode) { | ||||||
| 		// get the account the request is referring to | 	// get the account the request is referring to | ||||||
| 		requestedAccount := >smodel.Account{} | 	requestedAccount := >smodel.Account{} | ||||||
| 		if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil { | 	if err := p.db.GetLocalAccountByUsername(requestedUsername, requestedAccount); err != nil { | ||||||
| 			return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err)) | 		return nil, NewErrorNotFound(fmt.Errorf("database error getting account with username %s: %s", requestedUsername, err)) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		// authenticate the request | 	// authenticate the request | ||||||
| 		requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request) | 	requestingAccount, err := p.authenticateAndDereferenceFediRequest(requestedUsername, request) | ||||||
| 		if err != nil { | 	if err != nil { | ||||||
| 			return nil, NewErrorNotAuthorized(err) | 		return nil, NewErrorNotAuthorized(err) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID) | 	blocked, err := p.db.Blocked(requestedAccount.ID, requestingAccount.ID) | ||||||
| 		if err != nil { | 	if err != nil { | ||||||
| 			return nil, NewErrorInternalError(err) | 		return nil, NewErrorInternalError(err) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		if blocked { | 	if blocked { | ||||||
| 			return nil, NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID)) | 		return nil, NewErrorNotAuthorized(fmt.Errorf("block exists between accounts %s and %s", requestedAccount.ID, requestingAccount.ID)) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		s := >smodel.Status{} | 	s := >smodel.Status{} | ||||||
| 		if err := p.db.GetWhere([]db.Where{ | 	if err := p.db.GetWhere([]db.Where{ | ||||||
| 			{Key: "id", Value: requestedStatusID}, | 		{Key: "id", Value: requestedStatusID}, | ||||||
| 			{Key: "account_id", Value: requestedAccount.ID}, | 		{Key: "account_id", Value: requestedAccount.ID}, | ||||||
| 		}, s); err != nil { | 	}, s); err != nil { | ||||||
| 			return nil, NewErrorNotFound(fmt.Errorf("database error getting status with id %s and account id %s: %s", requestedStatusID, requestedAccount.ID, err)) | 		return nil, NewErrorNotFound(fmt.Errorf("database error getting status with id %s and account id %s: %s", requestedStatusID, requestedAccount.ID, err)) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		asStatus, err := p.tc.StatusToAS(s) | 	asStatus, err := p.tc.StatusToAS(s) | ||||||
| 		if err != nil { | 	if err != nil { | ||||||
| 			return nil, NewErrorInternalError(err) | 		return nil, NewErrorInternalError(err) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		data, err := streams.Serialize(asStatus) | 	data, err := streams.Serialize(asStatus) | ||||||
| 		if err != nil { | 	if err != nil { | ||||||
| 			return nil, NewErrorInternalError(err) | 		return nil, NewErrorInternalError(err) | ||||||
| 		} | 	} | ||||||
| 
 | 
 | ||||||
| 		return data, nil | 	return data, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *processor) GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, ErrorWithCode) { | func (p *processor) GetWebfingerAccount(requestedUsername string, request *http.Request) (*apimodel.WebfingerAccountResponse, ErrorWithCode) { | ||||||
|  |  | ||||||
|  | @ -25,5 +25,5 @@ func (p *processor) notifyStatus(status *gtsmodel.Status) error { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (p *processor) notifyFollow(follow *gtsmodel.Follow) error { | func (p *processor) notifyFollow(follow *gtsmodel.Follow) error { | ||||||
|    return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -56,7 +56,7 @@ func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) (*ap | ||||||
| 
 | 
 | ||||||
| 	p.fromClientAPI <- gtsmodel.FromClientAPI{ | 	p.fromClientAPI <- gtsmodel.FromClientAPI{ | ||||||
| 		APActivityType: gtsmodel.ActivityStreamsAccept, | 		APActivityType: gtsmodel.ActivityStreamsAccept, | ||||||
| 		GTSModel: follow, | 		GTSModel:       follow, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	gtsR, err := p.db.GetRelationship(auth.Account.ID, accountID) | 	gtsR, err := p.db.GetRelationship(auth.Account.ID, accountID) | ||||||
|  | @ -65,7 +65,7 @@ func (p *processor) FollowRequestAccept(auth *oauth.Auth, accountID string) (*ap | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	r, err := p.tc.RelationshipToMasto(gtsR) | 	r, err := p.tc.RelationshipToMasto(gtsR) | ||||||
| 	if  err != nil { | 	if err != nil { | ||||||
| 		return nil, NewErrorInternalError(err) | 		return nil, NewErrorInternalError(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -121,6 +121,9 @@ type Processor interface { | ||||||
| 	// StatusUnfave processes the unfaving of a given status, returning the updated status if the fave goes through. | 	// StatusUnfave processes the unfaving of a given status, returning the updated status if the fave goes through. | ||||||
| 	StatusUnfave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) | 	StatusUnfave(authed *oauth.Auth, targetStatusID string) (*apimodel.Status, error) | ||||||
| 
 | 
 | ||||||
|  | 	// HomeTimelineGet returns statuses from the home timeline, with the given filters/parameters. | ||||||
|  | 	HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]apimodel.Status, ErrorWithCode) | ||||||
|  | 
 | ||||||
| 	/* | 	/* | ||||||
| 		FEDERATION API-FACING PROCESSING FUNCTIONS | 		FEDERATION API-FACING PROCESSING FUNCTIONS | ||||||
| 		These functions are intended to be called when the federating client needs an immediate (ie., synchronous) reply | 		These functions are intended to be called when the federating client needs an immediate (ie., synchronous) reply | ||||||
|  |  | ||||||
							
								
								
									
										67
									
								
								internal/message/timelineprocess.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								internal/message/timelineprocess.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,67 @@ | ||||||
|  | package message | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (p *processor) HomeTimelineGet(authed *oauth.Auth, maxID string, sinceID string, minID string, limit int, local bool) ([]apimodel.Status, ErrorWithCode) { | ||||||
|  | 	statuses, err := p.db.GetHomeTimelineForAccount(authed.Account.ID, maxID, sinceID, minID, limit, local) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, NewErrorInternalError(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	apiStatuses := []apimodel.Status{} | ||||||
|  | 	for _, s := range statuses { | ||||||
|  | 		targetAccount := >smodel.Account{} | ||||||
|  | 		if err := p.db.GetByID(s.AccountID, targetAccount); err != nil { | ||||||
|  | 			return nil, NewErrorInternalError(fmt.Errorf("error getting status author: %s", err)) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		relevantAccounts, err := p.db.PullRelevantAccountsFromStatus(s) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, NewErrorInternalError(fmt.Errorf("error getting relevant statuses: %s", err)) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		visible, err := p.db.StatusVisible(s, targetAccount, authed.Account, relevantAccounts) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, NewErrorInternalError(fmt.Errorf("error checking status visibility: %s", err)) | ||||||
|  | 		} | ||||||
|  | 		if !visible { | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		var boostedStatus *gtsmodel.Status | ||||||
|  | 		if s.BoostOfID != "" { | ||||||
|  | 			bs := >smodel.Status{} | ||||||
|  | 			if err := p.db.GetByID(s.BoostOfID, bs); err != nil { | ||||||
|  | 				return nil, NewErrorInternalError(fmt.Errorf("error getting boosted status: %s", err)) | ||||||
|  | 			} | ||||||
|  | 			boostedRelevantAccounts, err := p.db.PullRelevantAccountsFromStatus(bs) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, NewErrorInternalError(fmt.Errorf("error getting relevant accounts from boosted status: %s", err)) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			boostedVisible, err := p.db.StatusVisible(bs, relevantAccounts.BoostedAccount, authed.Account, boostedRelevantAccounts) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return nil, NewErrorInternalError(fmt.Errorf("error checking boosted status visibility: %s", err)) | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			if boostedVisible { | ||||||
|  | 				boostedStatus = bs | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		apiStatus, err := p.tc.StatusToMasto(s, targetAccount, authed.Account, relevantAccounts.BoostedAccount, relevantAccounts.ReplyToAccount, boostedStatus) | ||||||
|  | 		if err != nil { | ||||||
|  | 			return nil, NewErrorInternalError(fmt.Errorf("error converting status to masto: %s", err)) | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		apiStatuses = append(apiStatuses, *apiStatus) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return apiStatuses, nil | ||||||
|  | } | ||||||
|  | @ -121,7 +121,7 @@ func (c *converter) ASRepresentationToAccount(accountable Accountable) (*gtsmode | ||||||
| 	acct.URL = url.String() | 	acct.URL = url.String() | ||||||
| 
 | 
 | ||||||
| 	// InboxURI | 	// InboxURI | ||||||
| 	if accountable.GetActivityStreamsInbox() != nil || accountable.GetActivityStreamsInbox().GetIRI() != nil { | 	if accountable.GetActivityStreamsInbox() != nil && accountable.GetActivityStreamsInbox().GetIRI() != nil { | ||||||
| 		acct.InboxURI = accountable.GetActivityStreamsInbox().GetIRI().String() | 		acct.InboxURI = accountable.GetActivityStreamsInbox().GetIRI().String() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -439,7 +439,7 @@ func (c *converter) StatusToAS(s *gtsmodel.Status) (vocab.ActivityStreamsNote, e | ||||||
| 
 | 
 | ||||||
| 	// replies | 	// replies | ||||||
| 	// TODO | 	// TODO | ||||||
| 	 | 
 | ||||||
| 	return status, nil | 	return status, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -575,18 +575,18 @@ func (c *converter) InstanceToMasto(i *gtsmodel.Instance) (*model.Instance, erro | ||||||
| 
 | 
 | ||||||
| func (c *converter) RelationshipToMasto(r *gtsmodel.Relationship) (*model.Relationship, error) { | func (c *converter) RelationshipToMasto(r *gtsmodel.Relationship) (*model.Relationship, error) { | ||||||
| 	return &model.Relationship{ | 	return &model.Relationship{ | ||||||
| 		ID: r.ID, | 		ID:                  r.ID, | ||||||
| 		Following: r.Following, | 		Following:           r.Following, | ||||||
| 		ShowingReblogs: r.ShowingReblogs, | 		ShowingReblogs:      r.ShowingReblogs, | ||||||
| 		Notifying: r.Notifying, | 		Notifying:           r.Notifying, | ||||||
| 		FollowedBy: r.FollowedBy, | 		FollowedBy:          r.FollowedBy, | ||||||
| 		Blocking: r.Blocking, | 		Blocking:            r.Blocking, | ||||||
| 		BlockedBy: r.BlockedBy, | 		BlockedBy:           r.BlockedBy, | ||||||
| 		Muting: r.Muting, | 		Muting:              r.Muting, | ||||||
| 		MutingNotifications: r.MutingNotifications, | 		MutingNotifications: r.MutingNotifications, | ||||||
| 		Requested: r.Requested, | 		Requested:           r.Requested, | ||||||
| 		DomainBlocking: r.DomainBlocking, | 		DomainBlocking:      r.DomainBlocking, | ||||||
| 		Endorsed: r.Endorsed, | 		Endorsed:            r.Endorsed, | ||||||
| 		Note: r.Note, | 		Note:                r.Note, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue