User reporting and moderation operations.
The reporting system allows users to report problematic accounts or content to instance moderators. Reports are reviewed by instance administrators.
type Report struct {
ID ID
ActionTaken bool
}Report holds information for a Mastodon report.
Fields:
ID - Unique report identifierActionTaken - Whether moderators have acted on this reportfunc (c *Client) GetReports(ctx context.Context) ([]*Report, error)Returns reports filed by the current user.
Parameters:
ctx - Context for cancellation/timeoutReturns: Slice of Report objects or error
Example:
reports, err := client.GetReports(ctx)
if err != nil {
log.Fatal(err)
}
fmt.Printf("You have filed %d report(s):\n", len(reports))
for _, report := range reports {
status := "Pending"
if report.ActionTaken {
status = "Action Taken"
}
fmt.Printf(" Report %s: %s\n", report.ID, status)
}func (c *Client) Report(ctx context.Context, accountID ID, ids []ID, comment string) (*Report, error)Creates a report against an account.
Parameters:
ctx - Context for cancellation/timeoutaccountID - ID of account to reportids - Slice of status IDs as evidence (can be empty)comment - Explanation of why you're reporting (required)Returns: Created Report object or error
Example:
accountID := "123456"
statusIDs := []mastodon.ID{"789012", "345678"} // Problematic statuses
comment := "This account is posting spam and harassment"
report, err := client.Report(ctx, accountID, statusIDs, comment)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Report filed successfully. Report ID: %s\n", report.ID)func reportSpammer(client *mastodon.Client, accountID mastodon.ID) error {
ctx := context.Background()
// Get recent statuses from the account as evidence
statuses, err := client.GetAccountStatuses(ctx, accountID, &mastodon.Pagination{Limit: 5})
if err != nil {
return fmt.Errorf("failed to get account statuses: %w", err)
}
// Collect status IDs
var statusIDs []mastodon.ID
for _, status := range statuses {
statusIDs = append(statusIDs, status.ID)
}
// File report
comment := "This account is posting spam links repeatedly"
report, err := client.Report(ctx, accountID, statusIDs, comment)
if err != nil {
return fmt.Errorf("failed to file report: %w", err)
}
fmt.Printf("Report filed successfully (ID: %s)\n", report.ID)
fmt.Printf("Moderators will review the account and take appropriate action.\n")
return nil
}func reportHarassment(client *mastodon.Client, accountID mastodon.ID, offendingStatusIDs []mastodon.ID) error {
ctx := context.Background()
comment := `This account has been sending harassing messages and making threatening statements.
The attached statuses contain personal attacks and violate the instance's code of conduct.`
report, err := client.Report(ctx, accountID, offendingStatusIDs, comment)
if err != nil {
return err
}
fmt.Printf("Harassment report filed (ID: %s)\n", report.ID)
return nil
}func checkReportStatus(client *mastodon.Client, reportID mastodon.ID) error {
ctx := context.Background()
reports, err := client.GetReports(ctx)
if err != nil {
return err
}
for _, report := range reports {
if report.ID == reportID {
if report.ActionTaken {
fmt.Printf("Report %s: Action has been taken by moderators\n", reportID)
} else {
fmt.Printf("Report %s: Pending review\n", reportID)
}
return nil
}
}
return fmt.Errorf("report %s not found", reportID)
}func listMyReports(client *mastodon.Client) error {
ctx := context.Background()
reports, err := client.GetReports(ctx)
if err != nil {
return err
}
if len(reports) == 0 {
fmt.Println("You have not filed any reports")
return nil
}
fmt.Printf("Your Reports (%d total)\n", len(reports))
fmt.Println(strings.Repeat("=", 50))
pending := 0
resolved := 0
for _, report := range reports {
status := "⏳ Pending"
if report.ActionTaken {
status = "✓ Action Taken"
resolved++
} else {
pending++
}
fmt.Printf("Report %s: %s\n", report.ID, status)
}
fmt.Printf("\nSummary: %d pending, %d resolved\n", pending, resolved)
return nil
}type ReportContext struct {
AccountID mastodon.ID
Reason string
StatusIDs []mastodon.ID
AdditionalInfo string
}
func fileDetailedReport(client *mastodon.Client, ctx ReportContext) error {
context := context.Background()
// Build comprehensive comment
comment := fmt.Sprintf("Reason: %s\n\n", ctx.Reason)
if ctx.AdditionalInfo != "" {
comment += fmt.Sprintf("Additional Information:\n%s\n\n", ctx.AdditionalInfo)
}
comment += fmt.Sprintf("Number of problematic posts: %d", len(ctx.StatusIDs))
report, err := client.Report(context, ctx.AccountID, ctx.StatusIDs, comment)
if err != nil {
return err
}
fmt.Printf("Detailed report filed: %s\n", report.ID)
return nil
}
// Usage
reportCtx := ReportContext{
AccountID: "123456",
Reason: "Spam and misleading information",
StatusIDs: []mastodon.ID{"789", "012", "345"},
AdditionalInfo: "Account created recently and only posts promotional content",
}
err := fileDetailedReport(client, reportCtx)type ReportManager struct {
client *mastodon.Client
}
func (rm *ReportManager) FileReport(ctx context.Context, accountID mastodon.ID, reason string, statusIDs []mastodon.ID) (*mastodon.Report, error) {
report, err := rm.client.Report(ctx, accountID, statusIDs, reason)
if err != nil {
return nil, fmt.Errorf("failed to file report: %w", err)
}
log.Printf("Report filed: %s against account %s", report.ID, accountID)
return report, nil
}
func (rm *ReportManager) GetPendingReports(ctx context.Context) ([]*mastodon.Report, error) {
reports, err := rm.client.GetReports(ctx)
if err != nil {
return nil, err
}
var pending []*mastodon.Report
for _, report := range reports {
if !report.ActionTaken {
pending = append(pending, report)
}
}
return pending, nil
}
func (rm *ReportManager) GetResolvedReports(ctx context.Context) ([]*mastodon.Report, error) {
reports, err := rm.client.GetReports(ctx)
if err != nil {
return nil, err
}
var resolved []*mastodon.Report
for _, report := range reports {
if report.ActionTaken {
resolved = append(resolved, report)
}
}
return resolved, nil
}
func (rm *ReportManager) Summary(ctx context.Context) error {
reports, err := rm.client.GetReports(ctx)
if err != nil {
return err
}
fmt.Println("Report Summary")
fmt.Println(strings.Repeat("=", 50))
fmt.Printf("Total reports filed: %d\n", len(reports))
pending := 0
resolved := 0
for _, report := range reports {
if report.ActionTaken {
resolved++
} else {
pending++
}
}
fmt.Printf("Pending review: %d\n", pending)
fmt.Printf("Action taken: %d\n", resolved)
if len(reports) > 0 {
resolvedPercent := float64(resolved) / float64(len(reports)) * 100
fmt.Printf("Resolution rate: %.1f%%\n", resolvedPercent)
}
return nil
}func interactiveReport(client *mastodon.Client, reader *bufio.Reader) error {
ctx := context.Background()
fmt.Println("File a Report")
fmt.Println(strings.Repeat("=", 50))
// Get account ID
fmt.Print("Enter account ID or username: ")
input, _ := reader.ReadString('\n')
input = strings.TrimSpace(input)
var accountID mastodon.ID
if strings.HasPrefix(input, "@") || strings.Contains(input, "@") {
// Lookup by username
account, err := client.AccountLookup(ctx, input)
if err != nil {
return fmt.Errorf("account not found: %w", err)
}
accountID = account.ID
fmt.Printf("Found: @%s (%s)\n", account.Acct, account.DisplayName)
} else {
accountID = mastodon.ID(input)
}
// Get status IDs
fmt.Print("Enter status IDs (comma-separated, or press Enter to skip): ")
statusInput, _ := reader.ReadString('\n')
statusInput = strings.TrimSpace(statusInput)
var statusIDs []mastodon.ID
if statusInput != "" {
ids := strings.Split(statusInput, ",")
for _, id := range ids {
statusIDs = append(statusIDs, mastodon.ID(strings.TrimSpace(id)))
}
}
// Get reason
fmt.Print("Enter reason for report: ")
reason, _ := reader.ReadString('\n')
reason = strings.TrimSpace(reason)
if reason == "" {
return fmt.Errorf("reason is required")
}
// Confirm
fmt.Printf("\nReview Report:\n")
fmt.Printf(" Account: %s\n", accountID)
fmt.Printf(" Statuses: %d\n", len(statusIDs))
fmt.Printf(" Reason: %s\n", reason)
fmt.Print("\nFile this report? (yes/no): ")
confirm, _ := reader.ReadString('\n')
confirm = strings.TrimSpace(strings.ToLower(confirm))
if confirm != "yes" && confirm != "y" {
fmt.Println("Report cancelled")
return nil
}
// File report
report, err := client.Report(ctx, accountID, statusIDs, reason)
if err != nil {
return err
}
fmt.Printf("\n✓ Report filed successfully (ID: %s)\n", report.ID)
return nil
}Common reasons for filing reports:
comment := "This account is posting unsolicited advertisements and spam links"comment := "This account is sending harassing messages and personal attacks"comment := "This account is posting hate speech and discriminatory content"comment := "This account is impersonating another user or public figure"comment := "This account is sharing private information without consent"comment := "This account is posting illegal or prohibited content"comment := "This account is deliberately spreading false information"Include specific status IDs that violate rules:
// Good: Specific problematic statuses
statusIDs := []mastodon.ID{"123", "456", "789"}
// Less helpful: No evidence
statusIDs := []mastodon.ID{}Explain the issue clearly for moderators:
// Good: Detailed explanation
comment := `This account is repeatedly posting spam links to external sites.
The pattern started 3 days ago with multiple posts per hour containing
identical links. The content appears to be commercial advertising that
violates the instance's spam policy.`
// Less helpful: Vague
comment := "spam"Only report actual violations:
// Report actual violations
if isSpam || isHarassment || isHateSpeech {
client.Report(ctx, accountID, statusIDs, reason)
}
// Don't report disagreements
// Use mute/block insteadFor personal issues, use block or mute instead of reporting:
// For annoying but not rule-breaking content
client.AccountMute(ctx, accountID)
// For harassment or rule violations
client.Report(ctx, accountID, statusIDs, "Harassment: ...")Don't file multiple reports for the same issue:
// File once with all evidence
client.Report(ctx, accountID, allStatusIDs, detailedReason)
// Don't file multiple reports for same accountCheck status but don't spam moderators:
// Check after reasonable time
time.Sleep(24 * time.Hour)
checkReportStatus(client, reportID)
// Don't repeatedly checkAfter filing a report, moderators may:
The ActionTaken field indicates whether moderators have processed the report, not necessarily that punitive action was taken.
See also: