diff options
Diffstat (limited to 'internal/processing/account')
| -rw-r--r-- | internal/processing/account/account.go | 3 | ||||
| -rw-r--r-- | internal/processing/account/getrss.go | 108 | ||||
| -rw-r--r-- | internal/processing/account/getrss_test.go | 61 | ||||
| -rw-r--r-- | internal/processing/account/update.go | 4 | 
4 files changed, 176 insertions, 0 deletions
diff --git a/internal/processing/account/account.go b/internal/processing/account/account.go index aca46394a..b18e705ca 100644 --- a/internal/processing/account/account.go +++ b/internal/processing/account/account.go @@ -21,6 +21,7 @@ package account  import (  	"context"  	"mime/multipart" +	"time"  	apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"  	"github.com/superseriousbusiness/gotosocial/internal/concurrency" @@ -53,6 +54,8 @@ type Processor interface {  	GetLocalByUsername(ctx context.Context, requestingAccount *gtsmodel.Account, username string) (*apimodel.Account, gtserror.WithCode)  	// GetCustomCSSForUsername returns custom css for the given local username.  	GetCustomCSSForUsername(ctx context.Context, username string) (string, gtserror.WithCode) +	// GetRSSFeedForUsername returns RSS feed for the given local username. +	GetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode)  	// Update processes the update of an account with the given form  	Update(ctx context.Context, account *gtsmodel.Account, form *apimodel.UpdateCredentialsRequest) (*apimodel.Account, gtserror.WithCode)  	// StatusesGet fetches a number of statuses (in time descending order) from the given account, filtered by visibility for diff --git a/internal/processing/account/getrss.go b/internal/processing/account/getrss.go new file mode 100644 index 000000000..f07204b56 --- /dev/null +++ b/internal/processing/account/getrss.go @@ -0,0 +1,108 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 account + +import ( +	"context" +	"errors" +	"fmt" +	"time" + +	"github.com/gorilla/feeds" +	"github.com/superseriousbusiness/gotosocial/internal/config" +	"github.com/superseriousbusiness/gotosocial/internal/db" +	"github.com/superseriousbusiness/gotosocial/internal/gtserror" +) + +const rssFeedLength = 20 + +func (p *processor) GetRSSFeedForUsername(ctx context.Context, username string) (func() (string, gtserror.WithCode), time.Time, gtserror.WithCode) { +	account, err := p.db.GetAccountByUsernameDomain(ctx, username, "") +	if err != nil { +		if err == db.ErrNoEntries { +			return nil, time.Time{}, gtserror.NewErrorNotFound(errors.New("GetRSSFeedForUsername: account not found")) +		} +		return nil, time.Time{}, gtserror.NewErrorInternalError(fmt.Errorf("GetRSSFeedForUsername: db error: %s", err)) +	} + +	if !*account.EnableRSS { +		return nil, time.Time{}, gtserror.NewErrorNotFound(errors.New("GetRSSFeedForUsername: account RSS feed not enabled")) +	} + +	lastModified, err := p.db.GetAccountLastPosted(ctx, account.ID, true) +	if err != nil { +		return nil, time.Time{}, gtserror.NewErrorInternalError(fmt.Errorf("GetRSSFeedForUsername: db error: %s", err)) +	} + +	return func() (string, gtserror.WithCode) { +		statuses, err := p.db.GetAccountWebStatuses(ctx, account.ID, rssFeedLength, "") +		if err != nil && err != db.ErrNoEntries { +			return "", gtserror.NewErrorInternalError(fmt.Errorf("GetRSSFeedForUsername: db error: %s", err)) +		} + +		author := "@" + account.Username + "@" + config.GetAccountDomain() +		title := "Posts from " + author +		description := "Posts from " + author +		link := &feeds.Link{Href: account.URL} + +		var image *feeds.Image +		if account.AvatarMediaAttachmentID != "" { +			if account.AvatarMediaAttachment == nil { +				avatar, err := p.db.GetAttachmentByID(ctx, account.AvatarMediaAttachmentID) +				if err != nil { +					return "", gtserror.NewErrorInternalError(fmt.Errorf("GetRSSFeedForUsername: db error fetching avatar attachment: %s", err)) +				} +				account.AvatarMediaAttachment = avatar +			} +			image = &feeds.Image{ +				Url:   account.AvatarMediaAttachment.Thumbnail.URL, +				Title: "Avatar for " + author, +				Link:  account.URL, +			} +		} + +		feed := &feeds.Feed{ +			Title:       title, +			Description: description, +			Link:        link, +			Image:       image, +		} + +		for i, s := range statuses { +			// take the date of the first (ie., latest) status as feed updated value +			if i == 0 { +				feed.Updated = s.UpdatedAt +			} + +			item, err := p.tc.StatusToRSSItem(ctx, s) +			if err != nil { +				return "", gtserror.NewErrorInternalError(fmt.Errorf("GetRSSFeedForUsername: error converting status to feed item: %s", err)) +			} + +			feed.Add(item) +		} + +		rss, err := feed.ToRss() +		if err != nil { +			return "", gtserror.NewErrorInternalError(fmt.Errorf("GetRSSFeedForUsername: error converting feed to rss string: %s", err)) +		} + +		return rss, nil +	}, lastModified, nil +} diff --git a/internal/processing/account/getrss_test.go b/internal/processing/account/getrss_test.go new file mode 100644 index 000000000..dc81434a0 --- /dev/null +++ b/internal/processing/account/getrss_test.go @@ -0,0 +1,61 @@ +/* +   GoToSocial +   Copyright (C) 2021-2022 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 account_test + +import ( +	"context" +	"fmt" +	"testing" + +	"github.com/stretchr/testify/suite" +) + +type GetRSSTestSuite struct { +	AccountStandardTestSuite +} + +func (suite *GetRSSTestSuite) TestGetAccountRSSAdmin() { +	getFeed, lastModified, err := suite.accountProcessor.GetRSSFeedForUsername(context.Background(), "admin") +	suite.NoError(err) +	suite.EqualValues(1634733405, lastModified.Unix()) + +	feed, err := getFeed() +	suite.NoError(err) + +	fmt.Println(feed) + +	suite.Equal("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n  <channel>\n    <title>Posts from @admin@localhost:8080</title>\n    <link>http://localhost:8080/@admin</link>\n    <description>Posts from @admin@localhost:8080</description>\n    <pubDate>Wed, 20 Oct 2021 12:36:45 +0000</pubDate>\n    <lastBuildDate>Wed, 20 Oct 2021 12:36:45 +0000</lastBuildDate>\n    <item>\n      <title>open to see some puppies</title>\n      <link>http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37</link>\n      <description>@admin@localhost:8080 made a new post: "🐕🐕🐕🐕🐕"</description>\n      <content:encoded><![CDATA[🐕🐕🐕🐕🐕]]></content:encoded>\n      <author>@admin@localhost:8080</author>\n      <guid>http://localhost:8080/@admin/statuses/01F8MHAAY43M6RJ473VQFCVH37</guid>\n      <pubDate>Wed, 20 Oct 2021 12:36:45 +0000</pubDate>\n      <source>http://localhost:8080/@admin/feed.rss</source>\n    </item>\n    <item>\n      <title>hello world! #welcome ! first post on the instance :rainbow: !</title>\n      <link>http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R</link>\n      <description>@admin@localhost:8080 posted 1 attachment: "hello world! #welcome ! first post on the instance :rainbow: !"</description>\n      <content:encoded><![CDATA[hello world! #welcome ! first post on the instance <img src=\"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/emoji/original/01F8MH9H8E4VG3KDYJR9EGPXCQ.png\" title=\":rainbow:\" alt=\":rainbow:\" class=\"emoji\"/> !]]></content:encoded>\n      <author>@admin@localhost:8080</author>\n      <enclosure url=\"http://localhost:8080/fileserver/01F8MH17FWEB39HZJ76B6VXSKF/attachment/original/01F8MH6NEM8D7527KZAECTCR76.jpeg\" length=\"62529\" type=\"image/jpeg\"></enclosure>\n      <guid>http://localhost:8080/@admin/statuses/01F8MH75CBF9JFX4ZAD54N0W0R</guid>\n      <pubDate>Wed, 20 Oct 2021 11:36:45 +0000</pubDate>\n      <source>http://localhost:8080/@admin/feed.rss</source>\n    </item>\n  </channel>\n</rss>", feed) +} + +func (suite *GetRSSTestSuite) TestGetAccountRSSZork() { +	getFeed, lastModified, err := suite.accountProcessor.GetRSSFeedForUsername(context.Background(), "the_mighty_zork") +	suite.NoError(err) +	suite.EqualValues(1634726437, lastModified.Unix()) + +	feed, err := getFeed() +	suite.NoError(err) + +	fmt.Println(feed) + +	suite.Equal("<?xml version=\"1.0\" encoding=\"UTF-8\"?><rss version=\"2.0\" xmlns:content=\"http://purl.org/rss/1.0/modules/content/\">\n  <channel>\n    <title>Posts from @the_mighty_zork@localhost:8080</title>\n    <link>http://localhost:8080/@the_mighty_zork</link>\n    <description>Posts from @the_mighty_zork@localhost:8080</description>\n    <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n    <lastBuildDate>Wed, 20 Oct 2021 10:40:37 +0000</lastBuildDate>\n    <image>\n      <url>http://localhost:8080/fileserver/01F8MH1H7YV1Z7D2C8K2730QBF/avatar/small/01F8MH58A357CV5K7R7TJMSH6S.jpeg</url>\n      <title>Avatar for @the_mighty_zork@localhost:8080</title>\n      <link>http://localhost:8080/@the_mighty_zork</link>\n    </image>\n    <item>\n      <title>introduction post</title>\n      <link>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</link>\n      <description>@the_mighty_zork@localhost:8080 made a new post: "hello everyone!"</description>\n      <content:encoded><![CDATA[hello everyone!]]></content:encoded>\n      <author>@the_mighty_zork@localhost:8080</author>\n      <guid>http://localhost:8080/@the_mighty_zork/statuses/01F8MHAMCHF6Y650WCRSCP4WMY</guid>\n      <pubDate>Wed, 20 Oct 2021 10:40:37 +0000</pubDate>\n      <source>http://localhost:8080/@the_mighty_zork/feed.rss</source>\n    </item>\n  </channel>\n</rss>", feed) +} + +func TestGetRSSTestSuite(t *testing.T) { +	suite.Run(t, new(GetRSSTestSuite)) +} diff --git a/internal/processing/account/update.go b/internal/processing/account/update.go index 94e91ca4c..f39361c06 100644 --- a/internal/processing/account/update.go +++ b/internal/processing/account/update.go @@ -160,6 +160,10 @@ func (p *processor) Update(ctx context.Context, account *gtsmodel.Account, form  		account.CustomCSS = text.SanitizePlaintext(customCSS)  	} +	if form.EnableRSS != nil { +		account.EnableRSS = form.EnableRSS +	} +  	updatedAccount, err := p.db.UpdateAccount(ctx, account)  	if err != nil {  		return nil, gtserror.NewErrorInternalError(fmt.Errorf("could not update account %s: %s", account.ID, err))  | 
