discordrb 3.2.1 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of discordrb might be problematic. Click here for more details.

Files changed (47) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/.rubocop.yml +3 -3
  4. data/.travis.yml +28 -3
  5. data/.yardopts +1 -1
  6. data/CHANGELOG.md +555 -144
  7. data/CONTRIBUTING.md +1 -1
  8. data/Gemfile +0 -4
  9. data/README.md +86 -15
  10. data/Rakefile +2 -2
  11. data/bin/travis_build_docs.sh +17 -0
  12. data/discordrb-webhooks.gemspec +2 -1
  13. data/discordrb.gemspec +12 -5
  14. data/lib/discordrb.rb +2 -2
  15. data/lib/discordrb/api.rb +94 -25
  16. data/lib/discordrb/api/channel.rb +53 -17
  17. data/lib/discordrb/api/invite.rb +7 -4
  18. data/lib/discordrb/api/server.rb +173 -36
  19. data/lib/discordrb/api/user.rb +18 -4
  20. data/lib/discordrb/api/webhook.rb +83 -0
  21. data/lib/discordrb/await.rb +1 -1
  22. data/lib/discordrb/bot.rb +191 -102
  23. data/lib/discordrb/cache.rb +39 -9
  24. data/lib/discordrb/commands/command_bot.rb +79 -24
  25. data/lib/discordrb/commands/container.rb +16 -2
  26. data/lib/discordrb/commands/parser.rb +46 -7
  27. data/lib/discordrb/commands/rate_limiter.rb +8 -6
  28. data/lib/discordrb/container.rb +51 -7
  29. data/lib/discordrb/data.rb +1729 -286
  30. data/lib/discordrb/errors.rb +34 -1
  31. data/lib/discordrb/events/generic.rb +1 -1
  32. data/lib/discordrb/events/guilds.rb +1 -0
  33. data/lib/discordrb/events/message.rb +18 -12
  34. data/lib/discordrb/events/presence.rb +7 -2
  35. data/lib/discordrb/events/reactions.rb +13 -4
  36. data/lib/discordrb/events/roles.rb +7 -6
  37. data/lib/discordrb/events/typing.rb +1 -1
  38. data/lib/discordrb/events/webhooks.rb +61 -0
  39. data/lib/discordrb/gateway.rb +85 -32
  40. data/lib/discordrb/light.rb +1 -1
  41. data/lib/discordrb/logger.rb +8 -7
  42. data/lib/discordrb/permissions.rb +41 -4
  43. data/lib/discordrb/version.rb +1 -1
  44. data/lib/discordrb/voice/encoder.rb +10 -8
  45. data/lib/discordrb/voice/voice_bot.rb +4 -4
  46. data/lib/discordrb/websocket.rb +2 -2
  47. metadata +59 -14
@@ -15,6 +15,8 @@ module Discordrb
15
15
  def init_cache
16
16
  @users = {}
17
17
 
18
+ @voice_regions = {}
19
+
18
20
  @servers = {}
19
21
 
20
22
  @channels = {}
@@ -23,6 +25,18 @@ module Discordrb
23
25
  @restricted_channels = []
24
26
  end
25
27
 
28
+ # Returns or caches the available voice regions
29
+ def voice_regions
30
+ return @voice_regions unless @voice_regions.empty?
31
+
32
+ regions = JSON.parse API.voice_regions(token)
33
+ regions.each do |data|
34
+ @voice_regions[data['id']] = VoiceRegion.new(data)
35
+ end
36
+
37
+ @voice_regions
38
+ end
39
+
26
40
  # Gets a channel given its ID. This queries the internal channel cache, and if the channel doesn't
27
41
  # exist in there, it will get the data from Discord.
28
42
  # @param id [Integer] The channel ID for which to search for.
@@ -83,7 +97,7 @@ module Discordrb
83
97
  LOGGER.out("Resolving server #{id}")
84
98
  begin
