Class: Async::Discord::Client
- Inherits:
-
Object
- Object
- Async::Discord::Client
- Defined in:
- lib/async/discord/client.rb
Overview
Constant Summary collapse
- DEFAULT_BASE_URL =
"https://discord.com"- DEFAULT_MAX_RETRIES =
Retry defaults
3- DEFAULT_RETRY_BASE =
0.5- DEFAULT_MAX_RETRY_DELAY =
30- RATE_LIMIT_STATUS =
Status codes eligible for retry
429- GATEWAY_ERROR_STATUSES =
[502, 503, 504].freeze
- DEFAULT_RESPONSE_SIZE_LIMIT =
Response size limits (bytes)
50 * 1024 * 1024
- DEFAULT_ERROR_RESPONSE_SIZE_LIMIT =
50 MiB
512 * 1024
Instance Attribute Summary collapse
-
#token ⇒ Object
readonly
512 KiB.
Instance Method Summary collapse
-
#api ⇒ Object
Returns a Gateway that provides method-chained access to every Discord HTTP API endpoint.
- #close ⇒ Object
-
#get(path, max_retries: nil) ⇒ Object
── Low-level HTTP ────────────────────────────────────────.
-
#initialize(token:, base_url: DEFAULT_BASE_URL, max_retries: DEFAULT_MAX_RETRIES, retry_base_delay: DEFAULT_RETRY_BASE, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, response_size_limit: DEFAULT_RESPONSE_SIZE_LIMIT, error_response_size_limit: DEFAULT_ERROR_RESPONSE_SIZE_LIMIT) ⇒ Client
constructor
A new instance of Client.
- #post(path, body = {}, max_retries: nil) ⇒ Object
- #put(path, body = {}, max_retries: nil) ⇒ Object
-
#request(method, path, body = nil, max_retries: nil) ⇒ Object
General-purpose request method supporting any HTTP method.
Constructor Details
#initialize(token:, base_url: DEFAULT_BASE_URL, max_retries: DEFAULT_MAX_RETRIES, retry_base_delay: DEFAULT_RETRY_BASE, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, response_size_limit: DEFAULT_RESPONSE_SIZE_LIMIT, error_response_size_limit: DEFAULT_ERROR_RESPONSE_SIZE_LIMIT) ⇒ Client
Returns a new instance of Client.
43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/async/discord/client.rb', line 43 def initialize(token:, base_url: DEFAULT_BASE_URL, max_retries: DEFAULT_MAX_RETRIES, retry_base_delay: DEFAULT_RETRY_BASE, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, response_size_limit: DEFAULT_RESPONSE_SIZE_LIMIT, error_response_size_limit: DEFAULT_ERROR_RESPONSE_SIZE_LIMIT) @token = token @base = base_url @max_retries = max_retries @retry_base_delay = retry_base_delay @max_retry_delay = max_retry_delay @response_size_limit = response_size_limit @error_response_size_limit = error_response_size_limit @headers = [ ["authorization", "Bot #{token}"], ["content-type", "application/json"], ["user-agent", "AsyncDiscord (https://github.com/general-intelligence-systems/async-matrix, 1.0)"] ] end |
Instance Attribute Details
#token ⇒ Object (readonly)
512 KiB
41 42 43 |
# File 'lib/async/discord/client.rb', line 41 def token @token end |
Instance Method Details
#api ⇒ Object
Returns a Gateway that provides method-chained access to every Discord HTTP API endpoint. Chains are validated against the official OpenAPI path tree and terminated by .get(), .post(), .put(), .patch(), or .delete().
client.api.channels("123")..post(content: "hello")
client.api.guilds("789").get
client.api.users("@me").get
74 75 76 |
# File 'lib/async/discord/client.rb', line 74 def api Api::Gateway.new(self) end |
#close ⇒ Object
92 93 94 95 |
# File 'lib/async/discord/client.rb', line 92 def close @internet&.close @internet = nil end |
#get(path, max_retries: nil) ⇒ Object
── Low-level HTTP ────────────────────────────────────────
80 81 82 |
# File 'lib/async/discord/client.rb', line 80 def get(path, max_retries: nil) request("GET", path, nil, max_retries: max_retries) end |
#post(path, body = {}, max_retries: nil) ⇒ Object
84 85 86 |
# File 'lib/async/discord/client.rb', line 84 def post(path, body = {}, max_retries: nil) request("POST", path, body, max_retries: max_retries) end |
#put(path, body = {}, max_retries: nil) ⇒ Object
88 89 90 |
# File 'lib/async/discord/client.rb', line 88 def put(path, body = {}, max_retries: nil) request("PUT", path, body, max_retries: max_retries) end |
#request(method, path, body = nil, max_retries: nil) ⇒ Object
General-purpose request method supporting any HTTP method.
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/async/discord/client.rb', line 98 def request(method, path, body = nil, max_retries: nil) url = "#{@base}#{path}" json_body = body ? JSON.generate(body) : nil effective_max_retries = max_retries || @max_retries Console.debug(self) { "#{method} #{path}" } attempt = 0 loop do response = internet.call(method, url, @headers, json_body) status = response.status if (200..299).cover?(status) payload = read_limited(response, @response_size_limit) return payload && !payload.empty? ? JSON.parse(payload) : {} end attempt += 1 if attempt <= effective_max_retries && retryable_status?(status) delay = compute_retry_delay(status, response, attempt) Console.warn(self) { "#{method} #{path} returned #{status}, retry #{attempt}/#{effective_max_retries} in #{delay.round(2)}s" } response.close if response.respond_to?(:close) sleep(delay) next end payload = read_limited(response, @error_response_size_limit) parsed = begin; JSON.parse(payload || "{}"); rescue; {} end discord_code = parsed["code"] discord_msg = parsed["message"] || payload.to_s[0..200] Console.error(self) { "Discord API #{status}: #{discord_code} — #{discord_msg}" } error_class = case status when 401 then AuthError when 429 then RateLimitError when 400..499 then ApiError else ServerError end raise error_class.new( discord_code.to_s, discord_msg, status: status ) end end |