twitter

package module
v0.6.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 23, 2026 License: MIT Imports: 29 Imported by: 0

README

go-twitter

Twitter/X scraping library for Go — multi-account pool, GraphQL API, anti-ban, CAPTCHA solving, TOTP 2FA, and session persistence.

Built on go-stealth for TLS fingerprinting, proxy rotation, and rate limiting.

Features

  • Account Pool — round-robin rotation with per-account health tracking and rate limits
  • GraphQL API — users, tweets, followers, following, retweeters, search, post
  • Anti-Ban — TLS fingerprinting, header ordering, client hints, x-client-transaction-id (xtid)
  • Auth — multi-step login flow with password, TOTP 2FA, CAPTCHA (Capsolver)
  • Error Recovery — CSRF rotation, session refresh, ban cooldown, guest token fallback
  • Session Persistence — JSON file cache with TTL
  • Proxy Support — per-account proxy, automatic backoff on failures

Install

go get github.com/anatolykoptev/go-twitter

Quick Start

cfg := &twitter.ClientConfig{
    Accounts: []*twitter.Account{
        {Username: "user1", Password: "pass1", TOTPSecret: "BASE32SECRET"},
        {Username: "user2", AuthToken: "existing_token"},
    },
    DefaultProxy:  "http://proxy:8080",
    CaptchaSolver: captcha.NewCapsolver(os.Getenv("CAPSOLVER_KEY")),
}

client, err := twitter.NewClient(cfg)

// Read
user, _ := client.GetUserByScreenName(ctx, "elonmusk")
tweets, _ := client.GetUserTweets(ctx, user.ID, 50)
followers, _ := client.GetFollowers(ctx, user.ID, 100)

// Search
results, _ := client.SearchTimeline(ctx, "$BTC", 20)

// Write (account-specific)
tweetID, _ := client.PostWithAccount(ctx, "user1", "Hello Twitter!")

API

Method Auth Description
GetUserByScreenName Guest/Auth Get user profile
GetUserTweets Guest/Auth Get user's tweets
GetFollowers Auth Paginated follower list
GetFollowing Auth Paginated following list
GetRetweeters Auth Users who retweeted
SearchTimeline Auth Search tweets
CreateTweet Auth Post a tweet
PostWithAccount Auth Post from specific account

Error Handling

Automatic recovery per error class:

Error Code Action
CSRF mismatch 353 Rotate CT0 token, retry
Auth expired 32 Re-login
Rate limited 429 Back off, mark endpoint
Banned 88 Soft-deactivate 6h
Suspended 64 Permanent deactivation
Locked 326 CAPTCHA unlock attempt

Anti-Detection

  • xtid — x-client-transaction-id generated from page animation keys (auto-refresh 30min)
  • CT0 — CSRF token proactively rotated every 4h
  • TLS — browser-grade JA3 fingerprints via go-stealth
  • Headers — exact Chrome/Firefox/Safari header ordering
  • Jitter — randomized delays between requests

Types

type TwitterUser struct {
    ID, Handle, DisplayName, Bio string
    Followers, Following, TweetCount int
    IsVerified bool
}

type Tweet struct {
    ID, AuthorID, Text string
    CreatedAt          time.Time
    Views, Likes, Retweets, Quotes int
    TokenMentions      []string // extracted $TICKER mentions
}

CI

gql-sync drift check (.github/workflows/gql-sync-drift.yml) — runs weekly (Mon 06:00 UTC, plus manual workflow_dispatch). It regenerates the generated GraphQL artifacts (queryids_gen.go, features_gen.go) from x.com's live JS bundle and, if they changed, opens a PR for human review (it never auto-merges generated values — a wrong queryID/flag default is an HTTP 400 on every call).

Requires repo secret TWITTER_PROXY: a proxy URL that can reach x.com from GitHub's runner IPs (bare datacenter IPs are blocked). The job runs with -fail-on-empty, so a missing proxy or a bundle break exits non-zero and fails closed instead of overwriting the committed IDs with nothing.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var BearerToken = bearerTokens[0]

BearerToken is the active bearer token (first in list).

