discordrb 3.3.0 → 3.5.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +152 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/pull_request_template.md +37 -0
- data/.github/workflows/codeql.yml +65 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +39 -36
- data/CHANGELOG.md +874 -552
- data/Gemfile +2 -0
- data/LICENSE.txt +1 -1
- data/README.md +80 -86
- data/Rakefile +2 -0
- data/bin/console +1 -0
- data/discordrb-webhooks.gemspec +9 -6
- data/discordrb.gemspec +21 -18
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api/application.rb +202 -0
- data/lib/discordrb/api/channel.rb +236 -47
- data/lib/discordrb/api/interaction.rb +54 -0
- data/lib/discordrb/api/invite.rb +5 -5
- data/lib/discordrb/api/server.rb +94 -66
- data/lib/discordrb/api/user.rb +17 -11
- data/lib/discordrb/api/webhook.rb +63 -6
- data/lib/discordrb/api.rb +55 -16
- data/lib/discordrb/await.rb +0 -1
- data/lib/discordrb/bot.rb +480 -93
- data/lib/discordrb/cache.rb +31 -24
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +35 -12
- data/lib/discordrb/commands/container.rb +21 -24
- data/lib/discordrb/commands/parser.rb +20 -20
- data/lib/discordrb/commands/rate_limiter.rb +4 -3
- data/lib/discordrb/container.rb +209 -20
- data/lib/discordrb/data/activity.rb +271 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +71 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +993 -0
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +122 -0
- data/lib/discordrb/data/interaction.rb +800 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +372 -0
- data/lib/discordrb/data/message.rb +414 -0
- data/lib/discordrb/data/overwrite.rb +108 -0
- data/lib/discordrb/data/profile.rb +91 -0
- data/lib/discordrb/data/reaction.rb +33 -0
- data/lib/discordrb/data/recipient.rb +34 -0
- data/lib/discordrb/data/role.rb +248 -0
- data/lib/discordrb/data/server.rb +1004 -0
- data/lib/discordrb/data/user.rb +264 -0
- data/lib/discordrb/data/voice_region.rb +45 -0
- data/lib/discordrb/data/voice_state.rb +41 -0
- data/lib/discordrb/data/webhook.rb +238 -0
- data/lib/discordrb/data.rb +28 -4180
- data/lib/discordrb/errors.rb +46 -4
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +3 -1
- data/lib/discordrb/events/guilds.rb +16 -9
- data/lib/discordrb/events/interactions.rb +482 -0
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +72 -27
- data/lib/discordrb/events/presence.rb +35 -18
- data/lib/discordrb/events/raw.rb +1 -3
- data/lib/discordrb/events/reactions.rb +49 -4
- data/lib/discordrb/events/threads.rb +96 -0
- data/lib/discordrb/events/typing.rb +6 -4
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +15 -10
- data/lib/discordrb/events/webhooks.rb +9 -6
- data/lib/discordrb/gateway.rb +99 -71
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +4 -4
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +159 -39
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +16 -7
- data/lib/discordrb/voice/network.rb +99 -47
- data/lib/discordrb/voice/sodium.rb +98 -0
- data/lib/discordrb/voice/voice_bot.rb +33 -25
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb.rb +107 -1
- metadata +126 -54
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -33
- data/bin/travis_build_docs.sh +0 -17
- /data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
data/lib/discordrb/cache.rb
CHANGED
@@ -21,8 +21,7 @@ module Discordrb
|
|
21
21
|
|
22
22
|
@channels = {}
|
23
23
|
@pm_channels = {}
|
24
|
-
|
25
|
-
@restricted_channels = []
|
24
|
+
@thread_members = {}
|
26
25
|
end
|
27
26
|
|
28
27
|
# Returns or caches the available voice regions
|
@@ -42,28 +41,21 @@ module Discordrb
|
|
42
41
|
# @param id [Integer] The channel ID for which to search for.
|
43
42
|
# @param server [Server] The server for which to search the channel for. If this isn't specified, it will be
|
44
43
|
# inferred using the API
|
45
|
-
# @return [Channel] The channel identified by the ID.
|
44
|
+
# @return [Channel, nil] The channel identified by the ID.
|
45
|
+
# @raise Discordrb::Errors::NoPermission
|
46
46
|
def channel(id, server = nil)
|
47
47
|
id = id.resolve_id
|
48
48
|
|
49
|
-
raise Discordrb::Errors::NoPermission if @restricted_channels.include? id
|
50
|
-
|
51
49
|
debug("Obtaining data for channel with id #{id}")
|
52
50
|
return @channels[id] if @channels[id]
|
53
51
|
|
54
52
|
begin
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
return nil
|
59
|
-
end
|
60
|
-
channel = Channel.new(JSON.parse(response), self, server)
|
61
|
-
@channels[id] = channel
|
62
|
-
rescue Discordrb::Errors::NoPermission
|
63
|
-
debug "Tried to get access to restricted channel #{id}, blacklisting it"
|
64
|
-
@restricted_channels << id
|
65
|
-
raise
|
53
|
+
response = API::Channel.resolve(token, id)
|
54
|
+
rescue Discordrb::Errors::UnknownChannel
|
55
|
+
return nil
|
66
56
|
end
|
57
|
+
channel = Channel.new(JSON.parse(response), self, server)
|
58
|
+
@channels[id] = channel
|
67
59
|
end
|
68
60
|
|
69
61
|
alias_method :group_channel, :channel
|
@@ -79,7 +71,7 @@ module Discordrb
|
|
79
71
|
LOGGER.out("Resolving user #{id}")
|
80
72
|
begin
|
81
73
|
response = API::User.resolve(token, id)
|
82
|
-
rescue
|
74
|
+
rescue Discordrb::Errors::UnknownUser
|
83
75
|
return nil
|
84
76
|
end
|
85
77
|
user = User.new(JSON.parse(response), self)
|
@@ -111,7 +103,6 @@ module Discordrb
|
|
111
103
|
def member(server_or_id, user_id)
|
112
104
|
server_id = server_or_id.resolve_id
|
113
105
|
user_id = user_id.resolve_id
|
114
|
-
|
115
106
|
server = server_or_id.is_a?(Server) ? server_or_id : self.server(server_id)
|
116
107
|
|
117
108
|
return server.member(user_id) if server.member_cached?(user_id)
|
@@ -119,7 +110,7 @@ module Discordrb
|
|
119
110
|
LOGGER.out("Resolving member #{server_id} on server #{user_id}")
|
120
111
|
begin
|
121
112
|
response = API::Server.resolve_member(token, server_id, user_id)
|
122
|
-
rescue
|
113
|
+
rescue Discordrb::Errors::UnknownUser, Discordrb::Errors::UnknownMember
|
123
114
|
return nil
|
124
115
|
end
|
125
116
|
member = Member.new(JSON.parse(response), server, self)
|
@@ -134,6 +125,7 @@ module Discordrb
|
|
134
125
|
def pm_channel(id)
|
135
126
|
id = id.resolve_id
|
136
127
|
return @pm_channels[id] if @pm_channels[id]
|
128
|
+
|
137
129
|
debug("Creating pm channel with user id #{id}")
|
138
130
|
response = API::User.create_pm(token, id)
|
139
131
|
channel = Channel.new(JSON.parse(response), self)
|
@@ -155,10 +147,14 @@ module Discordrb
|
|
155
147
|
|
156
148
|
# Ensures a given server object is cached and if not, cache it from the given data hash.
|
157
149
|
# @param data [Hash] A data hash representing a server.
|
150
|
+
# @param force_cache [true, false] Whether the object in cache should be updated with the given
|
151
|
+
# data if it already exists.
|
158
152
|
# @return [Server] the server represented by the data hash.
|
159
|
-
def ensure_server(data)
|
153
|
+
def ensure_server(data, force_cache = false)
|
160
154
|
if @servers.include?(data['id'].to_i)
|
161
|
-
@servers[data['id'].to_i]
|
155
|
+
server = @servers[data['id'].to_i]
|
156
|
+
server.update_data(data) if force_cache
|
157
|
+
server
|
162
158
|
else
|
163
159
|
@servers[data['id'].to_i] = Server.new(data, self)
|
164
160
|
end
|
@@ -176,6 +172,16 @@ module Discordrb
|
|
176
172
|
end
|
177
173
|
end
|
178
174
|
|
175
|
+
# Ensures a given thread member object is cached.
|
176
|
+
# @param data [Hash] Thread member data.
|
177
|
+
def ensure_thread_member(data)
|
178
|
+
thread_id = data['id'].to_i
|
179
|
+
user_id = data['user_id'].to_i
|
180
|
+
|
181
|
+
@thread_members[thread_id] ||= {}
|
182
|
+
@thread_members[thread_id][user_id] = data.slice('join_timestamp', 'flags')
|
183
|
+
end
|
184
|
+
|
179
185
|
# Requests member chunks for a given server ID.
|
180
186
|
# @param id [Integer] The server ID to request chunks for.
|
181
187
|
def request_chunks(id)
|
@@ -187,13 +193,13 @@ module Discordrb
|
|
187
193
|
#
|
188
194
|
# * An {Invite} object
|
189
195
|
# * The code for an invite
|
190
|
-
# * A fully qualified invite URL (e.g. `https://
|
196
|
+
# * A fully qualified invite URL (e.g. `https://discord.com/invite/0A37aN7fasF7n83q`)
|
191
197
|
# * A short invite URL with protocol (e.g. `https://discord.gg/0A37aN7fasF7n83q`)
|
192
198
|
# * A short invite URL without protocol (e.g. `discord.gg/0A37aN7fasF7n83q`)
|
193
199
|
# @return [String] Only the code for the invite.
|
194
200
|
def resolve_invite_code(invite)
|
195
201
|
invite = invite.code if invite.is_a? Discordrb::Invite
|
196
|
-
invite = invite[invite.rindex('/') + 1
|
202
|
+
invite = invite[invite.rindex('/') + 1..] if invite.start_with?('http', 'discord.gg')
|
197
203
|
invite
|
198
204
|
end
|
199
205
|
|
@@ -219,7 +225,7 @@ module Discordrb
|
|
219
225
|
return [channel(id)]
|
220
226
|
end
|
221
227
|
|
222
|
-
@servers.
|
228
|
+
@servers.each_value do |server|
|
223
229
|
server.channels.each do |channel|
|
224
230
|
results << channel if channel.name == channel_name && (server_name || server.name) == server.name && (!type || (channel.type == type))
|
225
231
|
end
|
@@ -248,6 +254,7 @@ module Discordrb
|
|
248
254
|
def find_user(username, discrim = nil)
|
249
255
|
users = @users.values.find_all { |e| e.username == username }
|
250
256
|
return users.find { |u| u.discrim == discrim } if discrim
|
257
|
+
|
251
258
|
users
|
252
259
|
end
|
253
260
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discordrb
|
4
|
+
# A colour (red, green and blue values). Used for role colours. If you prefer the American spelling, the alias
|
5
|
+
# {ColorRGB} is also available.
|
6
|
+
class ColourRGB
|
7
|
+
# @return [Integer] the red part of this colour (0-255).
|
8
|
+
attr_reader :red
|
9
|
+
|
10
|
+
# @return [Integer] the green part of this colour (0-255).
|
11
|
+
attr_reader :green
|
12
|
+
|
13
|
+
# @return [Integer] the blue part of this colour (0-255).
|
14
|
+
attr_reader :blue
|
15
|
+
|
16
|
+
# @return [Integer] the colour's RGB values combined into one integer.
|
17
|
+
attr_reader :combined
|
18
|
+
alias_method :to_i, :combined
|
19
|
+
|
20
|
+
# Make a new colour from the combined value.
|
21
|
+
# @param combined [String, Integer] The colour's RGB values combined into one integer or a hexadecimal string
|
22
|
+
# @example Initialize a with a base 10 integer
|
23
|
+
# ColourRGB.new(7506394) #=> ColourRGB
|
24
|
+
# ColourRGB.new(0x7289da) #=> ColourRGB
|
25
|
+
# @example Initialize a with a hexadecimal string
|
26
|
+
# ColourRGB.new('7289da') #=> ColourRGB
|
27
|
+
def initialize(combined)
|
28
|
+
@combined = combined.is_a?(String) ? combined.to_i(16) : combined
|
29
|
+
@red = (@combined >> 16) & 0xFF
|
30
|
+
@green = (@combined >> 8) & 0xFF
|
31
|
+
@blue = @combined & 0xFF
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [String] the colour as a hexadecimal.
|
35
|
+
def hex
|
36
|
+
@combined.to_s(16)
|
37
|
+
end
|
38
|
+
alias_method :hexadecimal, :hex
|
39
|
+
end
|
40
|
+
|
41
|
+
# Alias for the class {ColourRGB}
|
42
|
+
ColorRGB = ColourRGB
|
43
|
+
end
|
@@ -24,7 +24,7 @@ module Discordrb::Commands
|
|
24
24
|
|
25
25
|
# Creates a new CommandBot and logs in to Discord.
|
26
26
|
# @param attributes [Hash] The attributes to initialize the CommandBot with.
|
27
|
-
# @see
|
27
|
+
# @see Discordrb::Bot#initialize Discordrb::Bot#initialize for other attributes that should be used to create the underlying regular bot.
|
28
28
|
# @option attributes [String, Array<String>, #call] :prefix The prefix that should trigger this bot's commands. It
|
29
29
|
# can be:
|
30
30
|
#
|
@@ -39,14 +39,15 @@ module Discordrb::Commands
|
|
39
39
|
# complicated dynamic prefixes (e. g. based on server), or even something else entirely (suffixes, or most
|
40
40
|
# adventurous, infixes).
|
41
41
|
# @option attributes [true, false] :advanced_functionality Whether to enable advanced functionality (very powerful
|
42
|
-
# way to nest commands into chains, see https://github.com/
|
42
|
+
# way to nest commands into chains, see https://github.com/shardlab/discordrb/wiki/Commands#command-chain-syntax
|
43
43
|
# for info. Default is false.
|
44
44
|
# @option attributes [Symbol, Array<Symbol>, false] :help_command The name of the command that displays info for
|
45
45
|
# other commands. Use an array if you want to have aliases. Default is "help". If none should be created, use
|
46
46
|
# `false` as the value.
|
47
|
-
# @option attributes [String] :command_doesnt_exist_message The message that should be displayed if a user attempts
|
47
|
+
# @option attributes [String, #call] :command_doesnt_exist_message The message that should be displayed if a user attempts
|
48
48
|
# to use a command that does not exist. If none is specified, no message will be displayed. In the message, you
|
49
|
-
# can use the string '%command%' that will be replaced with the name of the command.
|
49
|
+
# can use the string '%command%' that will be replaced with the name of the command. Anything responding to call
|
50
|
+
# such as a proc will be called with the event, and is expected to return a String or nil.
|
50
51
|
# @option attributes [String] :no_permission_message The message to be displayed when `NoPermission` error is raised.
|
51
52
|
# @option attributes [true, false] :spaces_allowed Whether spaces are allowed to occur between the prefix and the
|
52
53
|
# command. Default is false.
|
@@ -69,7 +70,9 @@ module Discordrb::Commands
|
|
69
70
|
# @option attributes [String] :quote_end Character that should end a quoted string (see
|
70
71
|
# :advanced_functionality). Default is '"' or the same as :quote_start. Set to an empty string to disable.
|
71
72
|
# @option attributes [true, false] :ignore_bots Whether the bot should ignore bot accounts or not. Default is false.
|
72
|
-
def initialize(attributes
|
73
|
+
def initialize(**attributes)
|
74
|
+
# TODO: This needs to be revisited. undefined attributes are treated
|
75
|
+
# as explicitly passed nils.
|
73
76
|
super(
|
74
77
|
log_mode: attributes[:log_mode],
|
75
78
|
token: attributes[:token],
|
@@ -83,7 +86,9 @@ module Discordrb::Commands
|
|
83
86
|
num_shards: attributes[:num_shards],
|
84
87
|
redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true,
|
85
88
|
ignore_bots: attributes[:ignore_bots],
|
86
|
-
compress_mode: attributes[:compress_mode]
|
89
|
+
compress_mode: attributes[:compress_mode],
|
90
|
+
intents: attributes[:intents] || :all
|
91
|
+
)
|
87
92
|
|
88
93
|
@prefix = attributes[:prefix]
|
89
94
|
@attributes = {
|
@@ -140,6 +145,7 @@ module Discordrb::Commands
|
|
140
145
|
}
|
141
146
|
|
142
147
|
return unless @attributes[:help_command]
|
148
|
+
|
143
149
|
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|
|
144
150
|
if command_name
|
145
151
|
command = @commands[command_name.to_sym]
|
@@ -148,6 +154,7 @@ module Discordrb::Commands
|
|
148
154
|
command_name = command.name
|
149
155
|
end
|
150
156
|
return "The command `#{command_name}` does not exist!" unless command
|
157
|
+
|
151
158
|
desc = command.attributes[:description] || '*No description available*'
|
152
159
|
usage = command.attributes[:usage]
|
153
160
|
parameters = command.attributes[:parameters]
|
@@ -206,15 +213,22 @@ module Discordrb::Commands
|
|
206
213
|
def execute_command(name, event, arguments, chained = false, check_permissions = true)
|
207
214
|
debug("Executing command #{name} with arguments #{arguments}")
|
208
215
|
return unless @commands
|
216
|
+
|
209
217
|
command = @commands[name]
|
210
218
|
command = command.aliased_command if command.is_a?(CommandAlias)
|
211
219
|
return unless !check_permissions || channels?(event.channel, @attributes[:channels]) ||
|
212
220
|
(command && !command.attributes[:channels].nil?)
|
221
|
+
|
213
222
|
unless command
|
214
|
-
|
223
|
+
if @attributes[:command_doesnt_exist_message]
|
224
|
+
message = @attributes[:command_doesnt_exist_message]
|
225
|
+
message = message.call(event) if message.respond_to?(:call)
|
226
|
+
event.respond message.gsub('%command%', name.to_s) if message
|
227
|
+
end
|
215
228
|
return
|
216
229
|
end
|
217
230
|
return unless !check_permissions || channels?(event.channel, command.attributes[:channels])
|
231
|
+
|
218
232
|
arguments = arg_check(arguments, command.attributes[:arg_types], event.server) if check_permissions
|
219
233
|
if (check_permissions &&
|
220
234
|
permission?(event.author, command.attributes[:permission_level], event.server) &&
|
@@ -238,11 +252,13 @@ module Discordrb::Commands
|
|
238
252
|
# For example, `['1', '10..14']` with types `[Integer, Range]` would turn into `[1, 10..14]`.
|
239
253
|
def arg_check(args, types = nil, server = nil)
|
240
254
|
return args unless types
|
255
|
+
|
241
256
|
args.each_with_index.map do |arg, i|
|
242
257
|
next arg if types[i].nil? || types[i] == String
|
258
|
+
|
243
259
|
if types[i] == Integer
|
244
260
|
begin
|
245
|
-
Integer(arg)
|
261
|
+
Integer(arg, 10)
|
246
262
|
rescue ArgumentError
|
247
263
|
nil
|
248
264
|
end
|
@@ -304,7 +320,7 @@ module Discordrb::Commands
|
|
304
320
|
elsif types[i].respond_to?(:from_argument)
|
305
321
|
begin
|
306
322
|
types[i].from_argument arg
|
307
|
-
rescue
|
323
|
+
rescue StandardError
|
308
324
|
nil
|
309
325
|
end
|
310
326
|
else
|
@@ -319,8 +335,9 @@ module Discordrb::Commands
|
|
319
335
|
# @return [String, nil] the command's result, if there is any.
|
320
336
|
def simple_execute(chain, event)
|
321
337
|
return nil if chain.empty?
|
338
|
+
|
322
339
|
args = chain.split(' ')
|
323
|
-
execute_command(args[0].to_sym, event, args[1
|
340
|
+
execute_command(args[0].to_sym, event, args[1..])
|
324
341
|
end
|
325
342
|
|
326
343
|
# Sets the permission level of a user
|
@@ -369,6 +386,7 @@ module Discordrb::Commands
|
|
369
386
|
# @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be added
|
370
387
|
def add_channel(channel)
|
371
388
|
return if @attributes[:channels].find { |c| channel.resolve_id == c.resolve_id }
|
389
|
+
|
372
390
|
@attributes[:channels] << channel
|
373
391
|
end
|
374
392
|
|
@@ -426,7 +444,8 @@ module Discordrb::Commands
|
|
426
444
|
|
427
445
|
def standard_prefix_trigger(message, prefix)
|
428
446
|
return nil unless message.start_with? prefix
|
429
|
-
|
447
|
+
|
448
|
+
message[prefix.length..]
|
430
449
|
end
|
431
450
|
|
432
451
|
def required_permissions?(member, required, channel = nil)
|
@@ -437,11 +456,13 @@ module Discordrb::Commands
|
|
437
456
|
|
438
457
|
def required_roles?(member, required)
|
439
458
|
return true if member.webhook? || member.is_a?(Discordrb::Recipient) || required.nil? || required.empty?
|
459
|
+
|
440
460
|
required.is_a?(Array) ? check_multiple_roles(member, required) : member.role?(role)
|
441
461
|
end
|
442
462
|
|
443
463
|
def allowed_roles?(member, required)
|
444
464
|
return true if member.webhook? || member.is_a?(Discordrb::Recipient) || required.nil? || required.empty?
|
465
|
+
|
445
466
|
required.is_a?(Array) ? check_multiple_roles(member, required, false) : member.role?(role)
|
446
467
|
end
|
447
468
|
|
@@ -459,9 +480,11 @@ module Discordrb::Commands
|
|
459
480
|
|
460
481
|
def channels?(channel, channels)
|
461
482
|
return true if channels.nil? || channels.empty?
|
483
|
+
|
462
484
|
channels.any? do |c|
|
463
485
|
# if c is string, make sure to remove the "#" from channel names in case it was specified
|
464
486
|
return true if c.is_a?(String) && c.delete('#') == channel.name
|
487
|
+
|
465
488
|
c.resolve_id == channel.resolve_id
|
466
489
|
end
|
467
490
|
end
|
@@ -480,7 +503,7 @@ module Discordrb::Commands
|
|
480
503
|
else
|
481
504
|
event.respond result unless result.nil? || result.empty?
|
482
505
|
end
|
483
|
-
rescue => e
|
506
|
+
rescue StandardError => e
|
484
507
|
log_exception(e)
|
485
508
|
ensure
|
486
509
|
@event_threads.delete(t)
|
@@ -9,22 +9,25 @@ module Discordrb::Commands
|
|
9
9
|
module CommandContainer
|
10
10
|
include RateLimiter
|
11
11
|
|
12
|
-
# @return [Hash<Symbol, Command>] hash of command names and commands this container has.
|
12
|
+
# @return [Hash<Symbol, Command, CommandAlias>] hash of command names and commands this container has.
|
13
13
|
attr_reader :commands
|
14
14
|
|
15
15
|
# Adds a new command to the container.
|
16
|
-
# @param name [Symbol
|
16
|
+
# @param name [Symbol] The name of the command to add.
|
17
17
|
# @param attributes [Hash] The attributes to initialize the command with.
|
18
|
+
# @option attributes [Array<Symbol>] :aliases A list of additional names for this command. This in effect
|
19
|
+
# creates {CommandAlias} objects in the container ({#commands}) that refer to the newly created command.
|
20
|
+
# Additionally, the default help command will identify these command names as an alias where applicable.
|
18
21
|
# @option attributes [Integer] :permission_level The minimum permission level that can use this command, inclusive.
|
19
22
|
# See {CommandBot#set_user_permission} and {CommandBot#set_role_permission}.
|
20
23
|
# @option attributes [String, false] :permission_message Message to display when a user does not have sufficient
|
21
24
|
# permissions to execute a command. %name% in the message will be replaced with the name of the command. Disable
|
22
25
|
# the message by setting this option to false.
|
23
26
|
# @option attributes [Array<Symbol>] :required_permissions Discord action permissions (e.g. `:kick_members`) that
|
24
|
-
# should be required to use this command. See {Discordrb::Permissions::
|
25
|
-
# @option attributes [Array<Role>, Array
|
27
|
+
# should be required to use this command. See {Discordrb::Permissions::FLAGS} for a list.
|
28
|
+
# @option attributes [Array<Role>, Array<String, Integer>] :required_roles Roles, or their IDs, that user must have to use this command
|
26
29
|
# (user must have all of them).
|
27
|
-
# @option attributes [Array<Role>, Array
|
30
|
+
# @option attributes [Array<Role>, Array<String, Integer>] :allowed_roles Roles, or their IDs, that user should have to use this command
|
28
31
|
# (user should have at least one of them).
|
29
32
|
# @option attributes [Array<String, Integer, Channel>] :channels The channels that this command can be used on. An
|
30
33
|
# empty array indicates it can be used on any channel. Supersedes the command bot attribute.
|
@@ -55,26 +58,22 @@ module Discordrb::Commands
|
|
55
58
|
# @note `LocalJumpError`s are rescued from internally, giving bots the opportunity to use `return` or `break` in
|
56
59
|
# their blocks without propagating an exception.
|
57
60
|
# @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.
|
60
61
|
def command(name, attributes = {}, &block)
|
61
62
|
@commands ||= {}
|
62
|
-
if name.is_a? Array
|
63
|
-
new_command = nil
|
64
63
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
64
|
+
# TODO: Remove in 4.0
|
65
|
+
if name.is_a?(Array)
|
66
|
+
name, *aliases = name
|
67
|
+
attributes[:aliases] = aliases if attributes[:aliases].nil?
|
68
|
+
Discordrb::LOGGER.warn("While registering command #{name.inspect}")
|
69
|
+
Discordrb::LOGGER.warn('Arrays for command aliases is removed. Please use `aliases` argument instead.')
|
70
|
+
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
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
|
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)
|
77
75
|
end
|
76
|
+
@commands[name] = new_command
|
78
77
|
end
|
79
78
|
|
80
79
|
# Removes a specific command from this container.
|
@@ -87,7 +86,7 @@ module Discordrb::Commands
|
|
87
86
|
# Adds all commands from another container into this one. Existing commands will be overwritten.
|
88
87
|
# @param container [Module] A module that `extend`s {CommandContainer} from which the commands will be added.
|
89
88
|
def include_commands(container)
|
90
|
-
handlers = container.instance_variable_get
|
89
|
+
handlers = container.instance_variable_get :@commands
|
91
90
|
return unless handlers
|
92
91
|
|
93
92
|
@commands ||= {}
|
@@ -100,9 +99,7 @@ module Discordrb::Commands
|
|
100
99
|
container_modules = container.singleton_class.included_modules
|
101
100
|
|
102
101
|
# If the container is an EventContainer and we can include it, then do that
|
103
|
-
if container_modules.include?(Discordrb::EventContainer) && respond_to?(:include_events)
|
104
|
-
include_events(container)
|
105
|
-
end
|
102
|
+
include_events(container) if container_modules.include?(Discordrb::EventContainer) && respond_to?(:include_events)
|
106
103
|
|
107
104
|
if container_modules.include? Discordrb::Commands::CommandContainer
|
108
105
|
include_commands(container)
|
@@ -81,45 +81,43 @@ module Discordrb::Commands
|
|
81
81
|
# @return [String] the result of the execution.
|
82
82
|
def call(event, arguments, chained = false, check_permissions = true)
|
83
83
|
if arguments.length < @attributes[:min_args]
|
84
|
-
|
85
|
-
|
84
|
+
response = "Too few arguments for command `#{name}`!"
|
85
|
+
response += "\nUsage: `#{@attributes[:usage]}`" if @attributes[:usage]
|
86
|
+
event.respond(response)
|
86
87
|
return
|
87
88
|
end
|
88
89
|
if @attributes[:max_args] >= 0 && arguments.length > @attributes[:max_args]
|
89
|
-
|
90
|
-
|
90
|
+
response = "Too many arguments for command `#{name}`!"
|
91
|
+
response += "\nUsage: `#{@attributes[:usage]}`" if @attributes[:usage]
|
92
|
+
event.respond(response)
|
91
93
|
return
|
92
94
|
end
|
93
|
-
unless @attributes[:chain_usable]
|
94
|
-
|
95
|
-
|
96
|
-
return
|
97
|
-
end
|
95
|
+
unless @attributes[:chain_usable] && !chained
|
96
|
+
event.respond "Command `#{name}` cannot be used in a command chain!"
|
97
|
+
return
|
98
98
|
end
|
99
99
|
|
100
100
|
if check_permissions
|
101
101
|
rate_limited = event.bot.rate_limited?(@attributes[:bucket], event.author)
|
102
102
|
if @attributes[:bucket] && rate_limited
|
103
|
-
if @attributes[:rate_limit_message]
|
104
|
-
event.respond @attributes[:rate_limit_message].gsub('%time%', rate_limited.round(2).to_s)
|
105
|
-
end
|
103
|
+
event.respond @attributes[:rate_limit_message].gsub('%time%', rate_limited.round(2).to_s) if @attributes[:rate_limit_message]
|
106
104
|
return
|
107
105
|
end
|
108
106
|
end
|
109
107
|
|
110
108
|
result = @block.call(event, *arguments)
|
111
109
|
event.drain_into(result)
|
112
|
-
rescue LocalJumpError =>
|
113
|
-
result =
|
110
|
+
rescue LocalJumpError => e # occurs when breaking
|
111
|
+
result = e.exit_value
|
114
112
|
event.drain_into(result)
|
115
|
-
rescue =>
|
113
|
+
rescue StandardError => e # Something went wrong inside our @block!
|
116
114
|
rescue_value = @attributes[:rescue] || event.bot.attributes[:rescue]
|
117
115
|
if rescue_value
|
118
|
-
event.respond(rescue_value.gsub('%exception%',
|
119
|
-
rescue_value.call(event,
|
116
|
+
event.respond(rescue_value.gsub('%exception%', e.message)) if rescue_value.is_a?(String)
|
117
|
+
rescue_value.call(event, e) if rescue_value.respond_to?(:call)
|
120
118
|
end
|
121
119
|
|
122
|
-
raise
|
120
|
+
raise e
|
123
121
|
end
|
124
122
|
end
|
125
123
|
|
@@ -209,8 +207,10 @@ module Discordrb::Commands
|
|
209
207
|
result += char if b_level <= 0
|
210
208
|
|
211
209
|
next unless char == @attributes[:sub_chain_end] && !quoted
|
210
|
+
|
212
211
|
b_level -= 1
|
213
212
|
next unless b_level.zero?
|
213
|
+
|
214
214
|
nested = @chain[b_start + 1..index - 1]
|
215
215
|
subchain = CommandChain.new(nested, @bot, true)
|
216
216
|
result += subchain.execute(event)
|
@@ -246,7 +246,7 @@ module Discordrb::Commands
|
|
246
246
|
|
247
247
|
first_space = command.index ' '
|
248
248
|
command_name = first_space ? command[0..first_space - 1] : command
|
249
|
-
arguments = first_space ? command[first_space + 1
|
249
|
+
arguments = first_space ? command[first_space + 1..] : ''
|
250
250
|
|
251
251
|
# Append a previous sign if none is present
|
252
252
|
arguments += @attributes[:previous] unless arguments.include? @attributes[:previous]
|
@@ -318,7 +318,7 @@ module Discordrb::Commands
|
|
318
318
|
arg.split ' '
|
319
319
|
end
|
320
320
|
|
321
|
-
chain = chain[chain_args_index + 1
|
321
|
+
chain = chain[chain_args_index + 1..]
|
322
322
|
end
|
323
323
|
|
324
324
|
[chain_args, chain]
|
@@ -35,7 +35,7 @@ module Discordrb::Commands
|
|
35
35
|
end
|
36
36
|
|
37
37
|
# Performs a rate limiting request
|
38
|
-
# @param thing [
|
38
|
+
# @param thing [String, 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
40
|
# @param increment [Integer] How much to increment the rate-limit counter. Default is 1.
|
41
41
|
# @return [Integer, false] the waiting time until the next request, in seconds, or false if the request succeeded
|
@@ -83,6 +83,7 @@ module Discordrb::Commands
|
|
83
83
|
def resolve_key(thing)
|
84
84
|
return thing.resolve_id if thing.respond_to?(:resolve_id) && !thing.is_a?(String)
|
85
85
|
return thing if thing.is_a?(Integer) || thing.is_a?(Symbol)
|
86
|
+
|
86
87
|
raise ArgumentError, "Cannot use a #{thing.class} as a rate limiting key!"
|
87
88
|
end
|
88
89
|
end
|
@@ -104,7 +105,7 @@ module Discordrb::Commands
|
|
104
105
|
|
105
106
|
# Performs a rate limit request.
|
106
107
|
# @param key [Symbol] Which bucket to perform the request for.
|
107
|
-
# @param thing [
|
108
|
+
# @param thing [String, Integer, Symbol] What should be rate-limited.
|
108
109
|
# @param increment (see Bucket#rate_limited?)
|
109
110
|
# @see Bucket#rate_limited?
|
110
111
|
# @return [Integer, false] How much time to wait or false if the request succeeded.
|
@@ -124,7 +125,7 @@ module Discordrb::Commands
|
|
124
125
|
# Adds all the buckets from another RateLimiter onto this one.
|
125
126
|
# @param limiter [Module] Another {RateLimiter} module
|
126
127
|
def include_buckets(limiter)
|
127
|
-
buckets = limiter.instance_variable_get(
|
128
|
+
buckets = limiter.instance_variable_get(:@buckets) || {}
|
128
129
|
@buckets ||= {}
|
129
130
|
@buckets.merge! buckets
|
130
131
|
end
|