2023-03-12 16:00:57 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// GoToSocial  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Copyright (C) GoToSocial Authors admin@gotosocial.org  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// SPDX-License-Identifier: AGPL-3.0-or-later  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// 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-08-25 15:34:33 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								package  status  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								import  (  
						 
					
						
							
								
									
										
										
										
											2021-08-25 15:34:33 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"context" 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"errors" 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"fmt" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"time" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-31 15:59:12 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/ap" 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									apimodel  "github.com/superseriousbusiness/gotosocial/internal/api/model" 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/config" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/db" 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/gtserror" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/id" 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-31 15:59:12 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/messages" 
							 
						 
					
						
							
								
									
										
										
										
											2021-07-26 20:25:54 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/text" 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/typeutils" 
							 
						 
					
						
							
								
									
										
										
										
											2021-12-20 15:19:53 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									"github.com/superseriousbusiness/gotosocial/internal/uris" 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								)  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								// Create processes the given form to create a new status, returning the api model representation of that status if it's OK.  
						 
					
						
							
								
									
										
										
										
											2023-08-07 01:25:54 -07:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								//  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								// Precondition: the form's fields should have already been validated and normalized by the caller.  
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								func  ( p  * Processor )  Create ( ctx  context . Context ,  account  * gtsmodel . Account ,  application  * gtsmodel . Application ,  form  * apimodel . AdvancedStatusCreateForm )  ( * apimodel . Status ,  gtserror . WithCode )  {  
						 
					
						
							
								
									
										
										
										
											2021-12-20 15:19:53 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									accountURIs  :=  uris . GenerateURIsForAccount ( account . Username ) 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-03 20:03:05 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									thisStatusID  :=  id . NewULID ( ) 
							 
						 
					
						
							
								
									
										
										
										
											2022-08-15 12:35:05 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									local  :=  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									sensitive  :=  form . Sensitive 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									newStatus  :=  & gtsmodel . Status { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										ID :                        thisStatusID , 
							 
						 
					
						
							
								
									
										
										
										
											2022-05-07 16:55:27 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										URI :                       accountURIs . StatusesURI  +  "/"  +  thisStatusID , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										URL :                       accountURIs . StatusesURL  +  "/"  +  thisStatusID , 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										CreatedAt :                 time . Now ( ) , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										UpdatedAt :                 time . Now ( ) , 
							 
						 
					
						
							
								
									
										
										
										
											2022-08-15 12:35:05 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										Local :                     & local , 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										AccountID :                 account . ID , 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-17 18:02:33 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										AccountURI :                account . URI , 
							 
						 
					
						
							
								
									
										
										
										
											2023-08-11 14:40:11 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										ContentWarning :            text . SanitizeToPlaintext ( form . SpoilerText ) , 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-31 15:59:12 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										ActivityStreamsType :       ap . ObjectNote , 
							 
						 
					
						
							
								
									
										
										
										
											2022-08-15 12:35:05 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										Sensitive :                 & sensitive , 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										CreatedWithApplicationID :  application . ID , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										Text :                      form . Status , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-01 18:26:53 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  errWithCode  :=  processReplyToID ( ctx ,  p . state . DB ,  form ,  account . ID ,  newStatus ) ;  errWithCode  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-06-08 20:38:03 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  errWithCode 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-01 18:26:53 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  errWithCode  :=  processMediaIDs ( ctx ,  p . state . DB ,  form ,  account . ID ,  newStatus ) ;  errWithCode  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2022-11-05 13:33:38 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  nil ,  errWithCode 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  :=  processVisibility ( ctx ,  form ,  account . Privacy ,  newStatus ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  :=  processLanguage ( ctx ,  form ,  account . Language ,  newStatus ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-01 18:26:53 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  :=  processContent ( ctx ,  p . state . DB ,  p . formatter ,  p . parseMention ,  form ,  account . ID ,  newStatus ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2021-08-20 12:26:56 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// put the new status in the database 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-01 18:26:53 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  err  :=  p . state . DB . PutStatus ( ctx ,  newStatus ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										return  nil ,  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// send it back to the processor for async processing 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-01 18:26:53 +00:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									p . state . Workers . EnqueueClientAPI ( ctx ,  messages . FromClientAPI { 
							 
						 
					
						
							
								
									
										
										
										
											2021-08-31 15:59:12 +02:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										APObjectType :    ap . ObjectNote , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										APActivityType :  ap . ActivityCreate , 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
										GTSModel :        newStatus , 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										OriginAccount :   account , 
							 
						 
					
						
							
								
									
										
										
										
											2022-04-28 13:23:11 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-20 19:10:08 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									return  p . apiStatus ( ctx ,  newStatus ,  account ) 
							 
						 
					
						
							
								
									
										
										
										
											2021-06-13 18:42:28 +02:00 
										
									 
								 
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  processReplyToID ( ctx  context . Context ,  dbService  db . DB ,  form  * apimodel . AdvancedStatusCreateForm ,  thisAccountID  string ,  status  * gtsmodel . Status )  gtserror . WithCode  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  form . InReplyToID  ==  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// If this status is a reply to another status, we need to do a bit of work to establish whether or not this status can be posted: 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 1. Does the replied status exist in the database? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 2. Is the replied status marked as replyable? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 3. Does a block exist between either the current account or the account that posted the status it's replying to? 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// If this is all OK, then we fetch the repliedStatus and the repliedAccount for later processing. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									repliedStatus  :=  & gtsmodel . Status { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									repliedAccount  :=  & gtsmodel . Account { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  :=  dbService . GetByID ( ctx ,  form . InReplyToID ,  repliedStatus ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  err  ==  db . ErrNoEntries  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											err  :=  fmt . Errorf ( "status with id %s not replyable because it doesn't exist" ,  form . InReplyToID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  gtserror . NewErrorBadRequest ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err  :=  fmt . Errorf ( "db error fetching status with id %s: %s" ,  form . InReplyToID ,  err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  ! * repliedStatus . Replyable  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err  :=  fmt . Errorf ( "status with id %s is marked as not replyable" ,  form . InReplyToID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  gtserror . NewErrorForbidden ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  err  :=  dbService . GetByID ( ctx ,  repliedStatus . AccountID ,  repliedAccount ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  err  ==  db . ErrNoEntries  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											err  :=  fmt . Errorf ( "status with id %s not replyable because account id %s is not known" ,  form . InReplyToID ,  repliedStatus . AccountID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  gtserror . NewErrorBadRequest ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err  :=  fmt . Errorf ( "db error fetching account with id %s: %s" ,  repliedStatus . AccountID ,  err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
											 
										
											
												[performance] refactoring + add fave / follow / request / visibility caching (#1607)
* refactor visibility checking, add caching for visibility
* invalidate visibility cache items on account / status deletes
* fix requester ID passed to visibility cache nil ptr
* de-interface caches, fix home / public timeline caching + visibility
* finish adding code comments for visibility filter
* fix angry goconst linter warnings
* actually finish adding filter visibility code comments for timeline functions
* move home timeline status author check to after visibility
* remove now-unused code
* add more code comments
* add TODO code comment, update printed cache start names
* update printed cache names on stop
* start adding separate follow(request) delete db functions, add specific visibility cache tests
* add relationship type caching
* fix getting local account follows / followed-bys, other small codebase improvements
* simplify invalidation using cache hooks, add more GetAccountBy___() functions
* fix boosting to return 404 if not boostable but no error (to not leak status ID)
* remove dead code
* improved placement of cache invalidation
* update license headers
* add example follow, follow-request config entries
* add example visibility cache configuration to config file
* use specific PutFollowRequest() instead of just Put()
* add tests for all GetAccountBy()
* add GetBlockBy() tests
* update block to check primitive fields
* update and finish adding Get{Account,Block,Follow,FollowRequest}By() tests
* fix copy-pasted code
* update envparsing test
* whitespace
* fix bun struct tag
* add license header to gtscontext
* fix old license header
* improved error creation to not use fmt.Errorf() when not needed
* fix various rebase conflicts, fix account test
* remove commented-out code, fix-up mention caching
* fix mention select bun statement
* ensure mention target account populated, pass in context to customrenderer logging
* remove more uncommented code, fix typeutil test
* add statusfave database model caching
* add status fave cache configuration
* add status fave cache example config
* woops, catch missed error. nice catch linter!
* add back testrig panic on nil db
* update example configuration to match defaults, slight tweak to cache configuration defaults
* update envparsing test with new defaults
* fetch followingget to use the follow target account
* use accounnt.IsLocal() instead of empty domain check
* use constants for the cache visibility type check
* use bun.In() for notification type restriction in db query
* include replies when fetching PublicTimeline() (to account for single-author threads in Visibility{}.StatusPublicTimelineable())
* use bun query building for nested select statements to ensure working with postgres
* update public timeline future status checks to match visibility filter
* same as previous, for home timeline
* update public timeline tests to dynamically check for appropriate statuses
* migrate accounts to allow unique constraint on public_key
* provide minimal account with publicKey
---------
Signed-off-by: kim <grufwub@gmail.com>
Co-authored-by: tsmethurst <tobi.smethurst@protonmail.com>
											 
										 
										
											2023-03-28 14:03:14 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									if  blocked ,  err  :=  dbService . IsEitherBlocked ( ctx ,  thisAccountID ,  repliedAccount . ID ) ;  err  !=  nil  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										err  :=  fmt . Errorf ( "db error checking block: %s" ,  err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  if  blocked  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										err  :=  fmt . Errorf ( "status with id %s not replyable" ,  form . InReplyToID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  gtserror . NewErrorNotFound ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . InReplyToID  =  repliedStatus . ID 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . InReplyToURI  =  repliedStatus . URI 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . InReplyToAccountID  =  repliedAccount . ID 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  processMediaIDs ( ctx  context . Context ,  dbService  db . DB ,  form  * apimodel . AdvancedStatusCreateForm ,  thisAccountID  string ,  status  * gtsmodel . Status )  gtserror . WithCode  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  form . MediaIDs  ==  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									attachments  :=  [ ] * gtsmodel . MediaAttachment { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									attachmentIDs  :=  [ ] string { } 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  mediaID  :=  range  form . MediaIDs  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachment ,  err  :=  dbService . GetAttachmentByID ( ctx ,  mediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											if  errors . Is ( err ,  db . ErrNoEntries )  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												err  =  fmt . Errorf ( "ProcessMediaIDs: media not found for media id %s" ,  mediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
												return  gtserror . NewErrorBadRequest ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											err  =  fmt . Errorf ( "ProcessMediaIDs: db error for media id %s" ,  mediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  gtserror . NewErrorInternalError ( err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  attachment . AccountID  !=  thisAccountID  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											err  =  fmt . Errorf ( "ProcessMediaIDs: media with id %s does not belong to account %s" ,  mediaID ,  thisAccountID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  gtserror . NewErrorBadRequest ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  attachment . StatusID  !=  ""  ||  attachment . ScheduledStatusID  !=  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											err  =  fmt . Errorf ( "ProcessMediaIDs: media with id %s is already attached to a status" ,  mediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  gtserror . NewErrorBadRequest ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										minDescriptionChars  :=  config . GetMediaDescriptionMinChars ( ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  descriptionLength  :=  len ( [ ] rune ( attachment . Description ) ) ;  descriptionLength  <  minDescriptionChars  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											err  =  fmt . Errorf ( "ProcessMediaIDs: description too short! media description of at least %d chararacters is required but %d was provided for media with id %s" ,  minDescriptionChars ,  descriptionLength ,  mediaID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  gtserror . NewErrorBadRequest ( err ,  err . Error ( ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachments  =  append ( attachments ,  attachment ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										attachmentIDs  =  append ( attachmentIDs ,  attachment . ID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Attachments  =  attachments 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . AttachmentIDs  =  attachmentIDs 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  processVisibility ( ctx  context . Context ,  form  * apimodel . AdvancedStatusCreateForm ,  accountDefaultVis  gtsmodel . Visibility ,  status  * gtsmodel . Status )  error  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// by default all flags are set to true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									federated  :=  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									boostable  :=  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									replyable  :=  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									likeable  :=  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// If visibility isn't set on the form, then just take the account default. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// If that's also not set, take the default for the whole instance. 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									var  vis  gtsmodel . Visibility 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									switch  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  form . Visibility  !=  "" : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										vis  =  typeutils . APIVisToVis ( form . Visibility ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  accountDefaultVis  !=  "" : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										vis  =  accountDefaultVis 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									default : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										vis  =  gtsmodel . VisibilityDefault 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									switch  vis  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  gtsmodel . VisibilityPublic : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// for public, there's no need to change any of the advanced flags from true regardless of what the user filled out 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										break 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  gtsmodel . VisibilityUnlocked : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// for unlocked the user can set any combination of flags they like so look at them all to see if they're set and then apply them 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Federated  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											federated  =  * form . Federated 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Boostable  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											boostable  =  * form . Boostable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Replyable  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											replyable  =  * form . Replyable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Likeable  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											likeable  =  * form . Likeable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  gtsmodel . VisibilityFollowersOnly ,  gtsmodel . VisibilityMutualsOnly : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// for followers or mutuals only, boostable will *always* be false, but the other fields can be set so check and apply them 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										boostable  =  false 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Federated  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											federated  =  * form . Federated 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Replyable  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											replyable  =  * form . Replyable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  form . Likeable  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											likeable  =  * form . Likeable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  gtsmodel . VisibilityDirect : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										// direct is pretty easy: there's only one possible setting so return it 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										federated  =  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										boostable  =  false 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										replyable  =  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										likeable  =  true 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Visibility  =  vis 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Federated  =  & federated 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Boostable  =  & boostable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Replyable  =  & replyable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Likeable  =  & likeable 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  processLanguage ( ctx  context . Context ,  form  * apimodel . AdvancedStatusCreateForm ,  accountDefaultLanguage  string ,  status  * gtsmodel . Status )  error  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  form . Language  !=  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . Language  =  form . Language 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									}  else  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . Language  =  accountDefaultLanguage 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  status . Language  ==  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  errors . New ( "no language given either in status create form or account default" ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								func  processContent ( ctx  context . Context ,  dbService  db . DB ,  formatter  text . Formatter ,  parseMention  gtsmodel . ParseMentionFunc ,  form  * apimodel . AdvancedStatusCreateForm ,  accountID  string ,  status  * gtsmodel . Status )  error  {  
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// if there's nothing in the status at all we can just return early 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  form . Status  ==  ""  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . Content  =  "" 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// if content type wasn't specified we should try to figure out what content type this user prefers 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									if  form . ContentType  ==  ""  { 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										acct ,  err  :=  dbService . GetAccountByID ( ctx ,  accountID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										if  err  !=  nil  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											return  fmt . Errorf ( "error processing new content: couldn't retrieve account from db to check post format: %s" ,  err ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										switch  acct . StatusContentType  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										case  "text/plain" : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											form . ContentType  =  apimodel . StatusContentTypePlain 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										case  "text/markdown" : 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
											form . ContentType  =  apimodel . StatusContentTypeMarkdown 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										default : 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
											form . ContentType  =  apimodel . StatusContentTypeDefault 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									// parse content out of the status depending on what content type has been submitted 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									var  f  text . FormatFunc 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									switch  form . ContentType  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									case  apimodel . StatusContentTypePlain : 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										f  =  formatter . FromPlain 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									case  apimodel . StatusContentTypeMarkdown : 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										f  =  formatter . FromMarkdown 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									default : 
							 
						 
					
						
							
								
									
										
										
										
											2023-03-02 03:06:40 -08:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
										return  fmt . Errorf ( "format %s not recognised as a valid status format" ,  form . ContentType ) 
							 
						 
					
						
							
								
									
										
										
										
											2023-02-22 16:05:26 +01:00 
										
									 
								 
							 
							
								
									
										 
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									formatted  :=  f ( ctx ,  parseMention ,  accountID ,  status . ID ,  form . Status ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// add full populated gts {mentions, tags, emojis} to the status for passing them around conveniently 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									// add just their ids to the status for putting in the db 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Mentions  =  formatted . Mentions 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . MentionIDs  =  make ( [ ] string ,  0 ,  len ( formatted . Mentions ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  gtsmention  :=  range  formatted . Mentions  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . MentionIDs  =  append ( status . MentionIDs ,  gtsmention . ID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Tags  =  formatted . Tags 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . TagIDs  =  make ( [ ] string ,  0 ,  len ( formatted . Tags ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  gtstag  :=  range  formatted . Tags  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . TagIDs  =  append ( status . TagIDs ,  gtstag . ID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Emojis  =  formatted . Emojis 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . EmojiIDs  =  make ( [ ] string ,  0 ,  len ( formatted . Emojis ) ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  gtsemoji  :=  range  formatted . Emojis  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . EmojiIDs  =  append ( status . EmojiIDs ,  gtsemoji . ID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									spoilerformatted  :=  formatter . FromPlainEmojiOnly ( ctx ,  parseMention ,  accountID ,  status . ID ,  form . SpoilerText ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									for  _ ,  gtsemoji  :=  range  spoilerformatted . Emojis  { 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . Emojis  =  append ( status . Emojis ,  gtsemoji ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
										status . EmojiIDs  =  append ( status . EmojiIDs ,  gtsemoji . ID ) 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									} 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									status . Content  =  formatted . HTML 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
									return  nil 
							 
						 
					
						
							
								
							 
							
								
							 
							
								 
							
							
								}