View Source
var Endpoints = map[string]Endpoint{
	"UserByScreenName": {ID: "IGgvgiOx4QZndDHuD3x9TQ", Name: "UserByScreenName", Features: gqlFeatures()},
	"UserByRestId":     {ID: "VQfQ9wwYdk6j_u2O4vt64Q", Name: "UserByRestId", Features: gqlFeatures()},
	"Followers":        {ID: "9jsVJ9l2uXUIKslHvJqIhw", Name: "Followers", Features: followersFeatures()},
	"Following":        {ID: "OLm4oHZBfqWx8jbcEhWoFw", Name: "Following", Features: followersFeatures()},
	"UserTweets":       {ID: "FOlovQsiHGDls3c0Q_HaSQ", Name: "UserTweets", Features: gqlFeatures()},
	"SearchTimeline":   {ID: "GcXk9vN_d1jUfHNqLacXQA", Name: "SearchTimeline", Features: gqlFeatures()},
	"TweetDetail":      {ID: "VWFGPVAGkZMGRKGe3GFFnA", Name: "TweetDetail", Features: gqlFeatures()},
	"Retweeters":       {ID: "FeoLYPQ-q4bmjGLTZTGs0g", Name: "Retweeters", Features: gqlFeatures()},
	"CreateTweet":      {ID: "7TKRKCPuAGsmYde0CudbVg", Name: "CreateTweet", Features: gqlFeatures()},

	"Bookmarks": {ID: "i8QZ1qqy36ffA3bxfTaf7w", Name: "Bookmarks", Features: gqlFeatures()},

	"HomeTimeline":       {ID: "HCosKfLNW1AcOo3la3mMgg", Name: "HomeTimeline", Features: gqlFeatures()},
	"HomeLatestTimeline": {ID: "zhX91JE87mWvfprhYE97xA", Name: "HomeLatestTimeline", Features: gqlFeatures()},

	"ListLatestTweetsTimeline": {ID: "27HKUy8ulrflZ9Tole038g", Name: "ListLatestTweetsTimeline", Features: gqlFeatures()},
	"CommunityTweetsTimeline":  {ID: "Mvs5UOOEkpXVMDZtUcxR-Q", Name: "CommunityTweetsTimeline", Features: gqlFeatures()},
	"BlueVerifiedFollowers":    {ID: "OBBd6Dw-4qEYbsu3hGkyxg", Name: "BlueVerifiedFollowers", Features: gqlFeatures()},

	"FavoriteTweet":   {ID: "lI07N6Otwv1PhnEgXILM7A", Name: "FavoriteTweet", Features: gqlFeatures()},
	"UnfavoriteTweet": {ID: "ZYKSe-w7KEslx3JhSIk5LA", Name: "UnfavoriteTweet", Features: gqlFeatures()},
	"CreateRetweet":   {ID: "mbRO74GrOvSfRcJnlMapnQ", Name: "CreateRetweet", Features: gqlFeatures()},
	"DeleteRetweet":   {ID: "ZyZigVsNiFO6v1dEks1eWg", Name: "DeleteRetweet", Features: gqlFeatures()},
}

Endpoints maps operation names to their current GraphQL IDs and feature flags.

Functions

func ApplyEnvOverrides

func ApplyEnvOverrides()

ApplyEnvOverrides reads TWITTER_QID_* env vars and overrides queryIds in Endpoints. Called automatically by init(); can also be called manually in tests.

func AssignBrowserProfile

func AssignBrowserProfile(acc *Account, idx int)

AssignBrowserProfile sets a browser profile based on index.

func EndpointURL

func EndpointURL(operation string) (string, error)

EndpointURL returns the URL for a named operation, or an error if unknown.

func GenerateCT0

func GenerateCT0() string

GenerateCT0 generates a random 32-byte hex string for use as a ct0 CSRF token.

Types

type Account

type Account struct {
	Username   string
	Password   string
	AuthToken  string
	CT0        string
	TOTPSecret string
	Proxy      string
	UserAgent  string
	Profile    stealth.BrowserProfile

	pool.HealthTracker
	// contains filtered or unexported fields
}

Account represents a Twitter account with credentials for the pool.

func ParseAccounts

func ParseAccounts(raw string) []*Account

ParseAccounts parses a comma-separated list of accounts. Format: "user1:pass1,user2:pass2" or "user1:pass1:auth_token:ct0,..." or "user1:pass1:auth_token:ct0:totp_secret,...".

func (*Account) AllowRequest

func (a *Account) AllowRequest(endpoint string) bool

AllowRequest checks if this account can make a request to the given endpoint.

func (*Account) CT0Age

func (a *Account) CT0Age() time.Duration

CT0Age returns the time since the ct0 token was last refreshed.

func (*Account) Credentials

func (a *Account) Credentials() (authToken, ct0, userAgent string)

Credentials returns a snapshot of (authToken, ct0, userAgent) under lock.

func (*Account) EndpointAvailableAt

func (a *Account) EndpointAvailableAt(endpoint string) time.Time

EndpointAvailableAt returns when this account will be available for the given endpoint.

func (*Account) ID

func (a *Account) ID() string

ID implements pool.Identity.

func (*Account) IsActive

func (a *Account) IsActive() bool

IsActive implements pool.Identity.

func (*Account) IsEndpointRateLimited

func (a *Account) IsEndpointRateLimited(endpoint string) bool

IsEndpointRateLimited returns true if the endpoint is currently blocked.

func (*Account) MarkEndpointRateLimited

func (a *Account) MarkEndpointRateLimited(endpoint string, until time.Time)

MarkEndpointRateLimited marks an endpoint as rate-limited for this account.

func (*Account) ReactivateAt

func (a *Account) ReactivateAt() time.Time

ReactivateAt implements pool.Identity.

func (*Account) RotateCT0

func (a *Account) RotateCT0()

RotateCT0 generates a fresh ct0 token and updates the refresh timestamp.

func (*Account) SetActive

func (a *Account) SetActive(v bool)

SetActive implements pool.Identity.

func (*Account) SetCT0

func (a *Account) SetCT0(ct0 string)

SetCT0 updates the ct0 from a server response.

func (*Account) SetCredentials

func (a *Account) SetCredentials(authToken, ct0 string)

SetCredentials atomically updates auth_token and ct0.

func (*Account) SetReactivateAt

func (a *Account) SetReactivateAt(t time.Time)

SetReactivateAt implements pool.Identity.

func (*Account) SyncRateLimit added in v0.6.6

func (a *Account) SyncRateLimit(endpoint string, respHdrs map[string]string)

SyncRateLimit reads the x-rate-limit-limit header from a response and updates this account's per-endpoint cap when the header carries a valid positive value. A no-op when the header is absent or malformed (the prior cap stands). Called on every observed response (200 and 429) so the cap tracks X's real, possibly-shifting per-account budget.

func (*Account) UpdateEndpointLimit added in v0.6.6

func (a *Account) UpdateEndpointLimit(endpoint string, limit int)

UpdateEndpointLimit sets this account's per-window request cap for the given endpoint, overriding the static default. Driven by the x-rate-limit-limit response header so the limiter self-tunes to X's real per-account-per-endpoint budget. Non-positive limits are ignored by the underlying limiter.

type AccountHealth

type AccountHealth struct {
	Username    string
	Active      bool
	Total       int
	Failed      int
	ConsecFails int
}

AccountHealth describes the health state of a single pool account.

type AutoReloginGate

type AutoReloginGate interface {
	Allowed(ctx context.Context, username string) (allowed bool, reason string)
}

AutoReloginGate lets an external controller veto automatic re-login flows. A nil gate is treated as "always allow" — preserves backward compatibility for consumers that don't use go-social.

type AutoReloginGateFunc added in v0.6.7

type AutoReloginGateFunc func(ctx context.Context, username string) (allowed bool, reason string)

AutoReloginGateFunc adapts a plain function to the AutoReloginGate interface.

OPT-OUT: as of v0.6.7 a client that never calls SetAutoReloginGate gets the internal per-account relogin breaker by default (see wireDefaultReloginGate), which stops auto-relogin for an account after reloginBreakerThreshold consecutive failures for reloginBreakerWindow. A consumer that wants the pre-v0.6.7 "always relogin, unlimited" behavior must install an always-allow gate explicitly:

