diff options
author | 2021-05-17 19:06:58 +0200 | |
---|---|---|
committer | 2021-05-17 19:06:58 +0200 | |
commit | 6cd033449fd328410128bc3e840f4b8d3d74f052 (patch) | |
tree | 9868db8439533e078dea5bb34ae4949e9460f2cb /internal/db | |
parent | update progress (diff) | |
download | gotosocial-6cd033449fd328410128bc3e840f4b8d3d74f052.tar.xz |
Refine statuses (#26)
Remote media is now dereferenced and attached properly to incoming federated statuses.
Mentions are now dereferenced and attached properly to incoming federated statuses.
Small fixes to status visibility.
Allow URL params for filtering statuses:
// ExcludeRepliesKey is for specifying whether to exclude replies in a list of returned statuses by an account.
// PinnedKey is for specifying whether to include pinned statuses in a list of returned statuses by an account.
// MaxIDKey is for specifying the maximum ID of the status to retrieve.
// MediaOnlyKey is for specifying that only statuses with media should be returned in a list of returned statuses by an account.
Add endpoint for fetching an account's statuses.
Diffstat (limited to 'internal/db')
-rw-r--r-- | internal/db/db.go | 11 | ||||
-rw-r--r-- | internal/db/pg/pg.go | 91 |
2 files changed, 61 insertions, 41 deletions
diff --git a/internal/db/db.go b/internal/db/db.go index a354ddee8..cbcd698c9 100644 --- a/internal/db/db.go +++ b/internal/db/db.go @@ -160,16 +160,14 @@ type DB interface { // In case of no entries, a 'no entries' error will be returned GetFavesByAccountID(accountID string, faves *[]gtsmodel.StatusFave) error - // GetStatusesByAccountID is a shortcut for the common action of fetching a list of statuses produced by accountID. - // The given slice 'statuses' will be set to the result of the query, whatever it is. - // In case of no entries, a 'no entries' error will be returned - GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error + // CountStatusesByAccountID is a shortcut for the common action of counting statuses produced by accountID. + CountStatusesByAccountID(accountID string) (int, error) // GetStatusesByTimeDescending is a shortcut for getting the most recent statuses. accountID is optional, if not provided // then all statuses will be returned. If limit is set to 0, the size of the returned slice will not be limited. This can // be very memory intensive so you probably shouldn't do this! // In case of no entries, a 'no entries' error will be returned - GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error + GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) error // GetLastStatusForAccountID simply gets the most recent status by the given account. // The given slice 'status' pointer will be set to the result of the query, whatever it is. @@ -251,9 +249,6 @@ type DB interface { // StatusBookmarkedBy checks if a given status has been bookmarked by a given account ID StatusBookmarkedBy(status *gtsmodel.Status, accountID string) (bool, error) - // StatusPinnedBy checks if a given status has been pinned by a given account ID - StatusPinnedBy(status *gtsmodel.Status, accountID string) (bool, error) - // FaveStatus faves the given status, using accountID as the faver. // The returned fave will be nil if the status was already faved. FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) diff --git a/internal/db/pg/pg.go b/internal/db/pg/pg.go index f8c2fdbe8..d3590a027 100644 --- a/internal/db/pg/pg.go +++ b/internal/db/pg/pg.go @@ -456,23 +456,35 @@ func (ps *postgresService) GetFavesByAccountID(accountID string, faves *[]gtsmod return nil } -func (ps *postgresService) GetStatusesByAccountID(accountID string, statuses *[]gtsmodel.Status) error { - if err := ps.conn.Model(statuses).Where("account_id = ?", accountID).Select(); err != nil { +func (ps *postgresService) CountStatusesByAccountID(accountID string) (int, error) { + count, err := ps.conn.Model(>smodel.Status{}).Where("account_id = ?", accountID).Count() + if err != nil { if err == pg.ErrNoRows { - return db.ErrNoEntries{} + return 0, nil } - return err + return 0, err } - return nil + return count, nil } -func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int) error { +func (ps *postgresService) GetStatusesByTimeDescending(accountID string, statuses *[]gtsmodel.Status, limit int, excludeReplies bool, maxID string, pinned bool, mediaOnly bool) error { q := ps.conn.Model(statuses).Order("created_at DESC") + if accountID != "" { + q = q.Where("account_id = ?", accountID) + } if limit != 0 { q = q.Limit(limit) } - if accountID != "" { - q = q.Where("account_id = ?", accountID) + if excludeReplies { + q = q.Where("? IS NULL", pg.Ident("in_reply_to_id")) + } + if pinned { + q = q.Where("pinned = ?", true) + } + if mediaOnly { + q = q.WhereGroup(func(q *pg.Query) (*pg.Query, error) { + return q.Where("? IS NOT NULL", pg.Ident("attachments")).Where("attachments != '{}'"), nil + }) } if err := q.Select(); err != nil { if err == pg.ErrNoRows { @@ -679,20 +691,23 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc } // if the target user doesn't exist (anymore) then the status also shouldn't be visible - targetUser := >smodel.User{} - if err := ps.conn.Model(targetUser).Where("account_id = ?", targetAccount.ID).Select(); err != nil { - l.Debug("target user could not be selected") - if err == pg.ErrNoRows { - return false, db.ErrNoEntries{} + // note: we only do this for local users + if targetAccount.Domain == "" { + targetUser := >smodel.User{} + if err := ps.conn.Model(targetUser).Where("account_id = ?", targetAccount.ID).Select(); err != nil { + l.Debug("target user could not be selected") + if err == pg.ErrNoRows { + return false, db.ErrNoEntries{} + } + return false, err } - return false, err - } - // if target user is disabled, not yet approved, or not confirmed then don't show the status - // (although in the latter two cases it's unlikely they posted a status yet anyway, but you never know!) - if targetUser.Disabled || !targetUser.Approved || targetUser.ConfirmedAt.IsZero() { - l.Debug("target user is disabled, not approved, or not confirmed") - return false, nil + // if target user is disabled, not yet approved, or not confirmed then don't show the status + // (although in the latter two cases it's unlikely they posted a status yet anyway, but you never know!) + if targetUser.Disabled || !targetUser.Approved || targetUser.ConfirmedAt.IsZero() { + l.Debug("target user is disabled, not approved, or not confirmed") + return false, nil + } } // If requesting account is nil, that means whoever requested the status didn't auth, or their auth failed. @@ -755,6 +770,7 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc if blocked, err := ps.Blocked(relevantAccounts.ReplyToAccount.ID, requestingAccount.ID); err != nil { return false, err } else if blocked { + l.Debug("a block exists between requesting account and reply to account") return false, nil } } @@ -764,6 +780,7 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc if blocked, err := ps.Blocked(relevantAccounts.BoostedAccount.ID, requestingAccount.ID); err != nil { return false, err } else if blocked { + l.Debug("a block exists between requesting account and boosted account") return false, nil } } @@ -773,6 +790,7 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc if blocked, err := ps.Blocked(relevantAccounts.BoostedReplyToAccount.ID, requestingAccount.ID); err != nil { return false, err } else if blocked { + l.Debug("a block exists between requesting account and boosted reply to account") return false, nil } } @@ -782,9 +800,17 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc if blocked, err := ps.Blocked(a.ID, requestingAccount.ID); err != nil { return false, err } else if blocked { + l.Debug("a block exists between requesting account and a mentioned account") return false, nil } } + + // if the requesting account is mentioned in the status it should always be visible + for _, acct := range relevantAccounts.MentionedAccounts { + if acct.ID == requestingAccount.ID { + return true, nil // yep it's mentioned! + } + } } // at this point we know neither account blocks the other, or another account mentioned or otherwise referred to in the status @@ -800,6 +826,7 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc return false, err } if !follows { + l.Debug("requested status is followers only but requesting account is not a follower") return false, nil } return true, nil @@ -810,16 +837,12 @@ func (ps *postgresService) StatusVisible(targetStatus *gtsmodel.Status, targetAc return false, err } if !mutuals { + l.Debug("requested status is mutuals only but accounts aren't mufos") return false, nil } return true, nil case gtsmodel.VisibilityDirect: - // make sure the requesting account is mentioned in the status - for _, menchie := range targetStatus.Mentions { - if menchie == requestingAccount.ID { - return true, nil // yep it's mentioned! - } - } + l.Debug("requesting account requests a status it's not mentioned in") return false, nil // it's not mentioned -_- } @@ -890,10 +913,16 @@ func (ps *postgresService) PullRelevantAccountsFromStatus(targetStatus *gtsmodel } // now get all accounts with IDs that are mentioned in the status - for _, mentionedAccountID := range targetStatus.Mentions { + for _, mentionID := range targetStatus.Mentions { + + mention := >smodel.Mention{} + if err := ps.conn.Model(mention).Where("id = ?", mentionID).Select(); err != nil { + return accounts, fmt.Errorf("error getting mention with id %s: %s", mentionID, err) + } + mentionedAccount := >smodel.Account{} - if err := ps.conn.Model(mentionedAccount).Where("id = ?", mentionedAccountID).Select(); err != nil { - return accounts, err + if err := ps.conn.Model(mentionedAccount).Where("id = ?", mention.TargetAccountID).Select(); err != nil { + return accounts, fmt.Errorf("error getting mentioned account: %s", err) } accounts.MentionedAccounts = append(accounts.MentionedAccounts, mentionedAccount) } @@ -929,10 +958,6 @@ func (ps *postgresService) StatusBookmarkedBy(status *gtsmodel.Status, accountID return ps.conn.Model(>smodel.StatusBookmark{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists() } -func (ps *postgresService) StatusPinnedBy(status *gtsmodel.Status, accountID string) (bool, error) { - return ps.conn.Model(>smodel.StatusPin{}).Where("status_id = ?", status.ID).Where("account_id = ?", accountID).Exists() -} - func (ps *postgresService) FaveStatus(status *gtsmodel.Status, accountID string) (*gtsmodel.StatusFave, error) { // first check if a fave already exists, we can just return if so existingFave := >smodel.StatusFave{} |