Go client library for Mastodon API with complete coverage of v1 and v2 endpoints
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:
Install with Tessl CLI
npx tessl i tessl/golang-go-mastodon