discordrb 3.1.1 → 3.4.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/.circleci/config.yml +126 -0
- data/.codeclimate.yml +16 -0
- data/.github/CONTRIBUTING.md +13 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +37 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +39 -33
- data/.travis.yml +27 -2
- data/.yardopts +1 -1
- data/CHANGELOG.md +808 -208
- data/Gemfile +4 -1
- data/LICENSE.txt +1 -1
- data/README.md +108 -53
- data/Rakefile +14 -1
- data/bin/console +1 -0
- data/bin/travis_build_docs.sh +17 -0
- data/discordrb-webhooks.gemspec +26 -0
- data/discordrb.gemspec +24 -15
- data/lib/discordrb.rb +75 -2
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api.rb +126 -27
- data/lib/discordrb/api/channel.rb +165 -43
- data/lib/discordrb/api/invite.rb +10 -7
- data/lib/discordrb/api/server.rb +240 -61
- data/lib/discordrb/api/user.rb +26 -24
- data/lib/discordrb/api/webhook.rb +83 -0
- data/lib/discordrb/await.rb +1 -2
- data/lib/discordrb/bot.rb +417 -149
- data/lib/discordrb/cache.rb +42 -10
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +186 -31
- data/lib/discordrb/commands/container.rb +30 -16
- data/lib/discordrb/commands/parser.rb +102 -47
- data/lib/discordrb/commands/rate_limiter.rb +18 -17
- data/lib/discordrb/container.rb +245 -41
- data/lib/discordrb/data.rb +27 -2511
- data/lib/discordrb/data/activity.rb +264 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +56 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +849 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +83 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +297 -0
- data/lib/discordrb/data/message.rb +334 -0
- data/lib/discordrb/data/overwrite.rb +102 -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 +191 -0
- data/lib/discordrb/data/server.rb +1002 -0
- data/lib/discordrb/data/user.rb +204 -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 +145 -0
- data/lib/discordrb/errors.rb +36 -2
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +2 -0
- data/lib/discordrb/events/generic.rb +19 -3
- data/lib/discordrb/events/guilds.rb +129 -6
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +86 -36
- data/lib/discordrb/events/presence.rb +23 -16
- data/lib/discordrb/events/raw.rb +47 -0
- data/lib/discordrb/events/reactions.rb +159 -0
- data/lib/discordrb/events/roles.rb +7 -6
- data/lib/discordrb/events/typing.rb +9 -5
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +29 -9
- data/lib/discordrb/events/webhooks.rb +64 -0
- data/lib/discordrb/gateway.rb +219 -88
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light.rb +1 -1
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +12 -11
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +148 -14
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +14 -15
- data/lib/discordrb/voice/network.rb +86 -45
- data/lib/discordrb/voice/sodium.rb +96 -0
- data/lib/discordrb/voice/voice_bot.rb +52 -40
- data/lib/discordrb/webhooks.rb +12 -0
- data/lib/discordrb/websocket.rb +2 -2
- metadata +137 -34
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)
|
@@ -120,6 +134,7 @@ module Discordrb
|
|
120
134
|
def pm_channel(id)
|
121
135
|
id = id.resolve_id
|
122
136
|
return @pm_channels[id] if @pm_channels[id]
|
137
|
+
|
123
138
|
debug("Creating pm channel with user id #{id}")
|
124
139
|
response = API::User.create_pm(token, id)
|
125
140
|
channel = Channel.new(JSON.parse(response), self)
|
@@ -173,9 +188,9 @@ module Discordrb
|
|
173
188
|
#
|
174
189
|
# * An {Invite} object
|
175
190
|
# * 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.
|
191
|
+
# * A fully qualified invite URL (e.g. `https://discord.com/invite/0A37aN7fasF7n83q`)
|
192
|
+
# * A short invite URL with protocol (e.g. `https://discord.gg/0A37aN7fasF7n83q`)
|
193
|
+
# * A short invite URL without protocol (e.g. `discord.gg/0A37aN7fasF7n83q`)
|
179
194
|
# @return [String] Only the code for the invite.
|
180
195
|
def resolve_invite_code(invite)
|
181
196
|
invite = invite.code if invite.is_a? Discordrb::Invite
|
@@ -205,7 +220,7 @@ module Discordrb
|
|
205
220
|
return [channel(id)]
|
206
221
|
end
|
207
222
|
|
208
|
-
@servers.
|
223
|
+
@servers.each_value do |server|
|
209
224
|
server.channels.each do |channel|
|
210
225
|
results << channel if channel.name == channel_name && (server_name || server.name) == server.name && (!type || (channel.type == type))
|
211
226
|
end
|
@@ -214,11 +229,28 @@ module Discordrb
|
|
214
229
|
results
|
215
230
|
end
|
216
231
|
|
217
|
-
# Finds a user given its username.
|
218
|
-
# @
|
219
|
-
#
|
220
|
-
|
221
|
-
|
232
|
+
# Finds a user given its username or username & discriminator.
|
233
|
+
# @overload find_user(username)
|
234
|
+
# Find all cached users with a certain username.
|
235
|
+
# @param username [String] The username to look for.
|
236
|
+
# @return [Array<User>] The array of users that were found. May be empty if none were found.
|
237
|
+
# @overload find_user(username, discrim)
|
238
|
+
# Find a cached user with a certain username and discriminator.
|
239
|
+
# Find a user by name and discriminator
|
240
|
+
# @param username [String] The username to look for.
|
241
|
+
# @param discrim [String] The user's discriminator
|
242
|
+
# @return [User, nil] The user that was found, or `nil` if none was found
|
243
|
+
# @note This method only searches through users that have been cached. Users that have not yet been cached
|
244
|
+
# by the bot but still share a connection with the user (mutual server) will not be found.
|
245
|
+
# @example Find users by name
|
246
|
+
# bot.find_user('z64') #=> Array<User>
|
247
|
+
# @example Find a user by name and discriminator
|
248
|
+
# bot.find_user('z64', '2639') #=> User
|
249
|
+
def find_user(username, discrim = nil)
|
250
|
+
users = @users.values.find_all { |e| e.username == username }
|
251
|
+
return users.find { |u| u.discrim == discrim } if discrim
|
252
|
+
|
253
|
+
users
|
222
254
|
end
|
223
255
|
end
|
224
256
|
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
|
@@ -6,6 +6,7 @@ require 'discordrb/commands/parser'
|
|
6
6
|
require 'discordrb/commands/events'
|
7
7
|
require 'discordrb/commands/container'
|
8
8
|
require 'discordrb/commands/rate_limiter'
|
9
|
+
require 'time'
|
9
10
|
|
10
11
|
# Specialized bot to run commands
|
11
12
|
|
@@ -15,14 +16,15 @@ module Discordrb::Commands
|
|
15
16
|
# @return [Hash] this bot's attributes.
|
16
17
|
attr_reader :attributes
|
17
18
|
|
18
|
-
# @return [String] the prefix commands are triggered with.
|
19
|
+
# @return [String, Array<String>, #call] the prefix commands are triggered with.
|
20
|
+
# @see #initialize
|
19
21
|
attr_reader :prefix
|
20
22
|
|
21
23
|
include CommandContainer
|
22
24
|
|
23
25
|
# Creates a new CommandBot and logs in to Discord.
|
24
26
|
# @param attributes [Hash] The attributes to initialize the CommandBot with.
|
25
|
-
# @see
|
27
|
+
# @see Discordrb::Bot#initialize Discordrb::Bot#initialize for other attributes that should be used to create the underlying regular bot.
|
26
28
|
# @option attributes [String, Array<String>, #call] :prefix The prefix that should trigger this bot's commands. It
|
27
29
|
# can be:
|
28
30
|
#
|
@@ -37,7 +39,7 @@ module Discordrb::Commands
|
|
37
39
|
# complicated dynamic prefixes (e. g. based on server), or even something else entirely (suffixes, or most
|
38
40
|
# adventurous, infixes).
|
39
41
|
# @option attributes [true, false] :advanced_functionality Whether to enable advanced functionality (very powerful
|
40
|
-
# 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
|
41
43
|
# for info. Default is false.
|
42
44
|
# @option attributes [Symbol, Array<Symbol>, false] :help_command The name of the command that displays info for
|
43
45
|
# other commands. Use an array if you want to have aliases. Default is "help". If none should be created, use
|
@@ -53,24 +55,24 @@ module Discordrb::Commands
|
|
53
55
|
# @option attributes [Array<String, Integer, Channel>] :channels The channels this command bot accepts commands on.
|
54
56
|
# Superseded if a command has a 'channels' attribute.
|
55
57
|
# @option attributes [String] :previous Character that should designate the result of the previous command in
|
56
|
-
# a command chain (see :advanced_functionality). Default is '~'.
|
58
|
+
# a command chain (see :advanced_functionality). Default is '~'. Set to an empty string to disable.
|
57
59
|
# @option attributes [String] :chain_delimiter Character that should designate that a new command begins in the
|
58
|
-
# command chain (see :advanced_functionality). Default is '>'.
|
60
|
+
# command chain (see :advanced_functionality). Default is '>'. Set to an empty string to disable.
|
59
61
|
# @option attributes [String] :chain_args_delim Character that should separate the command chain arguments from the
|
60
|
-
# chain itself (see :advanced_functionality). Default is ':'.
|
62
|
+
# chain itself (see :advanced_functionality). Default is ':'. Set to an empty string to disable.
|
61
63
|
# @option attributes [String] :sub_chain_start Character that should start a sub-chain (see
|
62
|
-
# :advanced_functionality). Default is '['.
|
64
|
+
# :advanced_functionality). Default is '['. Set to an empty string to disable.
|
63
65
|
# @option attributes [String] :sub_chain_end Character that should end a sub-chain (see
|
64
|
-
# :advanced_functionality). Default is ']'.
|
66
|
+
# :advanced_functionality). Default is ']'. Set to an empty string to disable.
|
65
67
|
# @option attributes [String] :quote_start Character that should start a quoted string (see
|
66
|
-
# :advanced_functionality). Default is '"'.
|
68
|
+
# :advanced_functionality). Default is '"'. Set to an empty string to disable.
|
67
69
|
# @option attributes [String] :quote_end Character that should end a quoted string (see
|
68
|
-
# :advanced_functionality). Default is '"'.
|
70
|
+
# :advanced_functionality). Default is '"' or the same as :quote_start. Set to an empty string to disable.
|
71
|
+
# @option attributes [true, false] :ignore_bots Whether the bot should ignore bot accounts or not. Default is false.
|
69
72
|
def initialize(attributes = {})
|
70
73
|
super(
|
71
74
|
log_mode: attributes[:log_mode],
|
72
75
|
token: attributes[:token],
|
73
|
-
application_id: attributes[:application_id],
|
74
76
|
client_id: attributes[:client_id],
|
75
77
|
type: attributes[:type],
|
76
78
|
name: attributes[:name],
|
@@ -79,7 +81,11 @@ module Discordrb::Commands
|
|
79
81
|
parse_self: attributes[:parse_self],
|
80
82
|
shard_id: attributes[:shard_id],
|
81
83
|
num_shards: attributes[:num_shards],
|
82
|
-
redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true
|
84
|
+
redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true,
|
85
|
+
ignore_bots: attributes[:ignore_bots],
|
86
|
+
compress_mode: attributes[:compress_mode],
|
87
|
+
intents: attributes[:intents]
|
88
|
+
)
|
83
89
|
|
84
90
|
@prefix = attributes[:prefix]
|
85
91
|
@attributes = {
|
@@ -124,7 +130,10 @@ module Discordrb::Commands
|
|
124
130
|
quote_start: attributes[:quote_start] || '"',
|
125
131
|
|
126
132
|
# Quoted mode ending character
|
127
|
-
quote_end: attributes[:quote_end] || '"'
|
133
|
+
quote_end: attributes[:quote_end] || attributes[:quote_start] || '"',
|
134
|
+
|
135
|
+
# Default block for handling internal exceptions, or a string to respond with
|
136
|
+
rescue: attributes[:rescue]
|
128
137
|
}
|
129
138
|
|
130
139
|
@permissions = {
|
@@ -133,14 +142,25 @@ module Discordrb::Commands
|
|
133
142
|
}
|
134
143
|
|
135
144
|
return unless @attributes[:help_command]
|
145
|
+
|
136
146
|
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|
|
137
147
|
if command_name
|
138
148
|
command = @commands[command_name.to_sym]
|
149
|
+
if command.is_a?(CommandAlias)
|
150
|
+
command = command.aliased_command
|
151
|
+
command_name = command.name
|
152
|
+
end
|
139
153
|
return "The command `#{command_name}` does not exist!" unless command
|
154
|
+
|
140
155
|
desc = command.attributes[:description] || '*No description available*'
|
141
156
|
usage = command.attributes[:usage]
|
142
157
|
parameters = command.attributes[:parameters]
|
143
158
|
result = "**`#{command_name}`**: #{desc}"
|
159
|
+
aliases = command_aliases(command_name.to_sym)
|
160
|
+
unless aliases.empty?
|
161
|
+
result += "\nAliases: "
|
162
|
+
result += aliases.map { |a| "`#{a.name}`" }.join(', ')
|
163
|
+
end
|
144
164
|
result += "\nUsage: `#{usage}`" if usage
|
145
165
|
if parameters
|
146
166
|
result += "\nAccepted Parameters:\n```"
|
@@ -149,7 +169,9 @@ module Discordrb::Commands
|
|
149
169
|
end
|
150
170
|
result
|
151
171
|
else
|
152
|
-
available_commands = @commands.values.reject
|
172
|
+
available_commands = @commands.values.reject do |c|
|
173
|
+
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)
|
174
|
+
end
|
153
175
|
case available_commands.length
|
154
176
|
when 0..5
|
155
177
|
available_commands.reduce "**List of commands:**\n" do |memo, c|
|
@@ -160,13 +182,22 @@ module Discordrb::Commands
|
|
160
182
|
memo + "`#{c.name}`, "
|
161
183
|
end)[0..-3]
|
162
184
|
else
|
163
|
-
event.user.pm(available_commands.reduce("**List of commands:**\n") { |
|
164
|
-
'Sending list in PM!'
|
185
|
+
event.user.pm(available_commands.reduce("**List of commands:**\n") { |m, e| m + "`#{e.name}`, " }[0..-3])
|
186
|
+
event.channel.pm? ? '' : 'Sending list in PM!'
|
165
187
|
end
|
166
188
|
end
|
167
189
|
end
|
168
190
|
end
|
169
191
|
|
192
|
+
# Returns all aliases for the command with the given name
|
193
|
+
# @param name [Symbol] the name of the `Command`
|
194
|
+
# @return [Array<CommandAlias>]
|
195
|
+
def command_aliases(name)
|
196
|
+
commands.values.select do |command|
|
197
|
+
command.is_a?(CommandAlias) && command.aliased_command.name == name
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
170
201
|
# Executes a particular command on the bot. Mostly useful for internal stuff, but one can never know.
|
171
202
|
# @param name [Symbol] The command to execute.
|
172
203
|
# @param event [CommandEvent] The event to pass to the command.
|
@@ -179,18 +210,24 @@ module Discordrb::Commands
|
|
179
210
|
def execute_command(name, event, arguments, chained = false, check_permissions = true)
|
180
211
|
debug("Executing command #{name} with arguments #{arguments}")
|
181
212
|
return unless @commands
|
213
|
+
|
182
214
|
command = @commands[name]
|
215
|
+
command = command.aliased_command if command.is_a?(CommandAlias)
|
183
216
|
return unless !check_permissions || channels?(event.channel, @attributes[:channels]) ||
|
184
217
|
(command && !command.attributes[:channels].nil?)
|
218
|
+
|
185
219
|
unless command
|
186
220
|
event.respond @attributes[:command_doesnt_exist_message].gsub('%command%', name.to_s) if @attributes[:command_doesnt_exist_message]
|
187
221
|
return
|
188
222
|
end
|
189
223
|
return unless !check_permissions || channels?(event.channel, command.attributes[:channels])
|
224
|
+
|
225
|
+
arguments = arg_check(arguments, command.attributes[:arg_types], event.server) if check_permissions
|
190
226
|
if (check_permissions &&
|
191
227
|
permission?(event.author, command.attributes[:permission_level], event.server) &&
|
192
228
|
required_permissions?(event.author, command.attributes[:required_permissions], event.channel) &&
|
193
|
-
required_roles?(event.author, command.attributes[:required_roles])
|
229
|
+
required_roles?(event.author, command.attributes[:required_roles]) &&
|
230
|
+
allowed_roles?(event.author, command.attributes[:allowed_roles])) ||
|
194
231
|
!check_permissions
|
195
232
|
event.command = command
|
196
233
|
result = command.call(event, arguments, chained, check_permissions)
|
@@ -204,12 +241,94 @@ module Discordrb::Commands
|
|
204
241
|
raise
|
205
242
|
end
|
206
243
|
|
244
|
+
# Transforms an array of string arguments based on types array.
|
245
|
+
# For example, `['1', '10..14']` with types `[Integer, Range]` would turn into `[1, 10..14]`.
|
246
|
+
def arg_check(args, types = nil, server = nil)
|
247
|
+
return args unless types
|
248
|
+
|
249
|
+
args.each_with_index.map do |arg, i|
|
250
|
+
next arg if types[i].nil? || types[i] == String
|
251
|
+
|
252
|
+
if types[i] == Integer
|
253
|
+
begin
|
254
|
+
Integer(arg, 10)
|
255
|
+
rescue ArgumentError
|
256
|
+
nil
|
257
|
+
end
|
258
|
+
elsif types[i] == Float
|
259
|
+
begin
|
260
|
+
Float(arg)
|
261
|
+
rescue ArgumentError
|
262
|
+
nil
|
263
|
+
end
|
264
|
+
elsif types[i] == Time
|
265
|
+
begin
|
266
|
+
Time.parse arg
|
267
|
+
rescue ArgumentError
|
268
|
+
nil
|
269
|
+
end
|
270
|
+
elsif types[i] == TrueClass || types[i] == FalseClass
|
271
|
+
if arg.casecmp('true').zero? || arg.downcase.start_with?('y')
|
272
|
+
true
|
273
|
+
elsif arg.casecmp('false').zero? || arg.downcase.start_with?('n')
|
274
|
+
false
|
275
|
+
end
|
276
|
+
elsif types[i] == Symbol
|
277
|
+
arg.to_sym
|
278
|
+
elsif types[i] == Encoding
|
279
|
+
begin
|
280
|
+
Encoding.find arg
|
281
|
+
rescue ArgumentError
|
282
|
+
nil
|
283
|
+
end
|
284
|
+
elsif types[i] == Regexp
|
285
|
+
begin
|
286
|
+
Regexp.new arg
|
287
|
+
rescue ArgumentError
|
288
|
+
nil
|
289
|
+
end
|
290
|
+
elsif types[i] == Rational
|
291
|
+
begin
|
292
|
+
Rational(arg)
|
293
|
+
rescue ArgumentError
|
294
|
+
nil
|
295
|
+
end
|
296
|
+
elsif types[i] == Range
|
297
|
+
begin
|
298
|
+
if arg.include? '...'
|
299
|
+
Range.new(*arg.split('...').map(&:to_i), true)
|
300
|
+
elsif arg.include? '..'
|
301
|
+
Range.new(*arg.split('..').map(&:to_i))
|
302
|
+
end
|
303
|
+
rescue ArgumentError
|
304
|
+
nil
|
305
|
+
end
|
306
|
+
elsif types[i] == NilClass
|
307
|
+
nil
|
308
|
+
elsif [Discordrb::User, Discordrb::Role, Discordrb::Emoji].include? types[i]
|
309
|
+
result = parse_mention arg, server
|
310
|
+
result if result.instance_of? types[i]
|
311
|
+
elsif types[i] == Discordrb::Invite
|
312
|
+
resolve_invite_code arg
|
313
|
+
elsif types[i].respond_to?(:from_argument)
|
314
|
+
begin
|
315
|
+
types[i].from_argument arg
|
316
|
+
rescue StandardError
|
317
|
+
nil
|
318
|
+
end
|
319
|
+
else
|
320
|
+
raise ArgumentError, "#{types[i]} doesn't implement from_argument"
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
207
325
|
# Executes a command in a simple manner, without command chains or permissions.
|
208
326
|
# @param chain [String] The command with its arguments separated by spaces.
|
209
327
|
# @param event [CommandEvent] The event to pass to the command.
|
210
328
|
# @return [String, nil] the command's result, if there is any.
|
211
329
|
def simple_execute(chain, event)
|
212
330
|
return nil if chain.empty?
|
331
|
+
|
213
332
|
args = chain.split(' ')
|
214
333
|
execute_command(args[0].to_sym, event, args[1..-1])
|
215
334
|
end
|
@@ -245,6 +364,31 @@ module Discordrb::Commands
|
|
245
364
|
[@permissions[:users][user.id] || 0, determined_level].max >= level
|
246
365
|
end
|
247
366
|
|
367
|
+
# @see CommandBot#update_channels
|
368
|
+
def channels=(channels)
|
369
|
+
update_channels(channels)
|
370
|
+
end
|
371
|
+
|
372
|
+
# Update the list of channels the bot accepts commands from.
|
373
|
+
# @param channels [Array<String, Integer, Channel>] The channels this command bot accepts commands on.
|
374
|
+
def update_channels(channels = [])
|
375
|
+
@attributes[:channels] = Array(channels)
|
376
|
+
end
|
377
|
+
|
378
|
+
# Add a channel to the list of channels the bot accepts commands from.
|
379
|
+
# @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be added
|
380
|
+
def add_channel(channel)
|
381
|
+
return if @attributes[:channels].find { |c| channel.resolve_id == c.resolve_id }
|
382
|
+
|
383
|
+
@attributes[:channels] << channel
|
384
|
+
end
|
385
|
+
|
386
|
+
# Remove a channel from the list of channels the bot accepts commands from.
|
387
|
+
# @param channel [String, Integer, Channel] The channel name, integer ID, or `Channel` object to be removed
|
388
|
+
def remove_channel(channel)
|
389
|
+
@attributes[:channels].delete_if { |c| channel.resolve_id == c.resolve_id }
|
390
|
+
end
|
391
|
+
|
248
392
|
private
|
249
393
|
|
250
394
|
# Internal handler for MESSAGE_CREATE that is overwritten to allow for command handling
|
@@ -285,7 +429,7 @@ module Discordrb::Commands
|
|
285
429
|
if @prefix.is_a? String
|
286
430
|
standard_prefix_trigger(message.content, @prefix)
|
287
431
|
elsif @prefix.is_a? Array
|
288
|
-
@prefix.map { |e| standard_prefix_trigger(message.content, e) }.reduce { |
|
432
|
+
@prefix.map { |e| standard_prefix_trigger(message.content, e) }.reduce { |m, e| m || e }
|
289
433
|
elsif @prefix.respond_to? :call
|
290
434
|
@prefix.call(message)
|
291
435
|
end
|
@@ -293,37 +437,48 @@ module Discordrb::Commands
|
|
293
437
|
|
294
438
|
def standard_prefix_trigger(message, prefix)
|
295
439
|
return nil unless message.start_with? prefix
|
440
|
+
|
296
441
|
message[prefix.length..-1]
|
297
442
|
end
|
298
443
|
|
299
444
|
def required_permissions?(member, required, channel = nil)
|
300
445
|
required.reduce(true) do |a, action|
|
301
|
-
a && !member.webhook? && member.permission?(action, channel)
|
446
|
+
a && !member.webhook? && !member.is_a?(Discordrb::Recipient) && member.permission?(action, channel)
|
302
447
|
end
|
303
448
|
end
|
304
449
|
|
305
450
|
def required_roles?(member, required)
|
306
|
-
return (required.nil? || required.empty?
|
307
|
-
|
451
|
+
return true if member.webhook? || member.is_a?(Discordrb::Recipient) || required.nil? || required.empty?
|
452
|
+
|
453
|
+
required.is_a?(Array) ? check_multiple_roles(member, required) : member.role?(role)
|
454
|
+
end
|
455
|
+
|
456
|
+
def allowed_roles?(member, required)
|
457
|
+
return true if member.webhook? || member.is_a?(Discordrb::Recipient) || required.nil? || required.empty?
|
458
|
+
|
459
|
+
required.is_a?(Array) ? check_multiple_roles(member, required, false) : member.role?(role)
|
460
|
+
end
|
461
|
+
|
462
|
+
def check_multiple_roles(member, required, all_roles = true)
|
463
|
+
if all_roles
|
308
464
|
required.all? do |role|
|
309
465
|
member.role?(role)
|
310
466
|
end
|
311
467
|
else
|
312
|
-
|
468
|
+
required.any? do |role|
|
469
|
+
member.role?(role)
|
470
|
+
end
|
313
471
|
end
|
314
472
|
end
|
315
473
|
|
316
474
|
def channels?(channel, channels)
|
317
475
|
return true if channels.nil? || channels.empty?
|
476
|
+
|
318
477
|
channels.any? do |c|
|
319
|
-
if c
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
c == channel.id
|
324
|
-
else
|
325
|
-
c == channel
|
326
|
-
end
|
478
|
+
# if c is string, make sure to remove the "#" from channel names in case it was specified
|
479
|
+
return true if c.is_a?(String) && c.delete('#') == channel.name
|
480
|
+
|
481
|
+
c.resolve_id == channel.resolve_id
|
327
482
|
end
|
328
483
|
end
|
329
484
|
|
@@ -341,7 +496,7 @@ module Discordrb::Commands
|
|
341
496
|
else
|
342
497
|
event.respond result unless result.nil? || result.empty?
|
343
498
|
end
|
344
|
-
rescue => e
|
499
|
+
rescue StandardError => e
|
345
500
|
log_exception(e)
|
346
501
|
ensure
|
347
502
|
@event_threads.delete(t)
|