Class: X::Client

Inherits:
Object
  • Object
show all
Includes:
CredentialHolder, Objects::API, Streaming::API, Uploader::API
Defined in:
x-core/lib/x/core/client.rb

Overview

A client for interacting with the X API

An endpoint is resolved against the base URL, and a request carries the client's credentials to the origin of that base URL alone: the scheme, host, and port it names. An endpoint that names a whole URL of another origin is sent there without them, as a redirect that leads to one is, so that the credentials of the API never reach a host they were not meant for; see X::Core::Origin.

The object_class of a request, of a stream, or of a client is one of two things. A class that JSON.parse builds each JSON object of the body into, as it does Hash, the default, OpenStruct, or a Struct, whose new takes no arguments and whose instances take each member with []=. Or anything that responds to from_response, which builds the result from the whole body instead, as the resource classes of x-objects do: it is passed the body parsed into Hashes and Arrays, whatever the array_class, and the client that made the request as client:, and what it returns is what the request returns, or, for a stream, what its block is passed for each object. Later versions of 1.x may pass it keyword arguments of their own, so it accepts the ones it does not read with **, as in def self.from_response(body, client:, **). The signatures of x-core state it as the X::_ResponseBuilder interface.

A client keeps its credentials, settings, and connection in an object of x-core it delegates to, and has no private methods but initialize, so that the methods x-objects and x-uploader include into it, which may be named as they like, take the place of none of its own.

Constant Summary collapse

DEFAULT_BASE_URL =

Default base URL for the X API

"https://api.x.com/2/"
DEFAULT_ARRAY_CLASS =

Default class for parsing JSON arrays

Array
DEFAULT_OBJECT_CLASS =

Default class for parsing JSON objects

Hash
DEFAULT_OPEN_TIMEOUT =

Default timeout for opening connections in seconds

Connection::DEFAULT_OPEN_TIMEOUT
DEFAULT_READ_TIMEOUT =

Default timeout for reading responses in seconds

Connection::DEFAULT_READ_TIMEOUT
DEFAULT_WRITE_TIMEOUT =

Default timeout for writing requests in seconds

Connection::DEFAULT_WRITE_TIMEOUT
DEFAULT_KEEP_ALIVE_TIMEOUT =

Default time to keep a connection open for the next request to the same host, in seconds

Connection::DEFAULT_KEEP_ALIVE_TIMEOUT
DEFAULT_MAX_REDIRECTS =

Default maximum number of redirects to follow

RedirectHandler::DEFAULT_MAX_REDIRECTS
DEFAULT_MAX_RATE_LIMIT_RETRIES =

Default maximum number of times to retry a request refused for a rate limit

RateLimitHandler::DEFAULT_MAX_RETRIES
DEFAULT_MAX_RATE_LIMIT_WAIT =

Default maximum number of seconds to wait for a rate limit to reset

RateLimitHandler::DEFAULT_MAX_WAIT
DEFAULT_MAX_RETRIES =

Default maximum number of times to send an idempotent request again after a failure

RetryHandler::DEFAULT_MAX_RETRIES

Instance Method Summary collapse

Constructor Details

#initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil, bearer_token: nil, client_id: nil, client_secret: nil, refresh_token: nil, expires_at: nil, scopes: nil, authenticator: nil, base_url: DEFAULT_BASE_URL, open_timeout: DEFAULT_OPEN_TIMEOUT, read_timeout: DEFAULT_READ_TIMEOUT, write_timeout: DEFAULT_WRITE_TIMEOUT, keep_alive_timeout: DEFAULT_KEEP_ALIVE_TIMEOUT, debug_output: nil, proxy_url: nil, default_array_class: DEFAULT_ARRAY_CLASS, default_object_class: DEFAULT_OBJECT_CLASS, headers: {}, max_redirects: DEFAULT_MAX_REDIRECTS, max_rate_limit_retries: DEFAULT_MAX_RATE_LIMIT_RETRIES, max_rate_limit_wait: DEFAULT_MAX_RATE_LIMIT_WAIT, max_retries: DEFAULT_MAX_RETRIES, on_response: nil, save_tokens: nil, load_tokens: nil) ⇒ Client

Initialize a new X API client

Examples:

Create a client with bearer token authentication

client = X::Client.new(bearer_token: "your_bearer_token")

Create a client with OAuth 2.0 authentication that stores the tokens of each refresh

client = X::Client.new(client_id: "id", client_secret: "secret", access_token: "token", refresh_token: "refresh",
  expires_at: Time.now + 7200, save_tokens: ->(tokens) { store.save(tokens.refresh_token) })

Share the tokens of a user among processes, which store each refresh and read the store before one

stored = store.load(user)
client = X::Client.new(client_id: "id", **stored.to_h,
  save_tokens: ->(tokens) { store.save(user, tokens) },
  load_tokens: -> { store.load(user) })

Create a client with OAuth 1.0a authentication

client = X::Client.new(api_key: "key", api_key_secret: "secret", access_token: "token", access_token_secret: "token_secret")

Create a client that authenticates with an authenticator built elsewhere

client = X::Client.new(authenticator: X::OAuth2Authenticator.new(client_id: "id", access_token: "token",
  refresh_token: "refresh", expires_at: Time.now + 7200), save_tokens: ->(tokens) { store.save(tokens) })

Create a client that fetches an app-only bearer token with the API key and secret

client = X::Client.new(api_key: "key", api_key_secret: "secret")

Create a client that retries a rate-limited request up to three times

client = X::Client.new(bearer_token: "your_bearer_token", max_rate_limit_retries: 3)

Create a client that raises at once rather than send a lookup again the API failed to answer

client = X::Client.new(bearer_token: "your_bearer_token", max_retries: 0)

Create a client that names the application in the User-Agent of every request

client = X::Client.new(bearer_token: "your_bearer_token", headers: {"User-Agent" => "my-app/1.0"})

Parameters:

  • api_key (String, nil) (defaults to: nil) —

    the API key for OAuth 1.0a authentication

  • api_key_secret (String, nil) (defaults to: nil) —

    the API key secret for OAuth 1.0a authentication

  • access_token (String, nil) (defaults to: nil) —

    the access token for OAuth authentication

  • access_token_secret (String, nil) (defaults to: nil) —

    the access token secret for OAuth 1.0a authentication

  • bearer_token (String, nil) (defaults to: nil) —

    the bearer token for authentication

  • client_id (String, nil) (defaults to: nil) —

    the OAuth 2.0 client ID

  • client_secret (String, nil) (defaults to: nil) —

    the OAuth 2.0 client secret

  • refresh_token (String, nil) (defaults to: nil) —

    the OAuth 2.0 refresh token, or nil beside a client ID and access token issued without offline.access, which authenticate as the user until the access token expires, and cannot refresh

  • expires_at (Time, nil) (defaults to: nil) —

    the time the OAuth 2.0 access token expires, after which a request refreshes it, given only beside the client_id and access_token the client authenticates with

  • scopes (Array<String>, nil) (defaults to: nil) —

    the scopes X granted the OAuth 2.0 access token, as OAuth2Tokens#scopes holds them, given only beside the client_id and access_token the client authenticates with

  • authenticator (Authenticator, nil) (defaults to: nil) —

    an authenticator to authenticate with in place of credentials, such as an OAuth2Authenticator built elsewhere, or nil to build one of the credentials; see #authenticator

  • base_url (String) (defaults to: DEFAULT_BASE_URL) —

    the base URL for API requests

  • open_timeout (Integer, Float, nil) (defaults to: DEFAULT_OPEN_TIMEOUT) —

    the timeout for opening connections in seconds, or nil for none

  • read_timeout (Integer, Float, nil) (defaults to: DEFAULT_READ_TIMEOUT) —

    the timeout for reading responses in seconds, or nil for none

  • write_timeout (Integer, Float, nil) (defaults to: DEFAULT_WRITE_TIMEOUT) —

    the timeout for writing requests in seconds, or nil for none

  • keep_alive_timeout (Integer, Float) (defaults to: DEFAULT_KEEP_ALIVE_TIMEOUT) —

    the time to keep a connection open for the next request to the same host, in seconds, which a proxy that closes idle connections sooner than X does may need lowered

  • debug_output (IO, #<<, nil) (defaults to: nil) —

    the IO object for debug output, or anything else that takes a String with <<, such as a StringIO. It is written every request and response whole, in the clear: the Authorization header, the client secret a token request sends, and the tokens a token response holds. Send it to a file you control while debugging, never to a log that is shipped elsewhere, and leave it nil in production.

  • proxy_url (String, URI::Generic, nil) (defaults to: nil) —

    the proxy URL for requests

  • default_array_class (Class) (defaults to: DEFAULT_ARRAY_CLASS) —

    the default class for parsing JSON arrays

  • default_object_class (Class, #from_response) (defaults to: DEFAULT_OBJECT_CLASS) —

    the default class for parsing JSON objects, or one that responds to from_response and builds the result from the whole body; see X::Client

  • headers (Hash{String, Symbol => String}) (defaults to: {}) —

    headers sent with every request the client makes, as defaults: a header of the same name passed to a request is sent in place of one of these, and each of these is sent in place of a default of the gem, such as its User-Agent; a Symbol names the header its underscores name with hyphens, as :user_agent names User-Agent

  • max_redirects (Integer) (defaults to: DEFAULT_MAX_REDIRECTS) —

    the maximum number of redirects to follow, beyond which a redirect raises TooManyRedirects; 0 follows none, and raises for each redirect that could be followed

  • max_rate_limit_retries (Integer) (defaults to: DEFAULT_MAX_RATE_LIMIT_RETRIES) —

    the maximum number of times to retry a request refused for a rate limit, after waiting for the limit to reset

  • max_rate_limit_wait (Integer, Float) (defaults to: DEFAULT_MAX_RATE_LIMIT_WAIT) —

    the maximum number of seconds to wait for a rate limit to reset; a request whose limit resets later raises TooManyRequests at once, as does a stream that would wait longer to reconnect, and a few seconds are added at random to each wait of a request, so that the requests one reset releases are not sent again in one burst

  • max_retries (Integer) (defaults to: DEFAULT_MAX_RETRIES) —

    the maximum number of times to send a request again after the API failed to answer it, with a 5xx status or a 408, or after its answer never arrived, which is twice by default and is 0 for a client that raises at once; a retry waits up to a second before the first and up to twice as long before each after, but never more than a minute, a random share of each wait taken off so that the requests one failure of the API ended are not sent again together, or for as long as the response asks when it carries a Retry-After header, whichever is longer, and a response that asks for longer than a minute raises at once; a 429 is not among these, and waits for its rate limit to reset as max_rate_limit_wait allows, however long past a minute; only a GET, PUT, or DELETE is sent again, since the API may have acted on a POST whose answer never arrived, and one whose answer never arrived is sent again only when it never reached the API, such as for a connection refused or one that timed out opening, since the API bills a read it answered, such as one that timed out reading its response, whether or not the answer came

  • on_response (#call, nil) (defaults to: nil) —

    a callable passed an X::Response after every request, failed ones included, and every object a stream delivers; a block passed to a single request receives the same summary, after this

  • save_tokens (#call, nil) (defaults to: nil) —

    a callable passed the OAuth2Tokens of each refresh, to store them; the refreshes are reported one at a time, in the order they were made, and one already replaced is not reported; the client of OAuth2Authorization#client passes it the tokens of the exchange of the code as well; a callable that raises, as one whose storage is briefly down may, raises TokenReportFailed from the request that refreshed, which holds the tokens, since the refresh token they replaced is spent, and the client, with the error of the callable as its cause

  • load_tokens (#call, nil) (defaults to: nil) —

    a callable that takes no arguments and returns the OAuth2Tokens in the storage that save_tokens writes to, or nil for none there, for processes that share the tokens of a user: X accepts a refresh token once, so a refresh reads the storage first, under its lock, and takes the tokens there in place of its own when their refresh token is another, as it is once another process has refreshed; it sends the request with them when their access token has not expired, and refreshes with them when it has, and a refresh X refuses for a refresh token another process spent reads the storage again, and takes the tokens there in place of raising; the tokens it takes came from the storage, so save_tokens is not passed them; see #authenticator

Raises:

  • (ArgumentError) —

    if credentials are given that do not form a complete set, which would send requests without them, or authenticate as the app rather than a user

  • (ArgumentError) —

    if a credential is an empty String, as an environment variable that is not set is often read, which would send an Authorization header that authenticates nothing

  • (ArgumentError) —

    if expires_at is neither a Time nor nil, or scopes neither an Array of Strings that each name a scope nor nil, or either is given to a client that does not authenticate with OAuth 2.0 credentials, which would leave it unused

  • (ArgumentError) —

    if an authenticator is given that is not an Authenticator, or beside credentials, expires_at, or scopes, which it would leave unused

  • (ArgumentError) —

    if a timeout is neither a finite number of seconds of at least 0 nor, for any but keep_alive_timeout, nil, or if a maximum is not a count or a number of seconds of at least 0

  • (ArgumentError) —

    if base_url is not an absolute http or https URL with no user, password, query, or fragment, or headers are not a Hash that names each header with a String or a Symbol and gives it a String

  • (ArgumentError) —

    if on_response, save_tokens, or load_tokens is neither nil nor responds to call

  • (ArgumentError) —

    if default_array_class is not a Class, or default_object_class is neither a Class nor responds to from_response, which a response would be parsed with once the API had answered the request



364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
# File 'x-core/lib/x/core/client.rb', line 364

def initialize(api_key: nil, api_key_secret: nil, access_token: nil, access_token_secret: nil,
  bearer_token: nil, client_id: nil, client_secret: nil, refresh_token: nil, expires_at: nil, scopes: nil,
  authenticator: nil,
  base_url: DEFAULT_BASE_URL,
  open_timeout: DEFAULT_OPEN_TIMEOUT,
  read_timeout: DEFAULT_READ_TIMEOUT,
  write_timeout: DEFAULT_WRITE_TIMEOUT,
  keep_alive_timeout: DEFAULT_KEEP_ALIVE_TIMEOUT,
  debug_output: nil,
  proxy_url: nil,
  default_array_class: DEFAULT_ARRAY_CLASS,
  default_object_class: DEFAULT_OBJECT_CLASS,
  headers: {},
  max_redirects: DEFAULT_MAX_REDIRECTS,
  max_rate_limit_retries: DEFAULT_MAX_RATE_LIMIT_RETRIES,
  max_rate_limit_wait: DEFAULT_MAX_RATE_LIMIT_WAIT,
  max_retries: DEFAULT_MAX_RETRIES,
  on_response: nil,
  save_tokens: nil,
  load_tokens: nil)
  @internals = ClientInternals.new(self, api_key:, api_key_secret:, access_token:, access_token_secret:, bearer_token:,
    client_id:, client_secret:, refresh_token:, expires_at:, scopes:, authenticator:, base_url:, open_timeout:, read_timeout:,
    write_timeout:, keep_alive_timeout:, debug_output:, proxy_url:, default_array_class:, default_object_class:,
    headers:, max_redirects:, max_rate_limit_retries:, max_rate_limit_wait:, max_retries:, on_response:,
    save_tokens:, load_tokens:)
end

Instance Method Details

#add_alt_text(media, text) ⇒ UploadedMedia Originally defined in module Uploader::API

Describe uploaded media with alt text, for people who cannot see it

Examples:

Describe an image

client.add_alt_text(media, "A cat asleep on a keyboard")

Describe an image as it is uploaded

media = client.add_alt_text(client.upload_media("cat.jpg"), "A cat asleep on a keyboard")

Parameters:

  • media (UploadedMedia, Hash, #media_key, String, Integer) —

    the uploaded media, media that has a media key, such as X::Media, the media key, or the media identifier

  • text (String) —

    the alt text, of 1 to 1,000 characters

Returns:

  • (UploadedMedia) —

    the media given, as uploaded media, which a call can be chained to

Raises:

  • (ArgumentError) —

    if the alt text is empty or longer than the API takes, before a request

  • (ArgumentError) —

    if the media given is nil, holds no identifier, or is neither media, a media key, nor a media identifier, or its media key names none

  • (MissingMediaData) —

    if the response holds no metadata or carries no body at all

#add_list_member(list, user) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Add a member to a list as the authenticated user

Examples:

Add a member to a list

client.add_list_member("1234567890", user)

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the user is now a member

#add_subtitles(video, subtitles, language_code, **options) ⇒ UploadedMedia Originally defined in module Uploader::API

Attach uploaded subtitles to an uploaded video

Examples:

Subtitle a video in English

client.add_subtitles(video, subtitles, "EN", display_name: "English")

Parameters:

  • video (UploadedMedia, Hash, #media_key, String, Integer) —

    the uploaded video, media that has a media key, such as X::Media, or its media identifier

  • subtitles (UploadedMedia, Hash, #media_key, String, Integer) —

    the uploaded subtitles, media that has a media key, or their media identifier

  • language_code (String) —

    the language of the subtitles, such as EN

  • options (Hash) —

    the options of Metadata#add_subtitles

Options Hash (**options):

  • :display_name (String, nil) — default: nil —

    the name of the language shown to viewers, such as English, or nil for none

  • :media_category (String, Symbol) — default: "tweet_video" —

    the category the video was uploaded as, tweet_video or amplify_video, in any case, or TweetVideo or AmplifyVideo, as the subtitles endpoint names them

Returns:

  • (UploadedMedia) —

    the video given, as uploaded media, which a call can be chained to

Raises:

  • (ArgumentError) —

    if the media category is neither tweet_video nor amplify_video, or the language code is not two letters

  • (ArgumentError) —

    if the video or the subtitles are nil, hold no identifier, are neither media, a media key, nor a media identifier, have a media key that names none, or an identifier the API does not take

  • (MissingMediaData) —

    if the response holds no metadata or carries no body at all

#api_key ⇒ String?

The API key for OAuth 1.0a authentication

It is the one the client was given, or the one the OAuth1Authenticator or AppOnlyAuthenticator it was given in place of credentials holds.

Examples:

Get the API key

client.api_key

Returns:

  • (String, nil) —

    the API key for OAuth 1.0a authentication



166
# File 'x-core/lib/x/core/client.rb', line 166

def api_key = @internals.api_key_in_use

#app_only ⇒ Client

A client that authenticates as the app, for the endpoints that refuse OAuth 1.0a

A client that authenticates as a user, signing with OAuth 1.0a or with OAuth 2.0, returns a copy that authenticates with the app's bearer token: the one it was given, or one it fetches with its API key and secret the first time. It returns the same copy, with the connections it keeps open, from then on, since the credentials and settings of a client never change; threads that ask for the copy together get one. A copy of the client made with #with that holds the same API key and secret, and the same base URL, builds a copy of its own, but sends the token the client fetched, or fetches, rather than fetch one of its own from the token endpoint, which X limits the rate of. A client with a bearer token or an API key and secret alone already authenticates as the app, and is returned as it is, as is one given an authenticator that authenticates as the app, or as no one. A client given an OAuth1Authenticator fetches the token with the API key and secret it signs with. A client that authenticates with OAuth 2.0 as a user and holds neither the app's bearer token nor its API key and secret, as a client given an OAuth2Authenticator holds neither, raises, rather than send the user's credentials to an endpoint that would refuse them with 403 Forbidden.

Examples:

Add a filtered stream rule, which takes app-only authentication

client.app_only.post("tweets/search/stream/rules", {add: [{value: "ruby"}]})

Returns:

  • (Client) —

    a copy that authenticates with the bearer token, or the client itself

Raises:

  • (UnsupportedOperation) —

    if the client authenticates with OAuth 2.0 as a user and holds no credentials of the app



457
# File 'x-core/lib/x/core/client.rb', line 457

def app_only = @internals.app_only(self)

#authenticator ⇒ Authenticator

The authenticator for API requests

It is the one the client was given, or else the one it built of its credentials. A client sends the token requests of an authenticator that makes them, an AppOnlyAuthenticator or an OAuth2Authenticator, over its own connection, with its proxy, timeouts, and debug output, whether it built the authenticator or was given it; an authenticator given to several clients sends them over the connection of the first. The refreshes of an OAuth2Authenticator reach the save_tokens of each client that authenticates with it, a refresh reads the stored tokens with the load_tokens of the authenticator, or else with the load_tokens of a client that authenticates with it, and the expires_at of the client is the authenticator's.

Examples:

Check if the OAuth 2.0 token has expired

client.authenticator.token_expired?

Returns:



138
# File 'x-core/lib/x/core/client.rb', line 138

def authenticator = @internals.authenticator

#await_media_processing(media, **options) ⇒ UploadedMedia Originally defined in module Uploader::API

Wait until media has been processed, whether its processing succeeded or failed

It returns the status X reported, which failed? tells a failure by, and ready? a success by, since a status in no state X documents is neither; await_media_processing! raises for either instead. Media that already says its processing succeeded or failed, or holds an upload response that names no processing, such as that of an image, is returned as it is, without a request.

Examples:

Wait for a video uploaded with chunked_upload_media

video = client.await_media_processing(video)
warn video.processing_info.dig("error", "message") if video.failed?

Parameters:

  • media (UploadedMedia, Hash, #media_key, String, Integer) —

    the uploaded media, media that has a media key, such as X::Media, the media key, or the media identifier

  • options (Hash) —

Options Hash (**options):

  • :processing_timeout (Integer, Float, nil) — default: 600 —

    the seconds from now to wait for processing to finish, checks and all, of at least 0, or nil to wait for as long as processing takes

Returns:

  • (UploadedMedia) —

    the uploaded media, which holds the processing status, failed or not, or the media given, as uploaded media, if its processing has already ended

Raises:

  • (ArgumentError) —

    if the processing timeout is neither nil nor a finite number of seconds of at least 0

  • (ArgumentError) —

    if the media given is nil, holds no identifier, or is neither media, a media key, nor a media identifier, or its media key names none

  • (MissingMediaData) —

    if a status response holds no media or carries no body at all

  • (MediaProcessingTimeout) —

    if the media is still processing once the processing timeout would pass

#await_media_processing!(media, **options) ⇒ UploadedMedia Originally defined in module Uploader::API

Wait until media has been processed, raising if its processing failed

Examples:

Wait for a video uploaded with chunked_upload_media, raising if X could not process it

client.await_media_processing!(video)

Parameters:

  • media (UploadedMedia, Hash, #media_key, String, Integer) —

    the uploaded media, media that has a media key, such as X::Media, the media key, or the media identifier

  • options (Hash) —

Options Hash (**options):

  • :processing_timeout (Integer, Float, nil) — default: 600 —

    the seconds from now to wait for processing to finish, checks and all, of at least 0, or nil to wait for as long as processing takes

Returns:

  • (UploadedMedia) —

    the uploaded media, which holds the processing status, or the media given, as uploaded media, if its processing has already succeeded

Raises:

  • (ArgumentError) —

    if the processing timeout is neither nil nor a finite number of seconds of at least 0

  • (ArgumentError) —

    if the media given is nil, holds no identifier, or is neither media, a media key, nor a media identifier, or its media key names none

  • (MissingMediaData) —

    if a status response holds no media or carries no body at all

  • (MediaProcessingFailed) —

    if media processing failed, or ended in no state X documents, with the status X reported, or the media given, without a request, if it already says its processing failed

  • (MediaProcessingTimeout) —

    if the media is still processing once the processing timeout would pass

#base_url ⇒ String

The base URL for API requests

Examples:

Get the base URL

client.base_url # => "https://api.x.com/2/"

Returns:

  • (String) —

    the base URL for API requests, which ends with a slash



206
# File 'x-core/lib/x/core/client.rb', line 206

def base_url = @internals.base_url

#block(user) ⇒ Boolean Originally defined in module Objects::Actions::Relationships

Block a user as the authenticated user

Examples:

Block a user

client.block("7505382")

Parameters:

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the authenticated user now blocks the user

#bookmark(post) ⇒ Boolean Originally defined in module Objects::Actions::Engagement

Bookmark a post as the authenticated user

The bookmark endpoints take only OAuth 2.0 user context, which the object layer cannot route around, so a client that signs with OAuth 1.0a is refused.

Examples:

Bookmark a post

client.bookmark("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user has bookmarked the post

#chunked_upload_media(media, **options) ⇒ UploadedMedia Originally defined in module Uploader::API

Upload media in chunks, without waiting for it to be processed

It is the way to upload media without waiting for X to process it: #upload_media waits for the processing of media X processes, such as a video, and this does not. It uploads the media as #upload_media uploads a video, a chunk at a time, but returns once the upload is finalized, so that the caller can go on while X processes a long video, and wait for it with #await_media_processing or #await_media_processing! when it needs it. It uploads in chunks whatever the media, an image as well, and adds no alt text. The chunks are sent by threads of their own, so the on_response of the client runs on those threads for the response of each chunk.

Each chunk is a request a rate limit can refuse, which fails the upload with ChunkedUploadFailed unless the client retries it, which it does only max_rate_limit_retries times, 0 by default, so upload a large video with a client whose max_rate_limit_retries is set, such as X::Client.new(max_rate_limit_retries: 3).

Examples:

Upload a long video, and wait for X to process it once it is needed

video = client.chunked_upload_media("talk.mp4", concurrency: 8)
client.create_post("Watch the talk", media_ids: [client.await_media_processing!(video)])

Parameters:

  • media (String, Pathname, IO, StringIO) —

    the path to the media to upload, or an IO open on it

  • options (Hash) —

Options Hash (**options):

  • :media_category (String, Symbol, nil) — default: nil —

    the media category, in any case, inferred when nil from the bytes the media begins with, or else from the name of its file

  • :media_type (String, nil) — default: nil —

    the MIME type of the media, sent as it is given, or inferred from the media and category when nil

  • :chunk_size (Integer, nil) — default: nil —

    the size of each chunk in bytes, of at most 5,242,880, or nil for MediaUpload::DEFAULT_CHUNK_SIZE, or as much more as the media needs

  • :concurrency (Integer) — default: 4 —

    the number of chunks uploaded at once, of 1 to MediaUpload::MAX_CONCURRENCY

  • :shared (Boolean, nil) — default: nil —

    whether the media can be sent in more than one direct message, or nil to leave it to the API

  • :additional_owners (Array<Integer, String>, nil) — default: nil —

    the identifiers of the users, other than the one who uploads it, who may use the media, or nil for none

Returns:

  • (UploadedMedia) —

    the uploaded media, which holds the response that finalized the upload, and the processing status of media that X processes

Raises:

  • (ArgumentError) —

    if the media is neither a path nor an IO, or is a String that holds a NUL byte or a line break, as the contents of media given in place of its path do

  • (InvalidMedia) —

    if the file does not exist, the media cannot be read, or is empty, or it is larger than the API takes of its category

  • (ArgumentError) —

    if the media category is invalid, the chunk size is not a positive Integer, is larger than a segment the API takes, or would need more segments than the API numbers, the concurrency is not 1 to MAX_CONCURRENCY, shared is neither true, false, nor nil, or additional_owners is neither nil nor an Array of at least one user identifier

  • (InvalidMediaType) —

    if no media type is given and none can be inferred, or the one the media is, read from its bytes or else from the name of its file, is not one the category takes

  • (MissingMediaData) —

    if the response that initializes the upload holds no media to append the chunks to

  • (ChunkedUploadFailed) —

    if the upload is initialized, but a chunk cannot be appended, or it cannot be finalized, with the media it initialized

#client_id ⇒ String?

The OAuth 2.0 client ID

It is the one the client was given, or the one the OAuth2Authenticator it was given in place of credentials holds.

Examples:

Get the client ID

client.client_id

Returns:

  • (String, nil) —

    the OAuth 2.0 client ID



177
# File 'x-core/lib/x/core/client.rb', line 177

def client_id = @internals.client_id_in_use

#close ⇒ void

This method returns an undefined value.

Close the connections the client keeps open between requests

A later request opens a connection again. A client and the copies made of it with #with that open their connections as it does share their connections, as the app-only copy of a client that signs with OAuth 1.0a does, so closing one closes them for all of them.

Examples:

Close the connections before a long pause

client.close


618
# File 'x-core/lib/x/core/client.rb', line 618

def close = @internals.close

#count_all_posts(query, **params) ⇒ Integer Also known as: count_all_tweets Originally defined in module Objects::Lookups::Posts

Count the posts from the full archive that match a query, without reading them

The API bills a count by the request, not by the post, and refuses OAuth 1.0a for it, so a client that signs with OAuth 1.0a counts with a copy that authenticates as the app. A client signed in with OAuth 2.0 as a user that holds no credentials of the app counts as the user, which the full archive refuses with X::Forbidden.

Examples:

Count every post about Ruby from 2024

client.count_all_posts("ruby", start_time: "2024-01-01T00:00:00Z", end_time: "2025-01-01T00:00:00Z")

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters, such as start_time and end_time, and the max_pages of X::Post.count_all, which limits the pages of counts requested

Returns:

  • (Integer) —

    the number of matching posts

#count_all_posts_by_period(query, **params) ⇒ Hash{Range<Time> => Integer} Also known as: count_all_tweets_by_period Originally defined in module Objects::Lookups::Posts

Count the posts from the full archive that match a query, by period

The API bills a count by the request, not by the post, and refuses OAuth 1.0a for it, so a client that signs with OAuth 1.0a counts with a copy that authenticates as the app. A client signed in with OAuth 2.0 as a user that holds no credentials of the app counts as the user, which the full archive refuses with X::Forbidden.

Examples:

Count the posts about Ruby by day in 2024

client.count_all_posts_by_period("ruby", start_time: "2024-01-01T00:00:00Z", end_time: "2025-01-01T00:00:00Z")

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters, such as granularity, which is day by default, and the max_pages of X::Post.count_all_by_period, which limits the pages of counts requested

Returns:

  • (Hash{Range<Time> => Integer}) —

    the number of matching posts, keyed by the time each period spans, from its start up to, but not including, its end, oldest first

#count_posts(query, **params) ⇒ Integer Also known as: count_tweets Originally defined in module Objects::Lookups::Posts

Count the recent posts that match a query, without reading them

The API bills a count by the request, not by the post, and refuses OAuth 1.0a for it, so a client that signs with OAuth 1.0a counts with a copy that authenticates as the app.

Examples:

Count the recent posts about Ruby with an app-only client

client.count_posts("ruby")

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters, such as start_time and end_time, and max_pages, the most pages of counts to request

Returns:

  • (Integer) —

    the number of matching posts

#count_posts_by_period(query, **params) ⇒ Hash{Range<Time> => Integer} Also known as: count_tweets_by_period Originally defined in module Objects::Lookups::Posts

Count the posts from the last seven days that match a query, by period

The API bills a count by the request, not by the post, and refuses OAuth 1.0a for it, so a client that signs with OAuth 1.0a counts with a copy that authenticates as the app.

Examples:

Count the recent posts about Ruby by hour

client.count_posts_by_period("ruby", granularity: "hour")

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters, such as granularity, which is day by default, and max_pages, the most pages of counts to request

Returns:

  • (Hash{Range<Time> => Integer}) —

    the number of matching posts, keyed by the time each period spans, from its start up to, but not including, its end, oldest first

#create_direct_message(user, text = nil, **params) ⇒ DirectMessage Also known as: create_dm Originally defined in module Objects::Actions::DirectMessages

Send a direct message to a user as the authenticated user

Examples:

Send a direct message

client.create_direct_message(user, "Hello!")

Send an image without text

client.create_direct_message(user, media_ids: media)

Parameters:

  • user (User, String, Integer) —

    the recipient or their identifier

  • text (String, nil) (defaults to: nil) —

    the text of the message, or nil for a message of attachments alone

  • params (Hash) —

    additional request body fields, such as media_ids or attachments

Options Hash (**params):

  • :media_ids (Array<String, Integer, #fetch, Media>, String, Integer, #fetch, Media) —

    the identifiers or media keys of uploaded media to attach, what the uploads returned, or media, such as that of a post, one or many

Returns:

  • (DirectMessage) —

    the sent message, holding only its identifiers

Raises:

  • (ArgumentError) —

    if the message has neither text nor any other field, or has both media_ids and attachments

  • (MissingResource) —

    if the API answers without the message

#create_direct_message_in(conversation, text = nil, **params) ⇒ DirectMessage Also known as: create_dm_in Originally defined in module Objects::Actions::DirectMessages

Send a direct message to a conversation as the authenticated user

The conversation can be one-to-one or a group.

Examples:

Reply to the conversation of a message

client.create_direct_message_in(message, "Sounds good")

Reply with an image

client.create_direct_message_in(message, media_ids: media)

Parameters:

  • conversation (DirectMessage, String, Integer) —

    a message of the conversation, or the conversation's identifier

  • text (String, nil) (defaults to: nil) —

    the text of the message, or nil for a message of attachments alone

  • params (Hash) —

    additional request body fields, such as media_ids or attachments

Options Hash (**params):

  • :media_ids (Array<String, Integer, #fetch, Media>, String, Integer, #fetch, Media) —

    the identifiers or media keys of uploaded media to attach, what the uploads returned, or media, such as that of a post, one or many

Returns:

  • (DirectMessage) —

    the sent message, holding only its identifiers

Raises:

  • (ArgumentError) —

    if the conversation identifier is not one, the message has neither text nor any other field, or it has both media_ids and attachments

  • (MissingResource) —

    if the API answers without the message

#create_group_direct_message(users, text = nil, **params) ⇒ DirectMessage Also known as: create_group_dm Originally defined in module Objects::Actions::DirectMessages

Start a group conversation, sending its first message as the authenticated user

Examples:

Start a group conversation

client.create_group_direct_message([alice, bob], "Hello, both of you!")

Start a group conversation with an image

client.create_group_direct_message([alice, bob], media_ids: media)

Parameters:

  • users (Array<User, String, Integer>) —

    the other participants or their identifiers

  • text (String, nil) (defaults to: nil) —

    the text of the first message, or nil for a message of attachments alone

  • params (Hash) —

    additional fields of the message, such as media_ids or attachments

Options Hash (**params):

  • :media_ids (Array<String, Integer, #fetch, Media>, String, Integer, #fetch, Media) —

    the identifiers or media keys of uploaded media to attach, what the uploads returned, or media, such as that of a post, one or many

Returns:

  • (DirectMessage) —

    the sent message, holding only its identifiers, among them the conversation's

Raises:

  • (ArgumentError) —

    if the message has neither text nor any other field, or has both media_ids and attachments

  • (MissingResource) —

    if the API answers without the message

#create_list(name, **params) ⇒ List Originally defined in module Objects::Actions::Lists

Create a list owned by the authenticated user

Examples:

Create a private list

client.create_list("Rubyists", private: true)

Parameters:

  • name (String) —

    the name of the list

  • params (Hash) —

    additional request body fields: description and private

Returns:

  • (List) —

    the created list, holding only its identifier and name

Raises:

#create_post(text = nil, **params) ⇒ Post Also known as: create_tweet Originally defined in module Objects::Actions::Posts

Create a post as the authenticated user

The API bills each post created, and bills a post whose text holds a URL more than ten times as much. A post needs no text when it has something else to show, such as media.

Examples:

Create a post

client.create_post("Hello, World!")

Post an image without text

client.create_post(media_ids: [media])

Reply to a post with an image

client.create_post("Hello!", reply_to: post, media_ids: [media["id"]])

Quote a post

client.create_post("Worth reading", quote: post)

Parameters:

  • text (String, nil) (defaults to: nil) —

    the text of the post, or nil for a post without text, such as one of media alone

  • params (Hash) —

    additional request body fields, such as reply_to, quote, media_ids, or poll

Returns:

  • (Post) —

    the created post, holding only its identifier and text

Raises:

  • (ArgumentError) —

    if the post has neither text nor any other field

  • (MissingResource) —

    if the API answers without the post

#current_user(**params) {|problem| ... } ⇒ User? Originally defined in module Objects::Lookups::Users

Look up the authenticated user

Each call looks the user up, as X::User.current does, so its counts and profile are as they are now. Keep the user it returns to read them again without a request.

Examples:

Print the name of the authenticated user

puts client.current_user&.name

Parameters:

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported

Returns:

  • (User, nil) —

    the authenticated user, or nil if the API returns none

#current_user!(**params) ⇒ User Originally defined in module Objects::Lookups::Users

Look up the authenticated user, who must be found

Each call looks the user up, as X::User.current! does, so its counts and profile are as they are now. Keep the user it returns to read them again without a request.

Examples:

Print the home timeline of the authenticated user

client.current_user!.home_timeline.each { |post| puts post.text }

Parameters:

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (User) —

    the authenticated user

Raises:

#current_user_id ⇒ Integer Originally defined in module Objects::Lookups::Users

The identifier of the authenticated user, from an OAuth 1.0a token if possible

An OAuth 1.0a access token begins with the identifier of its user, so a client that holds one needs no lookup. Any other client looks the user up the first time, unless current_user or current_user! already has, and keeps the identifier, which never changes, for as long as it holds the same authenticator, since a client whose credentials change authenticates as someone else. A frozen client keeps nothing, and looks the user up each time.

Examples:

Get the identifier of the authenticated user

client.current_user_id # => 7505382

Returns:

  • (Integer) —

    the identifier

Raises:

#debug_output ⇒ IO, ...

The IO debug output is written to

Examples:

Get the debug output

client.debug_output

Returns:

  • (IO, #<<, nil) —

    the IO, or anything else that takes a String with <<, or nil for none



94
# File 'x-core/lib/x/core/client.rb', line 94

def debug_output = @internals.debug_output

#default_array_class ⇒ Class

The default class for parsing JSON arrays

Examples:

Get the default array class

client.default_array_class # => Array

Returns:

  • (Class) —

    the default class for parsing JSON arrays



213
# File 'x-core/lib/x/core/client.rb', line 213

def default_array_class = @internals.default_array_class

#default_object_class ⇒ Class, #from_response

The default class for parsing JSON objects

It is a class that JSON.parse builds each JSON object into, or one that responds to from_response and builds the result from the whole body; see X::Client.

Examples:

Get the default object class

client.default_object_class # => Hash

Returns:

  • (Class, #from_response) —

    the default class for parsing JSON objects



224
# File 'x-core/lib/x/core/client.rb', line 224

def default_object_class = @internals.default_object_class

#delete(endpoint, params: nil, headers: {}, array_class: default_array_class, object_class: default_object_class) {|response| ... } ⇒ Object?

Perform a DELETE request to the X API

Examples:

Delete a post

client.delete("tweets/1234567890")

Parameters:

  • endpoint (String) —

    the endpoint, relative to the base URL with or without a leading slash, with or without a query string

  • params (Hash, nil) (defaults to: nil) —

    query parameters appended to the endpoint

  • headers (Hash) (defaults to: {}) —

    additional headers for the request

  • array_class (Class) (defaults to: default_array_class) —

    the class for parsing JSON arrays

  • object_class (Class, #from_response) (defaults to: default_object_class) —

    the class for parsing JSON objects, or one that responds to from_response and builds the result from the whole body; see X::Client

Yield Parameters:

Returns:

  • (Object, nil) —

    the parsed response body, or what an object_class that responds to from_response builds

Raises:

  • (ArgumentError) —

    if the endpoint is not a String, is not a valid URL, or does not resolve to an http or https URL, before the request is sent

  • (ArgumentError) —

    if array_class is not a Class, or object_class is neither a Class nor responds to from_response, before the request is sent

  • (ArgumentError) —

    if headers are not a Hash of header names to Strings, before the request is sent



567
568
569
# File 'x-core/lib/x/core/client.rb', line 567

def delete(endpoint, params: nil, headers: {}, array_class: default_array_class, object_class: default_object_class, &)
  @internals.execute_request(self, :delete, endpoint, params:, headers:, array_class:, object_class:, &)
end

#delete_direct_message(message) ⇒ Boolean Also known as: delete_dm Originally defined in module Objects::Actions::DirectMessages

Delete a direct message event as the authenticated user

Examples:

Delete a direct message

client.delete_direct_message("1234567890")

Parameters:

  • message (DirectMessage, String, Integer) —

    the event or its identifier

Returns:

  • (Boolean) —

    true if the event was deleted

#delete_list(list) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Delete a list as the authenticated user

Examples:

Delete a list

client.delete_list("1234567890")

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

Returns:

  • (Boolean) —

    true if the list was deleted

#delete_post(post) ⇒ Boolean Also known as: delete_tweet Originally defined in module Objects::Actions::Posts

Delete a post as the authenticated user

Examples:

Delete a post

client.delete_post("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the post was deleted

#direct_messages(**params) ⇒ Cursor Also known as: dms Originally defined in module Objects::Lookups::DirectMessages

The most recent direct message events across every conversation

Examples:

Print the most recent direct messages

client.direct_messages.first(10).each { |message| puts message.text }

Parameters:

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the events

#direct_messages_in(conversation, **params) ⇒ Cursor Also known as: dms_in Originally defined in module Objects::Lookups::DirectMessages

The direct message events of a conversation, one-to-one or group

Examples:

Print the conversation a message belongs to

client.direct_messages_in(message).each { |event| puts event.text }

Parameters:

  • conversation (DirectMessage, String, Integer) —

    a message of the conversation, or the conversation's identifier

  • params (Hash) —

    query parameters merged over the default parameters, such as event_types

Returns:

  • (Cursor) —

    a cursor over the events

Raises:

  • (ArgumentError) —

    if the conversation identifier is not one

#direct_messages_with(user, **params) ⇒ Cursor Also known as: dms_with Originally defined in module Objects::Lookups::DirectMessages

The direct message events in the one-to-one conversation with a user

Examples:

Print the conversation with a user

client.direct_messages_with(user).each { |message| puts message.text }

Parameters:

  • user (User, String, Integer) —

    the other participant or their identifier

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the events

#expires_at ⇒ Time?

The time the OAuth 2.0 access token expires, as last refreshed

A refresh that reports no lifetime leaves it nil, rather than the time the client was given.

Examples:

Get the expiration time

client.expires_at

Returns:

  • (Time, nil) —

    the expiration time, or nil if it is not known



187
# File 'x-core/lib/x/core/client.rb', line 187

def expires_at = @internals.expires_at

#find_all_media(media, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<X::Media> Originally defined in module Objects::Lookups::Media

Look up media by media key, in parallel batches

Examples:

Look up the media of a post

client.find_all_media(post.media)

Look up media by media key

client.find_all_media(%w[3_1880028106020515840 3_1880028106020515841])

Parameters:

  • media (Array<String, X::Media, #media_key>) —

    the media keys, or the media, or what the uploads returned, whose keys are taken

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one; each is a request of up to 100 media keys, so a lower number spends a rate limit more slowly

  • params (Hash) —

    query parameters merged over the default parameters; one that overrides a default field parameter builds media that is not hydrated, so hydrate fetches the rest

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a media key that was not found

Returns:

  • (Array<X::Media>) —

    the media that was found

Raises:

  • (ArgumentError) —

    if the concurrency is less than one

#find_all_posts(ids, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<Post> Also known as: find_all_tweets Originally defined in module Objects::Lookups::Posts

Look up many posts by identifier, in parallel batches

Examples:

Look up many posts

client.find_all_posts([1234567890, 1234567891])

Look up many posts one batch at a time

client.find_all_posts(ids, concurrency: 1)

Parameters:

  • ids (Array<String, Integer, Post>) —

    the identifiers

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one; each is a request of up to 100 posts, so a lower number spends a rate limit more slowly

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Array<Post>) —

    the posts that were found

Raises:

  • (ArgumentError) —

    if the concurrency is less than one

#find_all_spaces(ids, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<Space> Originally defined in module Objects::Lookups::Spaces

Look up many spaces by identifier, in parallel batches

Examples:

Look up many spaces

client.find_all_spaces(["1DXxyRYNejbKM", "1OwGWzarWnNKQ"]).map(&:title)

Parameters:

  • ids (Array<String, Integer, Space>) —

    the identifiers

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Array<Space>) —

    the spaces that were found

Raises:

  • (ArgumentError) —

    if the concurrency is less than one

#find_all_spaces_by_creator(users, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<Space> Originally defined in module Objects::Lookups::Spaces

Look up the live and scheduled spaces many users created, in parallel batches

Examples:

Print the live spaces a user created

client.find_all_spaces_by_creator([7505382]).select { |space| space.state.eql?("live") }.map(&:title)

Parameters:

  • users (Array<User, String, Integer>) —

    the users who created the spaces, or their identifiers

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported

Returns:

  • (Array<Space>) —

    the spaces, frozen, empty if the users created none

Raises:

  • (ArgumentError) —

    if a user is not a user or the identifier of one, or the concurrency is less than one, before a request

#find_all_users(ids_or_usernames, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<User> Originally defined in module Objects::Lookups::Users

Look up many users by identifier or username, in parallel batches

Examples:

Look up many users by username

client.find_all_users(["sferik", "gem"])

Look up many users one batch at a time

client.find_all_users(ids, concurrency: 1)

Parameters:

  • ids_or_usernames (Array<Integer, User, String>) —

    identifiers or users, or usernames

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one; each is a request of up to 100 users, so a lower number spends a rate limit more slowly

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Array<User>) —

    the users that were found

Raises:

  • (ArgumentError) —

    if the concurrency is less than one

#find_all_users_by_id(ids, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<User> Originally defined in module Objects::Lookups::Users

Look up many users by identifier, in parallel batches

A String of digits is an identifier, as it is read from a response or an environment variable, so this looks the accounts those numbers identify up, where find_all_users would take them for usernames.

Examples:

Look up many users by identifier, read as Strings

client.find_all_users_by_id(ENV.fetch("USER_IDS").split(","))

Parameters:

  • ids (Array<String, Integer, User>) —

    the identifiers, or users

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as an identifier that was not found

Returns:

  • (Array<User>) —

    the users that were found

Raises:

  • (ArgumentError) —

    if a value is not an identifier, or if the concurrency is less than one

#find_all_users_by_username(usernames, concurrency: BatchFinders::DEFAULT_CONCURRENCY, **params) {|problem| ... } ⇒ Array<User> Originally defined in module Objects::Lookups::Users

Look up many users by username, in parallel batches

It looks every value up as a username, Strings of digits as the accounts whose handles are those numbers, as find_all_users looks up any String, so code that reads values from elsewhere says which it means, as find_all_users_by_id does.

Examples:

Look up many users by username

client.find_all_users_by_username(["sferik", "1234567890"])

Parameters:

  • usernames (Array<String>) —

    the usernames, with or without leading at signs

  • concurrency (Integer) (defaults to: BatchFinders::DEFAULT_CONCURRENCY) —

    the number of batches looked up at once, which must be at least one

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a username that was not found

Returns:

  • (Array<User>) —

    the users that were found

Raises:

  • (ArgumentError) —

    if a value is not a username, or if the concurrency is less than one

#find_community(id, **params) {|problem| ... } ⇒ Community? Originally defined in module Objects::Lookups::Communities

Look up a community by identifier

Examples:

Look up a community

client.find_community(1234567890).name

Parameters:

  • id (String, Integer, Community) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Community, nil) —

    the community or nil if the community was not found

#find_community!(id, **params) ⇒ Community Originally defined in module Objects::Lookups::Communities

Look up a community by identifier, which must exist

Examples:

Look up a community

client.find_community!(1234567890).name

Parameters:

  • id (String, Integer, Community) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

Raises:

#find_direct_message(id, **params) {|problem| ... } ⇒ DirectMessage? Also known as: find_dm Originally defined in module Objects::Lookups::DirectMessages

Look up a direct message event by identifier

Examples:

Look up a direct message

client.find_direct_message(1234567890).text

Parameters:

  • id (String, Integer, DirectMessage) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (DirectMessage, nil) —

    the event or nil if the event was not found

#find_direct_message!(id, **params) ⇒ DirectMessage Also known as: find_dm! Originally defined in module Objects::Lookups::DirectMessages

Look up a direct message event by identifier, which must exist

Examples:

Look up a direct message

client.find_direct_message!(1234567890).text

Parameters:

  • id (String, Integer, DirectMessage) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

Raises:

#find_list(id, **params) {|problem| ... } ⇒ List? Originally defined in module Objects::Lookups::Lists

Look up a list by identifier

Examples:

Look up a list

client.find_list(1234567890).name

Parameters:

  • id (String, Integer, List) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (List, nil) —

    the list or nil if the list was not found

#find_list!(id, **params) ⇒ List Originally defined in module Objects::Lookups::Lists

Look up a list by identifier, which must exist

Examples:

Look up a list

client.find_list!(1234567890).name

Parameters:

  • id (String, Integer, List) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (List) —

    the list

Raises:

#find_media(media_key, **params) {|problem| ... } ⇒ X::Media? Originally defined in module Objects::Lookups::Media

Look up media by media key

A post refers to its media by media key, and so does what an upload returns, so this reads the photo, video, or animated GIF that was uploaded, with its URL and variants.

Examples:

Look up media by media key

client.find_media("3_1880028106020515840")

Look up media that was uploaded

client.find_media(uploaded)

Parameters:

  • media_key (String, X::Media, #media_key) —

    the media key, such as 3_1880028106020515840, media, or what an upload returned

  • params (Hash) —

    query parameters merged over the default parameters; one that overrides a default field parameter to leave some out builds media that is not hydrated, so hydrate fetches the rest

Yield Parameters:

  • problem (Problem) —

    each problem the API reported

Returns:

  • (X::Media, nil) —

    the media, or nil if it was not found

#find_media!(media_key, **params) ⇒ X::Media Originally defined in module Objects::Lookups::Media

Look up media by media key, raising if it is not found

Examples:

Look up media that must exist

client.find_media!("3_1880028106020515840")

Parameters:

  • media_key (String, X::Media, #media_key) —

    the media key, media, or what an upload returned

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

Raises:

#find_post(id, **params) {|problem| ... } ⇒ Post? Also known as: find_tweet Originally defined in module Objects::Lookups::Posts

Look up a post by identifier

Examples:

Look up a post

client.find_post(1234567890).text

Parameters:

  • id (String, Integer, Post) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Post, nil) —

    the post or nil if the post was not found

#find_post!(id, **params) ⇒ Post Also known as: find_tweet! Originally defined in module Objects::Lookups::Posts

Look up a post by identifier, which must exist

Examples:

Look up a post

client.find_post!(1234567890).text

Parameters:

  • id (String, Integer, Post) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Post) —

    the post

Raises:

#find_space(id, **params) {|problem| ... } ⇒ Space? Originally defined in module Objects::Lookups::Spaces

Look up a space by identifier

Examples:

Look up a space

client.find_space("1DXxyRYNejbKM").title

Parameters:

  • id (String, Integer, Space) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (Space, nil) —

    the space or nil if the space was not found

#find_space!(id, **params) ⇒ Space Originally defined in module Objects::Lookups::Spaces

Look up a space by identifier, which must exist

Examples:

Look up a space

client.find_space!("1DXxyRYNejbKM").title

Parameters:

  • id (String, Integer, Space) —

    the identifier

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

Raises:

#find_user(id_or_username, **params) {|problem| ... } ⇒ User? Originally defined in module Objects::Lookups::Users

Look up a user by identifier or username

Examples:

Look up a user by username

client.find_user("sferik")

Look up a user by identifier

client.find_user(7505382)

Parameters:

  • id_or_username (Integer, User, String) —

    an identifier or a user, or a username

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (User, nil) —

    the user or nil if the user was not found

#find_user!(id_or_username, **params) ⇒ User Originally defined in module Objects::Lookups::Users

Look up a user by identifier or username, which must exist

Examples:

Look up a user by username

client.find_user!("sferik")

Parameters:

  • id_or_username (Integer, User, String) —

    an identifier or a user, or a username

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (User) —

    the user

Raises:

#find_user_by_id(id, **params) {|problem| ... } ⇒ User? Originally defined in module Objects::Lookups::Users

Look up a user by identifier

A String of digits is an identifier, as it is read from a response or an environment variable, so this looks the account that number identifies up, where find_user would take it for a username.

Examples:

Look up a user by an identifier read as a String

client.find_user_by_id(ENV.fetch("USER_ID"))

Parameters:

  • id (String, Integer, User) —

    the identifier, or a user

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (User, nil) —

    the user or nil if the user was not found

Raises:

  • (ArgumentError) —

    if the value is not an identifier

#find_user_by_id!(id, **params) ⇒ User Originally defined in module Objects::Lookups::Users

Look up a user by identifier, which must exist

Examples:

Look up a user by an identifier read as a String

client.find_user_by_id!("7505382")

Parameters:

  • id (String, Integer, User) —

    the identifier, or a user

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (User) —

    the user

Raises:

  • (ArgumentError) —

    if the value is not an identifier

  • (MissingResource) —

    if the user was not found

#find_user_by_username(username, **params) {|problem| ... } ⇒ User? Originally defined in module Objects::Lookups::Users

Look up a user by username

It looks every value up as a username, a String of digits as the account whose handle is that number, as find_user looks up any String, so code that reads a value from elsewhere says which it means, as find_user_by_id does.

Examples:

Look up a user whose username is a number

client.find_user_by_username("1234567890")

Parameters:

  • username (String) —

    the username, with or without a leading at sign

  • params (Hash) —

    query parameters merged over the default parameters

Yield Parameters:

  • problem (Problem) —

    each problem the API reported, such as a resource that was not found

Returns:

  • (User, nil) —

    the user or nil if the user was not found

Raises:

  • (ArgumentError) —

    if the value is not a username

#find_user_by_username!(username, **params) ⇒ User Originally defined in module Objects::Lookups::Users

Look up a user by username, which must exist

Examples:

Look up a user by username

client.find_user_by_username!("sferik")

Parameters:

  • username (String) —

    the username, with or without a leading at sign

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (User) —

    the user

Raises:

  • (ArgumentError) —

    if the value is not a username

  • (MissingResource) —

    if the user was not found

#follow(user) ⇒ Boolean Originally defined in module Objects::Actions::Relationships

Follow a user as the authenticated user

A protected user must accept a request to follow them first, so for a protected user true means the follow was requested, not that the authenticated user follows them: until they accept it, the follows? of the authenticated user, as in client.current_user!.follows?(user), answers false.

Examples:

Follow a user

client.follow("7505382")

Parameters:

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the authenticated user now follows the user, or, for a protected user, has requested to follow them

#follow_list(list) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Follow a list as the authenticated user

Examples:

Follow a list

client.follow_list("1234567890")

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user now follows the list

#get(endpoint, params: nil, headers: {}, array_class: default_array_class, object_class: default_object_class) {|response| ... } ⇒ Object?

Perform a GET request to the X API

Examples:

Get a user by username

client.get("users/by/username/sferik")

Get users by identifier, requesting only some fields

client.get("users", params: {ids: [1, 2], "user.fields": %w[id username]})

Read what a response reported of the rate limit it spent

user = client.get("users/me") { |response| limit = response.rate_limit }

Parameters:

  • endpoint (String) —

    the endpoint, relative to the base URL with or without a leading slash, with or without a query string

  • params (Hash, nil) (defaults to: nil) —

    query parameters appended to the endpoint; nil values are dropped and arrays are joined with commas

  • headers (Hash) (defaults to: {}) —

    additional headers for the request

  • array_class (Class) (defaults to: default_array_class) —

    the class for parsing JSON arrays

  • object_class (Class, #from_response) (defaults to: default_object_class) —

    the class for parsing JSON objects, or one that responds to from_response and builds the result from the whole body; see X::Client

Yield Parameters:

Returns:

  • (Object, nil) —

    the parsed response body, or what an object_class that responds to from_response builds

Raises:

  • (ArgumentError) —

    if the endpoint is not a String, is not a valid URL, or does not resolve to an http or https URL, before the request is sent

  • (ArgumentError) —

    if array_class is not a Class, or object_class is neither a Class nor responds to from_response, before the request is sent

  • (ArgumentError) —

    if headers are not a Hash of header names to Strings, before the request is sent



482
483
484
# File 'x-core/lib/x/core/client.rb', line 482

def get(endpoint, params: nil, headers: {}, array_class: default_array_class, object_class: default_object_class, &)
  @internals.execute_request(self, :get, endpoint, params:, headers:, array_class:, object_class:, &)
end

#get_stream(endpoint, params: nil, headers: {}) {|http_response| ... } ⇒ Object

Open a GET request whose body the block reads as it arrives, as a stream's is

The response is passed to the block before its body is read, so the block reads it, as with read_body, for as long as it likes; the connection is opened for the request alone, with the client's timeouts and proxy, and closed once the block returns. The request carries the client's credentials and headers as any other does, and a token the API rejects is refreshed, or fetched again, and the request sent once more, as for any other. It is neither retried after a failure nor redirected, and a response that is not successful raises the HTTPError of its status, once on_response is passed it, without reaching the block. Nothing the block reads is passed to on_response, since the body is the block's to read.

An error the block raises reaches the caller as it was raised, but for the errors of the socket the body is read from, such as the IOError of a body that could not be read, or a read that timed out, which raise a NetworkError that names the request, as a connection that fails does, so that a stream that dropped is told apart from a block that failed. An error is the socket's when read_body raises it from the socket, whatever its class, so the IOError or Errno::ENOSPC of a file the block writes to, in the block passed to read_body or out of it, is the block's, and raised as it was.

Examples:

Print the body of the sample stream as it arrives

client.app_only.get_stream("tweets/sample/stream") { |response| response.read_body { |chunk| print chunk } }

Parameters:

  • endpoint (String) —

    the endpoint, relative to the base URL with or without a leading slash, with or without a query string

  • params (Hash, nil) (defaults to: nil) —

    query parameters appended to the endpoint

  • headers (Hash) (defaults to: {}) —

    additional headers for the request

Yield Parameters:

  • http_response (Net::HTTPResponse) —

    the successful response, whose body is not yet read

Returns:

  • (Object) —

    what the block returns

Raises:

  • (ArgumentError) —

    if no block is given, or the endpoint is not a String, is not a valid URL, or does not resolve to an http or https URL, before the request is sent

  • (ArgumentError) —

    if headers are not a Hash of header names to Strings, before the request is sent

  • (HTTPError) —

    if the response is not successful

  • (NetworkError) —

    if the request cannot be sent, or its body cannot be read



602
603
604
605
606
# File 'x-core/lib/x/core/client.rb', line 602

def get_stream(endpoint, params: nil, headers: {}, &)
  raise ArgumentError, "get_stream takes a block, which reads the body of the response" unless block_given?

  @internals.execute_stream(self, endpoint, params:, headers:, &)
end

#headers ⇒ Hash{String => String}

The headers sent with every request the client makes

They are defaults: a header of the same name passed to a request, or to a stream, is sent in place of the client's, and each of them is sent in place of a default of the gem, such as its User-Agent. A header that carries credentials, such as Authorization or Cookie, is dropped by a redirect to another origin, as one passed to a request is.

Each is named by a String, a header the client was given by a Symbol among them, so that a header is read by the name it is sent with, whichever the client was given: a Symbol names the header its underscores name with hyphens, as :user_agent names User-Agent.

Examples:

Read the headers a client sends

client.headers # => {"User-Agent" => "my-app/1.0"}

Returns:

  • (Hash{String => String}) —

    the headers, frozen, each named by a String



252
# File 'x-core/lib/x/core/client.rb', line 252

def headers = @internals.headers

#hide_reply(post) ⇒ Boolean Originally defined in module Objects::Actions::Posts

Hide a reply to a post of the authenticated user

Examples:

Hide a reply

client.hide_reply("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the reply or its identifier

Returns:

  • (Boolean) —

    true if the reply is now hidden

#inspect ⇒ String

Summarize the client for the console without revealing credentials

Examples:

Inspect a client

client.inspect # => #<X::Client base_url="https://api.x.com/2/" authenticator=#<X::BearerTokenAuthenticator>>

Returns:

  • (String) —

    the class name, base URL, and authenticator



397
398
399
# File 'x-core/lib/x/core/client.rb', line 397

def inspect
  "#<#{self.class} base_url=#{base_url.inspect} authenticator=#{authenticator.inspect}>"
end

#keep_alive_timeout ⇒ Integer, Float

The time to keep an idle connection open for the next request, in seconds

Examples:

Get the keep-alive timeout

client.keep_alive_timeout # => 30

Returns:

  • (Integer, Float) —

    the timeout



87
# File 'x-core/lib/x/core/client.rb', line 87

def keep_alive_timeout = @internals.keep_alive_timeout

#like(post) ⇒ Boolean Originally defined in module Objects::Actions::Engagement

Like a post as the authenticated user

Examples:

Like a post

client.like("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user now likes the post

#load_tokens ⇒ #call?

The callable a refresh reads the stored OAuth2Tokens with

It returns the tokens in the storage that processes sharing the tokens of a user read, or nil for none there.

Examples:

Read the loader a refresh reads stored tokens with

client.load_tokens

Returns:

  • (#call, nil) —

    the callable, or nil for none



155
# File 'x-core/lib/x/core/client.rb', line 155

def load_tokens = @internals.load_tokens

#max_rate_limit_retries ⇒ Integer

The maximum number of times to retry a request refused for a rate limit

Examples:

Get the maximum number of rate limit retries

client.max_rate_limit_retries # => 0

Returns:

  • (Integer) —

    the maximum number of retries



108
# File 'x-core/lib/x/core/client.rb', line 108

def max_rate_limit_retries = @internals.max_rate_limit_retries

#max_rate_limit_wait ⇒ Integer, Float

The maximum number of seconds to wait for a rate limit to reset

Examples:

Get the maximum rate limit wait

client.max_rate_limit_wait # => 900

Returns:

  • (Integer, Float) —

    the maximum wait, in seconds



115
# File 'x-core/lib/x/core/client.rb', line 115

def max_rate_limit_wait = @internals.max_rate_limit_wait

#max_redirects ⇒ Integer

The maximum number of redirects to follow

Examples:

Get the maximum number of redirects

client.max_redirects # => 10

Returns:

  • (Integer) —

    the maximum number of redirects



101
# File 'x-core/lib/x/core/client.rb', line 101

def max_redirects = @internals.max_redirects

#max_retries ⇒ Integer

The maximum number of times to send an idempotent request again after a failure

Examples:

Get the maximum number of retries

client.max_retries # => 2

Returns:

  • (Integer) —

    the maximum number of retries



122
# File 'x-core/lib/x/core/client.rb', line 122

def max_retries = @internals.max_retries

#memoize(key, value) ⇒ Object

Keep a value under a key, for the authenticator of the client

For the gems that extend a client, and kept throughout 1.x, as #memoized is.

Examples:

Keep the identifier of the authenticated user

client.memoize(:x_objects_current_user_id, 7_505_382) # => 7505382

Parameters:

  • key (Symbol) —

    the key, which names the gem that keeps it

  • value (Object) —

    the value

Returns:

  • (Object) —

    the value



674
# File 'x-core/lib/x/core/client.rb', line 674

def memoize(key, value) = @internals.memoize(key, value)

#memoized(key) ⇒ Object?

The value kept under a key for the authenticator the client holds

For the gems that extend a client, such as x-objects, which keeps the identifier of the user its credentials act for with it, so that a later x-core 1.x, which installs beside an earlier x-objects 1.x, keeps its name and behavior throughout 1.x. A value is read only while the client holds the authenticator it was kept with, and a client keeps values though it is frozen, which a copy made with dup or clone shares, and a copy made with #with does not.

Examples:

Read the identifier of the authenticated user that x-objects kept

client.memoized(:x_objects_current_user_id) # => 7505382

Parameters:

  • key (Symbol) —

    the key, which names the gem that keeps it

Returns:

  • (Object, nil) —

    the value, or nil if none is kept for the authenticator of the client



662
# File 'x-core/lib/x/core/client.rb', line 662

def memoized(key) = @internals.memoized(key)

#mute(user) ⇒ Boolean Originally defined in module Objects::Actions::Relationships

Mute a user as the authenticated user

Examples:

Mute a user

client.mute("7505382")

Parameters:

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the authenticated user now mutes the user

#on_response ⇒ #call?

A callable passed an X::Response after each request and streamed object

It is the hook of every request a client makes. A block passed to a single request receives the same summary, after this, for code that reads the response of that one request rather than of all of them.

Examples:

Read the hook a client reports to

client.on_response

Returns:

  • (#call, nil) —

    the callable, or nil for none



235
# File 'x-core/lib/x/core/client.rb', line 235

def on_response = @internals.on_response

#open_timeout ⇒ Integer, ...

The timeout for opening connections, in seconds

Examples:

Get the open timeout

client.open_timeout # => 10

Returns:

  • (Integer, Float, nil) —

    the timeout, or nil for none



66
# File 'x-core/lib/x/core/client.rb', line 66

def open_timeout = @internals.open_timeout

The topics trending for the authenticated user

Examples:

Print the topics trending for the authenticated user

client.personalized_trends.each { |trend| puts trend.name }

Parameters:

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

#pin_list(list) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Pin a list as the authenticated user

Examples:

Pin a list

client.pin_list("1234567890")

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user has pinned the list

#post(endpoint, body = nil, params: nil, form: nil, headers: {}, array_class: default_array_class, object_class: default_object_class, **unknown) {|response| ... } ⇒ Object?

Perform a POST request to the X API

Examples:

Create a post

client.post("tweets", {text: "Hello, World!"})

Post a form to the v1.1 API

v1_client.post("account/settings.json", form: {lang: "en"})

Parameters:

  • endpoint (String) —

    the endpoint, relative to the base URL with or without a leading slash, with or without a query string

  • body (String, Hash, Array, nil) (defaults to: nil) —

    the request body; a body that is not a String, such as a Hash or an Array, is encoded as JSON

  • params (Hash, nil) (defaults to: nil) —

    query parameters appended to the endpoint

  • form (Hash, nil) (defaults to: nil) —

    fields to send as a form-encoded body, in place of a body; as with params, nil values are dropped and arrays are joined with commas

  • headers (Hash) (defaults to: {}) —

    additional headers for the request

  • array_class (Class) (defaults to: default_array_class) —

    the class for parsing JSON arrays

  • object_class (Class, #from_response) (defaults to: default_object_class) —

    the class for parsing JSON objects, or one that responds to from_response and builds the result from the whole body; see X::Client

Yield Parameters:

Returns:

  • (Object, nil) —

    the parsed response body, or what an object_class that responds to from_response builds

Raises:

  • (ArgumentError) —

    if both a body and form fields are given, or a keyword is given that the method takes none of, as the fields of a body given without the braces of a Hash are, before the request is sent

  • (ArgumentError) —

    if the endpoint is not a String, is not a valid URL, or does not resolve to an http or https URL, before the request is sent

  • (ArgumentError) —

    if array_class is not a Class, or object_class is neither a Class nor responds to from_response, before the request is sent

  • (ArgumentError) —

    if headers are not a Hash of header names to Strings, before the request is sent



513
514
515
516
# File 'x-core/lib/x/core/client.rb', line 513

def post(endpoint, body = nil, params: nil, form: nil, headers: {}, array_class: default_array_class, object_class: default_object_class, **unknown, &) # steep:ignore DifferentMethodParameterKind
  SettingValidator.no_unknown_keywords!(:post, endpoint, unknown)
  @internals.execute_request(self, :post, endpoint, body:, params:, form:, headers:, array_class:, object_class:, &)
end

#post_usage(**params) {|problem| ... } ⇒ PostUsage? Originally defined in module Objects::Lookups::Posts

Look up how many posts the app's project has read

The usage endpoint takes app-only authentication alone, so a client that signs with OAuth 1.0a looks it up with a copy that authenticates as the app, and one signed in with OAuth 2.0 as a user that holds no credentials of the app is refused with X::Forbidden.

A response that holds no usage returns nil, as current_user does for a users/me that holds no user, and passes the problems it reported to the block, if there is one.

Examples:

Check how much of the monthly cap remains

usage = client.post_usage
usage.project_cap - usage.project_usage if usage

Parameters:

  • params (Hash) —

    query parameters, such as days, the number of days to report, which is 7 by default

Yield Parameters:

  • problem (Problem) —

    each problem the API reported

Returns:

  • (PostUsage, nil) —

    the usage, or nil if the response holds none

#post_usage!(**params) ⇒ PostUsage Originally defined in module Objects::Lookups::Posts

Look up how many posts the app's project has read, which must be returned

Examples:

Check how much of the monthly cap remains

usage = client.post_usage!
usage.project_cap - usage.project_usage

Parameters:

  • params (Hash) —

    query parameters, such as days, the number of days to report, which is 7 by default

Returns:

Raises:

#put(endpoint, body = nil, params: nil, form: nil, headers: {}, array_class: default_array_class, object_class: default_object_class, **unknown) {|response| ... } ⇒ Object?

Perform a PUT request to the X API

Examples:

Update a resource

client.put("some/endpoint", {key: "value"})

Parameters:

  • endpoint (String) —

    the endpoint, relative to the base URL with or without a leading slash, with or without a query string

  • body (String, Hash, Array, nil) (defaults to: nil) —

    the request body; a body that is not a String, such as a Hash or an Array, is encoded as JSON

  • params (Hash, nil) (defaults to: nil) —

    query parameters appended to the endpoint

  • form (Hash, nil) (defaults to: nil) —

    fields to send as a form-encoded body, in place of a body; as with params, nil values are dropped and arrays are joined with commas

  • headers (Hash) (defaults to: {}) —

    additional headers for the request

  • array_class (Class) (defaults to: default_array_class) —

    the class for parsing JSON arrays

  • object_class (Class, #from_response) (defaults to: default_object_class) —

    the class for parsing JSON objects, or one that responds to from_response and builds the result from the whole body; see X::Client

Yield Parameters:

Returns:

  • (Object, nil) —

    the parsed response body, or what an object_class that responds to from_response builds

Raises:

  • (ArgumentError) —

    if both a body and form fields are given, or a keyword is given that the method takes none of, as the fields of a body given without the braces of a Hash are, before the request is sent

  • (ArgumentError) —

    if the endpoint is not a String, is not a valid URL, or does not resolve to an http or https URL, before the request is sent

  • (ArgumentError) —

    if array_class is not a Class, or object_class is neither a Class nor responds to from_response, before the request is sent

  • (ArgumentError) —

    if headers are not a Hash of header names to Strings, before the request is sent



543
544
545
546
# File 'x-core/lib/x/core/client.rb', line 543

def put(endpoint, body = nil, params: nil, form: nil, headers: {}, array_class: default_array_class, object_class: default_object_class, **unknown, &) # steep:ignore DifferentMethodParameterKind
  SettingValidator.no_unknown_keywords!(:put, endpoint, unknown)
  @internals.execute_request(self, :put, endpoint, body:, params:, form:, headers:, array_class:, object_class:, &)
end

#read_timeout ⇒ Integer, ...

The timeout for reading responses, in seconds

Examples:

Get the read timeout

client.read_timeout # => 60

Returns:

  • (Integer, Float, nil) —

    the timeout, or nil for none



73
# File 'x-core/lib/x/core/client.rb', line 73

def read_timeout = @internals.read_timeout

#remove_list_member(list, user) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Remove a member from a list as the authenticated user

Examples:

Remove a member from a list

client.remove_list_member("1234567890", user)

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the user is no longer a member

#repost(post) ⇒ Boolean Also known as: retweet Originally defined in module Objects::Actions::Engagement

Repost a post as the authenticated user

Examples:

Repost a post

client.repost("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user has reposted the post

#reposts_of_me(**params) ⇒ Cursor Also known as: retweets_of_me Originally defined in module Objects::Lookups::Posts

The posts of the authenticated user that other users have reposted

Examples:

Print the reposted posts

client.reposts_of_me.each { |post| puts post.text }

Parameters:

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the reposted posts

#save_tokens ⇒ #call?

A callable passed the OAuth2Tokens of each refresh, and of an authorization

Examples:

Read the hook a refresh reports to

client.save_tokens

Returns:

  • (#call, nil) —

    the callable, or nil for none



145
# File 'x-core/lib/x/core/client.rb', line 145

def save_tokens = @internals.save_tokens

#scopes ⇒ Array<String>?

The scopes X granted the OAuth 2.0 access token, as last refreshed

A refresh that names no scopes keeps those the client held, as OAuth 2.0 has it. They are the ones the client was given, as the client of OAuth2Authorization#client is given those of the exchange of the code, until a refresh names others.

Examples:

Check that the user let the app post

client.scopes&.include?("tweet.write")

Returns:

  • (Array<String>, nil) —

    the scopes, frozen, or nil if they are not known



199
# File 'x-core/lib/x/core/client.rb', line 199

def scopes = @internals.scopes

#search_all_posts(query, **params) ⇒ Cursor Also known as: search_all_tweets Originally defined in module Objects::Lookups::Posts

Search the full archive of posts

Examples:

Print every post about Ruby

client.search_all_posts("ruby -is:retweet").each { |post| puts post.text }

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the matching posts

#search_communities(query, **params) ⇒ Cursor Originally defined in module Objects::Lookups::Communities

Search communities

Examples:

Print the communities matching a query

client.search_communities("ruby").each { |community| puts community.name }

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the matching communities

#search_posts(query, **params) ⇒ Cursor Also known as: search_tweets Originally defined in module Objects::Lookups::Posts

Search recent posts

Examples:

Print posts about Ruby

client.search_posts("ruby -is:retweet").each { |post| puts post.text }

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the matching posts

#search_spaces(query, **params) ⇒ Cursor Originally defined in module Objects::Lookups::Spaces

Search spaces by their titles

Examples:

Print the live spaces about Ruby

client.search_spaces("ruby", state: "live").each { |space| puts space.title }

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters merged over the default parameters, such as state: live or scheduled

Returns:

  • (Cursor) —

    a cursor over the matching spaces

#search_users(query, **params) ⇒ Cursor Originally defined in module Objects::Lookups::Users

Search users

Examples:

Print the users matching a query

client.search_users("ruby").each { |user| puts user.username }

Parameters:

  • query (String) —

    the search query

  • params (Hash) —

    query parameters merged over the default parameters

Returns:

  • (Cursor) —

    a cursor over the matching users

#streaming(read_timeout: StreamingClient::DEFAULT_READ_TIMEOUT, max_reconnects: StreamingClient::DEFAULT_MAX_RECONNECTS, on_reconnect: nil) ⇒ StreamingClient Originally defined in module Streaming::API

A client for the streaming endpoints, which reads and reconnects differently

Each call builds a new streaming client, so the one a stream runs on is kept in a variable to stop it with X::StreamingClient#stop, and a streaming client that was stopped, which stays stopped, is replaced by another.

Examples:

Stream filtered posts, giving up after five reconnects in a row

client.streaming(max_reconnects: 5).stream("tweets/search/stream") { |post| puts post }

Log each reconnect of a stream

client.streaming(on_reconnect: ->(error, wait) { logger.warn("#{error.message}; reconnecting in #{wait}s") })

Parameters:

  • read_timeout (Integer, Float, nil) (defaults to: StreamingClient::DEFAULT_READ_TIMEOUT) —

    the timeout for reading from a stream in seconds, as X::StreamingClient#initialize takes it

  • max_reconnects (Integer, Float) (defaults to: StreamingClient::DEFAULT_MAX_RECONNECTS) —

    the maximum number of times in a row to reconnect a stream that drops, as X::StreamingClient#initialize takes it

  • on_reconnect (#call, nil) (defaults to: nil) —

    a callable passed the error that dropped a stream and the seconds it waits before each reconnect, as X::StreamingClient#initialize takes it, or nil for none

Returns:

  • (StreamingClient) —

    a streaming client that shares this client's credentials and settings

Raises:

  • (ArgumentError) —

    if the read timeout is neither a finite number of seconds of at least 25 nor nil, the maximum number of reconnects is neither a count nor Float::INFINITY, or on_reconnect neither responds to call nor is nil

The topics trending in a place

Examples:

Print the ten topics trending most in the world

client.trends(1, max_trends: 10).each { |trend| puts trend.name }

Parameters:

  • woeid (Integer, String) —

    the Yahoo! Where On Earth identifier of the place, such as 1 for the world

  • params (Hash) —

    query parameters, such as max_trends, which is 50, the most the API returns, unless given

Returns:

  • (Array<Trend>) —

    the trends, frozen

Raises:

  • (ArgumentError) —

    if the WOEID is not a number, before a request

#unblock(user) ⇒ Boolean Originally defined in module Objects::Actions::Relationships

Unblock a user as the authenticated user

Examples:

Unblock a user

client.unblock("7505382")

Parameters:

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer blocks the user

#unbookmark(post) ⇒ Boolean Originally defined in module Objects::Actions::Engagement

Remove a bookmark as the authenticated user

The bookmark endpoints take only OAuth 2.0 user context, which the object layer cannot route around, so a client that signs with OAuth 1.0a is refused.

Examples:

Remove a bookmark

client.unbookmark("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer has the post bookmarked

#unfollow(user) ⇒ Boolean Originally defined in module Objects::Actions::Relationships

Unfollow a user as the authenticated user

Examples:

Unfollow a user

client.unfollow("7505382")

Parameters:

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer follows the user

#unfollow_list(list) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Unfollow a list as the authenticated user

Examples:

Unfollow a list

client.unfollow_list("1234567890")

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer follows the list

#unhide_reply(post) ⇒ Boolean Originally defined in module Objects::Actions::Posts

Show a reply to a post of the authenticated user after hiding it

Examples:

Show a hidden reply

client.unhide_reply("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the reply or its identifier

Returns:

  • (Boolean) —

    true if the reply is no longer hidden

#unlike(post) ⇒ Boolean Originally defined in module Objects::Actions::Engagement

Unlike a post as the authenticated user

Examples:

Unlike a post

client.unlike("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer likes the post

#unmute(user) ⇒ Boolean Originally defined in module Objects::Actions::Relationships

Unmute a user as the authenticated user

Examples:

Unmute a user

client.unmute("7505382")

Parameters:

  • user (User, String, Integer) —

    the user or their identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer mutes the user

#unpin_list(list) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Unpin a list as the authenticated user

Examples:

Unpin a list

client.unpin_list("1234567890")

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer has the list pinned

#unrepost(post) ⇒ Boolean Also known as: unretweet Originally defined in module Objects::Actions::Engagement

Undo a repost as the authenticated user

Examples:

Undo a repost

client.unrepost("1234567890")

Parameters:

  • post (Post, String, Integer) —

    the post or its identifier

Returns:

  • (Boolean) —

    true if the authenticated user no longer reposts the post

#update_list(list, **params) ⇒ Boolean Originally defined in module Objects::Actions::Lists

Update the name, description, or privacy of a list as the authenticated user

Examples:

Make a list private

client.update_list("1234567890", private: true)

Parameters:

  • list (List, String, Integer) —

    the list or its identifier

  • params (Hash) —

    the request body fields to change: name, description, and private

Returns:

  • (Boolean) —

    true if the list was updated

Raises:

  • (ArgumentError) —

    if no field is given to change, before any request

#update_profile_banner(media, **options) ⇒ void Originally defined in module Uploader::API

This method returns an undefined value.

Update the profile banner of the authenticated user from a file

Examples:

Update the profile banner

client.update_profile_banner("banner.png", width: 1500, height: 500)

Parameters:

  • media (String, Pathname, IO, StringIO) —

    the path to the image, or an IO that reads it

  • options (Hash) —

    the options of X::Uploader::Account#update_profile_banner, which give the region of the image to use

Options Hash (**options):

  • :width (Integer, nil) — default: nil —

    the width of the region, in pixels, of at least 1

  • :height (Integer, nil) — default: nil —

    the height of the region, in pixels, of at least 1

  • :offset_left (Integer, nil) — default: nil —

    the pixels by which the region is offset from the left, of at least 0

  • :offset_top (Integer, nil) — default: nil —

    the pixels by which the region is offset from the top, of at least 0

Raises:

  • (InvalidMedia) —

    if the file does not exist

  • (ArgumentError) —

    if the media is neither a path nor an IO, or a width, height, or offset is neither nil nor an Integer of the pixels it takes

  • (InvalidMedia) —

    if the media cannot be read, is empty, or is larger than the 5 megabytes X takes

  • (InvalidMediaType) —

    if the image does not begin with the signature of a GIF, a JPEG, or a PNG

#update_profile_image(media) ⇒ void Originally defined in module Uploader::API

This method returns an undefined value.

Update the profile image of the authenticated user from a file

Examples:

Update the profile image

client.update_profile_image("avatar.png")

Parameters:

  • media (String, Pathname, IO, StringIO) —

    the path to the image, or an IO that reads it

Raises:

  • (InvalidMedia) —

    if the file does not exist

  • (ArgumentError) —

    if the media is neither a path nor an IO

  • (InvalidMedia) —

    if the media cannot be read, is empty, or is larger than the 700 kilobytes the API takes

  • (InvalidMediaType) —

    if the image does not begin with the signature of a GIF, a JPEG, or a PNG

#upload_media(media, **options) ⇒ UploadedMedia Originally defined in module Uploader::API

Upload media and wait for it to be processed

The media is a path, or an IO open on it. Media given as a String or a Pathname is read from the file it names, and media given as a File or a Tempfile through that IO, a chunk at a time, so media of any size uploads without being held in memory; media given as any other IO, such as a StringIO, is read to its end and held.

A video or subtitles upload in chunks, which send their media type, as a single request does not. The media category is inferred from the bytes the media begins with, or else from the name of its file, unless media_category says what it is. The chunks are sent by threads of their own, so the on_response of the client runs on those threads for the response of each chunk.

An image, and a GIF that a single request takes, upload in a single request, which takes no chunks and no media type, so chunk_size, concurrency, and media_type are ignored for them: a chunk_size or a concurrency that is not valid still raises, but none is sent. Media given shared: true uploads in chunks, and uses all three.

Each chunk is a request a rate limit can refuse, which fails the upload with ChunkedUploadFailed unless the client retries it, which it does only max_rate_limit_retries times, 0 by default, so upload a large video with a client whose max_rate_limit_retries is set, such as X::Client.new(max_rate_limit_retries: 3).

Examples:

Upload an image with alt text and post it

media = client.upload_media("cat.jpg", alt_text: "A cat asleep on a keyboard")
client.create_post("Look at this cat", media_ids: [media])

Upload an image held in memory, whose category its signature names

client.upload_media(StringIO.new(File.binread("cat.png")))

Upload media of a category no signature names

client.upload_media(StringIO.new(subtitles), media_category: "subtitles")

Parameters:

  • media (String, Pathname, IO, StringIO) —

    the path to the media to upload, or an IO open on it

  • options (Hash) —

    the options of MediaUpload#upload

Options Hash (**options):

  • :media_category (String, Symbol, nil) — default: nil —

    the media category, in any case, inferred when nil from the bytes the media begins with, or else from the name of its file

  • :alt_text (String, nil) — default: nil —

    alt text describing the media, of 1 to 1,000 characters, added once the media is uploaded and processed, or nil for none

  • :processing_timeout (Integer, Float, nil) — default: 600 —

    the seconds to wait for media that X processes, such as a video, to process, of at least 0, or nil to wait for as long as processing takes

  • :media_type (String, nil) — default: nil —

    the MIME type of media uploaded in chunks, inferred from the media and category when nil; ignored for media uploaded in a single request, such as an image

  • :chunk_size (Integer, nil) — default: nil —

    the size of each chunk in bytes, of at most 5,242,880, or nil for MediaUpload::DEFAULT_CHUNK_SIZE, or as much more as the media needs; ignored for media uploaded in a single request, such as an image

  • :concurrency (Integer) — default: 4 —

    the number of chunks uploaded at once, of 1 to MediaUpload::MAX_CONCURRENCY; ignored for media uploaded in a single request, such as an image

  • :shared (Boolean, nil) — default: nil —

    whether the media can be sent in more than one direct message, or nil to leave it to the API; media that is shared uploads in chunks

  • :additional_owners (Array<Integer, String>, nil) — default: nil —

    the identifiers of the users, other than the one who uploads it, who may use the media, or nil for none

Returns:

  • (UploadedMedia) —

    the uploaded media, which holds the upload response, or the processing status of media that X processes

Raises:

  • (ArgumentError) —

    if the media is neither a path nor an IO, or is a String that holds a NUL byte or a line break, as the contents of media given in place of its path do

  • (InvalidMedia) —

    if the file does not exist

  • (InvalidMedia) —

    if the media cannot be read, or is empty, which holds nothing to upload

  • (InvalidMedia) —

    if the media is larger than the API takes of its category, whatever the account: 5 megabytes of an image, 15 of a GIF, and one of subtitles, or larger than the 16 gigabytes it takes of any

  • (ArgumentError) —

    if the media category is invalid, the alt text is empty or longer than the API takes, the chunk size is not a positive Integer, is larger than a segment the API takes, or would need more segments than the API numbers, the concurrency is not 1 to MAX_CONCURRENCY, the processing timeout is neither nil nor a finite number of seconds of at least 0, shared is neither true, false, nor nil, or additional_owners is neither nil nor an Array of at least one user identifier

  • (InvalidMediaType) —

    if no media category is given for media whose type neither its bytes nor the name of its file names, or the category does not take the type of the media

  • (MissingMediaData) —

    if a response of the upload holds no media, or carries no body at all

  • (ChunkedUploadFailed) —

    if media uploaded in chunks is initialized, but a chunk cannot be appended, or it cannot be finalized, with the media it initialized

  • (MediaProcessingFailed) —

    if media processing failed, or ended in no state X documents, with the status X reported

  • (MediaProcessingTimeout) —

    if the media is still processing once the processing timeout would pass

  • (MediaProcessingCheckFailed) —

    if the media is uploaded, but a check of its processing fails, as when the API answers it with an error, with the media it uploaded

  • (AltTextFailed) —

    if the media is uploaded, but its alt text cannot be added, with the media it uploaded

#with(**options) ⇒ Client

Copy the client with some of its options changed

A copy that authenticates with OAuth 2.0 shares the client's authenticator, so that a refresh by either client reaches the other, since X accepts a refresh token once, unless it is given a client ID, client secret, access token, or refresh token that the authenticator does not hold. It shares it whatever the tokens are when it is built, so a refresh on another thread while it is built reaches it too. A refresh then passes the tokens it issued to the save_tokens of each client that shares it, once for each distinct callable. The expiration time and the scopes belong to the access token they share, so such a copy is refused either: give expires_at or scopes beside the access token and refresh token they describe.

A copy that does not share the OAuth 2.0 authenticator, since it is given credentials the authenticator does not hold or an authenticator of its own, holds tokens that may be another user's, so it holds none of the refresh token, expiration time, scopes, save_tokens, or load_tokens of the client unless it is given them.

A copy of a client that was given its authenticator shares it, unless the copy is given a credential, which replaces it, or an authenticator of its own, which also replaces the credentials of a client that holds them.

A copy that opens its connections as the client does, with the same timeouts, keep-alive timeout, debug output, and proxy, shares the connections the client keeps open, so that a copy made for each request, such as to send a header of its own, opens none of its own; #close on either closes them for both, and a later request of either opens them again.

Examples:

Derive an API v1.1 client

v1_client = client.with(base_url: "https://api.x.com/1.1/")

Derive an app-only client from the API key and secret

app_client = client.with(access_token: nil, access_token_secret: nil)

Derive a client that authenticates with another authenticator

user_client = app_client.with(authenticator: X::OAuth2Authenticator.new(**stored_tokens))

Parameters:

  • options (Hash) —

    the options to change, as accepted by initialize

Returns:

  • (Client) —

    a new client with the same credentials and settings, apart from the options given

Raises:

  • (ArgumentError) —

    if the copy shares the OAuth 2.0 authenticator of the client and is given expires_at or scopes



434
# File 'x-core/lib/x/core/client.rb', line 434

def with(**options) = @internals.with(self, options) # steep:ignore DifferentMethodParameterKind

#with_retries { ... } ⇒ Object

Send a request that is safe to send twice again after a failure

The client sends no POST again, since the API may have acted on one whose answer never arrived, and sends no request again after its answer failed to arrive, since the API bills a read it answered whether or not the answer arrived. A request that is safe to send again anyway, such as the chunk of an upload, which names the segment it is appended at and which the API bills nothing for, is sent again with this: after a ServerError, a RequestTimeout, or a NetworkError of any kind, up to max_retries times, as the client sends an idempotent request again, after the wait a response asks for, or a backoff that doubles with each retry up to a minute and is cut short at random. A response that asks to be left alone for longer than a minute raises at once. The block must build its request anew each time, so that each attempt is signed afresh, as a request of the client is.

Wrap a request the client sends no more than once, such as a POST: the client sends a GET, a PUT, or a DELETE again itself, so one wrapped in this is sent max_retries times more for each time this sends it, nine times in all with the defaults, rather than three.

For the gems that extend a client, such as x-uploader, which sends each chunk of an upload with it, so that a later x-core 1.x, which installs beside an earlier x-uploader 1.x, keeps its name and behavior throughout 1.x.

Examples:

Append a chunk of an upload, again after a failure

client.with_retries { client.post("media/upload/1/append", body, headers:) }

Yields:

  • sends the request

Returns:

  • (Object) —

    what the block returns

Raises:

  • (NetworkError) —

    if the request fails once more than the retries allow

  • (ServerError, RequestTimeout) —

    if the API fails to answer once more than the retries allow, or asks for a wait longer than a minute



647
# File 'x-core/lib/x/core/client.rb', line 647

def with_retries(&) = @internals.with_retries(&)

#write_timeout ⇒ Integer, ...

The timeout for writing requests, in seconds

Examples:

Get the write timeout

client.write_timeout # => 60

Returns:

  • (Integer, Float, nil) —

    the timeout, or nil for none



80
# File 'x-core/lib/x/core/client.rb', line 80

def write_timeout = @internals.write_timeout