rubycord 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/lib/rubycord/allowed_mentions.rb +34 -0
  3. data/lib/rubycord/api/application.rb +200 -0
  4. data/lib/rubycord/api/channel.rb +597 -0
  5. data/lib/rubycord/api/interaction.rb +52 -0
  6. data/lib/rubycord/api/invite.rb +42 -0
  7. data/lib/rubycord/api/server.rb +557 -0
  8. data/lib/rubycord/api/user.rb +153 -0
  9. data/lib/rubycord/api/webhook.rb +138 -0
  10. data/lib/rubycord/api.rb +356 -0
  11. data/lib/rubycord/await.rb +49 -0
  12. data/lib/rubycord/bot.rb +1757 -0
  13. data/lib/rubycord/cache.rb +259 -0
  14. data/lib/rubycord/colour_rgb.rb +41 -0
  15. data/lib/rubycord/commands/command_bot.rb +519 -0
  16. data/lib/rubycord/commands/container.rb +110 -0
  17. data/lib/rubycord/commands/events.rb +9 -0
  18. data/lib/rubycord/commands/parser.rb +325 -0
  19. data/lib/rubycord/commands/rate_limiter.rb +142 -0
  20. data/lib/rubycord/container.rb +753 -0
  21. data/lib/rubycord/data/activity.rb +269 -0
  22. data/lib/rubycord/data/application.rb +48 -0
  23. data/lib/rubycord/data/attachment.rb +109 -0
  24. data/lib/rubycord/data/audit_logs.rb +343 -0
  25. data/lib/rubycord/data/channel.rb +996 -0
  26. data/lib/rubycord/data/component.rb +227 -0
  27. data/lib/rubycord/data/embed.rb +249 -0
  28. data/lib/rubycord/data/emoji.rb +80 -0
  29. data/lib/rubycord/data/integration.rb +120 -0
  30. data/lib/rubycord/data/interaction.rb +798 -0
  31. data/lib/rubycord/data/invite.rb +135 -0
  32. data/lib/rubycord/data/member.rb +370 -0
  33. data/lib/rubycord/data/message.rb +412 -0
  34. data/lib/rubycord/data/overwrite.rb +106 -0
  35. data/lib/rubycord/data/profile.rb +89 -0
  36. data/lib/rubycord/data/reaction.rb +31 -0
  37. data/lib/rubycord/data/recipient.rb +32 -0
  38. data/lib/rubycord/data/role.rb +246 -0
  39. data/lib/rubycord/data/server.rb +1002 -0
  40. data/lib/rubycord/data/user.rb +261 -0
  41. data/lib/rubycord/data/voice_region.rb +43 -0
  42. data/lib/rubycord/data/voice_state.rb +39 -0
  43. data/lib/rubycord/data/webhook.rb +232 -0
  44. data/lib/rubycord/data.rb +40 -0
  45. data/lib/rubycord/errors.rb +737 -0
  46. data/lib/rubycord/events/await.rb +46 -0
  47. data/lib/rubycord/events/bans.rb +58 -0
  48. data/lib/rubycord/events/channels.rb +186 -0
  49. data/lib/rubycord/events/generic.rb +126 -0
  50. data/lib/rubycord/events/guilds.rb +191 -0
  51. data/lib/rubycord/events/interactions.rb +480 -0
  52. data/lib/rubycord/events/invites.rb +123 -0
  53. data/lib/rubycord/events/lifetime.rb +29 -0
  54. data/lib/rubycord/events/members.rb +91 -0
  55. data/lib/rubycord/events/message.rb +337 -0
  56. data/lib/rubycord/events/presence.rb +127 -0
  57. data/lib/rubycord/events/raw.rb +45 -0
  58. data/lib/rubycord/events/reactions.rb +156 -0
  59. data/lib/rubycord/events/roles.rb +86 -0
  60. data/lib/rubycord/events/threads.rb +94 -0
  61. data/lib/rubycord/events/typing.rb +70 -0
  62. data/lib/rubycord/events/voice_server_update.rb +45 -0
  63. data/lib/rubycord/events/voice_state_update.rb +103 -0
  64. data/lib/rubycord/events/webhooks.rb +62 -0
  65. data/lib/rubycord/gateway.rb +867 -0
  66. data/lib/rubycord/id_object.rb +37 -0
  67. data/lib/rubycord/light/data.rb +60 -0
  68. data/lib/rubycord/light/integrations.rb +71 -0
  69. data/lib/rubycord/light/light_bot.rb +56 -0
  70. data/lib/rubycord/light.rb +6 -0
  71. data/lib/rubycord/logger.rb +118 -0
  72. data/lib/rubycord/paginator.rb +55 -0
  73. data/lib/rubycord/permissions.rb +251 -0
  74. data/lib/rubycord/version.rb +5 -0
  75. data/lib/rubycord/voice/encoder.rb +113 -0
  76. data/lib/rubycord/voice/network.rb +366 -0
  77. data/lib/rubycord/voice/sodium.rb +96 -0
  78. data/lib/rubycord/voice/voice_bot.rb +408 -0
  79. data/lib/rubycord/webhooks/builder.rb +100 -0
  80. data/lib/rubycord/webhooks/client.rb +132 -0
  81. data/lib/rubycord/webhooks/embeds.rb +248 -0
  82. data/lib/rubycord/webhooks/modal.rb +78 -0
  83. data/lib/rubycord/webhooks/version.rb +7 -0
  84. data/lib/rubycord/webhooks/view.rb +192 -0
  85. data/lib/rubycord/webhooks.rb +12 -0
  86. data/lib/rubycord/websocket.rb +70 -0
  87. data/lib/rubycord.rb +140 -0
  88. metadata +231 -0
@@ -0,0 +1,138 @@
1
+ # API calls for Webhook object
2
+ module Rubycord::API::Webhook
3
+ module_function
4
+
5
+ # Get a webhook
6
+ # https://discord.com/developers/docs/resources/webhook#get-webhook
7
+ def webhook(token, webhook_id)
8
+ Rubycord::API.request(
9
+ :webhooks_wid,
10
+ nil,
11
+ :get,
12
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}",
13
+ Authorization: token
14
+ )
15
+ end
16
+
17
+ # Get a webhook via webhook token
18
+ # https://discord.com/developers/docs/resources/webhook#get-webhook-with-token
19
+ def token_webhook(webhook_token, webhook_id)
20
+ Rubycord::API.request(
21
+ :webhooks_wid,
22
+ nil,
23
+ :get,
24
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}"
25
+ )
26
+ end
27
+
28
+ # Execute a webhook via token.
29
+ # https://discord.com/developers/docs/resources/webhook#execute-webhook
30
+ def token_execute_webhook(webhook_token, webhook_id, wait = false, content = nil, username = nil, avatar_url = nil, tts = nil, file = nil, embeds = nil, allowed_mentions = nil, flags = nil, components = nil)
31
+ body = {content: content, username: username, avatar_url: avatar_url, tts: tts, embeds: embeds&.map(&:to_hash), allowed_mentions: allowed_mentions, flags: flags, components: components}
32
+ body = if file
33
+ {file: file, payload_json: body.to_json}
34
+ else
35
+ body.to_json
36
+ end
37
+
38
+ headers = {content_type: :json} unless file
39
+
40
+ Rubycord::API.request(
41
+ :webhooks_wid,
42
+ webhook_id,
43
+ :post,
44
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}?wait=#{wait}",
45
+ body,
46
+ headers
47
+ )
48
+ end
49
+
50
+ # Update a webhook
51
+ # https://discord.com/developers/docs/resources/webhook#modify-webhook
52
+ def update_webhook(token, webhook_id, data, reason = nil)
53
+ Rubycord::API.request(
54
+ :webhooks_wid,
55
+ webhook_id,
56
+ :patch,
57
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}",
58
+ data.to_json,
59
+ Authorization: token,
60
+ content_type: :json,
61
+ "X-Audit-Log-Reason": reason
62
+ )
63
+ end
64
+
65
+ # Update a webhook via webhook token
66
+ # https://discord.com/developers/docs/resources/webhook#modify-webhook-with-token
67
+ def token_update_webhook(webhook_token, webhook_id, data, reason = nil)
68
+ Rubycord::API.request(
69
+ :webhooks_wid,
70
+ webhook_id,
71
+ :patch,
72
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}",
73
+ data.to_json,
74
+ content_type: :json,
75
+ "X-Audit-Log-Reason": reason
76
+ )
77
+ end
78
+
79
+ # Deletes a webhook
80
+ # https://discord.com/developers/docs/resources/webhook#delete-webhook
81
+ def delete_webhook(token, webhook_id, reason = nil)
82
+ Rubycord::API.request(
83
+ :webhooks_wid,
84
+ webhook_id,
85
+ :delete,
86
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}",
87
+ Authorization: token,
88
+ "X-Audit-Log-Reason": reason
89
+ )
90
+ end
91
+
92
+ # Deletes a webhook via webhook token
93
+ # https://discord.com/developers/docs/resources/webhook#delete-webhook-with-token
94
+ def token_delete_webhook(webhook_token, webhook_id, reason = nil)
95
+ Rubycord::API.request(
96
+ :webhooks_wid,
97
+ webhook_id,
98
+ :delete,
99
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}",
100
+ "X-Audit-Log-Reason": reason
101
+ )
102
+ end
103
+
104
+ # Get a message that was created by the webhook corresponding to the provided token.
105
+ # https://discord.com/developers/docs/resources/webhook#get-webhook-message
106
+ def token_get_message(webhook_token, webhook_id, message_id)
107
+ Rubycord::API.request(
108
+ :webhooks_wid_messages_mid,
109
+ webhook_id,
110
+ :get,
111
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}/messages/#{message_id}"
112
+ )
113
+ end
114
+
115
+ # Edit a webhook message via webhook token
116
+ # https://discord.com/developers/docs/resources/webhook#edit-webhook-message
117
+ def token_edit_message(webhook_token, webhook_id, message_id, content = nil, embeds = nil, allowed_mentions = nil, components = nil)
118
+ Rubycord::API.request(
119
+ :webhooks_wid_messages,
120
+ webhook_id,
121
+ :patch,
122
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}/messages/#{message_id}",
123
+ {content: content, embeds: embeds, allowed_mentions: allowed_mentions, components: components}.to_json,
124
+ content_type: :json
125
+ )
126
+ end
127
+
128
+ # Delete a webhook message via webhook token.
129
+ # https://discord.com/developers/docs/resources/webhook#delete-webhook-message
130
+ def token_delete_message(webhook_token, webhook_id, message_id)
131
+ Rubycord::API.request(
132
+ :webhooks_wid_messages,
133
+ webhook_id,
134
+ :delete,
135
+ "#{Rubycord::API.api_base}/webhooks/#{webhook_id}/#{webhook_token}/messages/#{message_id}"
136
+ )
137
+ end
138
+ end
@@ -0,0 +1,356 @@
1
+ require "rest-client"
2
+ require "json"
3
+ require "time"
4
+
5
+ require "rubycord/errors"
6
+
7
+ # List of methods representing endpoints in Discord's API
8
+ module Rubycord::API
9
+ # The base URL of the Discord REST API.
10
+ APIBASE = "https://discord.com/api/v9"
11
+
12
+ # The URL of Discord's CDN
13
+ CDN_URL = "https://cdn.discordapp.com"
14
+
15
+ module_function
16
+
17
+ # @return [String] the currently used API base URL.
18
+ def api_base
19
+ @api_base || APIBASE
20
+ end
21
+
22
+ # Sets the API base URL to something.
23
+ def api_base=(value)
24
+ @api_base = value
25
+ end
26
+
27
+ # @return [String] the currently used CDN url
28
+ def cdn_url
29
+ @cdn_url || CDN_URL
30
+ end
31
+
32
+ # @return [String] the bot name, previously specified using {.bot_name=}.
33
+ def bot_name
34
+ @bot_name
35
+ end
36
+
37
+ # Sets the bot name to something. Used in {.user_agent}. For the bot's username, see {Profile#username=}.
38
+ def bot_name=(value)
39
+ @bot_name = value
40
+ end
41
+
42
+ # Changes the rate limit tracing behaviour. If rate limit tracing is on, a full backtrace will be logged on every RL
43
+ # hit.
44
+ # @param value [true, false] whether or not to enable rate limit tracing
45
+ def trace=(value)
46
+ @trace = value
47
+ end
48
+
49
+ # Generate a user agent identifying this requester as rubycord.
50
+ def user_agent
51
+ # This particular string is required by the Discord devs.
52
+ required = "DiscordBot (https://github.com/dakurei-gems/rubycord, v#{Rubycord::VERSION})"
53
+ @bot_name ||= ""
54
+
55
+ "#{required} rest-client/#{RestClient::VERSION} #{RUBY_ENGINE}/#{RUBY_VERSION}p#{RUBY_PATCHLEVEL} rubycord/#{Rubycord::VERSION} #{@bot_name}"
56
+ end
57
+
58
+ # Resets all rate limit mutexes
59
+ def reset_mutexes
60
+ @mutexes = {}
61
+ @global_mutex = Mutex.new
62
+ end
63
+
64
+ # Wait a specified amount of time synchronised with the specified mutex.
65
+ def sync_wait(time, mutex)
66
+ mutex.synchronize { sleep time }
67
+ end
68
+
69
+ # Wait for a specified mutex to unlock and do nothing with it afterwards.
70
+ def mutex_wait(mutex)
71
+ mutex.lock
72
+ mutex.unlock
73
+ end
74
+
75
+ # Performs a RestClient request.
76
+ # @param type [Symbol] The type of HTTP request to use.
77
+ # @param attributes [Array] The attributes for the request.
78
+ def raw_request(type, attributes)
79
+ RestClient.send(type, *attributes)
80
+ rescue RestClient::Forbidden => e
81
+ # HACK: for #request, dynamically inject restclient's response into NoPermission - this allows us to rate limit
82
+ noprm = Rubycord::Errors::NoPermission.new
83
+ noprm.define_singleton_method(:_rc_response) { e.response }
84
+ raise noprm, "The bot doesn't have the required permission to do this!"
85
+ rescue RestClient::BadGateway
86
+ Rubycord::LOGGER.warn("Got a 502 while sending a request! Not a big deal, retrying the request")
87
+ retry
88
+ end
89
+
90
+ # Make an API request, including rate limit handling.
91
+ def request(key, major_parameter, type, *attributes)
92
+ # Add a custom user agent
93
+ attributes.last[:user_agent] = user_agent if attributes.last.is_a? Hash
94
+
95
+ # The most recent Discord rate limit requirements require the support of major parameters, where a particular route
96
+ # and major parameter combination (*not* the HTTP method) uniquely identifies a RL bucket.
97
+ key = [key, major_parameter].freeze
98
+
99
+ begin
100
+ mutex = @mutexes[key] ||= Mutex.new
101
+
102
+ # Lock and unlock, i.e. wait for the mutex to unlock and don't do anything with it afterwards
103
+ mutex_wait(mutex)
104
+
105
+ # If the global mutex happens to be locked right now, wait for that as well.
106
+ mutex_wait(@global_mutex) if @global_mutex.locked?
107
+
108
+ response = nil
109
+ begin
110
+ response = raw_request(type, attributes)
111
+ rescue RestClient::Exception => e
112
+ response = e.response
113
+
114
+ if response.body && !e.is_a?(RestClient::TooManyRequests)
115
+ data = JSON.parse(response.body)
116
+ err_klass = Rubycord::Errors.error_class_for(data["code"] || 0)
117
+ e = err_klass.new(data["message"], data["errors"])
118
+
119
+ Rubycord::LOGGER.error(e.full_message)
120
+ end
121
+
122
+ raise e
123
+ rescue Rubycord::Errors::NoPermission => e
124
+ if e.respond_to?(:_rc_response)
125
+ response = e._rc_response
126
+ else
127
+ Rubycord::LOGGER.warn("NoPermission doesn't respond_to? _rc_response!")
128
+ end
129
+
130
+ raise e
131
+ ensure
132
+ if response
133
+ handle_preemptive_rl(response.headers, mutex, key) if response.headers[:x_ratelimit_remaining] == "0" && !mutex.locked?
134
+ else
135
+ Rubycord::LOGGER.ratelimit("Response was nil before trying to preemptively rate limit!")
136
+ end
137
+ end
138
+ rescue RestClient::TooManyRequests => e
139
+ # If the 429 is from the global RL, then we have to use the global mutex instead.
140
+ mutex = @global_mutex if e.response.headers[:x_ratelimit_global] == "true"
141
+
142
+ unless mutex.locked?
143
+ response = JSON.parse(e.response)
144
+ wait_seconds = response["retry_after"] ? response["retry_after"].to_f : e.response.headers[:retry_after].to_i
145
+ Rubycord::LOGGER.ratelimit("Locking RL mutex (key: #{key}) for #{wait_seconds} seconds due to Discord rate limiting")
146
+ trace("429 #{key.join(" ")}")
147
+
148
+ # Wait the required time synchronized by the mutex (so other incoming requests have to wait) but only do it if
149
+ # the mutex isn't locked already so it will only ever wait once
150
+ sync_wait(wait_seconds, mutex)
151
+ end
152
+
153
+ retry
154
+ end
155
+
156
+ response
157
+ end
158
+
159
+ # Handles pre-emptive rate limiting by waiting the given mutex by the difference of the Date header to the
160
+ # X-Ratelimit-Reset header, thus making sure we don't get 429'd in any subsequent requests.
161
+ def handle_preemptive_rl(headers, mutex, key)
162
+ Rubycord::LOGGER.ratelimit "RL bucket depletion detected! Date: #{headers[:date]} Reset: #{headers[:x_ratelimit_reset]}"
163
+ delta = headers[:x_ratelimit_reset_after].to_f
164
+ Rubycord::LOGGER.warn("Locking RL mutex (key: #{key}) for #{delta} seconds pre-emptively")
165
+ sync_wait(delta, mutex)
166
+ end
167
+
168
+ # Perform rate limit tracing. All this method does is log the current backtrace to the console with the `:ratelimit`
169
+ # level.
170
+ # @param reason [String] the reason to include with the backtrace.
171
+ def trace(reason)
172
+ unless @trace
173
+ Rubycord::LOGGER.debug("trace was called with reason #{reason}, but tracing is not enabled")
174
+ return
175
+ end
176
+
177
+ Rubycord::LOGGER.ratelimit("Trace (#{reason}):")
178
+
179
+ caller.each do |str|
180
+ Rubycord::LOGGER.ratelimit(" #{str}")
181
+ end
182
+ end
183
+
184
+ # Make an icon URL from server and icon IDs
185
+ def icon_url(server_id, icon_id, format = "webp")
186
+ "#{cdn_url}/icons/#{server_id}/#{icon_id}.#{format}"
187
+ end
188
+
189
+ # Make an icon URL from application and icon IDs
190
+ def app_icon_url(app_id, icon_id, format = "webp")
191
+ "#{cdn_url}/app-icons/#{app_id}/#{icon_id}.#{format}"
192
+ end
193
+
194
+ # Make a widget picture URL from server ID
195
+ def widget_url(server_id, style = "shield")
196
+ "#{api_base}/guilds/#{server_id}/widget.png?style=#{style}"
197
+ end
198
+
199
+ # Make a splash URL from server and splash IDs
200
+ def splash_url(server_id, splash_id, format = "webp")
201
+ "#{cdn_url}/splashes/#{server_id}/#{splash_id}.#{format}"
202
+ end
203
+
204
+ # Make a banner URL from server and banner IDs
205
+ def banner_url(server_id, banner_id, format = "webp")
206
+ "#{cdn_url}/banners/#{server_id}/#{banner_id}.#{format}"
207
+ end
208
+
209
+ # Make an emoji icon URL from emoji ID
210
+ def emoji_icon_url(emoji_id, format = "webp")
211
+ "#{cdn_url}/emojis/#{emoji_id}.#{format}"
212
+ end
213
+
214
+ # Make an asset URL from application and asset IDs
215
+ def asset_url(application_id, asset_id, format = "webp")
216
+ "#{cdn_url}/app-assets/#{application_id}/#{asset_id}.#{format}"
217
+ end
218
+
219
+ # Make an achievement icon URL from application ID, achievement ID, and icon hash
220
+ def achievement_icon_url(application_id, achievement_id, icon_hash, format = "webp")
221
+ "#{cdn_url}/app-assets/#{application_id}/achievements/#{achievement_id}/icons/#{icon_hash}.#{format}"
222
+ end
223
+
224
+ # @param role_id [String, Integer]
225
+ # @param icon_hash [String]
226
+ # @param format ['webp', 'png', 'jpeg']
227
+ # @return [String]
228
+ def role_icon_url(role_id, icon_hash, format = "webp")
229
+ "#{cdn_url}/role-icons/#{role_id}/#{icon_hash}.#{format}"
230
+ end
231
+
232
+ # Login to the server
233
+ def login(email, password)
234
+ request(
235
+ :auth_login,
236
+ nil,
237
+ :post,
238
+ "#{api_base}/auth/login",
239
+ email: email,
240
+ password: password
241
+ )
242
+ end
243
+
244
+ # Logout from the server
245
+ def logout(token)
246
+ request(
247
+ :auth_logout,
248
+ nil,
249
+ :post,
250
+ "#{api_base}/auth/logout",
251
+ nil,
252
+ Authorization: token
253
+ )
254
+ end
255
+
256
+ # Create an OAuth application
257
+ def create_oauth_application(token, name, redirect_uris)
258
+ request(
259
+ :oauth2_applications,
260
+ nil,
261
+ :post,
262
+ "#{api_base}/oauth2/applications",
263
+ {name: name, redirect_uris: redirect_uris}.to_json,
264
+ Authorization: token,
265
+ content_type: :json
266
+ )
267
+ end
268
+
269
+ # Change an OAuth application's properties
270
+ def update_oauth_application(token, name, redirect_uris, description = "", icon = nil)
271
+ request(
272
+ :oauth2_applications,
273
+ nil,
274
+ :put,
275
+ "#{api_base}/oauth2/applications",
276
+ {name: name, redirect_uris: redirect_uris, description: description, icon: icon}.to_json,
277
+ Authorization: token,
278
+ content_type: :json
279
+ )
280
+ end
281
+
282
+ # Get the bot's OAuth application's information
283
+ def oauth_application(token)
284
+ request(
285
+ :oauth2_applications_me,
286
+ nil,
287
+ :get,
288
+ "#{api_base}/oauth2/applications/@me",
289
+ Authorization: token
290
+ )
291
+ end
292
+
293
+ # Acknowledge that a message has been received
294
+ # The last acknowledged message will be sent in the ready packet,
295
+ # so this is an easy way to catch up on messages
296
+ def acknowledge_message(token, channel_id, message_id)
297
+ request(
298
+ :channels_cid_messages_mid_ack,
299
+ nil, # This endpoint is unavailable for bot accounts and thus isn't subject to its rate limit requirements.
300
+ :post,
301
+ "#{api_base}/channels/#{channel_id}/messages/#{message_id}/ack",
302
+ nil,
303
+ Authorization: token
304
+ )
305
+ end
306
+
307
+ # Get the gateway to be used
308
+ def gateway(token)
309
+ request(
310
+ :gateway,
311
+ nil,
312
+ :get,
313
+ "#{api_base}/gateway",
314
+ Authorization: token
315
+ )
316
+ end
317
+
318
+ # Get the gateway to be used, with additional information for sharding and
319
+ # session start limits
320
+ def gateway_bot(token)
321
+ request(
322
+ :gateway_bot,
323
+ nil,
324
+ :get,
325
+ "#{api_base}/gateway/bot",
326
+ Authorization: token
327
+ )
328
+ end
329
+
330
+ # Validate a token (this request will fail if the token is invalid)
331
+ def validate_token(token)
332
+ request(
333
+ :auth_login,
334
+ nil,
335
+ :post,
336
+ "#{api_base}/auth/login",
337
+ {}.to_json,
338
+ Authorization: token,
339
+ content_type: :json
340
+ )
341
+ end
342
+
343
+ # Get a list of available voice regions
344
+ def voice_regions(token)
345
+ request(
346
+ :voice_regions,
347
+ nil,
348
+ :get,
349
+ "#{api_base}/voice/regions",
350
+ Authorization: token,
351
+ content_type: :json
352
+ )
353
+ end
354
+ end
355
+
356
+ Rubycord::API.reset_mutexes
@@ -0,0 +1,49 @@
1
+ module Rubycord
2
+ # Awaits are a way to register new, temporary event handlers on the fly. Awaits can be
3
+ # registered using {Bot#add_await}, {User#await}, {Message#await} and {Channel#await}.
4
+ #
5
+ # Awaits contain a block that will be called before the await event will be triggered.
6
+ # If this block returns anything that is not `false` exactly, the await will be deleted.
7
+ # If no block is present, the await will also be deleted. This is an easy way to make
8
+ # temporary events that are only temporary under certain conditions.
9
+ #
10
+ # Besides the given block, an {Rubycord::Events::AwaitEvent} will also be executed with the key and
11
+ # the type of the await that was triggered. It's possible to register multiple events
12
+ # that trigger on the same await.
13
+ class Await
14
+ # The key that uniquely identifies this await.
15
+ # @return [Symbol] The unique key.
16
+ attr_reader :key
17
+
18
+ # The class of the event that this await listens for.
19
+ # @return [Class] The event class.
20
+ attr_reader :type
21
+
22
+ # The attributes of the event that will be listened for.
23
+ # @return [Hash] A hash of attributes.
24
+ attr_reader :attributes
25
+
26
+ # Makes a new await. For internal use only.
27
+ # @!visibility private
28
+ def initialize(bot, key, type, attributes, block = nil)
29
+ @bot = bot
30
+ @key = key
31
+ @type = type
32
+ @attributes = attributes
33
+ @block = block
34
+ end
35
+
36
+ # Checks whether the await can be triggered by the given event, and if it can, execute the block
37
+ # and return its result along with this await's key.
38
+ # @param event [Event] An event to check for.
39
+ # @return [Array] This await's key and whether or not it should be deleted. If there was no match, both are nil.
40
+ def match(event)
41
+ dummy_handler = EventContainer.handler_class(@type).new(@attributes, @bot)
42
+ return [nil, nil] unless event.instance_of?(@type) && dummy_handler.matches?(event)
43
+
44
+ should_delete = true if (@block && @block.call(event) != false) || !@block
45
+
46
+ [@key, should_delete]
47
+ end
48
+ end
49
+ end