Module: MijDiscord::Core::API

Defined in:
lib/mij-discord/core/api.rb

Defined Under Namespace

Modules: Channel, Invite, Server, User, Webhook

Constant Summary collapse

APIBASE_URL =
'https://discordapp.com/api/v6'
CDN_URL =
'https://cdn.discordapp.com'

Class Method Summary collapse

Class Method Details

.acknowledge_message(auth, channel_id, message_id) ⇒ Object

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



110
111
112
113
114
115
116
117
118
119
# File 'lib/mij-discord/core/api.rb', line 110

def acknowledge_message(auth, 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,
    "#{APIBASE_URL}/channels/#{channel_id}/messages/#{message_id}/ack",
    nil,
    Authorization: auth
  )
end

.app_icon_url(app_id, icon_id, format = :png) ⇒ Object

Make an icon URL from application and icon IDs



27
28
29
# File 'lib/mij-discord/core/api.rb', line 27

def app_icon_url(app_id, icon_id, format = :png)
  "#{CDN_URL}/app-icons/#{app_id}/#{icon_id}.#{format}"
end

.create_oauth_application(auth, name, redirect_uris) ⇒ Object

Create an OAuth application



71
72
73
74
75
76
77
78
79
80
81
# File 'lib/mij-discord/core/api.rb', line 71

def create_oauth_application(auth, name, redirect_uris)
  request(
    :oauth2_applications,
    nil,
    :post,
    "#{APIBASE_URL}/oauth2/applications",
    { name: name, redirect_uris: redirect_uris }.to_json,
    Authorization: auth,
    content_type: :json
  )
end

.emoji_icon_url(emoji_id, format = :png) ⇒ Object

Make an emoji icon URL from emoji ID



42
43
44
# File 'lib/mij-discord/core/api.rb', line 42

def emoji_icon_url(emoji_id, format = :png)
  "#{CDN_URL}/emojis/#{emoji_id}.#{format}"
end

.gateway(auth) ⇒ Object

Get the gateway to be used



122
123
124
125
126
127
128
129
130
# File 'lib/mij-discord/core/api.rb', line 122

def gateway(auth)
  request(
    :gateway,
    nil,
    :get,
    "#{APIBASE_URL}/gateway",
    Authorization: auth,
  )
end

.icon_url(server_id, icon_id, format = :png) ⇒ Object

Make an icon URL from server and icon IDs



22
23
24
# File 'lib/mij-discord/core/api.rb', line 22

def icon_url(server_id, icon_id, format = :png)
  "#{CDN_URL}/icons/#{server_id}/#{icon_id}.#{format}"
end

.login(email, password) ⇒ Object

Login to the server



47
48
49
50
51
52
53
54
55
56
# File 'lib/mij-discord/core/api.rb', line 47

def (email, password)
  request(
    :auth_login,
    nil,
    :post,
    "#{APIBASE_URL}/auth/login",
    email: email,
    password: password
  )
end

.logout(auth) ⇒ Object

Logout from the server



59
60
61
62
63
64
65
66
67
68
# File 'lib/mij-discord/core/api.rb', line 59

def logout(auth)
  request(
    :auth_logout,
    nil,
    :post,
    "#{APIBASE_URL}/auth/logout",
    nil,
    Authorization: auth
  )
end

.oauth_application(auth) ⇒ Object

Get the bot’s OAuth application’s information



97
98
99
100
101
102
103
104
105
# File 'lib/mij-discord/core/api.rb', line 97

def oauth_application(auth)
  request(
    :oauth2_applications_me,
    nil,
    :get,
    "#{APIBASE_URL}/oauth2/applications/@me",
    Authorization: auth
  )
end

.raw_request(type, *attributes) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/mij-discord/core/api.rb', line 157

def raw_request(type, *attributes)
  RestClient.send(type, *attributes)
rescue RestClient::RequestFailed => e
  # Holy fuck, Discord…
  if (klazz = MijDiscord::Errors::HTTP_ERRORS[e.http_code])
    data = JSON.parse(e.response)
    if data['message'] || data['code']
      raise klazz.new(data['code'], data['message'], e.response)
    elsif (error = (data['content'] || data['embed']).join)
      if MijDiscord::Errors::MessageTooLong.match_pattern?(error)
        raise MijDiscord::Errors::MessageTooLong.new(error, e.response)
      end
    end
  end
  raise
rescue RestClient::BadGateway
  MijDiscord::LOGGER.warn('HTTP') { 'Received 502 Bad Gateway during API request' }
  retry
end

.request(key, major_param, type, *attributes) ⇒ Object



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/mij-discord/core/api.rb', line 177

def request(key, major_param, type, *attributes)
  ratelimit_delta, response = nil, nil

  if (params = attributes.last).is_a?(Hash)
    params[:user_agent] = user_agent(params[:Authorization])
    ratelimit_delta = params.delete(:header_bypass_delay)
  end

  key = [key, major_param].freeze
  key_mutex = (@rate_limit_mutex[key] ||= Mutex.new)
  global_mutex = @rate_limit_mutex[:global]

  begin
    mutex_wait(key_mutex)
    mutex_wait(global_mutex) if global_mutex.locked?

    response = raw_request(type, *attributes)
  rescue RestClient::TooManyRequests => e
    response = e.response

    is_global = response.headers[:x_ratelimit_global]
    mutex = is_global == 'true' ? global_mutex : key_mutex

    unless mutex.locked?
      response = JSON.parse(e.response)
      retry_after = response['retry_after'].to_i / 1000.0

      MijDiscord::LOGGER.info('HTTP') { "Hit Discord rate limit on <#{key}>, waiting for #{retry_after} seconds" }
      sync_wait(retry_after, mutex)
    end

    retry
  rescue RestClient::Exception => e
    response = e.response
    raise
  ensure
    headers = response&.headers
    if headers && headers[:x_ratelimit_remaining] == '0' && !key_mutex.locked?
      unless ratelimit_delta
        now = Time.rfc2822(headers[:date])
        reset = Time.at(headers[:x_ratelimit_reset].to_i)
        ratelimit_delta = reset - now
      end

      sync_wait(ratelimit_delta, key_mutex)
    end
  end

  response
end

.splash_url(server_id, splash_id) ⇒ Object

Make a splash URL from server and splash IDs



37
38
39
# File 'lib/mij-discord/core/api.rb', line 37

def splash_url(server_id, splash_id)
  "#{CDN_URL}{/splashes/#{server_id}/#{splash_id}.jpg"
end

.update_oauth_application(auth, name, redirect_uris, description = '', icon = nil) ⇒ Object

Change an OAuth application’s properties



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/mij-discord/core/api.rb', line 84

def update_oauth_application(auth, name, redirect_uris, description = '', icon = nil)
  request(
    :oauth2_applications,
    nil,
    :put,
    "#{APIBASE_URL}/oauth2/applications",
    { name: name, redirect_uris: redirect_uris, description: description, icon: icon }.to_json,
    Authorization: auth,
    content_type: :json
  )
end

.user_agent(auth) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
# File 'lib/mij-discord/core/api.rb', line 9

def user_agent(auth)
  case auth&.type
    when :bot
      bot_name = auth.name || 'generic'
      ua_base = "DiscordBot (https://github.com/Mijyuoon/mij-discord, v#{MijDiscord::VERSION})"
      "#{ua_base} mij-discord/#{MijDiscord::VERSION} #{bot_name}"

    when :user
      'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:54.0) Gecko/20100101 Firefox/54.0.1'
  end
end

.validate_token(auth) ⇒ Object

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



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/mij-discord/core/api.rb', line 133

def validate_token(auth)
  request(
    :auth_login,
    nil,
    :post,
    "#{APIBASE_URL}/auth/login",
    {}.to_json,
    Authorization: auth,
    content_type: :json
  )
end

.voice_regions(auth) ⇒ Object

Get a list of available voice regions



146
147
148
149
150
151
152
153
154
155
# File 'lib/mij-discord/core/api.rb', line 146

def voice_regions(auth)
  request(
    :voice_regions,
    nil,
    :get,
    "#{APIBASE_URL}/voice/regions",
    Authorization: auth,
    content_type: :json
  )
end

.widget_url(server_id, style = 'shield') ⇒ Object

Make a widget picture URL from server ID



32
33
34
# File 'lib/mij-discord/core/api.rb', line 32

def widget_url(server_id, style = 'shield')
  "#{APIBASE_URL}/guilds/#{server_id}/widget.png?style=#{style}"
end