85
99
  response = API::Server.resolve(token, id)
86
- rescue RestClient::ResourceNotFound
100
+ rescue Discordrb::Errors::NoPermission
87
101
  return nil
88
102
  end
89
103
  server = Server.new(JSON.parse(response), self)
@@ -173,9 +187,9 @@ module Discordrb
173
187
  #
174
188
  # * An {Invite} object
175
189
  # * The code for an invite
176
- # * A fully qualified invite URL (e. g. `https://discordapp.com/invite/0A37aN7fasF7n83q`)
177
- # * A short invite URL with protocol (e. g. `https://discord.gg/0A37aN7fasF7n83q`)
178
- # * A short invite URL without protocol (e. g. `discord.gg/0A37aN7fasF7n83q`)
190
+ # * A fully qualified invite URL (e.g. `https://discordapp.com/invite/0A37aN7fasF7n83q`)
191
+ # * A short invite URL with protocol (e.g. `https://discord.gg/0A37aN7fasF7n83q`)
192
+ # * A short invite URL without protocol (e.g. `discord.gg/0A37aN7fasF7n83q`)
179
193
  # @return [String] Only the code for the invite.
180
194
  def resolve_invite_code(invite)
181
195
  invite = invite.code if invite.is_a? Discordrb::Invite
@@ -214,11 +228,27 @@ module Discordrb
214
228
  results
215
229
  end
216
230
 
217
- # Finds a user given its username.
218
- # @param username [String] The username to look for.
219
- # @return [Array<User>] The array of users that were found. May be empty if none were found.
220
- def find_user(username)
221
- @users.values.find_all { |e| e.username == username }
231
+ # Finds a user given its username or username & discriminator.
232
+ # @overload find_user(username)
233
+ # Find all cached users with a certain username.
234
+ # @param username [String] The username to look for.
235
+ # @return [Array<User>] The array of users that were found. May be empty if none were found.
236
+ # @overload find_user(username, discrim)
237
+ # Find a cached user with a certain username and discriminator.
238
+ # Find a user by name and discriminator
239
+ # @param username [String] The username to look for.
240
+ # @param discrim [String] The user's discriminator
241
+ # @return [User, nil] The user that was found, or `nil` if none was found
242
+ # @note This method only searches through users that have been cached. Users that have not yet been cached
243
+ # by the bot but still share a connection with the user (mutual server) will not be found.
244
+ # @example Find users by name
245
+ # bot.find_user('z64') #=> Array<User>
246
+ # @example Find a user by name and discriminator
247
+ # bot.find_user('z64', '2639') #=> User
248
+ def find_user(username, discrim = nil)
249
+ users = @users.values.find_all { |e| e.username == username }
250
+ return users.find { |u| u.discrim == discrim } if discrim
251
+ users
222
252
  end
223
253
  end
224
254
  end
@@ -16,7 +16,8 @@ module Discordrb::Commands
16
16
  # @return [Hash] this bot's attributes.
17
17
  attr_reader :attributes
18
18
 
19
- # @return [String] the prefix commands are triggered with.
19
+ # @return [String, Array<String>, #call] the prefix commands are triggered with.
20
+ # @see #initialize
20
21
  attr_reader :prefix
21
22
 
22
23
  include CommandContainer
@@ -54,19 +55,19 @@ module Discordrb::Commands
54
55
  # @option attributes [Array<String, Integer, Channel>] :channels The channels this command bot accepts commands on.
55
56
  # Superseded if a command has a 'channels' attribute.
56
57
  # @option attributes [String] :previous Character that should designate the result of the previous command in
57
- # a command chain (see :advanced_functionality). Default is '~'.
58
+ # a command chain (see :advanced_functionality). Default is '~'. Set to an empty string to disable.
58
59
  # @option attributes [String] :chain_delimiter Character that should designate that a new command begins in the
