module Discordrb::API

List of methods representing endpoints in Discord's API

Constants

APIBASE

The base URL of the Discord REST API.

CDN_URL

The URL of Discord's CDN

Public Instance Methods

acknowledge_message(token, channel_id, message_id) click to toggle source

Acknowledge that a message has been received The last acknowledged message will be sent in the ready packet, so this is an easy way to catch up on messages

# File lib/discordrb/api.rb, line 271
def acknowledge_message(token, channel_id, message_id)
  request(
    :channels_cid_messages_mid_ack,
    nil, # This endpoint is unavailable for bot accounts and thus isn't subject to its rate limit requirements.
    :post,
    "#{api_base}/channels/#{channel_id}/messages/#{message_id}/ack",
    nil,
    Authorization: token
  )
end
api_base() click to toggle source

@return [String] the currently used API base URL.

# File lib/discordrb/api.rb, line 20
def api_base
  @api_base || APIBASE
end
api_base=(value) click to toggle source

Sets the API base URL to something.

# File lib/discordrb/api.rb, line 25
def api_base=(value)
  @api_base = value
end
app_icon_url(app_id, icon_id, format = 'webp') click to toggle source

Make an icon URL from application and icon IDs

# File lib/discordrb/api.rb, line 188
def app_icon_url(app_id, icon_id, format = 'webp')
  "#{cdn_url}/app-icons/#{app_id}/#{icon_id}.#{format}"
end
bot_name() click to toggle source

@return [String] the bot name, previously specified using bot_name=.

# File lib/discordrb/api.rb, line 35
def bot_name
  @bot_name
end
bot_name=(value) click to toggle source

Sets the bot name to something.

# File lib/discordrb/api.rb, line 40
def bot_name=(value)
  @bot_name = value
end
cdn_url() click to toggle source

@return [String] the currently used CDN url

# File lib/discordrb/api.rb, line 30
def cdn_url
  @cdn_url || CDN_URL
end
create_oauth_application(token, name, redirect_uris) click to toggle source

Create an OAuth application

# File lib/discordrb/api.rb, line 232
def create_oauth_application(token, name, redirect_uris)
  request(
    :oauth2_applications,
    nil,
    :post,
    "#{api_base}/oauth2/applications",
    { name: name, redirect_uris: redirect_uris }.to_json,
    Authorization: token,
    content_type: :json
  )
end
emoji_icon_url(emoji_id, format = 'webp') click to toggle source

Make an emoji icon URL from emoji ID

# File lib/discordrb/api.rb, line 203
def emoji_icon_url(emoji_id, format = 'webp')
  "#{cdn_url}/emojis/#{emoji_id}.#{format}"
end
gateway(token) click to toggle source

Get the gateway to be used

# File lib/discordrb/api.rb, line 283
def gateway(token)
  request(
    :gateway,
    nil,
    :get,
    "#{api_base}/gateway",
    Authorization: token
  )
end
handle_preemptive_rl(headers, mutex, key) click to toggle source

Handles premeptive ratelimiting by waiting the given mutex by the difference of the Date header to the X-Ratelimit-Reset header, thus making sure we don't get 429'd in any subsequent requests.

# File lib/discordrb/api.rb, line 154
def handle_preemptive_rl(headers, mutex, key)
  Discordrb::LOGGER.ratelimit "RL bucket depletion detected! Date: #{headers[:date]} Reset: #{headers[:x_ratelimit_reset]}"

  now = Time.rfc2822(headers[:date])
  reset = Time.at(headers[:x_ratelimit_reset].to_i)

  delta = reset - now

  Discordrb::LOGGER.warn("Locking RL mutex (key: #{key}) for #{delta} seconds preemptively")
  sync_wait(delta, mutex)
end
icon_url(server_id, icon_id, format = 'webp') click to toggle source

Make an icon URL from server and icon IDs

# File lib/discordrb/api.rb, line 183
def icon_url(server_id, icon_id, format = 'webp')
  "#{cdn_url}/icons/#{server_id}/#{icon_id}.#{format}"
end
login(email, password) click to toggle source

Login to the server

# File lib/discordrb/api.rb, line 208
def login(email, password)
  request(
    :auth_login,
    nil,
    :post,
    "#{api_base}/auth/login",
    email: email,
    password: password
  )
end
logout(token) click to toggle source

Logout from the server

# File lib/discordrb/api.rb, line 220
def logout(token)
  request(
    :auth_logout,
    nil,
    :post,
    "#{api_base}/auth/logout",
    nil,
    Authorization: token
  )
end
mutex_wait(mutex) click to toggle source

Wait for a specified mutex to unlock and do nothing with it afterwards.

# File lib/discordrb/api.rb, line 72
def mutex_wait(mutex)
  mutex.lock
  mutex.unlock
end
oauth_application(token) click to toggle source

Get the bot's OAuth application's information

# File lib/discordrb/api.rb, line 258
def oauth_application(token)
  request(
    :oauth2_applications_me,
    nil,
    :get,
    "#{api_base}/oauth2/applications/@me",
    Authorization: token
  )
end
raw_request(type, attributes) click to toggle source

Performs a RestClient request. @param type [Symbol] The type of HTTP request to use. @param attributes [Array] The attributes for the request.

# File lib/discordrb/api.rb, line 80
def raw_request(type, attributes)
  RestClient.send(type, *attributes)
rescue RestClient::Forbidden => e
  # HACK: for #request, dynamically inject restclient's response into NoPermission - this allows us to ratelimit
  noprm = Discordrb::Errors::NoPermission.new
  noprm.define_singleton_method(:_rc_response) { e.response }
  raise noprm, "The bot doesn't have the required permission to do this!"
rescue RestClient::BadGateway
  Discordrb::LOGGER.warn('Got a 502 while sending a request! Not a big deal, retrying the request')
  retry
