or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

accounts.mdauthentication.mdconversations.mdfilters.mdindex.mdinstance.mdlists.mdmedia.mdnotifications.mdpolls.mdreports.mdsearch.mdstatuses.mdstreaming.mdtags.mdtimelines.mdtypes.md
tile.json

polls.mddocs/

Poll Operations

Complete poll creation and voting operations.

Overview

Polls allow users to create questions with multiple choice answers that can be voted on for a specified duration.

Types

Poll

type Poll struct {
    ID          ID
    ExpiresAt   time.Time
    Expired     bool
    Multiple    bool
    VotesCount  int64
    VotersCount int64
    Options     []PollOption
    Voted       bool
    OwnVotes    []int
    Emojis      []Emoji
}

Poll holds information for a Mastodon poll.

Fields:

  • ID - Unique poll identifier
  • ExpiresAt - When the poll expires
  • Expired - Whether the poll has expired
  • Multiple - Whether multiple choices are allowed
  • VotesCount - Total number of votes cast
  • VotersCount - Number of unique voters
  • Options - Poll options/choices
  • Voted - Whether current user has voted
  • OwnVotes - Current user's vote indices (if voted)
  • Emojis - Custom emojis used in poll

PollOption

type PollOption struct {
    Title      string
    VotesCount int64
}

PollOption holds information for a poll option.

Fields:

  • Title - Option text
  • VotesCount - Number of votes for this option

TootPoll

type TootPoll struct {
    Options          []string
    ExpiresInSeconds int
    Multiple         bool
    HideTotals       bool
}

TootPoll holds information for creating a poll in a toot.

Fields:

  • Options - Option texts (2-4 options)
  • ExpiresInSeconds - Poll duration in seconds
  • Multiple - Allow multiple choice selection
  • HideTotals - Hide vote counts until poll expires

Poll Operations

GetPoll { .api }

func (c *Client) GetPoll(ctx context.Context, id ID) (*Poll, error)

Returns poll by ID.

Parameters:

  • ctx - Context for cancellation/timeout
  • id - Poll ID to retrieve

Returns: Poll object or error

Example:

poll, err := client.GetPoll(ctx, "123456")
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Poll: expires at %s\n", poll.ExpiresAt)
fmt.Printf("Total votes: %d from %d voters\n", poll.VotesCount, poll.VotersCount)

for i, option := range poll.Options {
    percentage := 0.0
    if poll.VotesCount > 0 {
        percentage = float64(option.VotesCount) / float64(poll.VotesCount) * 100
    }
    fmt.Printf("%d. %s: %d votes (%.1f%%)\n",
        i+1, option.Title, option.VotesCount, percentage)
}

PollVote { .api }

func (c *Client) PollVote(ctx context.Context, id ID, choices ...int) (*Poll, error)

Votes on a poll. Choices are Poll.Options indices (0-based).

Parameters:

  • ctx - Context for cancellation/timeout
  • id - Poll ID to vote on
  • choices - One or more option indices to vote for

Returns: Updated Poll object or error

Example:

// Vote for option 1 (second option, 0-indexed)
poll, err := client.PollVote(ctx, "123456", 1)
if err != nil {
    log.Fatal(err)
}
fmt.Println("Vote recorded!")

// Multiple choice poll - vote for options 0 and 2
poll, err = client.PollVote(ctx, "123456", 0, 2)
if err != nil {
    log.Fatal(err)
}

Creating Polls

Polls are created as part of status posting using the TootPoll struct:

poll := &mastodon.TootPoll{
    Options:          []string{"Option A", "Option B", "Option C"},
    ExpiresInSeconds: 86400, // 24 hours
    Multiple:         false,
    HideTotals:       false,
}

toot := &mastodon.Toot{
    Status: "What's your favorite?",
    Poll:   poll,
}

status, err := client.PostStatus(ctx, toot)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("Created poll in status: %s\n", status.ID)

Usage Examples

Example: Create Simple Poll

func createPoll(client *mastodon.Client, question string, options []string, hours int) (*mastodon.Status, error) {
    ctx := context.Background()

    poll := &mastodon.TootPoll{
        Options:          options,
        ExpiresInSeconds: hours * 3600,
        Multiple:         false,
        HideTotals:       false,
    }

    toot := &mastodon.Toot{
        Status:     question,
        Poll:       poll,
        Visibility: mastodon.VisibilityPublic,
    }

    status, err := client.PostStatus(ctx, toot)
    if err != nil {
        return nil, err
    }

    fmt.Printf("Poll created! ID: %s\n", status.Poll.ID)
    return status, nil
}

// Usage
options := []string{"Go", "Rust", "Python", "JavaScript"}
status, err := createPoll(client, "What's your favorite language?", options, 24)

Example: Multiple Choice Poll

