| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |    GoToSocial | 
					
						
							| 
									
										
										
										
											2021-12-20 18:42:19 +01:00
										 |  |  |    Copyright (C) 2021-2022 GoToSocial Authors admin@gotosocial.org | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |    This program is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  |    it under the terms of the GNU Affero General Public License as published by | 
					
						
							|  |  |  |    the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  |    (at your option) any later version. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    This program is distributed in the hope that it will be useful, | 
					
						
							|  |  |  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
					
						
							|  |  |  |    GNU Affero General Public License for more details. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |    You should have received a copy of the GNU Affero General Public License | 
					
						
							|  |  |  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package timeline | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	"codeberg.org/gruf/go-kv" | 
					
						
							|  |  |  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Manager abstracts functions for creating timelines for multiple accounts, and adding, removing, and fetching entries from those timelines. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // By the time a timelineable hits the manager interface, it should already have been filtered and it should be established that the item indeed | 
					
						
							|  |  |  | // belongs in the timeline of the given account ID. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // The manager makes a distinction between *indexed* items and *prepared* items. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // Indexed items consist of just that item's ID (in the database) and the time it was created. An indexed item takes up very little memory, so | 
					
						
							|  |  |  | // it's not a huge priority to keep trimming the indexed items list. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // Prepared items consist of the item's database ID, the time it was created, AND the apimodel representation of that item, for quick serialization. | 
					
						
							|  |  |  | // Prepared items of course take up more memory than indexed items, so they should be regularly pruned if they're not being actively served. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | type Manager interface { | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// Ingest takes one item and indexes it into the timeline for the given account ID. | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// It should already be established before calling this function that the item actually belongs in the timeline! | 
					
						
							| 
									
										
										
										
											2021-06-19 11:18:55 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// The returned bool indicates whether the item was actually put in the timeline. This could be false in cases where | 
					
						
							|  |  |  | 	// the item is a boosted status, but a boost of the original status or the status itself already exists recently in the timeline. | 
					
						
							|  |  |  | 	Ingest(ctx context.Context, item Timelineable, timelineAccountID string) (bool, error) | 
					
						
							|  |  |  | 	// IngestAndPrepare takes one timelineable and indexes it into the timeline for the given account ID, and then immediately prepares it for serving. | 
					
						
							|  |  |  | 	// This is useful in cases where we know the item will need to be shown at the top of a user's timeline immediately (eg., a new status is created). | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// It should already be established before calling this function that the item actually belongs in the timeline! | 
					
						
							| 
									
										
										
										
											2021-06-19 11:18:55 +02:00
										 |  |  | 	// | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// The returned bool indicates whether the item was actually put in the timeline. This could be false in cases where | 
					
						
							|  |  |  | 	// a status is a boost, but a boost of the original status or the status itself already exists recently in the timeline. | 
					
						
							|  |  |  | 	IngestAndPrepare(ctx context.Context, item Timelineable, timelineAccountID string) (bool, error) | 
					
						
							|  |  |  | 	// GetTimeline returns limit n amount of prepared entries from the timeline of the given account ID, in descending chronological order. | 
					
						
							|  |  |  | 	// If maxID is provided, it will return prepared entries from that maxID onwards, inclusive. | 
					
						
							|  |  |  | 	GetTimeline(ctx context.Context, accountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]Preparable, error) | 
					
						
							|  |  |  | 	// GetIndexedLength returns the amount of items that have been *indexed* for the given account ID. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	GetIndexedLength(ctx context.Context, timelineAccountID string) int | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// GetOldestIndexedID returns the id ID for the oldest item that we have indexed for the given account. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	GetOldestIndexedID(ctx context.Context, timelineAccountID string) (string, error) | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// PrepareXFromTop prepares limit n amount of items, based on their indexed representations, from the top of the index. | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	PrepareXFromTop(ctx context.Context, timelineAccountID string, limit int) error | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	// Remove removes one item from the timeline of the given timelineAccountID | 
					
						
							|  |  |  | 	Remove(ctx context.Context, timelineAccountID string, itemID string) (int, error) | 
					
						
							|  |  |  | 	// WipeItemFromAllTimelines removes one item from the index and prepared items of all timelines | 
					
						
							|  |  |  | 	WipeItemFromAllTimelines(ctx context.Context, itemID string) error | 
					
						
							|  |  |  | 	// WipeStatusesFromAccountID removes all items by the given accountID from the timelineAccountID's timelines. | 
					
						
							|  |  |  | 	WipeItemsFromAccountID(ctx context.Context, timelineAccountID string, accountID string) error | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	// Start starts hourly cleanup jobs for this timeline manager. | 
					
						
							|  |  |  | 	Start() error | 
					
						
							|  |  |  | 	// Stop stops the timeline manager (currently a stub, doesn't do anything). | 
					
						
							|  |  |  | 	Stop() error | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | // NewManager returns a new timeline manager. | 
					
						
							|  |  |  | func NewManager(grabFunction GrabFunction, filterFunction FilterFunction, prepareFunction PrepareFunction, skipInsertFunction SkipInsertFunction) Manager { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	return &manager{ | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 		accountTimelines:   sync.Map{}, | 
					
						
							|  |  |  | 		grabFunction:       grabFunction, | 
					
						
							|  |  |  | 		filterFunction:     filterFunction, | 
					
						
							|  |  |  | 		prepareFunction:    prepareFunction, | 
					
						
							|  |  |  | 		skipInsertFunction: skipInsertFunction, | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type manager struct { | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	accountTimelines   sync.Map | 
					
						
							|  |  |  | 	grabFunction       GrabFunction | 
					
						
							|  |  |  | 	filterFunction     FilterFunction | 
					
						
							|  |  |  | 	prepareFunction    PrepareFunction | 
					
						
							|  |  |  | 	skipInsertFunction SkipInsertFunction | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | func (m *manager) Start() error { | 
					
						
							|  |  |  | 	// range through all timelines in the sync map once per hour + prune as necessary | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for now := range time.NewTicker(1 * time.Hour).C { | 
					
						
							|  |  |  | 			m.accountTimelines.Range(func(key any, value any) bool { | 
					
						
							|  |  |  | 				timelineAccountID, ok := key.(string) | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							|  |  |  | 					panic("couldn't parse timeline manager sync map key as string, this should never happen so panic") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				t, ok := value.(Timeline) | 
					
						
							|  |  |  | 				if !ok { | 
					
						
							|  |  |  | 					panic("couldn't parse timeline manager sync map value as Timeline, this should never happen so panic") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				anHourAgo := now.Add(-1 * time.Hour) | 
					
						
							|  |  |  | 				if lastGot := t.LastGot(); lastGot.Before(anHourAgo) { | 
					
						
							|  |  |  | 					amountPruned := t.Prune(defaultDesiredPreparedItemsLength, defaultDesiredIndexedItemsLength) | 
					
						
							|  |  |  | 					log.WithFields(kv.Fields{ | 
					
						
							|  |  |  | 						{"timelineAccountID", timelineAccountID}, | 
					
						
							|  |  |  | 						{"amountPruned", amountPruned}, | 
					
						
							|  |  |  | 					}...).Info("pruned indexed and prepared items from timeline") | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				return true | 
					
						
							|  |  |  | 			}) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (m *manager) Stop() error { | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func (m *manager) Ingest(ctx context.Context, item Timelineable, timelineAccountID string) (bool, error) { | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	l := log.WithFields(kv.Fields{ | 
					
						
							|  |  |  | 		{"timelineAccountID", timelineAccountID}, | 
					
						
							|  |  |  | 		{"itemID", item.GetID()}, | 
					
						
							|  |  |  | 	}...) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	l.Trace("ingesting item") | 
					
						
							|  |  |  | 	return t.IndexOne(ctx, item.GetID(), item.GetBoostOfID(), item.GetAccountID(), item.GetBoostOfAccountID()) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func (m *manager) IngestAndPrepare(ctx context.Context, item Timelineable, timelineAccountID string) (bool, error) { | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	l := log.WithFields(kv.Fields{ | 
					
						
							|  |  |  | 		{"timelineAccountID", timelineAccountID}, | 
					
						
							|  |  |  | 		{"itemID", item.GetID()}, | 
					
						
							|  |  |  | 	}...) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return false, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	l.Trace("ingesting item") | 
					
						
							|  |  |  | 	return t.IndexAndPrepareOne(ctx, item.GetID(), item.GetBoostOfID(), item.GetAccountID(), item.GetBoostOfAccountID()) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func (m *manager) Remove(ctx context.Context, timelineAccountID string, itemID string) (int, error) { | 
					
						
							| 
									
										
										
										
											2022-07-19 09:47:55 +01:00
										 |  |  | 	l := log.WithFields(kv.Fields{ | 
					
						
							|  |  |  | 		{"timelineAccountID", timelineAccountID}, | 
					
						
							|  |  |  | 		{"itemID", itemID}, | 
					
						
							|  |  |  | 	}...) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	l.Trace("removing item") | 
					
						
							|  |  |  | 	return t.Remove(ctx, itemID) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func (m *manager) GetTimeline(ctx context.Context, timelineAccountID string, maxID string, sinceID string, minID string, limit int, local bool) ([]Preparable, error) { | 
					
						
							| 
									
										
										
										
											2022-11-22 19:38:10 +01:00
										 |  |  | 	l := log.WithFields(kv.Fields{{"timelineAccountID", timelineAccountID}}...) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	items, err := t.Get(ctx, limit, maxID, sinceID, minID, true) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		l.Errorf("error getting statuses: %s", err) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	return items, nil | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (m *manager) GetIndexedLength(ctx context.Context, timelineAccountID string) int { | 
					
						
							|  |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return 0 | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	return t.ItemIndexLength(ctx) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (m *manager) GetOldestIndexedID(ctx context.Context, timelineAccountID string) (string, error) { | 
					
						
							|  |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 	return t.OldestIndexedItemID(ctx) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (m *manager) PrepareXFromTop(ctx context.Context, timelineAccountID string, limit int) error { | 
					
						
							|  |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	return t.PrepareFromTop(ctx, limit) | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func (m *manager) WipeItemFromAllTimelines(ctx context.Context, statusID string) error { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	errors := []string{} | 
					
						
							|  |  |  | 	m.accountTimelines.Range(func(k interface{}, i interface{}) bool { | 
					
						
							|  |  |  | 		t, ok := i.(Timeline) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			panic("couldn't parse entry as Timeline, this should never happen so panic") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 		if _, err := t.Remove(ctx, statusID); err != nil { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 			errors = append(errors, err.Error()) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-21 15:56:00 +02:00
										 |  |  | 		return true | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var err error | 
					
						
							|  |  |  | 	if len(errors) > 0 { | 
					
						
							|  |  |  | 		err = fmt.Errorf("one or more errors removing status %s from all timelines: %s", statusID, strings.Join(errors, ";")) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | func (m *manager) WipeItemsFromAccountID(ctx context.Context, timelineAccountID string, accountID string) error { | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	t, err := m.getOrCreateTimeline(ctx, timelineAccountID) | 
					
						
							| 
									
										
										
										
											2021-07-11 16:22:21 +02:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | 	_, err = t.RemoveAllBy(ctx, accountID) | 
					
						
							| 
									
										
										
										
											2021-07-11 16:22:21 +02:00
										 |  |  | 	return err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-25 15:34:33 +02:00
										 |  |  | func (m *manager) getOrCreateTimeline(ctx context.Context, timelineAccountID string) (Timeline, error) { | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 	var t Timeline | 
					
						
							|  |  |  | 	i, ok := m.accountTimelines.Load(timelineAccountID) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 		var err error | 
					
						
							| 
									
										
										
										
											2022-02-05 12:47:38 +01:00
										 |  |  | 		t, err = NewTimeline(ctx, timelineAccountID, m.grabFunction, m.filterFunction, m.prepareFunction, m.skipInsertFunction) | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | 		m.accountTimelines.Store(timelineAccountID, t) | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		t, ok = i.(Timeline) | 
					
						
							|  |  |  | 		if !ok { | 
					
						
							|  |  |  | 			panic("couldn't parse entry as Timeline, this should never happen so panic") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-23 18:42:20 +02:00
										 |  |  | 	return t, nil | 
					
						
							| 
									
										
										
										
											2021-06-13 18:42:28 +02:00
										 |  |  | } |