2021-04-19 17:42:19 +00:00
/ *
GoToSocial
2023-01-05 11:43:00 +00:00
Copyright ( C ) 2021 - 2023 GoToSocial Authors admin @ gotosocial . org
2021-04-19 17:42:19 +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/>.
* /
2021-05-08 12:25:55 +00:00
package typeutils
2021-04-19 17:42:19 +00:00
import (
2021-08-25 13:34:33 +00:00
"context"
2022-11-08 17:11:06 +00:00
"errors"
2021-04-19 17:42:19 +00:00
"fmt"
2022-12-22 10:48:28 +00:00
"math"
"strconv"
2021-05-17 17:06:58 +00:00
"strings"
2021-04-19 17:42:19 +00:00
2023-01-02 12:10:50 +00:00
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
2021-12-07 12:31:39 +00:00
"github.com/superseriousbusiness/gotosocial/internal/config"
2021-04-19 17:42:19 +00:00
"github.com/superseriousbusiness/gotosocial/internal/db"
2022-12-22 10:48:28 +00:00
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
2021-05-08 12:25:55 +00:00
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
2022-07-19 08:47:55 +00:00
"github.com/superseriousbusiness/gotosocial/internal/log"
2022-06-26 08:58:45 +00:00
"github.com/superseriousbusiness/gotosocial/internal/media"
2022-05-24 16:21:27 +00:00
"github.com/superseriousbusiness/gotosocial/internal/util"
2021-04-19 17:42:19 +00:00
)
2022-09-08 10:36:42 +00:00
const (
instanceStatusesCharactersReservedPerURL = 25
instanceMediaAttachmentsImageMatrixLimit = 16777216 // width * height
instanceMediaAttachmentsVideoMatrixLimit = 16777216 // width * height
instanceMediaAttachmentsVideoFrameRateLimit = 60
instancePollsMinExpiration = 300 // seconds
instancePollsMaxExpiration = 2629746 // seconds
)
2023-01-02 12:10:50 +00:00
func ( c * converter ) AccountToAPIAccountSensitive ( ctx context . Context , a * gtsmodel . Account ) ( * apimodel . Account , error ) {
2021-04-19 17:42:19 +00:00
// we can build this sensitive account easily by first getting the public account....
2021-10-04 13:24:19 +00:00
apiAccount , err := c . AccountToAPIAccountPublic ( ctx , a )
2021-04-19 17:42:19 +00:00
if err != nil {
return nil , err
}
// then adding the Source object to it...
// check pending follow requests aimed at this account
2021-08-25 13:34:33 +00:00
frs , err := c . db . GetAccountFollowRequests ( ctx , a . ID )
2021-08-20 10:26:56 +00:00
if err != nil {
if err != db . ErrNoEntries {
2021-04-19 17:42:19 +00:00
return nil , fmt . Errorf ( "error getting follow requests: %s" , err )
}
}
var frc int
2021-08-20 10:26:56 +00:00
if frs != nil {
frc = len ( frs )
2021-04-19 17:42:19 +00:00
}
2023-01-02 12:10:50 +00:00
statusFormat := string ( apimodel . StatusFormatDefault )
2022-08-06 10:09:21 +00:00
if a . StatusFormat != "" {
statusFormat = a . StatusFormat
}
2023-01-02 12:10:50 +00:00
apiAccount . Source = & apimodel . Source {
2021-10-04 13:24:19 +00:00
Privacy : c . VisToAPIVis ( ctx , a . Privacy ) ,
2022-08-15 10:35:05 +00:00
Sensitive : * a . Sensitive ,
2021-04-19 17:42:19 +00:00
Language : a . Language ,
2022-08-06 10:09:21 +00:00
StatusFormat : statusFormat ,
2022-05-07 15:55:27 +00:00
Note : a . NoteRaw ,
2021-10-04 13:24:19 +00:00
Fields : apiAccount . Fields ,
2021-04-19 17:42:19 +00:00
FollowRequestsCount : frc ,
}
2021-10-04 13:24:19 +00:00
return apiAccount , nil
2021-04-19 17:42:19 +00:00
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) AccountToAPIAccountPublic ( ctx context . Context , a * gtsmodel . Account ) ( * apimodel . Account , error ) {
2021-08-20 10:26:56 +00:00
// count followers
2021-08-25 13:34:33 +00:00
followersCount , err := c . db . CountAccountFollowedBy ( ctx , a . ID , false )
2021-08-20 10:26:56 +00:00
if err != nil {
return nil , fmt . Errorf ( "error counting followers: %s" , err )
2021-04-19 17:42:19 +00:00
}
// count following
2021-08-25 13:34:33 +00:00
followingCount , err := c . db . CountAccountFollows ( ctx , a . ID , false )
2021-08-20 10:26:56 +00:00
if err != nil {
return nil , fmt . Errorf ( "error counting following: %s" , err )
2021-04-19 17:42:19 +00:00
}
// count statuses
2021-08-25 13:34:33 +00:00
statusesCount , err := c . db . CountAccountStatuses ( ctx , a . ID )
2021-05-17 17:06:58 +00:00
if err != nil {
2021-08-20 10:26:56 +00:00
return nil , fmt . Errorf ( "error counting statuses: %s" , err )
2021-04-19 17:42:19 +00:00
}
// check when the last status was
2022-11-13 20:38:01 +00:00
var lastStatusAt * string
2022-10-08 12:00:39 +00:00
lastPosted , err := c . db . GetAccountLastPosted ( ctx , a . ID , false )
2021-08-20 10:26:56 +00:00
if err == nil && ! lastPosted . IsZero ( ) {
2022-11-13 20:38:01 +00:00
lastStatusAtTemp := util . FormatISO8601 ( lastPosted )
lastStatusAt = & lastStatusAtTemp
2021-04-19 17:42:19 +00:00
}
2022-01-24 12:12:17 +00:00
// set account avatar fields if available
2021-08-20 10:26:56 +00:00
var aviURL string
var aviURLStatic string
if a . AvatarMediaAttachmentID != "" {
if a . AvatarMediaAttachment == nil {
2021-08-25 13:34:33 +00:00
avi , err := c . db . GetAttachmentByID ( ctx , a . AvatarMediaAttachmentID )
2022-11-29 17:59:59 +00:00
if err != nil {
2022-07-19 08:47:55 +00:00
log . Errorf ( "AccountToAPIAccountPublic: error getting Avatar with id %s: %s" , a . AvatarMediaAttachmentID , err )
2021-08-20 10:26:56 +00:00
}
2022-11-29 17:59:59 +00:00
a . AvatarMediaAttachment = avi
2021-04-19 17:42:19 +00:00
}
2022-01-25 11:03:25 +00:00
if a . AvatarMediaAttachment != nil {
aviURL = a . AvatarMediaAttachment . URL
aviURLStatic = a . AvatarMediaAttachment . Thumbnail . URL
}
2021-04-19 17:42:19 +00:00
}
2022-01-24 12:12:17 +00:00
// set account header fields if available
2021-08-20 10:26:56 +00:00
var headerURL string
var headerURLStatic string
if a . HeaderMediaAttachmentID != "" {
if a . HeaderMediaAttachment == nil {
2021-08-25 13:34:33 +00:00
avi , err := c . db . GetAttachmentByID ( ctx , a . HeaderMediaAttachmentID )
2022-11-29 17:59:59 +00:00
if err != nil {
2022-07-19 08:47:55 +00:00
log . Errorf ( "AccountToAPIAccountPublic: error getting Header with id %s: %s" , a . HeaderMediaAttachmentID , err )
2021-08-20 10:26:56 +00:00
}
2022-11-29 17:59:59 +00:00
a . HeaderMediaAttachment = avi
2021-04-19 17:42:19 +00:00
}
2022-01-25 11:03:25 +00:00
if a . HeaderMediaAttachment != nil {
headerURL = a . HeaderMediaAttachment . URL
headerURLStatic = a . HeaderMediaAttachment . Thumbnail . URL
}
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
// preallocate frontend fields slice
2023-01-02 12:10:50 +00:00
fields := make ( [ ] apimodel . Field , len ( a . Fields ) )
2022-11-29 17:59:59 +00:00
// Convert account GTS model fields to frontend
for i , field := range a . Fields {
2023-01-02 12:10:50 +00:00
mField := apimodel . Field {
2022-11-29 17:59:59 +00:00
Name : field . Name ,
Value : field . Value ,
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
if ! field . VerifiedAt . IsZero ( ) {
mField . VerifiedAt = util . FormatISO8601 ( field . VerifiedAt )
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
fields [ i ] = mField
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
// convert account gts model emojis to frontend api model emojis
apiEmojis , err := c . convertEmojisToAPIEmojis ( ctx , a . Emojis , a . EmojiIDs )
if err != nil {
log . Errorf ( "error converting account emojis: %v" , err )
2022-09-26 09:56:01 +00:00
}
2021-05-27 14:06:24 +00:00
2022-11-15 09:19:32 +00:00
var (
acct string
2023-01-02 12:10:50 +00:00
role = apimodel . AccountRoleUnknown
2022-11-15 09:19:32 +00:00
)
2021-04-19 17:42:19 +00:00
if a . Domain != "" {
// this is a remote user
2022-11-15 09:19:32 +00:00
acct = a . Username + "@" + a . Domain
2021-04-19 17:42:19 +00:00
} else {
// this is a local user
acct = a . Username
2022-11-15 09:19:32 +00:00
user , err := c . db . GetUserByAccountID ( ctx , a . ID )
if err != nil {
return nil , fmt . Errorf ( "AccountToAPIAccountPublic: error getting user from database for account id %s: %s" , a . ID , err )
}
switch {
case * user . Admin :
2023-01-02 12:10:50 +00:00
role = apimodel . AccountRoleAdmin
2022-11-15 09:19:32 +00:00
case * user . Moderator :
2023-01-02 12:10:50 +00:00
role = apimodel . AccountRoleModerator
2022-11-15 09:19:32 +00:00
default :
2023-01-02 12:10:50 +00:00
role = apimodel . AccountRoleUser
2022-11-15 09:19:32 +00:00
}
2021-04-19 17:42:19 +00:00
}
2021-07-11 14:22:21 +00:00
var suspended bool
if ! a . SuspendedAt . IsZero ( ) {
suspended = true
}
2023-01-02 12:10:50 +00:00
accountFrontend := & apimodel . Account {
2021-04-19 17:42:19 +00:00
ID : a . ID ,
Username : a . Username ,
Acct : acct ,
DisplayName : a . DisplayName ,
2022-08-15 10:35:05 +00:00
Locked : * a . Locked ,
Bot : * a . Bot ,
2022-05-24 16:21:27 +00:00
CreatedAt : util . FormatISO8601 ( a . CreatedAt ) ,
2021-04-19 17:42:19 +00:00
Note : a . Note ,
URL : a . URL ,
Avatar : aviURL ,
AvatarStatic : aviURLStatic ,
Header : headerURL ,
HeaderStatic : headerURLStatic ,
FollowersCount : followersCount ,
FollowingCount : followingCount ,
StatusesCount : statusesCount ,
LastStatusAt : lastStatusAt ,
2022-11-29 17:59:59 +00:00
Emojis : apiEmojis ,
2021-04-19 17:42:19 +00:00
Fields : fields ,
2021-07-11 14:22:21 +00:00
Suspended : suspended ,
2022-09-12 11:14:29 +00:00
CustomCSS : a . CustomCSS ,
2022-10-08 12:00:39 +00:00
EnableRSS : * a . EnableRSS ,
2022-11-15 09:19:32 +00:00
Role : role ,
2021-08-20 10:26:56 +00:00
}
2022-09-04 12:41:42 +00:00
c . ensureAvatar ( accountFrontend )
c . ensureHeader ( accountFrontend )
2021-08-20 10:26:56 +00:00
return accountFrontend , nil
2021-07-11 14:22:21 +00:00
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) AccountToAPIAccountBlocked ( ctx context . Context , a * gtsmodel . Account ) ( * apimodel . Account , error ) {
2021-07-11 14:22:21 +00:00
var acct string
if a . Domain != "" {
// this is a remote user
acct = fmt . Sprintf ( "%s@%s" , a . Username , a . Domain )
} else {
// this is a local user
acct = a . Username
}
var suspended bool
if ! a . SuspendedAt . IsZero ( ) {
suspended = true
}
2023-01-02 12:10:50 +00:00
return & apimodel . Account {
2021-07-11 14:22:21 +00:00
ID : a . ID ,
Username : a . Username ,
Acct : acct ,
DisplayName : a . DisplayName ,
2022-08-15 10:35:05 +00:00
Bot : * a . Bot ,
2022-05-24 16:21:27 +00:00
CreatedAt : util . FormatISO8601 ( a . CreatedAt ) ,
2021-07-11 14:22:21 +00:00
URL : a . URL ,
Suspended : suspended ,
2021-04-19 17:42:19 +00:00
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) AppToAPIAppSensitive ( ctx context . Context , a * gtsmodel . Application ) ( * apimodel . Application , error ) {
return & apimodel . Application {
2021-04-19 17:42:19 +00:00
ID : a . ID ,
Name : a . Name ,
Website : a . Website ,
RedirectURI : a . RedirectURI ,
ClientID : a . ClientID ,
ClientSecret : a . ClientSecret ,
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) AppToAPIAppPublic ( ctx context . Context , a * gtsmodel . Application ) ( * apimodel . Application , error ) {
return & apimodel . Application {
2021-04-19 17:42:19 +00:00
Name : a . Name ,
Website : a . Website ,
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) AttachmentToAPIAttachment ( ctx context . Context , a * gtsmodel . MediaAttachment ) ( apimodel . Attachment , error ) {
apiAttachment := apimodel . Attachment {
2022-07-22 10:48:19 +00:00
ID : a . ID ,
Type : strings . ToLower ( string ( a . Type ) ) ,
TextURL : a . URL ,
PreviewURL : a . Thumbnail . URL ,
2023-01-02 12:10:50 +00:00
Meta : apimodel . MediaMeta {
Original : apimodel . MediaDimensions {
2021-04-19 17:42:19 +00:00
Width : a . FileMeta . Original . Width ,
Height : a . FileMeta . Original . Height ,
Size : fmt . Sprintf ( "%dx%d" , a . FileMeta . Original . Width , a . FileMeta . Original . Height ) ,
Aspect : float32 ( a . FileMeta . Original . Aspect ) ,
} ,
2023-01-02 12:10:50 +00:00
Small : apimodel . MediaDimensions {
2021-04-19 17:42:19 +00:00
Width : a . FileMeta . Small . Width ,
Height : a . FileMeta . Small . Height ,
Size : fmt . Sprintf ( "%dx%d" , a . FileMeta . Small . Width , a . FileMeta . Small . Height ) ,
Aspect : float32 ( a . FileMeta . Small . Aspect ) ,
} ,
2023-01-02 12:10:50 +00:00
Focus : apimodel . MediaFocus {
2021-04-19 17:42:19 +00:00
X : a . FileMeta . Focus . X ,
Y : a . FileMeta . Focus . Y ,
} ,
} ,
2022-07-22 10:48:19 +00:00
Blurhash : a . Blurhash ,
}
// nullable fields
2022-12-22 10:48:28 +00:00
if i := a . URL ; i != "" {
2022-07-22 10:48:19 +00:00
apiAttachment . URL = & i
}
2022-12-22 10:48:28 +00:00
if i := a . RemoteURL ; i != "" {
2022-07-22 10:48:19 +00:00
apiAttachment . RemoteURL = & i
}
2022-12-22 10:48:28 +00:00
if i := a . Thumbnail . RemoteURL ; i != "" {
2022-07-22 10:48:19 +00:00
apiAttachment . PreviewRemoteURL = & i
}
2022-12-22 10:48:28 +00:00
if i := a . Description ; i != "" {
2022-07-22 10:48:19 +00:00
apiAttachment . Description = & i
}
2022-12-22 10:48:28 +00:00
if i := a . FileMeta . Original . Duration ; i != nil {
apiAttachment . Meta . Original . Duration = * i
}
if i := a . FileMeta . Original . Framerate ; i != nil {
// the masto api expects this as a string in
// the format `integer/1`, so 30fps is `30/1`
round := math . Round ( float64 ( * i ) )
fr := strconv . FormatInt ( int64 ( round ) , 10 )
apiAttachment . Meta . Original . FrameRate = fr + "/1"
}
if i := a . FileMeta . Original . Bitrate ; i != nil {
apiAttachment . Meta . Original . Bitrate = int ( * i )
}
2022-07-22 10:48:19 +00:00
return apiAttachment , nil
2021-04-19 17:42:19 +00:00
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) MentionToAPIMention ( ctx context . Context , m * gtsmodel . Mention ) ( apimodel . Mention , error ) {
2021-08-25 13:34:33 +00:00
if m . TargetAccount == nil {
targetAccount , err := c . db . GetAccountByID ( ctx , m . TargetAccountID )
if err != nil {
2023-01-02 12:10:50 +00:00
return apimodel . Mention { } , err
2021-08-25 13:34:33 +00:00
}
m . TargetAccount = targetAccount
2021-04-19 17:42:19 +00:00
}
var local bool
2021-08-25 13:34:33 +00:00
if m . TargetAccount . Domain == "" {
2021-04-19 17:42:19 +00:00
local = true
}
var acct string
if local {
2021-08-25 13:34:33 +00:00
acct = m . TargetAccount . Username
2021-04-19 17:42:19 +00:00
} else {
2021-08-25 13:34:33 +00:00
acct = fmt . Sprintf ( "%s@%s" , m . TargetAccount . Username , m . TargetAccount . Domain )
2021-04-19 17:42:19 +00:00
}
2023-01-02 12:10:50 +00:00
return apimodel . Mention {
2021-08-25 13:34:33 +00:00
ID : m . TargetAccount . ID ,
Username : m . TargetAccount . Username ,
URL : m . TargetAccount . URL ,
2021-04-19 17:42:19 +00:00
Acct : acct ,
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) EmojiToAPIEmoji ( ctx context . Context , e * gtsmodel . Emoji ) ( apimodel . Emoji , error ) {
2022-11-14 22:47:27 +00:00
var category string
if e . CategoryID != "" {
if e . Category == nil {
var err error
e . Category , err = c . db . GetEmojiCategory ( ctx , e . CategoryID )
if err != nil {
2023-01-02 12:10:50 +00:00
return apimodel . Emoji { } , err
2022-11-14 22:47:27 +00:00
}
}
category = e . Category . Name
}
2023-01-02 12:10:50 +00:00
return apimodel . Emoji {
2021-04-19 17:42:19 +00:00
Shortcode : e . Shortcode ,
URL : e . ImageURL ,
StaticURL : e . ImageStaticURL ,
2022-08-15 10:35:05 +00:00
VisibleInPicker : * e . VisibleInPicker ,
2022-11-14 22:47:27 +00:00
Category : category ,
2021-04-19 17:42:19 +00:00
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) EmojiToAdminAPIEmoji ( ctx context . Context , e * gtsmodel . Emoji ) ( * apimodel . AdminEmoji , error ) {
2022-10-12 13:01:42 +00:00
emoji , err := c . EmojiToAPIEmoji ( ctx , e )
if err != nil {
return nil , err
}
2023-01-02 12:10:50 +00:00
return & apimodel . AdminEmoji {
2022-10-12 13:01:42 +00:00
Emoji : emoji ,
ID : e . ID ,
Disabled : * e . Disabled ,
Domain : e . Domain ,
UpdatedAt : util . FormatISO8601 ( e . UpdatedAt ) ,
TotalFileSize : e . ImageFileSize + e . ImageStaticFileSize ,
ContentType : e . ImageContentType ,
URI : e . URI ,
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) EmojiCategoryToAPIEmojiCategory ( ctx context . Context , category * gtsmodel . EmojiCategory ) ( * apimodel . EmojiCategory , error ) {
return & apimodel . EmojiCategory {
2022-11-14 22:47:27 +00:00
ID : category . ID ,
Name : category . Name ,
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) TagToAPITag ( ctx context . Context , t * gtsmodel . Tag ) ( apimodel . Tag , error ) {
return apimodel . Tag {
2021-04-19 17:42:19 +00:00
Name : t . Name ,
2021-08-25 13:34:33 +00:00
URL : t . URL ,
2021-04-19 17:42:19 +00:00
} , nil
}
2023-01-02 12:10:50 +00:00
func ( c * converter ) StatusToAPIStatus ( ctx context . Context , s * gtsmodel . Status , requestingAccount * gtsmodel . Account ) ( * apimodel . Status , error ) {
2021-08-25 13:34:33 +00:00
repliesCount , err := c . db . CountStatusReplies ( ctx , s )
2021-04-19 17:42:19 +00:00
if err != nil {
return nil , fmt . Errorf ( "error counting replies: %s" , err )
}
2021-08-25 13:34:33 +00:00
reblogsCount , err := c . db . CountStatusReblogs ( ctx , s )
2021-04-19 17:42:19 +00:00
if err != nil {
return nil , fmt . Errorf ( "error counting reblogs: %s" , err )
}
2021-08-25 13:34:33 +00:00
favesCount , err := c . db . CountStatusFaves ( ctx , s )
2021-04-19 17:42:19 +00:00
if err != nil {
return nil , fmt . Errorf ( "error counting faves: %s" , err )
}
2023-01-02 12:10:50 +00:00
var apiRebloggedStatus * apimodel . Status
2021-05-08 13:16:24 +00:00
if s . BoostOfID != "" {
// the boosted status might have been set on this struct already so check first before doing db calls
2021-08-20 10:26:56 +00:00
if s . BoostOf == nil {
2021-05-08 13:16:24 +00:00
// it's not set so fetch it from the db
2021-08-25 13:34:33 +00:00
bs , err := c . db . GetStatusByID ( ctx , s . BoostOfID )
if err != nil {
2021-05-08 13:16:24 +00:00
return nil , fmt . Errorf ( "error getting boosted status with id %s: %s" , s . BoostOfID , err )
}
2021-08-20 10:26:56 +00:00
s . BoostOf = bs
2021-05-08 13:16:24 +00:00
}
// the boosted account might have been set on this struct already or passed as a param so check first before doing db calls
2021-08-20 10:26:56 +00:00
if s . BoostOfAccount == nil {
2021-05-08 13:16:24 +00:00
// it's not set so fetch it from the db
2021-08-25 13:34:33 +00:00
ba , err := c . db . GetAccountByID ( ctx , s . BoostOf . AccountID )
if err != nil {
2021-08-20 10:26:56 +00:00
return nil , fmt . Errorf ( "error getting boosted account %s from status with id %s: %s" , s . BoostOf . AccountID , s . BoostOfID , err )
2021-05-08 13:16:24 +00:00
}
2021-08-20 10:26:56 +00:00
s . BoostOfAccount = ba
s . BoostOf . Account = ba
2021-05-08 13:16:24 +00:00
}
2021-10-04 13:24:19 +00:00
apiRebloggedStatus , err = c . StatusToAPIStatus ( ctx , s . BoostOf , requestingAccount )
2021-06-17 16:02:33 +00:00
if err != nil {
2021-10-04 13:24:19 +00:00
return nil , fmt . Errorf ( "error converting boosted status to apitype: %s" , err )
2021-05-08 13:16:24 +00:00
}
}
2021-04-19 17:42:19 +00:00
2023-01-02 12:10:50 +00:00
var apiApplication * apimodel . Application
2021-04-19 17:42:19 +00:00
if s . CreatedWithApplicationID != "" {
gtsApplication := & gtsmodel . Application { }
2021-08-25 13:34:33 +00:00
if err := c . db . GetByID ( ctx , s . CreatedWithApplicationID , gtsApplication ) ; err != nil {
2021-04-19 17:42:19 +00:00
return nil , fmt . Errorf ( "error fetching application used to create status: %s" , err )
}
2021-10-04 13:24:19 +00:00
apiApplication , err = c . AppToAPIAppPublic ( ctx , gtsApplication )
2021-04-19 17:42:19 +00:00
if err != nil {
return nil , fmt . Errorf ( "error parsing application used to create status: %s" , err )
}
}
2021-08-20 10:26:56 +00:00
if s . Account == nil {
2021-08-25 13:34:33 +00:00
a , err := c . db . GetAccountByID ( ctx , s . AccountID )
if err != nil {
2021-06-17 16:02:33 +00:00
return nil , fmt . Errorf ( "error getting status author: %s" , err )
}
2021-08-20 10:26:56 +00:00
s . Account = a
2021-06-17 16:02:33 +00:00
}
2021-10-04 13:24:19 +00:00
apiAuthorAccount , err := c . AccountToAPIAccountPublic ( ctx , s . Account )
2021-04-19 17:42:19 +00:00
if err != nil {
return nil , fmt . Errorf ( "error parsing account of status author: %s" , err )
}
2022-11-29 17:59:59 +00:00
// convert status gts model attachments to frontend api model attachments
apiAttachments , err := c . convertAttachmentsToAPIAttachments ( ctx , s . Attachments , s . AttachmentIDs )
if err != nil {
log . Errorf ( "error converting status attachments: %v" , err )
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
// convert status gts model mentions to frontend api model mentions
apiMentions , err := c . convertMentionsToAPIMentions ( ctx , s . Mentions , s . MentionIDs )
if err != nil {
log . Errorf ( "error converting status mentions: %v" , err )
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
// convert status gts model tags to frontend api model tags
apiTags , err := c . convertTagsToAPITags ( ctx , s . Tags , s . TagIDs )
if err != nil {
log . Errorf ( "error converting status tags: %v" , err )
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
// convert status gts model emojis to frontend api model emojis
apiEmojis , err := c . convertEmojisToAPIEmojis ( ctx , s . Emojis , s . EmojiIDs )
if err != nil {
log . Errorf ( "error converting status emojis: %v" , err )
2021-04-19 17:42:19 +00:00
}
2022-11-29 17:59:59 +00:00
// Fetch status interaction flags for acccount
interacts , err := c . interactionsWithStatusForAccount ( ctx , s , requestingAccount )
if err != nil {
log . Errorf ( "error getting interactions for status %s for account %s: %v" , s . ID , requestingAccount . ID , err )
// Ensure a non nil object
interacts = & statusInteractions { }
2021-06-17 16:02:33 +00:00
}
2022-12-15 08:41:49 +00:00
var language * string
if s . Language != "" {
language = & s . Language
}
2023-01-02 12:10:50 +00:00
apiStatus := & apimodel . Status {
2021-04-19 17:42:19 +00:00
ID : s . ID ,
2022-05-24 16:21:27 +00:00
CreatedAt : util . FormatISO8601 ( s . CreatedAt ) ,
2022-09-02 15:00:11 +00:00
InReplyToID : nil ,
InReplyToAccountID : nil ,
2022-08-15 10:35:05 +00:00
Sensitive : * s . Sensitive ,
2021-04-19 17:42:19 +00:00
SpoilerText : s . ContentWarning ,
2021-10-04 13:24:19 +00:00
Visibility : c . VisToAPIVis ( ctx , s . Visibility ) ,
2022-12-15 08:41:49 +00:00
Language : language ,
2021-04-19 17:42:19 +00:00
URI : s . URI ,
URL : s . URL ,
RepliesCount : repliesCount ,
ReblogsCount : reblogsCount ,
FavouritesCount : favesCount ,
2022-11-29 17:59:59 +00:00
Favourited : interacts . Faved ,
Bookmarked : interacts . Bookmarked ,
Muted : interacts . Muted ,
Reblogged : interacts . Reblogged ,
2022-08-15 10:35:05 +00:00
Pinned : * s . Pinned ,
2021-04-19 17:42:19 +00:00
Content : s . Content ,
2022-09-02 15:00:11 +00:00
Reblog : nil ,
2021-10-04 13:24:19 +00:00
Application : apiApplication ,
Account : apiAuthorAccount ,
MediaAttachments : apiAttachments ,
Mentions : apiMentions ,
Tags : apiTags ,
Emojis : apiEmojis ,
2022-09-02 15:00:11 +00:00
Card : nil , // TODO: implement cards
Poll : nil , // TODO: implement polls
2021-04-19 17:42:19 +00:00
Text : s . Text ,
2021-08-02 17:06:44 +00:00
}
2022-09-02 15:00:11 +00:00
// nullable fields
if s . InReplyToID != "" {
i := s . InReplyToID
apiStatus . InReplyToID = & i
}
if s . InReplyToAccountID != "" {
i := s . InReplyToAccountID
apiStatus . InReplyToAccountID = & i
}
2021-10-04 13:24:19 +00:00
if apiRebloggedStatus != nil {
2023-01-02 12:10:50 +00:00
apiStatus . Reblog = & apimodel . StatusReblogged { Status : apiRebloggedStatus }
2021-08-02 17:06:44 +00:00
}
return apiStatus , nil
2021-04-19 17:42:19 +00:00
}
2021-05-08 12:25:55 +00:00
2021-10-04 13:24:19 +00:00
// VisToapi converts a gts visibility into its api equivalent
2023-01-02 12:10:50 +00:00
func ( c * converter ) VisToAPIVis ( ctx context . Context , m gtsmodel . Visibility ) apimodel . Visibility {
2021-05-08 12:25:55 +00:00
switch m {
case gtsmodel . VisibilityPublic :
2023-01-02 12:10:50 +00:00
return apimodel . VisibilityPublic
2021-05-08 12:25:55 +00:00
case gtsmodel . VisibilityUnlocked :
2023-01-02 12:10:50 +00:00
return apimodel . VisibilityUnlisted
2021-05-08 12:25:55 +00:00
case gtsmodel . VisibilityFollowersOnly , gtsmodel . VisibilityMutualsOnly :
2023-01-02 12:10:50 +00:00
return apimodel . VisibilityPrivate
2021-05-08 12:25:55 +00:00
case gtsmodel . VisibilityDirect :
2023-01-02 12:10:50 +00:00
return apimodel . VisibilityDirect
2021-05-08 12:25:55 +00:00
}
return ""
}
2021-05-09 12:06:06 +00:00
2023-01-02 12:10:50 +00:00
func ( c * converter ) InstanceToAPIInstance ( ctx context . Context , i * gtsmodel . Instance ) ( * apimodel . Instance , error ) {
mi := & apimodel . Instance {
2021-05-09 18:34:27 +00:00
URI : i . URI ,
Title : i . Title ,
Description : i . Description ,
2021-05-09 12:06:06 +00:00
ShortDescription : i . ShortDescription ,
2021-05-09 18:34:27 +00:00
Email : i . ContactEmail ,
2021-05-22 13:51:20 +00:00
Version : i . Version ,
2021-06-23 14:35:57 +00:00
Stats : make ( map [ string ] int ) ,
2021-05-09 12:06:06 +00:00
}
2021-06-23 14:35:57 +00:00
// if the requested instance is *this* instance, we can add some extra information
2022-05-30 12:41:24 +00:00
if host := config . GetHost ( ) ; i . Domain == host {
2022-07-05 12:03:44 +00:00
mi . AccountDomain = config . GetAccountDomain ( )
2022-06-26 10:33:11 +00:00
if ia , err := c . db . GetInstanceAccount ( ctx , "" ) ; err == nil {
2022-11-08 17:11:06 +00:00
// assume default logo
mi . Thumbnail = config . GetProtocol ( ) + "://" + host + "/assets/logo.png"
// take instance account avatar as instance thumbnail if we can
if ia . AvatarMediaAttachmentID != "" {
if ia . AvatarMediaAttachment == nil {
avi , err := c . db . GetAttachmentByID ( ctx , ia . AvatarMediaAttachmentID )
if err == nil {
ia . AvatarMediaAttachment = avi
} else if ! errors . Is ( err , db . ErrNoEntries ) {
log . Errorf ( "InstanceToAPIInstance: error getting instance avatar attachment with id %s: %s" , ia . AvatarMediaAttachmentID , err )
}
}
if ia . AvatarMediaAttachment != nil {
mi . Thumbnail = ia . AvatarMediaAttachment . URL
mi . ThumbnailType = ia . AvatarMediaAttachment . File . ContentType
mi . ThumbnailDescription = ia . AvatarMediaAttachment . Description
}
2022-06-26 10:33:11 +00:00
}
}
2021-12-07 12:31:39 +00:00
userCount , err := c . db . CountInstanceUsers ( ctx , host )
2021-06-23 14:35:57 +00:00
if err == nil {
2021-12-07 12:31:39 +00:00
mi . Stats [ "user_count" ] = userCount
2021-06-23 14:35:57 +00:00
}
2021-12-07 12:31:39 +00:00
statusCount , err := c . db . CountInstanceStatuses ( ctx , host )
2021-06-23 14:35:57 +00:00
if err == nil {
2021-12-07 12:31:39 +00:00
mi . Stats [ "status_count" ] = statusCount
2021-06-23 14:35:57 +00:00
}
2021-12-07 12:31:39 +00:00
domainCount , err := c . db . CountInstanceDomains ( ctx , host )
2021-06-23 14:35:57 +00:00
if err == nil {
2021-12-07 12:31:39 +00:00
mi . Stats [ "domain_count" ] = domainCount
2021-06-23 14:35:57 +00:00
}
2022-05-30 12:41:24 +00:00
mi . Registrations = config . GetAccountsRegistrationOpen ( )
mi . ApprovalRequired = config . GetAccountsApprovalRequired ( )
2021-05-09 12:06:06 +00:00
mi . InvitesEnabled = false // TODO
2022-05-30 12:41:24 +00:00
mi . MaxTootChars = uint ( config . GetStatusesMaxChars ( ) )
2023-01-02 12:10:50 +00:00
mi . URLS = & apimodel . InstanceURLs {
2022-06-26 08:58:45 +00:00
StreamingAPI : "wss://" + host ,
2021-05-22 13:51:20 +00:00
}
2022-05-30 12:41:24 +00:00
mi . Version = config . GetSoftwareVersion ( )
2022-06-26 08:58:45 +00:00
// todo: remove hardcoded values and put them in config somewhere
2023-01-02 12:10:50 +00:00
mi . Configuration = & apimodel . InstanceConfiguration {
Statuses : & apimodel . InstanceConfigurationStatuses {
2022-06-26 08:58:45 +00:00
MaxCharacters : config . GetStatusesMaxChars ( ) ,
MaxMediaAttachments : config . GetStatusesMediaMaxFiles ( ) ,
2022-09-08 10:36:42 +00:00
CharactersReservedPerURL : instanceStatusesCharactersReservedPerURL ,
2022-06-26 08:58:45 +00:00
} ,
2023-01-02 12:10:50 +00:00
MediaAttachments : & apimodel . InstanceConfigurationMediaAttachments {
2023-01-11 11:13:13 +00:00
SupportedMimeTypes : media . SupportedMIMETypes ,
2022-10-06 10:00:53 +00:00
ImageSizeLimit : int ( config . GetMediaImageMaxSize ( ) ) , // bytes
2022-09-08 10:36:42 +00:00
ImageMatrixLimit : instanceMediaAttachmentsImageMatrixLimit , // height*width
2022-10-06 10:00:53 +00:00
VideoSizeLimit : int ( config . GetMediaVideoMaxSize ( ) ) , // bytes
2022-09-08 10:36:42 +00:00
VideoFrameRateLimit : instanceMediaAttachmentsVideoFrameRateLimit ,
VideoMatrixLimit : instanceMediaAttachmentsVideoMatrixLimit , // height*width
2022-06-26 08:58:45 +00:00
} ,
2023-01-02 12:10:50 +00:00
Polls : & apimodel . InstanceConfigurationPolls {
2022-06-26 08:58:45 +00:00
MaxOptions : config . GetStatusesPollMaxOptions ( ) ,
MaxCharactersPerOption : config . GetStatusesPollOptionMaxChars ( ) ,
2022-09-08 10:36:42 +00:00
MinExpiration : instancePollsMinExpiration , // seconds
MaxExpiration : instancePollsMaxExpiration , // seconds
2022-06-26 08:58:45 +00:00
} ,
2023-01-02 12:10:50 +00:00
Accounts : & apimodel . InstanceConfigurationAccounts {
2022-09-12 11:14:29 +00:00
AllowCustomCSS : config . GetAccountsAllowCustomCSS ( ) ,
} ,
2023-01-02 12:10:50 +00:00
Emojis : & apimodel . InstanceConfigurationEmojis {
2022-10-06 10:00:53 +00:00
EmojiSizeLimit : int ( config . GetMediaEmojiLocalMaxSize ( ) ) , // bytes
} ,
2022-06-26 08:58:45 +00:00
}
2021-05-09 12:06:06 +00:00
}
// contact account is optional but let's try to get it
if i . ContactAccountID != "" {
2021-08-25 13:34:33 +00:00
if i . ContactAccount == nil {
contactAccount , err := c . db . GetAccountByID ( ctx , i . ContactAccountID )
2021-05-09 12:06:06 +00:00
if err == nil {
2021-08-25 13:34:33 +00:00
i . ContactAccount = contactAccount
2021-05-09 12:06:06 +00:00
}
}
2021-10-04 13:24:19 +00:00
ma , err := c . AccountToAPIAccountPublic ( ctx , i . ContactAccount )
2021-08-25 13:34:33 +00:00
if err == nil {
mi . ContactAccount = ma
}
2021-05-09 12:06:06 +00:00
}
return mi , nil
}
2021-05-21 13:48:26 +00:00
2023-01-02 12:10:50 +00:00
func ( c * converter ) RelationshipToAPIRelationship ( ctx context . Context , r * gtsmodel . Relationship ) ( * apimodel . Relationship , error ) {
return & apimodel . Relationship {
2021-05-21 21:04:59 +00:00
ID : r . ID ,
Following : r . Following ,
ShowingReblogs : r . ShowingReblogs ,
Notifying : r . Notifying ,
FollowedBy : r . FollowedBy ,
Blocking : r . Blocking ,
BlockedBy : r . BlockedBy ,
Muting : r . Muting ,
2021-05-21 13:48:26 +00:00
MutingNotifications : r . MutingNotifications ,
2021-05-21 21:04:59 +00:00
Requested : r . Requested ,
DomainBlocking : r . DomainBlocking ,
Endorsed : r . Endorsed ,
Note : r . Note ,
2021-05-21 13:48:26 +00:00
} , nil
}
2021-05-27 14:06:24 +00:00
2023-01-02 12:10:50 +00:00
func ( c * converter ) NotificationToAPINotification ( ctx context . Context , n * gtsmodel . Notification ) ( * apimodel . Notification , error ) {
2021-08-20 10:26:56 +00:00
if n . TargetAccount == nil {
2021-08-25 13:34:33 +00:00
tAccount , err := c . db . GetAccountByID ( ctx , n . TargetAccountID )
2021-08-20 10:26:56 +00:00
if err != nil {
2021-10-04 13:24:19 +00:00
return nil , fmt . Errorf ( "NotificationToapi: error getting target account with id %s from the db: %s" , n . TargetAccountID , err )
2021-05-27 14:06:24 +00:00
}
2021-08-20 10:26:56 +00:00
n . TargetAccount = tAccount
2021-05-27 14:06:24 +00:00
}
2021-08-20 10:26:56 +00:00
if n . OriginAccount == nil {
2021-08-25 13:34:33 +00:00
ogAccount , err := c . db . GetAccountByID ( ctx , n . OriginAccountID )
2021-08-20 10:26:56 +00:00
if err != nil {
2021-10-04 13:24:19 +00:00
return nil , fmt . Errorf ( "NotificationToapi: error getting origin account with id %s from the db: %s" , n . OriginAccountID , err )
2021-05-27 14:06:24 +00:00
}
2021-08-20 10:26:56 +00:00
n . OriginAccount = ogAccount
2021-05-27 14:06:24 +00:00
}
2021-08-20 10:26:56 +00:00
2021-10-04 13:24:19 +00:00
apiAccount , err := c . AccountToAPIAccountPublic ( ctx , n . OriginAccount )
2021-05-27 14:06:24 +00:00
if err != nil {
2021-10-04 13:24:19 +00:00
return nil , fmt . Errorf ( "NotificationToapi: error converting account to api: %s" , err )
2021-05-27 14:06:24 +00:00
}
2023-01-02 12:10:50 +00:00
var apiStatus * apimodel . Status
2021-05-27 14:06:24 +00:00
if n . StatusID != "" {
2021-08-20 10:26:56 +00:00
if n . Status == nil {
2021-08-25 13:34:33 +00:00
status , err := c . db . GetStatusByID ( ctx , n . StatusID )
2021-08-20 10:26:56 +00:00
if err != nil {
2021-10-04 13:24:19 +00:00
return nil , fmt . Errorf ( "NotificationToapi: error getting status with id %s from the db: %s" , n . StatusID , err )
2021-05-27 14:06:24 +00:00
}
2021-08-20 10:26:56 +00:00
n . Status = status
2021-05-27 14:06:24 +00:00
}
2021-08-20 10:26:56 +00:00
if n . Status . Account == nil {
if n . Status . AccountID == n . TargetAccount . ID {
n . Status . Account = n . TargetAccount
} else if n . Status . AccountID == n . OriginAccount . ID {
n . Status . Account = n . OriginAccount
2021-05-27 14:06:24 +00:00
}
}
var err error
2022-08-30 09:42:52 +00:00
apiStatus , err = c . StatusToAPIStatus ( ctx , n . Status , n . TargetAccount )
2021-05-27 14:06:24 +00:00
if err != nil {
2021-10-04 13:24:19 +00:00
return nil , fmt . Errorf ( "NotificationToapi: error converting status to api: %s" , err )
2021-05-27 14:06:24 +00:00
}
}
2022-08-29 09:06:37 +00:00
if apiStatus != nil && apiStatus . Reblog != nil {
// use the actual reblog status for the notifications endpoint
apiStatus = apiStatus . Reblog . Status
}
2023-01-02 12:10:50 +00:00
return & apimodel . Notification {
2021-05-27 14:06:24 +00:00
ID : n . ID ,
Type : string ( n . NotificationType ) ,
2022-05-24 16:21:27 +00:00
CreatedAt : util . FormatISO8601 ( n . CreatedAt ) ,
2021-10-04 13:24:19 +00:00
Account : apiAccount ,
Status : apiStatus ,
2021-05-27 14:06:24 +00:00
} , nil
}
2021-07-05 11:23:03 +00:00
2023-01-02 12:10:50 +00:00
func ( c * converter ) DomainBlockToAPIDomainBlock ( ctx context . Context , b * gtsmodel . DomainBlock , export bool ) ( * apimodel . DomainBlock , error ) {
domainBlock := & apimodel . DomainBlock {
Domain : apimodel . Domain {
2022-06-23 14:54:54 +00:00
Domain : b . Domain ,
PublicComment : b . PublicComment ,
} ,
2021-07-05 11:23:03 +00:00
}
// if we're exporting a domain block, return it with minimal information attached
if ! export {
domainBlock . ID = b . ID
2022-08-15 10:35:05 +00:00
domainBlock . Obfuscate = * b . Obfuscate
2021-07-05 11:23:03 +00:00
domainBlock . PrivateComment = b . PrivateComment
domainBlock . SubscriptionID = b . SubscriptionID
domainBlock . CreatedBy = b . CreatedByAccountID
2022-05-24 16:21:27 +00:00
domainBlock . CreatedAt = util . FormatISO8601 ( b . CreatedAt )
2021-07-05 11:23:03 +00:00
}
return domainBlock , nil
}
2022-11-29 17:59:59 +00:00
// convertAttachmentsToAPIAttachments will convert a slice of GTS model attachments to frontend API model attachments, falling back to IDs if no GTS models supplied.
2023-01-02 12:10:50 +00:00
func ( c * converter ) convertAttachmentsToAPIAttachments ( ctx context . Context , attachments [ ] * gtsmodel . MediaAttachment , attachmentIDs [ ] string ) ( [ ] apimodel . Attachment , error ) {
2022-12-22 10:48:28 +00:00
var errs gtserror . MultiError
2022-11-29 17:59:59 +00:00
if len ( attachments ) == 0 {
// GTS model attachments were not populated
// Preallocate expected GTS slice
attachments = make ( [ ] * gtsmodel . MediaAttachment , 0 , len ( attachmentIDs ) )
// Fetch GTS models for attachment IDs
for _ , id := range attachmentIDs {
attachment , err := c . db . GetAttachmentByID ( ctx , id )
if err != nil {
errs . Appendf ( "error fetching attachment %s from database: %v" , id , err )
continue
}
attachments = append ( attachments , attachment )
}
}
// Preallocate expected frontend slice
2023-01-02 12:10:50 +00:00
apiAttachments := make ( [ ] apimodel . Attachment , 0 , len ( attachments ) )
2022-11-29 17:59:59 +00:00
// Convert GTS models to frontend models
for _ , attachment := range attachments {
apiAttachment , err := c . AttachmentToAPIAttachment ( ctx , attachment )
if err != nil {
errs . Appendf ( "error converting attchment %s to api attachment: %v" , attachment . ID , err )
continue
}
apiAttachments = append ( apiAttachments , apiAttachment )
}
return apiAttachments , errs . Combine ( )
}
// convertEmojisToAPIEmojis will convert a slice of GTS model emojis to frontend API model emojis, falling back to IDs if no GTS models supplied.
2023-01-02 12:10:50 +00:00
func ( c * converter ) convertEmojisToAPIEmojis ( ctx context . Context , emojis [ ] * gtsmodel . Emoji , emojiIDs [ ] string ) ( [ ] apimodel . Emoji , error ) {
2022-12-22 10:48:28 +00:00
var errs gtserror . MultiError
2022-11-29 17:59:59 +00:00
if len ( emojis ) == 0 {
// GTS model attachments were not populated
// Preallocate expected GTS slice
emojis = make ( [ ] * gtsmodel . Emoji , 0 , len ( emojiIDs ) )
// Fetch GTS models for emoji IDs
for _ , id := range emojiIDs {
emoji , err := c . db . GetEmojiByID ( ctx , id )
if err != nil {
errs . Appendf ( "error fetching emoji %s from database: %v" , id , err )
continue
}
emojis = append ( emojis , emoji )
}
}
// Preallocate expected frontend slice
2023-01-02 12:10:50 +00:00
apiEmojis := make ( [ ] apimodel . Emoji , 0 , len ( emojis ) )
2022-11-29 17:59:59 +00:00
// Convert GTS models to frontend models
for _ , emoji := range emojis {
apiEmoji , err := c . EmojiToAPIEmoji ( ctx , emoji )
if err != nil {
errs . Appendf ( "error converting emoji %s to api emoji: %v" , emoji . ID , err )
continue
}
apiEmojis = append ( apiEmojis , apiEmoji )
}
return apiEmojis , errs . Combine ( )
}
// convertMentionsToAPIMentions will convert a slice of GTS model mentions to frontend API model mentions, falling back to IDs if no GTS models supplied.
2023-01-02 12:10:50 +00:00
func ( c * converter ) convertMentionsToAPIMentions ( ctx context . Context , mentions [ ] * gtsmodel . Mention , mentionIDs [ ] string ) ( [ ] apimodel . Mention , error ) {
2022-12-22 10:48:28 +00:00
var errs gtserror . MultiError
2022-11-29 17:59:59 +00:00
if len ( mentions ) == 0 {
var err error
// GTS model mentions were not populated
//
// Fetch GTS models for mention IDs
mentions , err = c . db . GetMentions ( ctx , mentionIDs )
if err != nil {
errs . Appendf ( "error fetching mentions from database: %v" , err )
}
}
// Preallocate expected frontend slice
2023-01-02 12:10:50 +00:00
apiMentions := make ( [ ] apimodel . Mention , 0 , len ( mentions ) )
2022-11-29 17:59:59 +00:00
// Convert GTS models to frontend models
for _ , mention := range mentions {
apiMention , err := c . MentionToAPIMention ( ctx , mention )
if err != nil {
errs . Appendf ( "error converting mention %s to api mention: %v" , mention . ID , err )
continue
}
apiMentions = append ( apiMentions , apiMention )
}
return apiMentions , errs . Combine ( )
}
// convertTagsToAPITags will convert a slice of GTS model tags to frontend API model tags, falling back to IDs if no GTS models supplied.
2023-01-02 12:10:50 +00:00
func ( c * converter ) convertTagsToAPITags ( ctx context . Context , tags [ ] * gtsmodel . Tag , tagIDs [ ] string ) ( [ ] apimodel . Tag , error ) {
2022-12-22 10:48:28 +00:00
var errs gtserror . MultiError
2022-11-29 17:59:59 +00:00
if len ( tags ) == 0 {
// GTS model tags were not populated
// Preallocate expected GTS slice
tags = make ( [ ] * gtsmodel . Tag , 0 , len ( tagIDs ) )
// Fetch GTS models for tag IDs
for _ , id := range tagIDs {
tag := new ( gtsmodel . Tag )
if err := c . db . GetByID ( ctx , id , tag ) ; err != nil {
errs . Appendf ( "error fetching tag %s from database: %v" , id , err )
continue
}
tags = append ( tags , tag )
}
}
// Preallocate expected frontend slice
2023-01-02 12:10:50 +00:00
apiTags := make ( [ ] apimodel . Tag , 0 , len ( tags ) )
2022-11-29 17:59:59 +00:00
// Convert GTS models to frontend models
for _ , tag := range tags {
apiTag , err := c . TagToAPITag ( ctx , tag )
if err != nil {
errs . Appendf ( "error converting tag %s to api tag: %v" , tag . ID , err )
continue
}
apiTags = append ( apiTags , apiTag )
}
return apiTags , errs . Combine ( )
}