59
- # command chain (see :advanced_functionality). Default is '>'.
60
+ # command chain (see :advanced_functionality). Default is '>'. Set to an empty string to disable.
60
61
  # @option attributes [String] :chain_args_delim Character that should separate the command chain arguments from the
61
- # chain itself (see :advanced_functionality). Default is ':'.
62
+ # chain itself (see :advanced_functionality). Default is ':'. Set to an empty string to disable.
62
63
  # @option attributes [String] :sub_chain_start Character that should start a sub-chain (see
63
- # :advanced_functionality). Default is '['.
64
+ # :advanced_functionality). Default is '['. Set to an empty string to disable.
64
65
  # @option attributes [String] :sub_chain_end Character that should end a sub-chain (see
65
- # :advanced_functionality). Default is ']'.
66
+ # :advanced_functionality). Default is ']'. Set to an empty string to disable.
66
67
  # @option attributes [String] :quote_start Character that should start a quoted string (see
67
- # :advanced_functionality). Default is '"'.
68
+ # :advanced_functionality). Default is '"'. Set to an empty string to disable.
68
69
  # @option attributes [String] :quote_end Character that should end a quoted string (see
69
- # :advanced_functionality). Default is '"'.
70
+ # :advanced_functionality). Default is '"' or the same as :quote_start. Set to an empty string to disable.
70
71
  # @option attributes [true, false] :ignore_bots Whether the bot should ignore bot accounts or not. Default is false.
71
72
  def initialize(attributes = {})
72
73
  super(
@@ -81,7 +82,8 @@ module Discordrb::Commands
81
82
  shard_id: attributes[:shard_id],
82
83
  num_shards: attributes[:num_shards],
83
84
  redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true,
84
- ignore_bots: attributes[:ignore_bots])
85
+ ignore_bots: attributes[:ignore_bots],
86
+ compress_mode: attributes[:compress_mode])
85
87
 
86
88
  @prefix = attributes[:prefix]
87
89
  @attributes = {
@@ -126,7 +128,10 @@ module Discordrb::Commands
126
128
  quote_start: attributes[:quote_start] || '"',
127
129
 
128
130
  # Quoted mode ending character
129
- quote_end: attributes[:quote_end] || '"'
131
+ quote_end: attributes[:quote_end] || attributes[:quote_start] || '"',
132
+
133
+ # Default block for handling internal exceptions, or a string to respond with
134
+ rescue: attributes[:rescue]
130
135
  }
131
136
 
132
137
  @permissions = {
@@ -138,11 +143,20 @@ module Discordrb::Commands
138
143
  command(@attributes[:help_command], max_args: 1, description: 'Shows a list of all the commands available or displays help for a specific command.', usage: 'help [command name]') do |event, command_name|
139
144
  if command_name
140
145
  command = @commands[command_name.to_sym]
146
+ if command.is_a?(CommandAlias)
147
+ command = command.aliased_command
148
+ command_name = command.name
149
+ end
141
150
  return "The command `#{command_name}` does not exist!" unless command
142
151
  desc = command.attributes[:description] || '*No description available*'
143
152
  usage = command.attributes[:usage]
144
153
  parameters = command.attributes[:parameters]
145
154
  result = "**`#{command_name}`**: #{desc}"
155
+ aliases = command_aliases(command_name.to_sym)
156
+ unless aliases.empty?
157
+ result += "\nAliases: "
158
+ result += aliases.map { |a| "`#{a.name}`" }.join(', ')
159
+ end
146
160
  result += "\nUsage: `#{usage}`" if usage
147
161
  if parameters
148
162
  result += "\nAccepted Parameters:\n```"
@@ -152,7 +166,7 @@ module Discordrb::Commands
152
166
  result
153
167
  else
154
168
  available_commands = @commands.values.reject do |c|
155
- !c.attributes[:help_available] || !required_roles?(event.user, c.attributes[:required_roles]) || !required_permissions?(event.user, c.attributes[:required_permissions], event.channel)
169
+ c.is_a?(CommandAlias) || !c.attributes[:help_available] || !required_roles?(event.user, c.attributes[:required_roles]) || !allowed_roles?(event.user, c.attributes[:allowed_roles]) || !required_permissions?(event.user, c.attributes[:required_permissions], event.channel)
156
170
  end
157
171
  case available_commands.length
158
172
  when 0..5
@@ -171,6 +185,15 @@ module Discordrb::Commands
171
185
  end
172
186
  end
173
187
 
188
+ # Returns all aliases for the command with the given name
189
+ # @param name [Symbol] the name of the `Command`
190
+ # @return [Array<CommandAlias>]
191
+ def command_aliases(name)
192
+ commands.values.select do |command|
193
+ command.is_a?(CommandAlias) && command.aliased_command.name == name
194
+ end
195
+ end
196
+
174
197
  # Executes a particular command on the bot. Mostly useful for internal stuff, but one can never know.
175
198
  # @param name [Symbol] The command to execute.
176
199
  # @param event [CommandEvent] The event to pass to the command.
@@ -184,6 +207,7 @@ module Discordrb::Commands
184
207
  debug("Executing command #{name} with arguments #{arguments}")
185
208
  return unless @commands
186
209
  command = @commands[name]
210
+ command = command.aliased_command if command.is_a?(CommandAlias)
187
211
  return unless !check_permissions || channels?(event.channel, @attributes[:channels]) ||
188
212
  (command && !command.attributes[:channels].nil?)
189
213
  unless command
@@ -195,7 +219,8 @@ module Discordrb::Commands
195
219
  if (check_permissions &&
196
220
  permission?(event.author, command.attributes[:permission_level], event.server) &&
197
221
  required_permissions?(event.author, command.attributes[:required_permissions], event.channel) &&
198
- required_roles?(event.author, command.attributes[:required_roles])) ||
222
+ required_roles?(event.author, command.attributes[:required_roles]) &&
223
+ allowed_roles?(event.author, command.attributes[:allowed_roles])) ||
199
224
  !check_permissions
200
225
  event.command = command
201
226
  result = command.call(event, arguments, chained, check_permissions)
@@ -283,7 +308,7 @@ module Discordrb::Commands
283
308
  nil
284
309
  end
285
310
  else
286
- raise ArgumentError, "#{type} doesn't implement from_argument"
311
+ raise ArgumentError, "#{types[i]} doesn't implement from_argument"
287
312
  end
288
313
  end
289
314
  end
@@ -329,6 +354,30 @@ module Discordrb::Commands
329
354
  [@permissions[:users][user.id] || 0, determined_level].max >= level
330
355
  end
331
356
 
357
+ # @see CommandBot#update_channels
358
+ def channels=(channels)
359
+ update_channels(channels)
360
+ end
361
+
362
+ # Update the list of channels the bot accepts commands from.
363
+ # @param channels [Array<String, Integer, Channel>] The channels this command bot accepts commands on.
364
+ def update_channels(channels = [])
365
+ @attributes[:channels] = Array(channels)
366
+ end
367
+
368
+ # Add a channel to the list of channels the bot accepts commands from.
369
+ # @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be added
370
+ def add_channel(channel)
371
+ return if @attributes[:channels].find { |c| channel.resolve_id == c.resolve_id }
372
+ @attributes[:channels] << channel
373
+ end
374
+
375
+ # Remove a channel from the list of channels the bot accepts commands from.
376
+ # @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be removed
377
+ def remove_channel(channel)
378
+ @attributes[:channels].delete_if { |c| channel.resolve_id == c.resolve_id }
379
+ end
380
+
332
381
  private
333
382
 
334
383
  # Internal handler for MESSAGE_CREATE that is overwritten to allow for command handling
@@ -387,27 +436,33 @@ module Discordrb::Commands
387
436
  end
388
437
 
389
438
  def required_roles?(member, required)
390
- return (required.nil? || required.empty?) if member.webhook? || member.is_a?(Discordrb::Recipient)
391
- if required.is_a? Array
439
+ return true if member.webhook? || member.is_a?(Discordrb::Recipient) || required.nil? || required.empty?
440
+ required.is_a?(Array) ? check_multiple_roles(member, required) : member.role?(role)
441
+ end
442
+
443
+ def allowed_roles?(member, required)
444
+ return true if member.webhook? || member.is_a?(Discordrb::Recipient) || required.nil? || required.empty?
445
+ required.is_a?(Array) ? check_multiple_roles(member, required, false) : member.role?(role)
446
+ end
447
+
448
+ def check_multiple_roles(member, required, all_roles = true)
449
+ if all_roles
392
450
  required.all? do |role|
393
451
  member.role?(role)
394
452
  end
395
453
  else
396
- member.role?(role)
454
+ required.any? do |role|
455
+ member.role?(role)
456
+ end
397
457
  end
398
458
  end
399
459
 
400
460
  def channels?(channel, channels)
401
461
  return true if channels.nil? || channels.empty?
402
462
  channels.any? do |c|
403
- if c.is_a? String
404
- # Make sure to remove the "#" from channel names in case it was specified
405
- c.delete('#') == channel.name
406
- elsif c.is_a? Integer
407
- c == channel.id
408
- else
409
- c == channel
410
- end
463
+ # if c is string, make sure to remove the "#" from channel names in case it was specified
464
+ return true if c.is_a?(String) && c.delete('#') == channel.name
465
+ c.resolve_id == channel.resolve_id
411
466
  end
412
467
  end
413
468
 
@@ -22,7 +22,10 @@ module Discordrb::Commands
22
22
  # the message by setting this option to false.
23
23
  # @option attributes [Array<Symbol>] :required_permissions Discord action permissions (e.g. `:kick_members`) that
24
24
  # should be required to use this command. See {Discordrb::Permissions::Flags} for a list.
25
- # @option attributes [Array<Role>, Array<#resolve_id>] :required_roles Roles that user should have to use this command.
25
+ # @option attributes [Array<Role>, Array<#resolve_id>] :required_roles Roles that user must have to use this command
26
+ # (user must have all of them).
27
+ # @option attributes [Array<Role>, Array<#resolve_id>] :allowed_roles Roles that user should have to use this command
28
+ # (user should have at least one of them).
26
29
  # @option attributes [Array<String, Integer, Channel>] :channels The channels that this command can be used on. An
27
30
  # empty array indicates it can be used on any channel. Supersedes the command bot attribute.
28
31
  # @option attributes [true, false] :chain_usable Whether this command is able to be used inside of a command chain
@@ -44,9 +47,16 @@ module Discordrb::Commands
44
47
  # command will be available again.
45
48
  # @option attributes [Symbol] :bucket The rate limit bucket that should be used for rate limiting. No rate limiting
46
49
  # will be done if unspecified or nil.
50
+ # @option attributes [String, #call] :rescue A string to respond with, or a block to be called in the event an exception
51
+ # is raised internally. If given a String, `%exception%` will be substituted with the exception's `#message`. If given
52
+ # a `Proc`, it will be passed the `CommandEvent` along with the `Exception`.
47
53
  # @yield The block is executed when the command is executed.
48
54
  # @yieldparam event [CommandEvent] The event of the message that contained the command.
55
+ # @note `LocalJumpError`s are rescued from internally, giving bots the opportunity to use `return` or `break` in
56
+ # their blocks without propagating an exception.
49
57
  # @return [Command] The command that was added.
58
+ # @deprecated The command name argument will no longer support arrays in the next release.
59
+ # Use the `aliases` attribute instead.
50
60
  def command(name, attributes = {}, &block)
51
61
  @commands ||= {}
52
62
  if name.is_a? Array
@@ -59,7 +69,11 @@ module Discordrb::Commands
59
69
 
60
70
  new_command
61
71
  else
62
- @commands[name] = Command.new(name, attributes, &block)
72
+ new_command = Command.new(name, attributes, &block)
73
+ new_command.attributes[:aliases].each do |aliased_name|
74
+ @commands[aliased_name] = CommandAlias.new(aliased_name, new_command)
75
+ end
76
+ @commands[name] = new_command
63
77
  end
64
78
  end
65
79
 
@@ -22,9 +22,12 @@ module Discordrb::Commands
22
22
  # Discord action permissions required to use this command
23
23
  required_permissions: attributes[:required_permissions] || [],
24
24
 
25
- # Roles required to use this command
25
+ # Roles required to use this command (all? comparison)
26
26
  required_roles: attributes[:required_roles] || [],
27
27
 
28
+ # Roles allowed to use this command (any? comparison)
29
+ allowed_roles: attributes[:allowed_roles] || [],
30
+
28
31
  # Channels this command can be used on
29
32
  channels: attributes[:channels] || nil,
30
33
 
@@ -57,7 +60,13 @@ module Discordrb::Commands
57
60
  rate_limit_message: attributes[:rate_limit_message],
58
61
 
59
62
  # Rate limiting bucket (nil for no rate limiting)
60
- bucket: attributes[:bucket]
63
+ bucket: attributes[:bucket],
64
+
65
+ # Block for handling internal exceptions, or a string to respond with
66
+ rescue: attributes[:rescue],
67
+
68
+ # A list of aliases that reference this command
69
+ aliases: attributes[:aliases] || []
61
70
  }
62
71
 
63
72
  @block = block
@@ -100,8 +109,31 @@ module Discordrb::Commands
100
109
 
101
110
  result = @block.call(event, *arguments)
102
111
  event.drain_into(result)
103
- rescue LocalJumpError # occurs when breaking
104
- nil
112
+ rescue LocalJumpError => ex # occurs when breaking
113
+ result = ex.exit_value
114
+ event.drain_into(result)
115
+ rescue => exception # Something went wrong inside our @block!
116
+ rescue_value = @attributes[:rescue] || event.bot.attributes[:rescue]
117
+ if rescue_value
118
+ event.respond(rescue_value.gsub('%exception%', exception.message)) if rescue_value.is_a?(String)
119
+ rescue_value.call(event, exception) if rescue_value.respond_to?(:call)
120
+ end
121
+
122
+ raise exception
123
+ end
124
+ end
125
+
126
+ # A command that references another command
127
+ class CommandAlias
128
+ # @return [Symbol] the name of this alias
129
+ attr_reader :name
130
+
131
+ # @return [Command] the command this alias points to
132
+ attr_reader :aliased_command
133
+
134
+ def initialize(name, aliased_command)
135
+ @name = name
136
+ @aliased_command = aliased_command
105
137
  end
106
138
  end
107
139
 
@@ -195,10 +227,14 @@ module Discordrb::Commands
195
227
  chain_to_split = @chain
196
228
 
197
229
  # Don't break if a command is called the same thing as the chain delimiter
198
- chain_to_split = chain_to_split.slice(1..-1) if chain_to_split.start_with?(@attributes[:chain_delimiter])
230
+ chain_to_split = chain_to_split.slice(1..-1) if !@attributes[:chain_delimiter].empty? && chain_to_split.start_with?(@attributes[:chain_delimiter])
199
231
 
200
232
  first = true
201
- split_chain = chain_to_split.split(@attributes[:chain_delimiter])
233
+ split_chain = if @attributes[:chain_delimiter].empty?
234
+ [chain_to_split]
235
+ else
236
+ chain_to_split.split(@attributes[:chain_delimiter])
237
+ end
202
238
  split_chain.each do |command|
203
239
  command = @attributes[:chain_delimiter] + command if first && @chain.start_with?(@attributes[:chain_delimiter])
204
240
  first = false
@@ -228,6 +264,9 @@ module Discordrb::Commands
228
264
 
229
265
  # Finally execute the command
230
266
  prev = @bot.execute_command(command_name.to_sym, event, arguments, split_chain.length > 1 || @subchain)
267
+
268
+ # Stop chain if execute_command failed (maybe the command doesn't exist, or permissions failed, etc.)
269
+ break unless prev
231
270
  end
232
271
 
233
272
  prev
@@ -267,7 +306,7 @@ module Discordrb::Commands
267
306
  private
268
307
 
269
308
  def divide_chain(chain)
270
- chain_args_index = chain.index @attributes[:chain_args_delim]
309
+ chain_args_index = chain.index(@attributes[:chain_args_delim]) unless @attributes[:chain_args_delim].empty?
271
310
  chain_args = []
272
311
 
273
312
  if chain_args_index
@@ -37,8 +37,9 @@ module Discordrb::Commands
37
37
  # Performs a rate limiting request
38
38
  # @param thing [#resolve_id, Integer, Symbol] The particular thing that should be rate-limited (usually a user/channel, but you can also choose arbitrary integers or symbols)
39
39
  # @param rate_limit_time [Time] The time to base the rate limiting on, only useful for testing.
40
+ # @param increment [Integer] How much to increment the rate-limit counter. Default is 1.
40
41
  # @return [Integer, false] the waiting time until the next request, in seconds, or false if the request succeeded
41
- def rate_limited?(thing, rate_limit_time = nil)
42
+ def rate_limited?(thing, rate_limit_time = nil, increment: 1)
42
43
  key = resolve_key thing
43
44
  limit_hash = @bucket[key]
44
45
 
@@ -47,7 +48,7 @@ module Discordrb::Commands
47
48
  @bucket[key] = {
48
49
  last_time: Time.now,
49
50
  set_time: Time.now,
50
- count: 1
51
+ count: increment
51
52
  }
52
53
 
53
54
  return false
@@ -56,7 +57,7 @@ module Discordrb::Commands
56
57
  # Define the time at which we're being rate limited once so it doesn't get inaccurate
57
58
  rate_limit_time ||= Time.now
58
59
 
59
- if @limit && (limit_hash[:count] + 1) > @limit
60
+ if @limit && (limit_hash[:count] + increment) > @limit
60
61
  # Second case: Count is over the limit and the time has not run out yet
61
62
  return (limit_hash[:set_time] + @time_span) - rate_limit_time if @time_span && rate_limit_time < (limit_hash[:set_time] + @time_span)
62
63
 
@@ -72,7 +73,7 @@ module Discordrb::Commands
72
73
  else
73
74
  # Fifth case: no rate limiting at all! Increment the count, set the last_time, and return false
74
75
  limit_hash[:last_time] = rate_limit_time
75
- limit_hash[:count] += 1
76
+ limit_hash[:count] += increment
76
77
  false
77
78
  end
78
79
  end
@@ -104,13 +105,14 @@ module Discordrb::Commands
104
105
  # Performs a rate limit request.
105
106
  # @param key [Symbol] Which bucket to perform the request for.
106
107
  # @param thing [#resolve_id, Integer, Symbol] What should be rate-limited.
108
+ # @param increment (see Bucket#rate_limited?)
107
109
  # @see Bucket#rate_limited?
108
110
  # @return [Integer, false] How much time to wait or false if the request succeeded.
109
- def rate_limited?(key, thing)
111
+ def rate_limited?(key, thing, increment: 1)
110
112
  # Check whether the bucket actually exists
111
113
  return false unless @buckets && @buckets[key]
112
114
 
113
- @buckets[key].rate_limited?(thing)
115
+ @buckets[key].rate_limited?(thing, increment: increment)
114
116
  end
115
117
 
116
118
  # Cleans all buckets