end
request(key, major_parameter, type, *attributes) click to toggle source

Make an API request, including rate limit handling.

# File lib/discordrb/api.rb, line 93
def request(key, major_parameter, type, *attributes)
  # Add a custom user agent
  attributes.last[:user_agent] = user_agent if attributes.last.is_a? Hash

  # The most recent Discord rate limit requirements require the support of major parameters, where a particular route
  # and major parameter combination (*not* the HTTP method) uniquely identifies a RL bucket.
  key = [key, major_parameter].freeze

  begin
    mutex = @mutexes[key] ||= Mutex.new

    # Lock and unlock, i.e. wait for the mutex to unlock and don't do anything with it afterwards
    mutex_wait(mutex)

    # If the global mutex happens to be locked right now, wait for that as well.
    mutex_wait(@global_mutex) if @global_mutex.locked?

    response = nil
    begin
      response = raw_request(type, attributes)
    rescue RestClient::Exception => e
      response = e.response
      raise e
    rescue Discordrb::Errors::NoPermission => e
      if e.respond_to?(:_rc_response)
        response = e._rc_response
      else
        Discordrb::LOGGER.warn("NoPermission doesn't respond_to? _rc_response!")
      end

      raise e
    ensure
      if response
        handle_preemptive_rl(response.headers, mutex, key) if response.headers[:x_ratelimit_remaining] == '0' && !mutex.locked?
      else
        Discordrb::LOGGER.ratelimit('Response was nil before trying to preemptively rate limit!')
      end
    end
  rescue RestClient::TooManyRequests => e
    # If the 429 is from the global RL, then we have to use the global mutex instead.
    mutex = @global_mutex if e.response.headers[:x_ratelimit_global] == 'true'

    unless mutex.locked?
      response = JSON.parse(e.response)
      wait_seconds = response['retry_after'].to_i / 1000.0
      Discordrb::LOGGER.ratelimit("Locking RL mutex (key: #{key}) for #{wait_seconds} seconds due to Discord rate limiting")
      trace("429 #{key.join(' ')}")

      # Wait the required time synchronized by the mutex (so other incoming requests have to wait) but only do it if
      # the mutex isn't locked already so it will only ever wait once
      sync_wait(wait_seconds, mutex)
    end

    retry
  end

  response
end
reset_mutexes() click to toggle source

Resets all rate limit mutexes

# File lib/discordrb/api.rb, line 61
def reset_mutexes
  @mutexes = {}
  @global_mutex = Mutex.new
end
splash_url(server_id, splash_id, format = 'webp') click to toggle source

Make a splash URL from server and splash IDs

# File lib/discordrb/api.rb, line 198
def splash_url(server_id, splash_id, format = 'webp')
  "#{cdn_url}/splashes/#{server_id}/#{splash_id}.#{format}"
end
sync_wait(time, mutex) click to toggle source

Wait a specified amount of time synchronised with the specified mutex.

# File lib/discordrb/api.rb, line 67
def sync_wait(time, mutex)
  mutex.synchronize { sleep time }
end
trace(reason) click to toggle source

Perform rate limit tracing. All this method does is log the current backtrace to the console with the `:ratelimit` level. @param reason [String] the reason to include with the backtrace.

# File lib/discordrb/api.rb, line 169
def trace(reason)
  unless @trace
    Discordrb::LOGGER.debug("trace was called with reason #{reason}, but tracing is not enabled")
    return
  end

  Discordrb::LOGGER.ratelimit("Trace (#{reason}):")

  caller.each do |str|
    Discordrb::LOGGER.ratelimit(' ' + str)
  end
end
trace=(value) click to toggle source

Changes the rate limit tracing behaviour. If rate limit tracing is on, a full backtrace will be logged on every RL hit. @param value [true, false] whether or not to enable rate limit tracing

# File lib/discordrb/api.rb, line 47
def trace=(value)
  @trace = value
end
update_oauth_application(token, name, redirect_uris, description = '', icon = nil) click to toggle source

Change an OAuth application's properties

# File lib/discordrb/api.rb, line 245
def update_oauth_application(token, name, redirect_uris, description = '', icon = nil)
  request(
    :oauth2_applications,
    nil,
    :put,
    "#{api_base}/oauth2/applications",
    { name: name, redirect_uris: redirect_uris, description: description, icon: icon }.to_json,
    Authorization: token,
    content_type: :json
  )
end
user_agent() click to toggle source

Generate a user agent identifying this requester as discordrb.

# File lib/discordrb/api.rb, line 52
def user_agent
  # This particular string is required by the Discord devs.
  required = "DiscordBot (https://github.com/meew0/discordrb, v#{Discordrb::VERSION})"
  @bot_name ||= ''

  "#{required} rest-client/#{RestClient::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL} discordrb/#{Discordrb::VERSION} #{@bot_name}"
end
validate_token(token) click to toggle source

Validate a token (this request will fail if the token is invalid)

# File lib/discordrb/api.rb, line 294
def validate_token(token)
  request(
    :auth_login,
    nil,
    :post,
    "#{api_base}/auth/login",
    {}.to_json,
    Authorization: token,
    content_type: :json
  )
end
voice_regions(token) click to toggle source

Get a list of available voice regions

# File lib/discordrb/api.rb, line 307
def voice_regions(token)
  request(
    :voice_regions,
    nil,
    :get,
    "#{api_base}/voice/regions",
    Authorization: token,
    content_type: :json
  )
end
widget_url(server_id, style = 'shield') click to toggle source

Make a widget picture URL from server ID

# File lib/discordrb/api.rb, line 193
def widget_url(server_id, style = 'shield')
  "#{api_base}/guilds/#{server_id}/widget.png?style=#{style}"
end