func createMultipleChoicePoll(client *mastodon.Client) (*mastodon.Status, error) {
    ctx := context.Background()

    poll := &mastodon.TootPoll{
        Options:          []string{"Morning", "Afternoon", "Evening", "Night"},
        ExpiresInSeconds: 3 * 24 * 3600, // 3 days
        Multiple:         true,           // Allow multiple choices
        HideTotals:       false,
    }

    toot := &mastodon.Toot{
        Status: "When are you most productive? (Select all that apply)",
        Poll:   poll,
    }

    return client.PostStatus(ctx, toot)
}

Example: Anonymous Poll (Hidden Totals)

func createAnonymousPoll(client *mastodon.Client) (*mastodon.Status, error) {
    ctx := context.Background()

    poll := &mastodon.TootPoll{
        Options:          []string{"Yes", "No", "Maybe"},
        ExpiresInSeconds: 7 * 24 * 3600, // 7 days
        Multiple:         false,
        HideTotals:       true, // Hide results until poll ends
    }

    toot := &mastodon.Toot{
        Status: "Anonymous poll: Do you agree? (Results hidden until poll ends)",
        Poll:   poll,
    }

    return client.PostStatus(ctx, toot)
}

Example: Poll Monitoring

func monitorPoll(client *mastodon.Client, pollID mastodon.ID, interval time.Duration) {
    ctx := context.Background()
    ticker := time.NewTicker(interval)
    defer ticker.Stop()

    var lastVoteCount int64

    for range ticker.C {
        poll, err := client.GetPoll(ctx, pollID)
        if err != nil {
            log.Printf("Error fetching poll: %v", err)
            continue
        }

        if poll.Expired {
            fmt.Println("Poll has ended!")
            displayPollResults(poll)
            break
        }

        if poll.VotesCount != lastVoteCount {
            fmt.Printf("New votes! Total: %d (from %d voters)\n",
                poll.VotesCount, poll.VotersCount)
            lastVoteCount = poll.VotesCount
        }

        remaining := time.Until(poll.ExpiresAt)
        fmt.Printf("Time remaining: %s\n", remaining.Round(time.Minute))
    }
}

func displayPollResults(poll *mastodon.Poll) {
    fmt.Println("\nFinal Results:")
    fmt.Printf("Total votes: %d from %d voters\n\n", poll.VotesCount, poll.VotersCount)

    // Find max votes for bar chart
    maxVotes := int64(0)
    for _, option := range poll.Options {
        if option.VotesCount > maxVotes {
            maxVotes = option.VotesCount
        }
    }

    for i, option := range poll.Options {
        percentage := 0.0
        if poll.VotesCount > 0 {
            percentage = float64(option.VotesCount) / float64(poll.VotesCount) * 100
        }

        // Simple bar chart
        barLength := 20
        if maxVotes > 0 {
            barLength = int(float64(option.VotesCount) / float64(maxVotes) * 20)
        }
        bar := strings.Repeat("█", barLength)

        fmt.Printf("%d. %-20s %s %.1f%% (%d votes)\n",
            i+1, option.Title, bar, percentage, option.VotesCount)
    }
}

Example: Vote Based on Criteria

func autoVote(client *mastodon.Client, pollID mastodon.ID, preferredOption string) error {
    ctx := context.Background()

    poll, err := client.GetPoll(ctx, pollID)
    if err != nil {
        return err
    }

    if poll.Expired {
        return fmt.Errorf("poll has expired")
    }

    if poll.Voted {
        return fmt.Errorf("already voted on this poll")
    }

    // Find matching option
    for i, option := range poll.Options {
        if strings.EqualFold(option.Title, preferredOption) {
            _, err := client.PollVote(ctx, pollID, i)
            if err != nil {
                return err
            }
            fmt.Printf("Voted for: %s\n", option.Title)
            return nil
        }
    }

    return fmt.Errorf("option not found: %s", preferredOption)
}

Example: Poll Statistics

type PollStats struct {
    ID              mastodon.ID
    Question        string
    TotalVotes      int64
    TotalVoters     int64
    Participation   float64 // VotersCount / FollowerCount
    LeadingOption   string
    LeadingVotes    int64
    LeadingPercent  float64
    TimeRemaining   time.Duration
}

func getPollStats(client *mastodon.Client, statusID mastodon.ID) (*PollStats, error) {
    ctx := context.Background()

    status, err := client.GetStatus(ctx, statusID)
    if err != nil {
        return nil, err
    }

    if status.Poll == nil {
        return nil, fmt.Errorf("status has no poll")
    }

    poll := status.Poll
    stats := &PollStats{
        ID:          poll.ID,
        Question:    status.Content,
        TotalVotes:  poll.VotesCount,
        TotalVoters: poll.VotersCount,
    }

    // Find leading option
    var maxVotes int64
    var leadingIdx int
    for i, option := range poll.Options {
        if option.VotesCount > maxVotes {
            maxVotes = option.VotesCount
            leadingIdx = i
        }
    }

    if len(poll.Options) > 0 {
        stats.LeadingOption = poll.Options[leadingIdx].Title
        stats.LeadingVotes = maxVotes
        if poll.VotesCount > 0 {
            stats.LeadingPercent = float64(maxVotes) / float64(poll.VotesCount) * 100
        }
    }

    if !poll.Expired {
        stats.TimeRemaining = time.Until(poll.ExpiresAt)
    }

    return stats, nil
}

func displayStats(stats *PollStats) {
    fmt.Printf("Poll Statistics\n")
    fmt.Printf("===============\n")
    fmt.Printf("Question: %s\n", stats.Question)
    fmt.Printf("Total Votes: %d\n", stats.TotalVotes)
    fmt.Printf("Total Voters: %d\n", stats.TotalVoters)
    fmt.Printf("Leading Option: %s (%.1f%%)\n",
        stats.LeadingOption, stats.LeadingPercent)

    if stats.TimeRemaining > 0 {
        fmt.Printf("Time Remaining: %s\n", stats.TimeRemaining.Round(time.Minute))
    } else {
        fmt.Println("Status: Ended")
    }
}

Example: Poll Comparison

func comparePolls(client *mastodon.Client, pollID1, pollID2 mastodon.ID) error {
    ctx := context.Background()

    poll1, err := client.GetPoll(ctx, pollID1)
    if err != nil {
        return err
    }

    poll2, err := client.GetPoll(ctx, pollID2)
    if err != nil {
        return err
    }

    fmt.Println("Poll Comparison")
    fmt.Println("===============")

    fmt.Printf("\nPoll 1: %d votes from %d voters\n", poll1.VotesCount, poll1.VotersCount)
    fmt.Printf("Poll 2: %d votes from %d voters\n", poll2.VotesCount, poll2.VotersCount)

    fmt.Printf("\nEngagement Rate:\n")
    if poll1.VotesCount > 0 {
        fmt.Printf("Poll 1: %.2f votes per voter\n",
            float64(poll1.VotesCount)/float64(poll1.VotersCount))
    }
    if poll2.VotesCount > 0 {
        fmt.Printf("Poll 2: %.2f votes per voter\n",
            float64(poll2.VotesCount)/float64(poll2.VotersCount))
    }

    return nil
}

Poll Duration Guidelines

Common poll durations:

const (
    FiveMinutes  = 5 * 60
    ThirtyMinutes = 30 * 60
    OneHour      = 3600
    SixHours     = 6 * 3600
    TwelveHours  = 12 * 3600
    OneDay       = 24 * 3600
    ThreeDays    = 3 * 24 * 3600
    OneWeek      = 7 * 24 * 3600
)

// Quick poll
poll := &mastodon.TootPoll{
    ExpiresInSeconds: OneHour,
}

// Standard poll
poll := &mastodon.TootPoll{
    ExpiresInSeconds: OneDay,
}

// Extended poll
poll := &mastodon.TootPoll{
    ExpiresInSeconds: OneWeek,
}

Best Practices

1. Choose Appropriate Duration

Match duration to audience activity:

// Quick question for active audience
ExpiresInSeconds: 3600 // 1 hour

// Standard community poll
ExpiresInSeconds: 86400 // 24 hours

// Important decision requiring broad input
ExpiresInSeconds: 7 * 24 * 3600 // 1 week

2. Provide Clear Options

Use clear, concise option text:

// Good
Options: []string{"Yes", "No", "Unsure"}

// Less clear
Options: []string{"Yup", "Nah", "IDK"}

3. Limit Option Count

Keep options manageable (2-4 options is typical):

// Good: 3-4 options
Options: []string{"Go", "Rust", "Python", "Other"}

// Overwhelming: too many options
// (API limit is 4 options)

4. Use Multiple Choice Appropriately

Enable multiple choice when appropriate:

// Single choice: "Which is your favorite?"
Multiple: false

// Multiple choice: "Which do you use?"
Multiple: true

5. Consider Hiding Totals

Hide totals to prevent vote influencing:

// Anonymous voting
poll := &mastodon.TootPoll{
    HideTotals: true, // Results hidden until end
}

6. Check Before Voting

Verify poll hasn't expired:

poll, err := client.GetPoll(ctx, pollID)
if err != nil {
    return err
}

if poll.Expired {
    return fmt.Errorf("poll has expired")
}

if poll.Voted {
    return fmt.Errorf("already voted")
}

// Now vote
client.PollVote(ctx, pollID, choiceIndex)

Related Types

See also:

  • Statuses - For creating polls in statuses
  • Types - For ID and other common types