NAME
Discord::Client::REST - Future-based Discord REST API v10 client
SYNOPSIS
use Discord::Client::REST;
# Create a standalone REST client.
my $rest = Discord::Client::REST->new(token => $ENV{DISCORD_BOT_TOKEN});
# Wait for one request outside a handler.
my $request = $rest->get_current_user;
$rest->loop->await($request);
my $user = $request->get;
# Release owned resources.
$rest->close->get;
DESCRIPTION
Makes Discord API v10 HTTP requests without a Gateway connection. The caller drives the loop. Asynchronous methods return Futures with decoded JSON, string IDs and unknown fields preserved. HTTP 204 returns undef. Message helpers send JSON, not multipart file uploads.
METHODS
new
my $rest = Discord::Client::REST->new(token => $token, loop => $loop);
Returns a client without network I/O. loop is optional; otherwise one is created. auth => 0 needs no bot token and omits Bot authorization by default. http can inject a caller-owned transport.
base_url defaults to https://discord.com/api/v10 and accepts another HTTP(S) URL for testing. HTTPS verifies the peer and hostname; redirects are disabled. user_agent_url sets the URL in DiscordBot (URL, VERSION). Resource limits are described under "RATE LIMITS AND OPTIONS".
request
request(method, path, query => $hashref, json => $body) returns a Future for an API request. Query and JSON arguments are optional.
# Fetch one page of messages.
my $page = $rest->request('GET', '/channels/'.$channel_id.'/messages',
query => {limit => 20, before => $message_id},
);
# Send a JSON message.
my $send = $rest->request('POST', '/channels/'.$channel_id.'/messages',
json => {content => 'Hello'},
);
Methods are uppercase GET, HEAD, POST, PUT, PATCH or DELETE. Paths start with one slash and are relative to the base URL. Requests use Bot authorization by default. Query arrays become repeated keys; JSON is UTF-8. Invalid arguments return a failed Future before I/O.
Success returns decoded JSON with string IDs and unknown fields preserved. HTTP 204 and HEAD return undef. raw_response => 1 returns a cloned HTTP::Response with its original request removed. Successful JSON is still validated. Malformed JSON fails with json.
For interaction callbacks and webhooks, auth => 0 on a request omits Bot authorization and uses a separate global-limit scope. A client constructed with auth => 0 needs no bot token and uses that scope by default.
get_current_user
Returns a Future containing the bot user hashref.
get_user
get_user(user_id) returns a Future containing a user hashref.
get_channel
get_channel(channel_id) returns a Future containing a channel hashref.
get_guild
get_guild(guild_id, query_pairs) returns a Future containing a guild hashref. Query pairs are optional.
get_guild_channels
get_guild_channels(guild_id) returns a Future containing an arrayref of channels.
get_guild_member
get_guild_member(guild_id, user_id) returns a Future containing a member hashref.
get_message
get_message(channel_id, message_id) returns a Future containing a message hashref.
get_messages
get_messages(channel_id, query_pairs) returns a Future containing one page of messages as an arrayref. Accepts limit (1-100) and at most one of before, after or around. It does not scan further pages.
create_message
create_message(channel_id, text_or_hash) returns a Future containing the created message. Hashrefs preserve embeds, components, allowed_mentions and other supplied JSON fields.
edit_message
edit_message(channel_id, message_id, text_or_hash) returns a Future containing the edited message.
delete_message
delete_message(channel_id, message_id) returns a Future resolving to undef.
create_dm
create_dm(user_id) returns a Future containing the DM channel hashref.
trigger_typing
trigger_typing(channel_id) returns a Future resolving to undef.
Helper IDs are decimal strings. Invalid IDs, pagination or body shapes fail before I/O. Discord validates endpoint permissions and payload semantics.
close
Returns a Future after failing outstanding work with shutdown, cancelling I/O and timers, and removing the owned HTTP notifier. Repeated calls succeed. An injected HTTP transport remains caller-owned.
loop
Returns the configured IO::Async::Loop.
stats
Returns a copied hashref of request, retry and queue counters.
ERRORS AND CANCELLATION
# Inspect a failed Future.
my ($message, $category, $error) = $request->failure;
my $status = $error->status;
my $code = $error->discord_code;
The error is a Discord::Client::REST::Error. It exposes message, category, status, discord_code, request_id and details. HTTP status and Discord code are separate. request_id comes from X-Request-ID or CF-Ray. Validation details redact known credentials and sensitive fields. Error strings omit tokens, bodies and raw transport exceptions.
request-
Invalid request arguments.
authentication-
HTTP 401 or previously rejected bot token.
permission-
HTTP 403.
not_found-
HTTP 404.
http-
Other unsuccessful HTTP response.
server-
HTTP 5xx.
json-
Invalid successful JSON response.
rate_limit-
Exhausted retries or unusable 429 delay.
timeout-
HTTP request or stall timeout.
transport-
Transport failure or invalid result.
queue_full-
Request or route capacity exhausted.
shutdown-
Client closed.
A Bot-authorized 401 rejects queued and future requests without sending them. Already sent requests may finish but cannot retry. Create a new client with a corrected token. Cancelling a Future cancels active local I/O; it does not undo an operation Discord may have received.
RATE LIMITS AND OPTIONS
Limits are learned from response headers. Channel, guild and webhook ID/token pairs separate buckets. One request runs per bucket; separate buckets can run concurrently. X-RateLimit-Bucket joins routes sharing quota. X-RateLimit-Reset-After is preferred over X-RateLimit-Reset.
A 429 waits for the larger usable delay from JSON retry_after and Retry-After. Global and shared limits are respected. Missing or nonpositive delays fail. Only received 429 responses are retried. Network failures, timeouts and 5xx responses are not retried automatically.
max_pending-
Queued and active requests; default 1000.
max_in_flight-
Concurrent HTTP requests; default 8.
max_buckets-
Route identities and learned bucket mappings; default 1024.
max_retries-
Additional 429 attempts; zero disables retries; default 3.
timeout-
Seconds per HTTP attempt; default 30.
Capacity exhaustion fails with queue_full. There is no queue-wait deadline; callers can cancel Futures. Use one REST client per bot token because separate clients do not coordinate limits.
stats returns request, retry and queue counters. on_diagnostic => CODE receives synchronous events with counts, status and timing, excluding request paths, headers, bodies and credentials.
TRANSPORT INJECTION
Pass http => $transport to supply an object with do_request(%args). It returns a cancellable Future yielding an HTTP::Response; an http failure containing a response is also accepted. Arguments include request, host, port, SSL, timeout and max_redirects. Attach the transport to the supplied loop and honor those options.
Tests can supply clock for monotonic seconds, wall_clock for Unix seconds, and delay returning a cancellable Future for the given delay in seconds.
LICENSE
Apache License 2.0.