Noizu.Discord.Request (Noizu.Discord v0.1.0)

Copy Markdown View Source

Request engine behind every resource module (REST only — no gateway).

Builds the Req call (Authorization: Bot <token>, Discord User-Agent), decodes JSON, applies the bounded retry policy, and normalizes failures to %Noizu.Discord.Error{}.

Return shapes

  • {:ok, decoded_body} — 2xx; decoded_body is nil for empty (204) responses.
  • {:ok, decoded_body, meta} — when the call is made with meta: true.
  • {:error, %Noizu.Discord.Error{}}.

meta is %{status: integer, attempts: pos_integer, rate_limit: rate_limit} where rate_limit is built from the X-RateLimit-* headers (see rate_limit_meta/1).

Retry policy

  • 429 — always retried (Discord rejects rate-limited requests without applying them, so this is safe for POST/PATCH too). The delay is the JSON retry_after (seconds, float), else X-RateLimit-Reset-After, else Retry-After, else exponential-ish backoff; clamped to :max_delay_ms.
  • 5xx — retried only for idempotent methods (GET, HEAD, PUT, DELETE).
  • At most :max_attempts (default 3) total attempts; then the last error is returned (tag: :rate_limited for a final 429).

Common per-call options

  • :meta — true to receive {:ok, body, meta}.
  • :reason — sets X-Audit-Log-Reason (URL-encoded) for moderation actions.

Summary

Functions

Percent-encodes one path segment (ids, emoji, tokens).

Extracts rate-limit info from response headers (X-RateLimit-Bucket, -Limit, -Remaining, -Reset, -Reset-After, -Scope, -Global). Missing values are nil.

Splits resource-call options into {query_params, request_opts}.

Types

meta()

@type meta() :: %{
  status: pos_integer(),
  attempts: pos_integer(),
  rate_limit: rate_limit()
}

method()

@type method() :: :get | :post | :put | :patch | :delete | :head

rate_limit()

@type rate_limit() :: %{
  bucket: String.t() | nil,
  limit: integer() | nil,
  remaining: integer() | nil,
  reset: float() | nil,
  reset_after: float() | nil,
  scope: String.t() | nil,
  global: boolean()
}

result()

@type result() ::
  {:ok, term()} | {:ok, term(), meta()} | {:error, Noizu.Discord.Error.t()}

Functions

encode_segment(segment)

@spec encode_segment(term()) :: String.t()

Percent-encodes one path segment (ids, emoji, tokens).

perform(client, method, path, opts \\ [])

@spec perform(Noizu.Discord.Client.t(), method(), String.t(), keyword()) :: result()

Performs one API call.

Options: :json (body), :params (query; nil values dropped), :headers, :auth (default true; false omits the bot token — webhooks/interactions), :meta, :reason.

rate_limit_meta(headers)

@spec rate_limit_meta(map()) :: rate_limit()

Extracts rate-limit info from response headers (X-RateLimit-Bucket, -Limit, -Remaining, -Reset, -Reset-After, -Scope, -Global). Missing values are nil.

split_opts(opts, query_keys)

@spec split_opts(keyword(), [atom()]) :: {keyword(), keyword()}

Splits resource-call options into {query_params, request_opts}.

query_keys are moved to query params; :meta, :reason, :headers stay request options. Unknown keys are ignored.