Documentation
¶
Index ¶
- Variables
- func ApplyEnvOverrides()
- func AssignBrowserProfile(acc *Account, idx int)
- func EndpointURL(operation string) (string, error)
- func GenerateCT0() string
- func GetTweetConversationWithSocial(ctx context.Context, sc *social.Client, tweetID string) (*Tweet, []*Tweet, error)
- func IsAuthExpired(err error) bool
- func IsBanned(err error) bool
- func IsBlocked(err error) bool
- func IsCSRF(err error) bool
- func IsEndpointFault(err error) bool
- func IsForbidden(err error) bool
- func IsInternal(err error) bool
- func IsLocked(err error) bool
- func IsNotAuthorized(err error) bool
- func IsRateLimited(err error) bool
- func IsSuspended(err error) bool
- func SetSocialMeterProvider(mp metric.MeterProvider)
- func SnowflakeTime(id string) (time.Time, error)
- type APIError
- type Account
- func (a *Account) AllowRequest(endpoint string) bool
- func (a *Account) CT0Age() time.Duration
- func (a *Account) Credentials() (authToken, ct0, userAgent string)
- func (a *Account) EndpointAvailableAt(endpoint string) time.Time
- func (a *Account) ID() string
- func (a *Account) InFlight() int
- func (a *Account) IsActive() bool
- func (a *Account) IsEndpointRateLimited(endpoint string) bool
- func (a *Account) MarkEndpointRateLimited(endpoint string, until time.Time)
- func (a *Account) ReactivateAt() time.Time
- func (a *Account) RecordSuccess()
- func (a *Account) RotateCT0()
- func (a *Account) SetActive(v bool)
- func (a *Account) SetCT0(ct0 string)
- func (a *Account) SetCredentials(authToken, ct0 string)
- func (a *Account) SetReactivateAt(t time.Time)
- func (a *Account) SyncRateLimit(endpoint string, respHdrs map[string]string)
- func (a *Account) UpdateEndpointLimit(endpoint string, limit int)
- type AccountHealth
- type AutoReloginGate
- type AutoReloginGateFunc
- type Client
- func (c *Client) AccountByUsername(username string) *Account
- func (c *Client) Close() error
- func (c *Client) CreateTweet(ctx context.Context, acc *Account, text string) (string, error)
- func (c *Client) CreateTweetWithMedia(ctx context.Context, acc *Account, text string, mediaIDs []string) (string, error)
- func (c *Client) GetBookmarks(ctx context.Context, maxCount int) ([]*Tweet, error)
- func (c *Client) GetCommunityTweets(ctx context.Context, communityID string, maxCount int) ([]*Tweet, error)
- func (c *Client) GetDMInbox(ctx context.Context) ([]*DMConversation, error)
- func (c *Client) GetFollowers(ctx context.Context, userID string, maxCount int) ([]*TwitterUser, error)
- func (c *Client) GetFollowing(ctx context.Context, userID string, maxCount int) ([]*TwitterUser, error)
- func (c *Client) GetHomeLatestTimeline(ctx context.Context, count int) ([]*Tweet, error)
- func (c *Client) GetHomeTimeline(ctx context.Context, count int) ([]*Tweet, error)
- func (c *Client) GetListTweets(ctx context.Context, listID string, maxCount int) ([]*Tweet, error)
- func (c *Client) GetRetweeters(ctx context.Context, tweetID string, maxCount int) ([]*TwitterUser, error)
- func (c *Client) GetTweetByID(ctx context.Context, tweetID string) (*Tweet, error)
- func (c *Client) GetTweetConversation(ctx context.Context, tweetID string) (*Tweet, []*Tweet, error)
- func (c *Client) GetUserByScreenName(ctx context.Context, handle string) (*TwitterUser, error)
- func (c *Client) GetUserTweets(ctx context.Context, userID string, count int) ([]*Tweet, error)
- func (c *Client) GetVerifiedFollowers(ctx context.Context, userID string, maxCount int) ([]*TwitterUser, error)
- func (c *Client) HealthReport() []AccountHealth
- func (c *Client) LikeTweet(ctx context.Context, acc *Account, tweetID string) error
- func (c *Client) Pool() *pool.Pool[*Account]
- func (c *Client) PostWithAccount(ctx context.Context, username, text string) (string, error)
- func (c *Client) PostWithMedia(ctx context.Context, username, text string, paths ...string) (string, error)
- func (c *Client) ReplyTweet(ctx context.Context, acc *Account, tweetID, text string) (string, error)
- func (c *Client) Retweet(ctx context.Context, acc *Account, tweetID string) (string, error)
- func (c *Client) SearchTimeline(ctx context.Context, query string, count int) ([]*Tweet, error)
- func (c *Client) SearchTweets(ctx context.Context, query string, product SearchProduct, count int) ([]*Tweet, error)
- func (c *Client) SearchUsers(ctx context.Context, query string, count int) ([]*TwitterUser, error)
- func (c *Client) SendDM(ctx context.Context, acc *Account, conversationID, text string) (string, error)
- func (c *Client) SetAutoReloginGate(g AutoReloginGate)
- func (c *Client) UnlikeTweet(ctx context.Context, acc *Account, tweetID string) error
- func (c *Client) Unretweet(ctx context.Context, acc *Account, tweetID string) (string, error)
- func (c *Client) UploadMedia(ctx context.Context, acc *Account, data []byte, mediaType string) (string, error)
- func (c *Client) UploadMediaFile(ctx context.Context, acc *Account, path string) (string, error)
- func (c *Client) ValidateAccount(ctx context.Context, acc *Account) error
- type ClientConfig
- type Cursor
- type DMConversation
- type DMMessage
- type Endpoint
- type FileSessionStore
- type SearchProduct
- type Session
- type SessionStore
- type Tweet
- func GetTweetByIDWithSocial(ctx context.Context, sc *social.Client, tweetID string) (*Tweet, error)
- func GetUserTweetsByIDWithSocial(ctx context.Context, sc *social.Client, userID string, limit int) ([]*Tweet, error)
- func GetUserTweetsWithSocial(ctx context.Context, sc *social.Client, handle string, limit int) ([]*Tweet, error)
- func SearchTweetsWithSocial(ctx context.Context, sc *social.Client, query string, product SearchProduct, ...) ([]*Tweet, error)
- func SearchWithSocial(ctx context.Context, sc *social.Client, query string, limit int) ([]*Tweet, error)
- type TwitterUser
- func GetFollowersWithSocial(ctx context.Context, sc *social.Client, userID string, limit int) ([]*TwitterUser, error)
- func GetFollowingWithSocial(ctx context.Context, sc *social.Client, userID string, limit int) ([]*TwitterUser, error)
- func GetRetweetersWithSocial(ctx context.Context, sc *social.Client, tweetID string, limit int) ([]*TwitterUser, error)
- func GetUserWithSocial(ctx context.Context, sc *social.Client, handle string) (*TwitterUser, error)
- func SearchUsersWithSocial(ctx context.Context, sc *social.Client, query string, limit int) ([]*TwitterUser, error)
Constants ¶
This section is empty.
Variables ¶
var BearerToken = bearerTokens[0]
BearerToken is the active bearer token (first in list).
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.
var ErrBadRequest = errors.New("twitter: bad request (input rejected)")
ErrBadRequest marks deterministic CALLER-side input errors: a 400 whose error entries carry X's input-validation code (214 — a bad variable, bad cursor, malformed request field). Rotating to another account reproduces the identical response, so the class is never retried, never charged to the serving account, and never counted as an endpoint fault. Match with errors.Is.
var ErrEndpoint = errors.New("twitter: endpoint fault")
ErrEndpoint marks endpoint-wide faults (HTTP 404, or 400 carrying a GraphQL-shaped errors body that is NOT a recognized caller-input rejection): the committed request shape is wrong for every account (stale queryId, missing xtid, feature mismatch), so the serving account must not be charged — reads rotate to the next account. Match with errors.Is.
var ErrNotFound = errors.New("twitter: resource not found")
ErrNotFound marks deterministic "the resource does not exist" outcomes — deleted tweets, suspended/unavailable handles. Pool callers treat it as a healthy-account response (no retry, no auth_error report): the credential worked; the thing asked for simply isn't there.
var ErrOverloaded = errors.New("twitter: x overloaded")
ErrOverloaded marks X-side overload (HTTP 5xx, or a LoadShed error entry — code -1 / message containing "LoadShed"): retryable on another account with backoff, never charged to the serving account. Match with errors.Is.
ErrProxyClientUnavailable is returned when an account is pinned to its own proxy (Account.Proxy != "") but its per-account transport could not be built. The shared client may be direct, so falling back to it would send the account's cookies from this host's IP, ignoring the proxy the caller asked for: the request fails closed instead.
var ErrSessionNotFound = errors.New("twitter: session not found")
ErrSessionNotFound is returned by SessionStore.Load when no session is stored for the username (or a store-side policy such as a TTL has expired it).
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 ¶
AssignBrowserProfile sets a browser profile based on index.
func EndpointURL ¶
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.
func GetTweetConversationWithSocial ¶ added in v0.6.11
func GetTweetConversationWithSocial(ctx context.Context, sc *social.Client, tweetID string) (*Tweet, []*Tweet, error)
GetTweetConversationWithSocial fetches the focal tweet and its conversation entries (replies/ancestors in page order).
func IsAuthExpired ¶ added in v0.6.9
IsAuthExpired reports an expired-authentication condition (code 32).
func IsBanned ¶ added in v0.6.9
IsBanned reports an account-banned condition (code 88 while the rate-limit window still has requests remaining).
func IsEndpointFault ¶ added in v0.6.19
IsEndpointFault reports an endpoint-wide fault — the failure belongs to the committed endpoint shape (404, GraphQL-shaped 400) or to X itself (5xx, LoadShed), never to the serving account. Covers ErrEndpoint and ErrOverloaded; callers must not charge the account or report it as an auth_error.
func IsForbidden ¶ added in v0.6.9
IsForbidden reports a 403 response without a recognized JSON code.
func IsInternal ¶ added in v0.6.9
IsInternal reports a Twitter internal error (code 131).
func IsNotAuthorized ¶ added in v0.6.9
IsNotAuthorized reports a not-authorized condition (codes 179 / 219).
func IsRateLimited ¶ added in v0.6.9
IsRateLimited reports a rate-limit condition (HTTP 429, or code 88 with an exhausted window).
func IsSuspended ¶ added in v0.6.9
IsSuspended reports an account-suspended condition (code 64).
func SetSocialMeterProvider ¶ added in v0.6.15
func SetSocialMeterProvider(mp metric.MeterProvider)
SetSocialMeterProvider sets the OpenTelemetry MeterProvider that the *WithSocial helpers record to (request.duration, timeline.freshness, relogins, proxy.failures; see docs/metrics.md). Call it once at start-up. nil (the default) falls back to otel.GetMeterProvider(). The per-call clients behind these helpers never register the pool.accounts gauge.
Types ¶
type APIError ¶ added in v0.6.9
APIError represents a Twitter/X API error with classification.
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 ¶
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 ¶
AllowRequest checks if this account can make a request to the given endpoint. While the endpoint's header window is live (see endpointGate) the account is allowed unless explicitly blocked: the headers are the authority and the local counter is skipped. Otherwise (cold start, headers absent, or the window expired) the local counter decides.
Under header authority each allowed pick spends one slot of the window's remaining estimate, so N concurrent callers cannot all pass on the strength of one stale response: when the estimate reaches the headroom the endpoint is held until the reset. The next response folds in by the recordWindow merge rule (minimum within a window), so the estimate never rises inside a window. A pick that never produces a response (transport error) keeps its slot spent until the window resets: conservative, bounded by the window.
func (*Account) Credentials ¶
Credentials returns a snapshot of (authToken, ct0, userAgent) under lock.
func (*Account) EndpointAvailableAt ¶
EndpointAvailableAt returns when this account will be available for the given endpoint.
func (*Account) InFlight ¶ added in v0.6.24
InFlight reports the account's current in-flight request count. The twitter.client.pool.inflight gauge sums it across the pool; tests assert on it.
func (*Account) IsActive ¶
IsActive implements pool.Identity.
Guarded by a.mu: the pool flips these fields (SetActive/SetReactivateAt) under its own lock while readers such as the metrics gauge callback run unlocked relative to the pool, so the Account must synchronise them itself.
func (*Account) IsEndpointRateLimited ¶
IsEndpointRateLimited returns true if the endpoint is currently blocked.
func (*Account) MarkEndpointRateLimited ¶
MarkEndpointRateLimited marks an endpoint as rate-limited for this account.
func (*Account) ReactivateAt ¶
ReactivateAt implements pool.Identity.
func (*Account) RecordSuccess ¶ added in v0.6.18
func (a *Account) RecordSuccess()
RecordSuccess records a successful request and clears the consecutive header-less-88 counter: a successful response proves the credentials work, so a later run of header-less 88s starts from zero. It shadows the embedded HealthTracker method on purpose — every success path (200, post-relogin, CSRF recovery, accepted 131) funnels through it, so the reset cannot be missed at a new call site.
func (*Account) RotateCT0 ¶
func (a *Account) RotateCT0()
RotateCT0 generates a fresh ct0 token and updates the refresh timestamp.
func (*Account) SetCredentials ¶
SetCredentials atomically updates auth_token and ct0.
func (*Account) SetReactivateAt ¶
SetReactivateAt implements pool.Identity.
func (*Account) SyncRateLimit ¶ added in v0.6.6
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
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
// ProxyConsecFails is the count of consecutive proxy-layer failures for this
// account. Non-zero while a dead proxy is being backed off; reset to 0 on
// the next successful HTTP response. Distinguishes a dead proxy from a dead
// account — the go-twitter #43 incident mis-attributed 5881 proxy failures
// to the account layer because this distinction was not observable.
ProxyConsecFails int
// ProxyBackoffUntil is when the account's proxy becomes eligible again after
// exponential backoff. Zero when no proxy backoff is in effect.
ProxyBackoffUntil time.Time
}
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
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, "" }))
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 ¶
AccountByUsername returns the pool account matching the given username (case-insensitive). Returns nil if not found.
func (*Client) Close ¶ added in v0.6.13
Close unregisters the client's pool.accounts gauge callback so the client can be garbage collected and a replacement client can report on the same MeterProvider. It is idempotent and safe on a nil client; it does not stop in-flight requests.
func (*Client) CreateTweet ¶
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
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
GetHomeLatestTimeline fetches the reverse-chronological "Following" home timeline. POST (pool), mirroring SearchTimeline.
func (*Client) GetHomeTimeline ¶ added in v0.6.0
GetHomeTimeline fetches the algorithmic "For you" home timeline. POST (pool), mirroring SearchTimeline.
func (*Client) GetListTweets ¶ added in v0.6.0
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 ¶
GetTweetByID fetches a single tweet by its ID.
func (*Client) GetTweetConversation ¶ added in v0.6.11
func (c *Client) GetTweetConversation(ctx context.Context, tweetID string) (*Tweet, []*Tweet, error)
GetTweetConversation fetches the focal tweet plus its conversation entries (replies and ancestors, in page order) via TweetDetail.
func (*Client) GetUserByScreenName ¶
GetUserByScreenName fetches a user profile by Twitter handle.
func (*Client) GetUserTweets ¶
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
LikeTweet favorites a tweet from a specific account. Returns nil on the "Done" ack, else an error.
func (*Client) PostWithAccount ¶
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
Retweet retweets a tweet from a specific account. Returns the new retweet ID.
func (*Client) SearchTimeline ¶
SearchTimeline searches for tweets matching a query on the Latest tab. Uses POST (Twitter migrated this endpoint from GET in March 2026).
func (*Client) SearchTweets ¶ added in v0.6.11
func (c *Client) SearchTweets(ctx context.Context, query string, product SearchProduct, count int) ([]*Tweet, error)
SearchTweets searches tweets on an explicit tab — Top or Latest. Anything else is rejected before the request so a typo can't silently read Latest.
func (*Client) SearchUsers ¶ added in v0.6.11
SearchUsers searches the People tab.
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
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
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
UploadMediaFile reads a file from disk, infers its MIME type, and uploads it.
func (*Client) ValidateAccount ¶
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. It
// configures the default file store only; a custom SessionStore owns its
// own expiry policy.
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.
//
// Deprecated: use MeterProvider. MetricsHook carries no latency or error
// class; it keeps working unchanged for existing consumers.
MetricsHook func(endpoint string, success, rateLimited bool)
// MeterProvider supplies the OpenTelemetry meter provider for the client's
// metrics (see docs/metrics.md). nil = otel.GetMeterProvider(), which is a
// no-op until the application installs an SDK provider.
MeterProvider metric.MeterProvider
// SessionDir overrides the default session persistence directory used by
// the default file store. Ignored when SessionStore is set.
// Default: ~/.go-twitter/sessions
SessionDir string
// SessionStore persists per-account sessions. nil = a FileSessionStore
// built from SessionDir and SessionTTL (the historical behaviour). Set it
// to make an external credential store the single source of truth.
SessionStore SessionStore
// 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
// MaxInFlightPerAccount caps the number of requests a single account may
// have in flight at once — the in-process counterpart of nitter's
// maxConcurrentReqs. A pool pick that would exceed the cap skips the
// account; when every eligible account is at the cap the call waits for a
// slot (bounded by the caller's ctx and the shared pool-wait budget, see
// twitter.client.pool.wait.duration). Account-pinned paths (doPOST, media
// upload) wait for the pinned account's slot the same way.
// Default: 2. A negative value disables the cap.
// The cap bounds in-flight SLOTS, not sockets: a cancelled request releases
// its slot at once, but its socket can outlive the slot until go-stealth's
// own request timeout.
MaxInFlightPerAccount int
// contains filtered or unexported fields
}
ClientConfig holds all configuration for the Twitter client.
type DMConversation ¶ added in v0.6.0
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 FileSessionStore ¶ added in v0.6.17
type FileSessionStore struct {
// Dir is the session directory. Empty means ~/.go-twitter/sessions.
Dir string
// TTL expires sessions older than this on Load (reported as
// ErrSessionNotFound). TTL <= 0 means no expiry; note NewClient defaults
// ClientConfig.SessionTTL 0 to 24h before building the default store.
TTL time.Duration
}
FileSessionStore keeps one JSON file per account at <Dir>/<username>.json. Writes are atomic (temp file + fsync + rename) so a concurrent Load sees the previous or the new complete file, never a truncated one. Files are 0600 in a 0700 directory.
func (*FileSessionStore) Delete ¶ added in v0.6.17
func (f *FileSessionStore) Delete(_ context.Context, username string) error
Delete implements SessionStore.
func (*FileSessionStore) Load ¶ added in v0.6.17
Load implements SessionStore. A missing, TTL-expired or undecodable (truncated/corrupt) file yields ErrSessionNotFound, as does a username that is not a valid file name: the file holds nothing usable, and treating it as a miss lets the client log in and overwrite it (Save re-creates a corrupt file; for an invalid name Save still rejects it and logs, exactly as before the store was pluggable). Genuine I/O failures, permission denied included, are returned: the file may hold a newer valid session that cannot be read, and a Save would most likely fail too, so the client must not log in blindly.
type SearchProduct ¶ added in v0.6.11
type SearchProduct string
SearchProduct selects the SearchTimeline results tab.
const ( SearchTop SearchProduct = "Top" SearchLatest SearchProduct = "Latest" SearchPeople SearchProduct = "People" SearchMedia SearchProduct = "Media" )
type Session ¶ added in v0.6.17
type Session struct {
AuthToken string `json:"auth_token"`
CT0 string `json:"ct0"`
SavedAt time.Time `json:"saved_at"`
}
Session is the persisted credential pair of one account. The JSON tags are the on-disk format of FileSessionStore and are kept stable.
String and LogValue redact the tokens so a Session printed or logged by accident does not leak them; JSON marshalling is unaffected.
func (Session) GoString ¶ added in v0.6.17
GoString implements fmt.GoStringer so %#v does not leak the tokens.
type SessionStore ¶ added in v0.6.17
type SessionStore interface {
// Load returns the stored session, or ErrSessionNotFound when there is none.
Load(ctx context.Context, username string) (Session, error)
// Save stores the session, replacing any previous one.
Save(ctx context.Context, username string, s Session) error
// Delete removes the session; deleting a missing session is not an error.
Delete(ctx context.Context, username string) error
}
SessionStore persists per-account sessions (auth_token + ct0) across process restarts and ct0 rotations. Implementations must be safe for concurrent use.
The client never lets a store failure fail an API request: errors are logged (username only, never tokens) and the request continues. Every call carries a context bounded to a few seconds.
Implementations MUST honour ctx: the client bounds every call with a 5s timeout and calls Save synchronously on the request path right after a ct0 rotation, so a store that ignores ctx stalls requests. Save is last-write-wins; the client sets Session.SavedAt (stores may compare it). Load must return ErrSessionNotFound for a miss; any other Load error is NOT treated as a miss: an account without provided credentials then fails instead of logging in with a password, so a flaky store cannot cause a login storm.
Session lifetime is the store's concern: the client applies no TTL of its own. FileSessionStore honours ClientConfig.SessionTTL; a database-backed store can ignore Session.SavedAt entirely or enforce its own policy.
Accounts leased by the *WithSocial helpers are credential-service owned and never touch the store.
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"]
// contains filtered or unexported fields
}
Tweet represents a single tweet.
func GetTweetByIDWithSocial ¶ added in v0.6.25
GetTweetByIDWithSocial fetches a single tweet via the TweetDetail page.
func GetUserTweetsByIDWithSocial ¶ added in v0.6.25
func GetUserTweetsByIDWithSocial(ctx context.Context, sc *social.Client, userID string, limit int) ([]*Tweet, error)
GetUserTweetsByIDWithSocial fetches recent tweets for a numeric user ID — the caller already resolved the handle, so no UserByScreenName lookup burns the lease's budget.
func GetUserTweetsWithSocial ¶ added in v0.6.11
func GetUserTweetsWithSocial(ctx context.Context, sc *social.Client, handle string, limit int) ([]*Tweet, error)
GetUserTweetsWithSocial resolves the handle and fetches its recent tweets — two GraphQL calls inside one account attempt.
func SearchTweetsWithSocial ¶ added in v0.6.11
func SearchTweetsWithSocial(ctx context.Context, sc *social.Client, query string, product SearchProduct, limit int) ([]*Tweet, error)
SearchTweetsWithSocial searches tweets on an explicit tab (Top or Latest).
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 on the Latest tab, 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.
func GetFollowersWithSocial ¶ added in v0.6.25
func GetFollowersWithSocial(ctx context.Context, sc *social.Client, userID string, limit int) ([]*TwitterUser, error)
GetFollowersWithSocial lists the accounts following userID.
func GetFollowingWithSocial ¶ added in v0.6.25
func GetFollowingWithSocial(ctx context.Context, sc *social.Client, userID string, limit int) ([]*TwitterUser, error)
GetFollowingWithSocial lists the accounts userID follows.
func GetRetweetersWithSocial ¶ added in v0.6.25
func GetRetweetersWithSocial(ctx context.Context, sc *social.Client, tweetID string, limit int) ([]*TwitterUser, error)
GetRetweetersWithSocial lists the accounts that retweeted tweetID.
func GetUserWithSocial ¶ added in v0.6.11
GetUserWithSocial fetches a profile by handle.
func SearchUsersWithSocial ¶ added in v0.6.11
func SearchUsersWithSocial(ctx context.Context, sc *social.Client, query string, limit int) ([]*TwitterUser, error)
SearchUsersWithSocial searches the People tab.
Source Files
¶
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. |
|
gqlextract
Package gqlextract extracts GraphQL queryIDs and feature flags from x.com's own bundles.
|
Package gqlextract extracts GraphQL queryIDs and feature flags from x.com's own bundles. |
|
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. |