c.SetAutoReloginGate(AutoReloginGateFunc(
	func(context.Context, string) (bool, string) { return true, "" }))

func (AutoReloginGateFunc) Allowed added in v0.6.7

func (f AutoReloginGateFunc) Allowed(ctx context.Context, username string) (bool, string)

Allowed implements AutoReloginGate.

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client is the top-level Twitter scraping client.

func NewClient

func NewClient(cfg ClientConfig) (*Client, error)

NewClient creates a fully-wired Twitter client.

func (*Client) AccountByUsername

func (c *Client) AccountByUsername(username string) *Account

AccountByUsername returns the pool account matching the given username (case-insensitive). Returns nil if not found.

func (*Client) CreateTweet

func (c *Client) CreateTweet(ctx context.Context, acc *Account, text string) (string, error)

CreateTweet posts a tweet from a specific account. Returns the tweet ID on success.

func (*Client) CreateTweetWithMedia added in v0.6.0

func (c *Client) CreateTweetWithMedia(ctx context.Context, acc *Account, text string, mediaIDs []string) (string, error)

CreateTweetWithMedia posts a tweet with attached media IDs from a specific account. With no media IDs it is equivalent to CreateTweet. Returns the tweet ID on success.

func (*Client) GetBookmarks added in v0.6.0

func (c *Client) GetBookmarks(ctx context.Context, maxCount int) ([]*Tweet, error)

GetBookmarks fetches the authenticated pool account's bookmarked tweets (paginated up to maxCount).

func (*Client) GetCommunityTweets added in v0.6.0

func (c *Client) GetCommunityTweets(ctx context.Context, communityID string, maxCount int) ([]*Tweet, error)

GetCommunityTweets fetches the latest tweets in a community (paginated up to maxCount).

func (*Client) GetDMInbox added in v0.6.0

func (c *Client) GetDMInbox(ctx context.Context) ([]*DMConversation, error)

GetDMInbox fetches the authenticated pool account's DM inbox. The inbox is per-account state, so doGET pins a real account from the pool (DMInbox is in requiresAuth — no guest fallback). Returns the conversations, each with its participants and the messages present in the initial inbox state. An empty inbox (0 conversations) is a valid, non-error result.

func (*Client) GetFollowers

func (c *Client) GetFollowers(ctx context.Context, userID string, maxCount int) ([]*TwitterUser, error)

GetFollowers fetches followers for a user (paginated).

func (*Client) GetFollowing

func (c *Client) GetFollowing(ctx context.Context, userID string, maxCount int) ([]*TwitterUser, error)

GetFollowing fetches accounts a user follows (paginated).

func (*Client) GetHomeLatestTimeline added in v0.6.0

func (c *Client) GetHomeLatestTimeline(ctx context.Context, count int) ([]*Tweet, error)

GetHomeLatestTimeline fetches the reverse-chronological "Following" home timeline. POST (pool), mirroring SearchTimeline.

func (*Client) GetHomeTimeline added in v0.6.0

func (c *Client) GetHomeTimeline(ctx context.Context, count int) ([]*Tweet, error)

GetHomeTimeline fetches the algorithmic "For you" home timeline. POST (pool), mirroring SearchTimeline.

func (*Client) GetListTweets added in v0.6.0

func (c *Client) GetListTweets(ctx context.Context, listID string, maxCount int) ([]*Tweet, error)

GetListTweets fetches the latest tweets in a list (paginated up to maxCount).

func (*Client) GetRetweeters

func (c *Client) GetRetweeters(ctx context.Context, tweetID string, maxCount int) ([]*TwitterUser, error)

GetRetweeters fetches users who retweeted a tweet (paginated).

func (*Client) GetTweetByID

func (c *Client) GetTweetByID(ctx context.Context, tweetID string) (*Tweet, error)

GetTweetByID fetches a single tweet by its ID.

func (*Client) GetUserByScreenName

func (c *Client) GetUserByScreenName(ctx context.Context, handle string) (*TwitterUser, error)

