Complete content filtering operations for hiding unwanted content.
Filters allow you to automatically hide statuses containing specific phrases or keywords from your timelines and notifications.
type Filter struct {
ID ID
Phrase string
Context []string
WholeWord bool
ExpiresAt time.Time
Irreversible bool
}Filter is metadata for a content filter.
Fields:
ID - Unique filter identifierPhrase - Text to filter (keyword or phrase)Context - Contexts where filter applies (see below)WholeWord - If true, only match whole wordsExpiresAt - When filter expires (zero value = never)Irreversible - If true, filtered statuses are dropped completely instead of hiddenFilter Contexts:
"home" - Home timeline"notifications" - Notifications"public" - Public timelines"thread" - Conversation threads"account" - Account pagestype FilterResult struct {
Filter struct {
ID string
Title string
Context []string
ExpiresAt time.Time
FilterAction string
}
KeywordMatches []string
StatusMatches []string
}FilterResult represents a filter match on a status.
Fields:
Filter - Filter information that matchedFilter.ID - Filter IDFilter.Title - Filter title/nameFilter.Context - Contexts where filter appliesFilter.ExpiresAt - Expiration timeFilter.FilterAction - Action taken ("warn" or "hide")KeywordMatches - Keywords that matchedStatusMatches - Status IDs that matchedfunc (c *Client) GetFilters(ctx context.Context) ([]*Filter, error)Returns all filters for the current account.
Parameters:
ctx - Context for cancellation/timeoutReturns: Slice of Filter objects or error
Example:
filters, err := client.GetFilters(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("You have %d active filters:\n", len(filters))
for _, filter := range filters {
fmt.Printf(" '%s' in %v\n", filter.Phrase, filter.Context)
}func (c *Client) GetFilter(ctx context.Context, id ID) (*Filter, error)Returns a filter by ID.
Parameters:
ctx - Context for cancellation/timeoutid - Filter ID to retrieveReturns: Filter object or error
Example:
filter, err := client.GetFilter(ctx, "123456")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Filter: '%s'\n", filter.Phrase)func (c *Client) CreateFilter(ctx context.Context, filter *Filter) (*Filter, error)Creates a new filter.
Parameters:
ctx - Context for cancellation/timeoutfilter - Filter configurationReturns: Created Filter object or error
Example:
// Filter out spoilers from home timeline
filter := &mastodon.Filter{
Phrase: "spoiler",
Context: []string{"home"},
WholeWord: true,
}
created, err := client.CreateFilter(ctx, filter)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created filter: %s (ID: %s)\n", created.Phrase, created.ID)Example: Temporary Filter
// Filter phrase for 24 hours
expiresAt := time.Now().Add(24 * time.Hour)
filter := &mastodon.Filter{
Phrase: "temporary keyword",
Context: []string{"home", "notifications"},
ExpiresAt: expiresAt,
}
created, err := client.CreateFilter(ctx, filter)
if err != nil {
log.Fatal(err)
}func (c *Client) UpdateFilter(ctx context.Context, id ID, filter *Filter) (*Filter, error)Updates an existing filter.
Parameters:
ctx - Context for cancellation/timeoutid - Filter ID to updatefilter - Updated filter configurationReturns: Updated Filter object or error
Example:
// Update filter to apply in more contexts
updated := &mastodon.Filter{
Phrase: "spoiler",
Context: []string{"home", "public", "notifications"},
WholeWord: true,
}
result, err := client.UpdateFilter(ctx, "123456", updated)
if err != nil {
log.Fatal(err)
}
fmt.Println("Filter updated")func (c *Client) DeleteFilter(ctx context.Context, id ID) errorDeletes a filter.
Parameters:
ctx - Context for cancellation/timeoutid - Filter ID to deleteReturns: Error if operation fails
Example:
err := client.DeleteFilter(ctx, "123456")
if err != nil {
log.Fatal(err)
}
fmt.Println("Filter deleted")func blockSpoilers(client *mastodon.Client, topics []string) error {
ctx := context.Background()
for _, topic := range topics {
filter := &mastodon.Filter{
Phrase: topic + " spoiler",
Context: []string{"home", "public", "notifications"},
WholeWord: false,
}
_, err := client.CreateFilter(ctx, filter)
if err != nil {
return fmt.Errorf("failed to create filter for %s: %w", topic, err)
}
fmt.Printf("Created spoiler filter for: %s\n", topic)
}
return nil
}
// Usage
topics := []string{"Game of Thrones", "Avengers", "Star Wars"}
err := blockSpoilers(client, topics)func filterEvent(client *mastodon.Client, eventName string, duration time.Duration) error {
ctx := context.Background()
filter := &mastodon.Filter{
Phrase: eventName,
Context: []string{"home", "public"},
WholeWord: true,
ExpiresAt: time.Now().Add(duration),
}
created, err := client.CreateFilter(ctx, filter)
if err != nil {
return err
}
fmt.Printf("Filtering '%s' for %v (until %s)\n",
eventName, duration, created.ExpiresAt.Format("2006-01-02 15:04"))
return nil
}
// Usage: Filter Olympics content for 2 weeks
err := filterEvent(client, "Olympics", 14*24*time.Hour)func createWholeWordFilter(client *mastodon.Client, word string) error {
ctx := context.Background()
filter := &mastodon.Filter{
Phrase: word,
Context: []string{"home", "notifications", "public", "thread"},
WholeWord: true, // Only match complete word
}
_, err := client.CreateFilter(ctx, filter)
if err != nil {
return err
}
// With WholeWord=true:
// "cat" matches: "my cat is cute"
// "cat" doesn't match: "category", "concatenate"
fmt.Printf("Created whole-word filter for: %s\n", word)
return nil
}func blockCompletely(client *mastodon.Client, phrase string) error {
ctx := context.Background()
filter := &mastodon.Filter{
Phrase: phrase,
Context: []string{"home", "notifications"},
Irreversible: true, // Completely drop from server
}
_, err := client.CreateFilter(ctx, filter)
if err != nil {
return err
}
fmt.Printf("Created irreversible filter for: %s\n", phrase)
fmt.Println("Warning: Filtered content will be completely dropped, not just hidden")
return nil
}type FilterManager struct {
client *mastodon.Client
}
func (fm *FilterManager) ListAll(ctx context.Context) error {
filters, err := fm.client.GetFilters(ctx)
if err != nil {
return err
}
if len(filters) == 0 {
fmt.Println("No active filters")
return nil
}
fmt.Printf("Active Filters (%d):\n", len(filters))
for i, filter := range filters {
fmt.Printf("\n%d. %s (ID: %s)\n", i+1, filter.Phrase, filter.ID)
fmt.Printf(" Contexts: %v\n", filter.Context)
fmt.Printf(" Whole word: %v\n", filter.WholeWord)
if !filter.ExpiresAt.IsZero() {
fmt.Printf(" Expires: %s\n", filter.ExpiresAt.Format("2006-01-02 15:04"))
} else {
fmt.Printf(" Expires: Never\n")
}
if filter.Irreversible {
fmt.Printf(" Irreversible: Yes (completely dropped)\n")
}
}
return nil
}
func (fm *FilterManager) RemoveExpired(ctx context.Context) error {
filters, err := fm.client.GetFilters(ctx)
if err != nil {
return err
}
now := time.Now()
var removed int
for _, filter := range filters {
if !filter.ExpiresAt.IsZero() && filter.ExpiresAt.Before(now) {
err := fm.client.DeleteFilter(ctx, filter.ID)
if err != nil {
log.Printf("Failed to delete expired filter %s: %v", filter.ID, err)
continue
}
removed++
}
}
fmt.Printf("Removed %d expired filters\n", removed)
return nil
}
func (fm *FilterManager) FindByPhrase(ctx context.Context, phrase string) ([]*mastodon.Filter, error) {
filters, err := fm.client.GetFilters(ctx)
if err != nil {
return nil, err
}
var matches []*mastodon.Filter
for _, filter := range filters {
if strings.Contains(strings.ToLower(filter.Phrase), strings.ToLower(phrase)) {
matches = append(matches, filter)
}
}
return matches, nil
}
func (fm *FilterManager) ExtendExpiration(ctx context.Context, filterID mastodon.ID, additionalTime time.Duration) error {
filter, err := fm.client.GetFilter(ctx, filterID)
if err != nil {
return err
}
// Calculate new expiration
var newExpiry time.Time
if filter.ExpiresAt.IsZero() {
newExpiry = time.Now().Add(additionalTime)
} else {
newExpiry = filter.ExpiresAt.Add(additionalTime)
}
// Update filter
filter.ExpiresAt = newExpiry
_, err = fm.client.UpdateFilter(ctx, filterID, filter)
if err != nil {
return err
}
fmt.Printf("Extended filter expiration to: %s\n", newExpiry.Format("2006-01-02 15:04"))
return nil
}func createMultipleFilters(client *mastodon.Client, phrases []string, contexts []string) error {
ctx := context.Background()
var created int
var failed int
for _, phrase := range phrases {
filter := &mastodon.Filter{
Phrase: phrase,
Context: contexts,
WholeWord: true,
}
_, err := client.CreateFilter(ctx, filter)
if err != nil {
log.Printf("Failed to create filter for '%s': %v", phrase, err)
failed++
continue
}
created++
}
fmt.Printf("Created %d filters, %d failed\n", created, failed)
return nil
}
func deleteAllFilters(client *mastodon.Client) error {
ctx := context.Background()
filters, err := client.GetFilters(ctx)
if err != nil {
return err
}
fmt.Printf("Deleting %d filters...\n", len(filters))
for _, filter := range filters {
err := client.DeleteFilter(ctx, filter.ID)
if err != nil {
log.Printf("Failed to delete filter %s: %v", filter.ID, err)
}
}
fmt.Println("All filters deleted")
return nil
}type FilterTemplate struct {
Name string
Phrases []string
Contexts []string
Duration time.Duration
}
var FilterTemplates = map[string]FilterTemplate{
"sports": {
Name: "Sports Events",
Phrases: []string{"Super Bowl", "World Cup", "Olympics"},
Contexts: []string{"home", "public"},
Duration: 7 * 24 * time.Hour,
},
"politics": {
Name: "Politics",
Phrases: []string{"election", "congress", "parliament"},
Contexts: []string{"home", "public", "notifications"},
Duration: 0, // Permanent
},
}
func applyFilterTemplate(client *mastodon.Client, templateName string) error {
ctx := context.Background()
template, ok := FilterTemplates[templateName]
if !ok {
return fmt.Errorf("template not found: %s", templateName)
}
fmt.Printf("Applying filter template: %s\n", template.Name)
for _, phrase := range template.Phrases {
filter := &mastodon.Filter{
Phrase: phrase,
Context: template.Contexts,
WholeWord: true,
}
if template.Duration > 0 {
filter.ExpiresAt = time.Now().Add(template.Duration)
}
_, err := client.CreateFilter(ctx, filter)
if err != nil {
log.Printf("Failed to create filter '%s': %v", phrase, err)
continue
}
fmt.Printf(" Created filter: %s\n", phrase)
}
return nil
}For common words, use WholeWord to avoid false positives:
filter := &mastodon.Filter{
Phrase: "cat",
WholeWord: true, // Avoids matching "category", "concatenate"
}Only filter in contexts where needed:
// For personal annoyances, filter from home
filter.Context = []string{"home"}
// For sensitive topics, filter everywhere
filter.Context = []string{"home", "public", "notifications", "thread"}Set expiration for time-limited topics:
filter := &mastodon.Filter{
Phrase: "conference2024",
ExpiresAt: time.Now().Add(7 * 24 * time.Hour),
}Irreversible filters permanently drop content:
// Use sparingly - you can't undo this
filter := &mastodon.Filter{
Irreversible: true, // Content is gone forever
}Start with specific contexts before applying everywhere:
// Test in home first
filter := &mastodon.Filter{
Phrase: "test phrase",
Context: []string{"home"}, // Just home timeline
}
// After testing, expand
updated := &mastodon.Filter{
Phrase: "test phrase",
Context: []string{"home", "public", "notifications"},
}Clean up outdated filters:
func reviewFilters(client *mastodon.Client) {
filters, _ := client.GetFilters(ctx)
for _, filter := range filters {
fmt.Printf("Keep filter '%s'? (y/n): ", filter.Phrase)
// Interactive review...
}
}See also: