2021-08-10 11:32:39 +00:00
/ *
GoToSocial
2021-12-20 17:42:19 +00:00
Copyright ( C ) 2021 - 2022 GoToSocial Authors admin @ gotosocial . org
2021-08-10 11:32:39 +00:00
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 dereferencing
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/url"
2021-08-29 10:03:08 +00:00
"strings"
2021-08-10 11:32:39 +00:00
2021-11-13 16:29:43 +00:00
"github.com/superseriousbusiness/activity/streams"
"github.com/superseriousbusiness/activity/streams/vocab"
2021-08-10 11:32:39 +00:00
"github.com/superseriousbusiness/gotosocial/internal/ap"
2022-11-29 09:24:55 +00:00
"github.com/superseriousbusiness/gotosocial/internal/config"
2022-09-12 11:03:23 +00:00
"github.com/superseriousbusiness/gotosocial/internal/db"
2021-08-10 11:32:39 +00:00
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
"github.com/superseriousbusiness/gotosocial/internal/id"
2022-07-19 08:47:55 +00:00
"github.com/superseriousbusiness/gotosocial/internal/log"
2022-01-09 17:41:22 +00:00
"github.com/superseriousbusiness/gotosocial/internal/media"
2021-08-10 11:32:39 +00:00
)
2022-11-29 09:24:55 +00:00
// EnrichRemoteStatus takes a remote status that's already been inserted into the database in a minimal form,
2021-08-10 11:32:39 +00:00
// and populates it with additional fields, media, etc.
//
// EnrichRemoteStatus is mostly useful for calling after a status has been initially created by
// the federatingDB's Create function, but additional dereferencing is needed on it.
2021-09-14 10:23:56 +00:00
func ( d * deref ) EnrichRemoteStatus ( ctx context . Context , username string , status * gtsmodel . Status , includeParent bool ) ( * gtsmodel . Status , error ) {
if err := d . populateStatusFields ( ctx , status , username , includeParent ) ; err != nil {
2021-08-10 11:32:39 +00:00
return nil , err
}
2022-11-15 18:45:15 +00:00
if err := d . db . UpdateStatus ( ctx , status ) ; err != nil {
return nil , err
}
return status , nil
2021-08-10 11:32:39 +00:00
}
2022-11-29 09:24:55 +00:00
// GetStatus completely dereferences a status, converts it to a GtS model status,
2022-05-23 15:40:03 +00:00
// puts it in the database, and returns it to a caller.
2021-08-10 11:32:39 +00:00
//
2022-05-23 15:40:03 +00:00
// If refetch is true, then regardless of whether we have the original status in the database or not,
// the ap.Statusable representation of the status will be dereferenced and returned.
2021-08-10 11:32:39 +00:00
//
2022-05-23 15:40:03 +00:00
// If refetch is false, the ap.Statusable will only be returned if this is a new status, so callers
// should check whether or not this is nil.
2021-08-10 11:32:39 +00:00
//
2022-11-29 09:24:55 +00:00
// GetAccount will guard against trying to do http calls to fetch a status that belongs to this instance.
// Instead of making calls, it will just return the status early if it finds it, or return an error.
func ( d * deref ) GetStatus ( ctx context . Context , username string , statusURI * url . URL , refetch , includeParent bool ) ( * gtsmodel . Status , ap . Statusable , error ) {
uriString := statusURI . String ( )
// try to get by URI first
status , dbErr := d . db . GetStatusByURI ( ctx , uriString )
if dbErr != nil {
if ! errors . Is ( dbErr , db . ErrNoEntries ) {
// real error
return nil , nil , newErrDB ( fmt . Errorf ( "GetRemoteStatus: error during GetStatusByURI for %s: %w" , uriString , dbErr ) )
}
// no problem, just press on
} else if ! refetch {
2022-05-23 15:40:03 +00:00
// we already had the status and we aren't being asked to refetch the AP representation
2022-11-29 09:24:55 +00:00
return status , nil , nil
2021-08-10 11:32:39 +00:00
}
2022-11-29 09:24:55 +00:00
// try to get by URL if we couldn't get by URI now
if status == nil {
status , dbErr = d . db . GetStatusByURL ( ctx , uriString )
if dbErr != nil {
if ! errors . Is ( dbErr , db . ErrNoEntries ) {
// real error
return nil , nil , newErrDB ( fmt . Errorf ( "GetRemoteStatus: error during GetStatusByURI for %s: %w" , uriString , dbErr ) )
}
// no problem, just press on
} else if ! refetch {
// we already had the status and we aren't being asked to refetch the AP representation
return status , nil , nil
}
2022-05-23 15:40:03 +00:00
}
2022-11-29 09:24:55 +00:00
// guard against having our own statuses passed in
if host := statusURI . Host ; host == config . GetHost ( ) || host == config . GetAccountDomain ( ) {
// this is our status, definitely don't search for it
if status != nil {
return status , nil , nil
}
return nil , nil , newErrNotRetrievable ( fmt . Errorf ( "GetRemoteStatus: uri %s is apparently ours, but we have nothing in the db for it, will not proceed to dereference our own status" , uriString ) )
}
// if we got here, either we didn't have the status
// in the db, or we had it but need to refetch it
statusable , derefErr := d . dereferenceStatusable ( ctx , username , statusURI )
if derefErr != nil {
return nil , nil , wrapDerefError ( derefErr , "GetRemoteStatus: error dereferencing statusable" )
}
if status != nil && refetch {
// we already had the status in the db, and we've also
// now fetched the AP representation as requested
return status , statusable , nil
2021-08-10 11:32:39 +00:00
}
2022-05-23 15:40:03 +00:00
// from here on out we can consider this to be a 'new' status because we didn't have the status in the db already
2021-08-10 11:32:39 +00:00
accountURI , err := ap . ExtractAttributedTo ( statusable )
if err != nil {
2022-11-29 09:24:55 +00:00
return nil , nil , newErrOther ( fmt . Errorf ( "GetRemoteStatus: error extracting attributedTo: %w" , err ) )
2021-08-10 11:32:39 +00:00
}
2022-11-29 09:24:55 +00:00
// we need to get the author of the status else we can't serialize it properly
if _ , err = d . GetAccount ( ctx , GetAccountParams {
2022-06-11 09:01:34 +00:00
RequestingUsername : username ,
RemoteAccountID : accountURI ,
2022-11-29 09:24:55 +00:00
Blocking : true ,
} ) ; err != nil {
return nil , nil , newErrOther ( fmt . Errorf ( "GetRemoteStatus: couldn't get status author: %s" , err ) )
2021-08-10 11:32:39 +00:00
}
2022-11-29 09:24:55 +00:00
status , err = d . typeConverter . ASStatusToStatus ( ctx , statusable )
2021-08-10 11:32:39 +00:00
if err != nil {
2022-11-29 09:24:55 +00:00
return nil , nil , newErrOther ( fmt . Errorf ( "GetRemoteStatus: error converting statusable to status: %s" , err ) )
2021-08-10 11:32:39 +00:00
}
2022-11-29 09:24:55 +00:00
ulid , err := id . NewULIDFromTime ( status . CreatedAt )
2022-05-23 15:40:03 +00:00
if err != nil {
2022-11-29 09:24:55 +00:00
return nil , nil , newErrOther ( fmt . Errorf ( "GetRemoteStatus: error generating new id for status: %s" , err ) )
2022-05-23 15:40:03 +00:00
}
2022-11-29 09:24:55 +00:00
status . ID = ulid
2021-08-10 11:32:39 +00:00
2022-11-29 09:24:55 +00:00
if err := d . populateStatusFields ( ctx , status , username , includeParent ) ; err != nil {
return nil , nil , newErrOther ( fmt . Errorf ( "GetRemoteStatus: error populating status fields: %s" , err ) )
2022-05-23 15:40:03 +00:00
}
2021-08-10 11:32:39 +00:00
2022-11-29 09:24:55 +00:00
if err := d . db . PutStatus ( ctx , status ) ; err != nil && ! errors . Is ( err , db . ErrAlreadyExists ) {
return nil , nil , newErrDB ( fmt . Errorf ( "GetRemoteStatus: error putting new status: %s" , err ) )
2021-08-10 11:32:39 +00:00
}
2022-11-29 09:24:55 +00:00
return status , statusable , nil
2021-08-10 11:32:39 +00:00
}
2021-08-25 13:34:33 +00:00
func ( d * deref ) dereferenceStatusable ( ctx context . Context , username string , remoteStatusID * url . URL ) ( ap . Statusable , error ) {
if blocked , err := d . db . IsDomainBlocked ( ctx , remoteStatusID . Host ) ; blocked || err != nil {
2021-08-10 11:32:39 +00:00
return nil , fmt . Errorf ( "DereferenceStatusable: domain %s is blocked" , remoteStatusID . Host )
}
2021-08-25 13:34:33 +00:00
transport , err := d . transportController . NewTransportForUsername ( ctx , username )
2021-08-10 11:32:39 +00:00
if err != nil {
return nil , fmt . Errorf ( "DereferenceStatusable: transport err: %s" , err )
}
2021-10-04 13:24:19 +00:00
b , err := transport . Dereference ( ctx , remoteStatusID )
2021-08-10 11:32:39 +00:00
if err != nil {
return nil , fmt . Errorf ( "DereferenceStatusable: error deferencing %s: %s" , remoteStatusID . String ( ) , err )
}
m := make ( map [ string ] interface { } )
if err := json . Unmarshal ( b , & m ) ; err != nil {
return nil , fmt . Errorf ( "DereferenceStatusable: error unmarshalling bytes into json: %s" , err )
}
2021-10-04 13:24:19 +00:00
t , err := streams . ToType ( ctx , m )
2021-08-10 11:32:39 +00:00
if err != nil {
return nil , fmt . Errorf ( "DereferenceStatusable: error resolving json into ap vocab type: %s" , err )
}
// Article, Document, Image, Video, Note, Page, Event, Place, Mention, Profile
switch t . GetTypeName ( ) {
2021-08-31 13:59:12 +00:00
case ap . ObjectArticle :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsArticle )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsArticle" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectDocument :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsDocument )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsDocument" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectImage :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsImage )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsImage" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectVideo :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsVideo )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsVideo" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectNote :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsNote )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsNote" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectPage :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsPage )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsPage" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectEvent :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsEvent )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsEvent" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectPlace :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsPlace )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsPlace" )
}
return p , nil
2021-08-31 13:59:12 +00:00
case ap . ObjectProfile :
2021-08-10 11:32:39 +00:00
p , ok := t . ( vocab . ActivityStreamsProfile )
if ! ok {
return nil , errors . New ( "DereferenceStatusable: error resolving type as ActivityStreamsProfile" )
}
return p , nil
}
2022-11-29 09:24:55 +00:00
return nil , newErrWrongType ( fmt . Errorf ( "DereferenceStatusable: type name %s not supported as Statusable" , t . GetTypeName ( ) ) )
2021-08-10 11:32:39 +00:00
}
// populateStatusFields fetches all the information we temporarily pinned to an incoming
// federated status, back in the federating db's Create function.
//
// When a status comes in from the federation API, there are certain fields that
// haven't been dereferenced yet, because we needed to provide a snappy synchronous
// response to the caller. By the time it reaches this function though, it's being
// processed asynchronously, so we have all the time in the world to fetch the various
// bits and bobs that are attached to the status, and properly flesh it out, before we
// send the status to any timelines and notify people.
//
// Things to dereference and fetch here:
//
// 1. Media attachments.
// 2. Hashtags.
// 3. Emojis.
// 4. Mentions.
2021-08-29 10:03:08 +00:00
// 5. Replied-to-status.
2021-08-10 11:32:39 +00:00
//
// SIDE EFFECTS:
// This function will deference all of the above, insert them in the database as necessary,
// and attach them to the status. The status itself will not be added to the database yet,
// that's up the caller to do.
2021-09-14 10:23:56 +00:00
func ( d * deref ) populateStatusFields ( ctx context . Context , status * gtsmodel . Status , requestingUsername string , includeParent bool ) error {
2021-08-29 10:03:08 +00:00
statusIRI , err := url . Parse ( status . URI )
2021-08-10 11:32:39 +00:00
if err != nil {
2021-08-29 10:03:08 +00:00
return fmt . Errorf ( "populateStatusFields: couldn't parse status URI %s: %s" , status . URI , err )
2021-08-10 11:32:39 +00:00
}
2021-08-29 10:03:08 +00:00
blocked , err := d . db . IsURIBlocked ( ctx , statusIRI )
2021-08-10 11:32:39 +00:00
if err != nil {
2021-08-29 10:03:08 +00:00
return fmt . Errorf ( "populateStatusFields: error checking blocked status of %s: %s" , statusIRI , err )
}
if blocked {
return fmt . Errorf ( "populateStatusFields: domain %s is blocked" , statusIRI )
2021-08-10 11:32:39 +00:00
}
// in case the status doesn't have an id yet (ie., it hasn't entered the database yet), then create one
if status . ID == "" {
newID , err := id . NewULIDFromTime ( status . CreatedAt )
if err != nil {
2021-08-29 10:03:08 +00:00
return fmt . Errorf ( "populateStatusFields: error creating ulid for status: %s" , err )
2021-08-10 11:32:39 +00:00
}
status . ID = newID
}
// 1. Media attachments.
2021-08-29 10:03:08 +00:00
if err := d . populateStatusAttachments ( ctx , status , requestingUsername ) ; err != nil {
return fmt . Errorf ( "populateStatusFields: error populating status attachments: %s" , err )
2021-08-10 11:32:39 +00:00
}
// 2. Hashtags
2021-08-29 10:03:08 +00:00
// TODO
2021-08-10 11:32:39 +00:00
// 3. Emojis
2022-09-12 11:03:23 +00:00
if err := d . populateStatusEmojis ( ctx , status , requestingUsername ) ; err != nil {
return fmt . Errorf ( "populateStatusFields: error populating status emojis: %s" , err )
}
2021-08-10 11:32:39 +00:00
2021-09-14 10:23:56 +00:00
// 4. Mentions
2021-09-01 09:08:21 +00:00
// TODO: do we need to handle removing empty mention objects and just using mention IDs slice?
2021-09-14 10:23:56 +00:00
if err := d . populateStatusMentions ( ctx , status , requestingUsername ) ; err != nil {
return fmt . Errorf ( "populateStatusFields: error populating status mentions: %s" , err )
2021-08-29 10:03:08 +00:00
}
2021-09-01 09:08:21 +00:00
// 5. Replied-to-status (only if requested)
if includeParent {
if err := d . populateStatusRepliedTo ( ctx , status , requestingUsername ) ; err != nil {
return fmt . Errorf ( "populateStatusFields: error populating status repliedTo: %s" , err )
}
2021-08-29 10:03:08 +00:00
}
return nil
}
func ( d * deref ) populateStatusMentions ( ctx context . Context , status * gtsmodel . Status , requestingUsername string ) error {
2021-08-10 11:32:39 +00:00
// At this point, mentions should have the namestring and mentionedAccountURI set on them.
2021-08-29 10:03:08 +00:00
// We can use these to find the accounts.
2021-08-20 10:26:56 +00:00
mentionIDs := [ ] string { }
2021-08-29 10:03:08 +00:00
newMentions := [ ] * gtsmodel . Mention { }
2021-08-20 10:26:56 +00:00
for _ , m := range status . Mentions {
2021-08-10 11:32:39 +00:00
if m . ID != "" {
// we've already populated this mention, since it has an ID
2022-07-19 08:47:55 +00:00
log . Debug ( "populateStatusMentions: mention already populated" )
2021-08-29 10:03:08 +00:00
mentionIDs = append ( mentionIDs , m . ID )
newMentions = append ( newMentions , m )
2021-08-20 10:26:56 +00:00
continue
2021-08-10 11:32:39 +00:00
}
2021-08-20 10:26:56 +00:00
if m . TargetAccountURI == "" {
2022-07-19 08:47:55 +00:00
log . Debug ( "populateStatusMentions: target URI not set on mention" )
2021-08-20 10:26:56 +00:00
continue
2021-08-10 11:32:39 +00:00
}
2021-08-20 10:26:56 +00:00
targetAccountURI , err := url . Parse ( m . TargetAccountURI )
2021-08-10 11:32:39 +00:00
if err != nil {
2022-07-19 08:47:55 +00:00
log . Debugf ( "populateStatusMentions: error parsing mentioned account uri %s: %s" , m . TargetAccountURI , err )
2021-08-10 11:32:39 +00:00
continue
}
2021-08-20 10:26:56 +00:00
var targetAccount * gtsmodel . Account
2021-08-29 10:03:08 +00:00
errs := [ ] string { }
// check if account is in the db already
2021-09-14 10:23:56 +00:00
if a , err := d . db . GetAccountByURI ( ctx , targetAccountURI . String ( ) ) ; err != nil {
2021-08-29 10:03:08 +00:00
errs = append ( errs , err . Error ( ) )
2021-08-20 10:26:56 +00:00
} else {
2022-07-19 08:47:55 +00:00
log . Debugf ( "populateStatusMentions: got target account %s with id %s through GetAccountByURI" , targetAccountURI , a . ID )
2021-08-29 10:03:08 +00:00
targetAccount = a
}
if targetAccount == nil {
// we didn't find the account in our database already
// check if we can get the account remotely (dereference it)
2022-11-29 09:24:55 +00:00
if a , err := d . GetAccount ( ctx , GetAccountParams {
2022-06-11 09:01:34 +00:00
RequestingUsername : requestingUsername ,
RemoteAccountID : targetAccountURI ,
} ) ; err != nil {
2021-08-29 10:03:08 +00:00
errs = append ( errs , err . Error ( ) )
} else {
2022-07-19 08:47:55 +00:00
log . Debugf ( "populateStatusMentions: got target account %s with id %s through GetRemoteAccount" , targetAccountURI , a . ID )
2021-08-29 10:03:08 +00:00
targetAccount = a
}
}
if targetAccount == nil {
2022-07-19 08:47:55 +00:00
log . Debugf ( "populateStatusMentions: couldn't get target account %s: %s" , m . TargetAccountURI , strings . Join ( errs , " : " ) )
2021-08-20 10:26:56 +00:00
continue
}
2021-08-10 11:32:39 +00:00
2021-08-20 10:26:56 +00:00
mID , err := id . NewRandomULID ( )
2021-08-10 11:32:39 +00:00
if err != nil {
2021-08-29 10:03:08 +00:00
return fmt . Errorf ( "populateStatusMentions: error generating ulid: %s" , err )
2021-08-20 10:26:56 +00:00
}
2021-08-29 10:03:08 +00:00
newMention := & gtsmodel . Mention {
2021-08-20 10:26:56 +00:00
ID : mID ,
StatusID : status . ID ,
Status : m . Status ,
CreatedAt : status . CreatedAt ,
UpdatedAt : status . UpdatedAt ,
2021-09-14 10:23:56 +00:00
OriginAccountID : status . AccountID ,
2021-08-20 10:26:56 +00:00
OriginAccountURI : status . AccountURI ,
OriginAccount : status . Account ,
TargetAccountID : targetAccount . ID ,
TargetAccount : targetAccount ,
NameString : m . NameString ,
TargetAccountURI : targetAccount . URI ,
TargetAccountURL : targetAccount . URL ,
2021-08-10 11:32:39 +00:00
}
2021-08-29 10:03:08 +00:00
if err := d . db . Put ( ctx , newMention ) ; err != nil {
return fmt . Errorf ( "populateStatusMentions: error creating mention: %s" , err )
2021-08-10 11:32:39 +00:00
}
2021-08-29 10:03:08 +00:00
mentionIDs = append ( mentionIDs , newMention . ID )
newMentions = append ( newMentions , newMention )
2021-08-10 11:32:39 +00:00
}
2021-08-29 10:03:08 +00:00
2021-08-20 10:26:56 +00:00
status . MentionIDs = mentionIDs
2021-08-29 10:03:08 +00:00
status . Mentions = newMentions
2021-08-10 11:32:39 +00:00
2021-08-29 10:03:08 +00:00
return nil
}
func ( d * deref ) populateStatusAttachments ( ctx context . Context , status * gtsmodel . Status , requestingUsername string ) error {
// At this point we should know:
// * the media type of the file we're looking for (a.File.ContentType)
// * the file type (a.Type)
// * the remote URL (a.RemoteURL)
// This should be enough to dereference the piece of media.
attachmentIDs := [ ] string { }
attachments := [ ] * gtsmodel . MediaAttachment { }
for _ , a := range status . Attachments {
2021-09-04 12:02:01 +00:00
a . AccountID = status . AccountID
a . StatusID = status . ID
2021-08-29 10:03:08 +00:00
2022-01-24 12:12:17 +00:00
processingMedia , err := d . GetRemoteMedia ( ctx , requestingUsername , a . AccountID , a . RemoteURL , & media . AdditionalMediaInfo {
2022-01-09 17:41:22 +00:00
CreatedAt : & a . CreatedAt ,
StatusID : & a . StatusID ,
RemoteURL : & a . RemoteURL ,
Description : & a . Description ,
Blurhash : & a . Blurhash ,
} )
2021-08-29 10:03:08 +00:00
if err != nil {
2022-07-19 08:47:55 +00:00
log . Errorf ( "populateStatusAttachments: couldn't get remote media %s: %s" , a . RemoteURL , err )
2022-01-08 16:17:01 +00:00
continue
}
2022-01-24 12:12:17 +00:00
attachment , err := processingMedia . LoadAttachment ( ctx )
2022-01-08 16:17:01 +00:00
if err != nil {
2022-07-19 08:47:55 +00:00
log . Errorf ( "populateStatusAttachments: couldn't load remote attachment %s: %s" , a . RemoteURL , err )
2021-09-01 09:08:21 +00:00
continue
2021-08-29 10:03:08 +00:00
}
attachmentIDs = append ( attachmentIDs , attachment . ID )
attachments = append ( attachments , attachment )
}
status . AttachmentIDs = attachmentIDs
status . Attachments = attachments
return nil
}
2022-09-12 11:03:23 +00:00
func ( d * deref ) populateStatusEmojis ( ctx context . Context , status * gtsmodel . Status , requestingUsername string ) error {
2022-09-26 09:56:01 +00:00
emojis , err := d . populateEmojis ( ctx , status . Emojis , requestingUsername )
if err != nil {
return err
}
2022-09-12 11:03:23 +00:00
2022-09-26 09:56:01 +00:00
emojiIDs := make ( [ ] string , 0 , len ( emojis ) )
for _ , e := range emojis {
emojiIDs = append ( emojiIDs , e . ID )
2022-09-12 11:03:23 +00:00
}
2022-09-26 09:56:01 +00:00
status . Emojis = emojis
2022-09-12 11:03:23 +00:00
status . EmojiIDs = emojiIDs
return nil
}
2021-08-29 10:03:08 +00:00
func ( d * deref ) populateStatusRepliedTo ( ctx context . Context , status * gtsmodel . Status , requestingUsername string ) error {
2021-08-10 11:32:39 +00:00
if status . InReplyToURI != "" && status . InReplyToID == "" {
2021-08-20 10:26:56 +00:00
statusURI , err := url . Parse ( status . InReplyToURI )
if err != nil {
return err
}
2021-08-29 10:03:08 +00:00
2022-11-29 09:24:55 +00:00
replyToStatus , _ , err := d . GetStatus ( ctx , requestingUsername , statusURI , false , false )
2021-09-01 09:08:21 +00:00
if err != nil {
2022-05-23 15:40:03 +00:00
return fmt . Errorf ( "populateStatusRepliedTo: couldn't get reply to status with uri %s: %s" , status . InReplyToURI , err )
2021-08-10 11:32:39 +00:00
}
2021-08-29 10:03:08 +00:00
// we have the status
status . InReplyToID = replyToStatus . ID
status . InReplyTo = replyToStatus
status . InReplyToAccountID = replyToStatus . AccountID
status . InReplyToAccount = replyToStatus . Account
2021-08-10 11:32:39 +00:00
}
2021-08-29 10:03:08 +00:00
2021-08-10 11:32:39 +00:00
return nil
}