GetUserByScreenName fetches a user profile by Twitter handle.

func (*Client) GetUserTweets

func (c *Client) GetUserTweets(ctx context.Context, userID string, count int) ([]*Tweet, error)

GetUserTweets fetches recent tweets for a user.

func (*Client) GetVerifiedFollowers added in v0.6.0

func (c *Client) GetVerifiedFollowers(ctx context.Context, userID string, maxCount int) ([]*TwitterUser, error)

GetVerifiedFollowers fetches a user's blue-verified followers (paginated up to maxCount). The response shares the followers shape (data.user.result.timeline.timeline), so it reuses the existing fetchUserList helper + parseUserList parser.

func (*Client) HealthReport

func (c *Client) HealthReport() []AccountHealth

HealthReport returns health stats for all accounts in the pool.

func (*Client) LikeTweet added in v0.6.0

func (c *Client) LikeTweet(ctx context.Context, acc *Account, tweetID string) error

LikeTweet favorites a tweet from a specific account. Returns nil on the "Done" ack, else an error.

func (*Client) Pool

func (c *Client) Pool() *pool.Pool[*Account]

Pool returns the underlying account pool.

func (*Client) PostWithAccount

func (c *Client) PostWithAccount(ctx context.Context, username, text string) (string, error)

PostWithAccount posts a tweet from a named account (by username). Returns the tweet ID on success.

func (*Client) PostWithMedia added in v0.6.0

func (c *Client) PostWithMedia(ctx context.Context, username, text string, paths ...string) (string, error)

PostWithMedia uploads the given files and posts a tweet attaching them, from a named account (by username). Returns the tweet ID on success.

func (*Client) ReplyTweet added in v0.6.0

func (c *Client) ReplyTweet(ctx context.Context, acc *Account, tweetID, text string) (string, error)

ReplyTweet posts a reply to tweetID from a specific account. Returns the reply tweet ID. It routes through the CreateTweet op (reply.in_reply_to_tweet_id set) and reuses parseCreateTweet for the result.

func (*Client) Retweet added in v0.6.0

func (c *Client) Retweet(ctx context.Context, acc *Account, tweetID string) (string, error)

Retweet retweets a tweet from a specific account. Returns the new retweet ID.

func (*Client) SearchTimeline

func (c *Client) SearchTimeline(ctx context.Context, query string, count int) ([]*Tweet, error)

SearchTimeline searches for tweets matching a query. Uses POST (Twitter migrated this endpoint from GET in March 2026).

func (*Client) SendDM added in v0.6.0

func (c *Client) SendDM(ctx context.Context, acc *Account, conversationID, text string) (string, error)

SendDM sends a text direct message into an existing conversation from a specific account (account-pinned write, mirroring CreateTweet/engagement mutations). Returns the created message id.

FAIL-CLOSED: a 200 response with no usable message id (empty body, a DM validation failure, or a surfaced API error) returns an error — never a silent "sent". Mirrors parseCreateTweet's empty-result guard. conversationID is a CONVERSATION id (1:1 form "<recipientID>-<selfID>", per twikit), NOT a user id; there is no derive helper — the caller forms it.

func (*Client) SetAutoReloginGate

func (c *Client) SetAutoReloginGate(g AutoReloginGate)

SetAutoReloginGate registers a gate checked before relogin attempts.

func (*Client) UnlikeTweet added in v0.6.0

func (c *Client) UnlikeTweet(ctx context.Context, acc *Account, tweetID string) error

UnlikeTweet un-favorites a tweet from a specific account. Returns nil on the "Done" ack, else an error.

func (*Client) Unretweet added in v0.6.0

func (c *Client) Unretweet(ctx context.Context, acc *Account, tweetID string) (string, error)

