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.
- checksums.yaml +5 -5
- data/.gitignore +4 -0
- data/.rubocop.yml +3 -3
- data/.travis.yml +28 -3
- data/.yardopts +1 -1
- data/CHANGELOG.md +555 -144
- data/CONTRIBUTING.md +1 -1
- data/Gemfile +0 -4
- data/README.md +86 -15
- data/Rakefile +2 -2
- data/bin/travis_build_docs.sh +17 -0
- data/discordrb-webhooks.gemspec +2 -1
- data/discordrb.gemspec +12 -5
- data/lib/discordrb.rb +2 -2
- data/lib/discordrb/api.rb +94 -25
- data/lib/discordrb/api/channel.rb +53 -17
- data/lib/discordrb/api/invite.rb +7 -4
- data/lib/discordrb/api/server.rb +173 -36
- data/lib/discordrb/api/user.rb +18 -4
- data/lib/discordrb/api/webhook.rb +83 -0
- data/lib/discordrb/await.rb +1 -1
- data/lib/discordrb/bot.rb +191 -102
- data/lib/discordrb/cache.rb +39 -9
- data/lib/discordrb/commands/command_bot.rb +79 -24
- data/lib/discordrb/commands/container.rb +16 -2
- data/lib/discordrb/commands/parser.rb +46 -7
- data/lib/discordrb/commands/rate_limiter.rb +8 -6
- data/lib/discordrb/container.rb +51 -7
- data/lib/discordrb/data.rb +1729 -286
- data/lib/discordrb/errors.rb +34 -1
- data/lib/discordrb/events/generic.rb +1 -1
- data/lib/discordrb/events/guilds.rb +1 -0
- data/lib/discordrb/events/message.rb +18 -12
- data/lib/discordrb/events/presence.rb +7 -2
- data/lib/discordrb/events/reactions.rb +13 -4
- data/lib/discordrb/events/roles.rb +7 -6
- data/lib/discordrb/events/typing.rb +1 -1
- data/lib/discordrb/events/webhooks.rb +61 -0
- data/lib/discordrb/gateway.rb +85 -32
- data/lib/discordrb/light.rb +1 -1
- data/lib/discordrb/logger.rb +8 -7
- data/lib/discordrb/permissions.rb +41 -4
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +10 -8
- data/lib/discordrb/voice/voice_bot.rb +4 -4
- data/lib/discordrb/websocket.rb +2 -2
- metadata +59 -14
data/lib/discordrb/cache.rb
CHANGED
@@ -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
|
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.
|
177
|
-
# * A short invite URL with protocol (e.
|
178
|
-
# * A short invite URL without protocol (e.
|
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
|
-
# @
|
219
|
-
#
|
220
|
-
|
221
|
-
|
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, "#{
|
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
|
391
|
-
|
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
|
-
|
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
|
404
|
-
|
405
|
-
|
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
|
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
|
-
|
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
|
-
|
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 =
|
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:
|
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] +
|
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] +=
|
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
|