Unretweet removes a retweet from a specific account. Returns the source tweet ID. The DeleteRetweet op keys the target by "source_tweet_id" (the ID of the ORIGINAL tweet, not the retweet's own ID) — confirmed live 2026-06-20: sending "tweet_id" yields HTTP 422 GRAPHQL_VALIDATION_FAILED ("source_tweet_id must be defined"). Matches trevorhobenshield/twitter-api-client + dimdenGD/OldTweetDeck.

func (*Client) UploadMedia added in v0.6.0

func (c *Client) UploadMedia(ctx context.Context, acc *Account, data []byte, mediaType string) (string, error)

UploadMedia uploads raw media bytes via the chunked INIT/APPEND/FINALIZE flow using the given account's authenticated stealth client, and returns the media_id string for use in CreateTweetWithMedia. mediaType is the MIME type (e.g. "image/jpeg", "image/gif", "video/mp4").

func (*Client) UploadMediaFile added in v0.6.0

func (c *Client) UploadMediaFile(ctx context.Context, acc *Account, path string) (string, error)

UploadMediaFile reads a file from disk, infers its MIME type, and uploads it.

func (*Client) ValidateAccount

func (c *Client) ValidateAccount(ctx context.Context, acc *Account) error

ValidateAccount checks that an account's auth_token and ct0 are still valid by calling the authenticated account/settings.json REST endpoint. Returns nil on HTTP 200, an error on 401/403, or a wrapped error on network failure.

type ClientConfig

type ClientConfig struct {
	// Accounts is the list of Twitter accounts to use.
	Accounts []*Account

	// DefaultProxy is the proxy URL for accounts without per-account proxies.
	DefaultProxy string

	// SessionTTL controls how long saved sessions are considered valid.
	SessionTTL time.Duration

	// AuthCooldown is the soft-deactivation duration for auth errors.
	AuthCooldown time.Duration

	// BanCooldown is the soft-deactivation duration for banned/locked accounts.
	BanCooldown time.Duration

	// NonResponsiveCooldown is the BASE backoff for accounts that trip the
	// consecutive-failure threshold against a transiently-broken endpoint. The
	// effective cooldown grows per trip (x2, capped at 30m, +/-30% jitter) so a
	// flapping upstream self-heals once it recovers instead of latching the pool
	// permanently. Default: 5m.
	NonResponsiveCooldown time.Duration

	// CaptchaSolver is the optional CAPTCHA solver for locked accounts.
	CaptchaSolver captcha.Solver

	// RateLimit configures per-account per-endpoint rate limiting.
	RateLimit ratelimit.Config

	// OpenAccountCount is the number of anonymous guest accounts to create at startup.
	OpenAccountCount int

	// MetricsHook is called on each API request for external metrics collection.
	// endpoint is the operation name, success and rateLimited indicate the outcome.
	MetricsHook func(endpoint string, success, rateLimited bool)

	// SessionDir overrides the default session persistence directory.
	// Default: ~/.go-twitter/sessions
	SessionDir string

	// ProxyBackoffInitial is the initial backoff for proxy failures.
	ProxyBackoffInitial time.Duration

	// ProxyBackoffMax is the maximum backoff for proxy failures.
	ProxyBackoffMax time.Duration

	// PoolAlertHook is called when the pool emits alerts (account deactivation, proxy failures, etc.).
	// topic is the alert type (e.g. "pool.deactivated"), payload contains details.
	PoolAlertHook func(topic string, payload any)

	// DisableGuestFallback disables the guest-token fallback path entirely.
	// When true, endpoints that would normally fall back to guest mode after
	// pool exhaustion will return an error instead. Recommended in production
	// where guest tokens from datacenter IPs return persistent 403 errors.
	// Default: false (guest fallback enabled for backward compatibility).
	DisableGuestFallback bool

	// AccountPaceMin is the minimum human-pace delay between consecutive requests
	// MADE BY THE SAME ACCOUNT, applied AFTER the pool selects an account and IN
	// ADDITION TO the anti-fingerprint jitter. Pacing is keyed by account, not by
	// domain: each account self-paces its own request rhythm via an independent
	// timestamp, so a low-frequency caller (seed) is never starved by a global
	// gate that a high-frequency caller (KOL/VC) holds. It is anti-burst stealth
	// spacing UNDER the per-account-per-endpoint rate-limit ceiling, never a
	// throughput cap below it.
	//
	// Together with AccountPaceRandom the realized inter-request spacing for one
	// account is [AccountPaceMin, AccountPaceMin+AccountPaceRandom) (variable,
	// never below AccountPaceMin). Default: 800ms. Set AccountPaceDisabled to off.
	AccountPaceMin time.Duration

	// AccountPaceRandom is the random jitter added on top of AccountPaceMin for
	// the per-account spacing (see AccountPaceMin). Default: 1.2s.
	AccountPaceRandom time.Duration

	// AccountPaceDisabled turns off the per-account human pace entirely. When
	// true only the anti-fingerprint jitter applies. Default: false (pacing
	// enabled with safe defaults).
	AccountPaceDisabled bool
}

ClientConfig holds all configuration for the Twitter client.

type Cursor

type Cursor struct {
	Value  string
	IsNext bool
}

Cursor is used for paginated GraphQL requests.

type DMConversation added in v0.6.0

type DMConversation struct {
	ConversationID string
	Participants   []string
	Messages       []DMMessage
}

DMConversation is a single direct-message conversation from the 1.1 inbox. Participants holds the user IDs in the thread (the inbox shape carries participant IDs, not full user objects). Messages are the messages seen in the inbox_initial_state entries for this conversation.

type DMMessage added in v0.6.0

type DMMessage struct {
	ID             string
	ConversationID string
	SenderID       string
	Text           string
	CreatedAt      time.Time
}

DMMessage is a single direct message. Fields mirror the 1.1 inbox message_data shape (id, sender_id, text, time as unix millis).

type Endpoint

type Endpoint struct {
	ID       string
	Name     string
	Features map[string]any
}

Endpoint holds the operation ID, path template, and per-operation feature flags.

func (Endpoint) URL

func (e Endpoint) URL() string

URL returns the full URL for this endpoint. The variable ID segment is path-escaped as defense-in-depth: a poisoned queryID must never break out of its path segment. e.Name is a hardcoded const, so it is left verbatim.

type Tweet

type Tweet struct {
	ID            string
	AuthorID      string
	AuthorHandle  string // @screen_name (from core.user_results)
	AuthorName    string // display name (from core.user_results)
	Text          string
	CreatedAt     time.Time
	Views         int
	Likes         int
	Retweets      int
	Quotes        int
	ReplyCount    int
	TokenMentions []string // extracted $TICKER patterns, e.g. ["BTC", "ETH"]
}

Tweet represents a single tweet.

func SearchWithSocial

func SearchWithSocial(ctx context.Context, sc *social.Client, query string, limit int) ([]*Tweet, error)

SearchWithSocial acquires accounts from go-social and searches Twitter, retrying with different accounts on failure. go-social rotates accounts on each call, so retries naturally use different credentials.

type TwitterUser

type TwitterUser struct {
	ID          string
	Handle      string
	DisplayName string
	Bio         string
	Followers   int
	Following   int
	TweetCount  int
	ListedCount int
	CreatedAt   time.Time
	IsVerified  bool
	HasAvatar   bool
	HasBio      bool
}

TwitterUser represents a Twitter/X account profile.

Directories

Path Synopsis
cmd
gql-sync command
Command gql-sync auto-extracts Twitter GraphQL queryIDs from x.com's webpack bundles and regenerates queryids_gen.go — the "generated" layer of the endpoints override chain (env > generated > committed).
Command gql-sync auto-extracts Twitter GraphQL queryIDs from x.com's webpack bundles and regenerates queryids_gen.go — the "generated" layer of the endpoints override chain (env > generated > committed).
internal
bundle
Package bundle fetches x.com warm pages, reassembles the webpack chunk map (chunkID->name, chunkID->hash), resolves a module name to its abs.twimg.com bundle URL, BFS-follows import() refs, and caches the snapshot to disk with a TTL.
Package bundle fetches x.com warm pages, reassembles the webpack chunk map (chunkID->name, chunkID->hash), resolves a module name to its abs.twimg.com bundle URL, BFS-follows import() refs, and caches the snapshot to disk with a TTL.
Package slogsample provides sampling utilities for slog handlers.
Package slogsample provides sampling utilities for slog handlers.
Package social provides a thin HTTP client for the go-social account management API.
Package social provides a thin HTTP client for the go-social account management API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL