discordrb 1.8.1 → 2.0.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 +4 -4
- data/.overcommit.yml +7 -0
- data/.rubocop.yml +5 -4
- data/CHANGELOG.md +77 -0
- data/README.md +25 -15
- data/discordrb.gemspec +2 -3
- data/examples/commands.rb +14 -2
- data/examples/ping.rb +1 -1
- data/examples/pm_send.rb +1 -1
- data/lib/discordrb.rb +9 -0
- data/lib/discordrb/api.rb +176 -50
- data/lib/discordrb/await.rb +3 -0
- data/lib/discordrb/bot.rb +607 -372
- data/lib/discordrb/cache.rb +208 -0
- data/lib/discordrb/commands/command_bot.rb +50 -18
- data/lib/discordrb/commands/container.rb +11 -2
- data/lib/discordrb/commands/events.rb +2 -0
- data/lib/discordrb/commands/parser.rb +10 -8
- data/lib/discordrb/commands/rate_limiter.rb +2 -0
- data/lib/discordrb/container.rb +24 -25
- data/lib/discordrb/data.rb +521 -219
- data/lib/discordrb/errors.rb +6 -7
- data/lib/discordrb/events/await.rb +2 -0
- data/lib/discordrb/events/bans.rb +3 -1
- data/lib/discordrb/events/channels.rb +124 -0
- data/lib/discordrb/events/generic.rb +2 -0
- data/lib/discordrb/events/guilds.rb +16 -13
- data/lib/discordrb/events/lifetime.rb +12 -2
- data/lib/discordrb/events/members.rb +26 -15
- data/lib/discordrb/events/message.rb +20 -7
- data/lib/discordrb/events/presence.rb +18 -2
- data/lib/discordrb/events/roles.rb +83 -0
- data/lib/discordrb/events/typing.rb +15 -2
- data/lib/discordrb/events/voice_state_update.rb +2 -0
- data/lib/discordrb/light.rb +8 -0
- data/lib/discordrb/light/data.rb +62 -0
- data/lib/discordrb/light/integrations.rb +73 -0
- data/lib/discordrb/light/light_bot.rb +56 -0
- data/lib/discordrb/logger.rb +4 -0
- data/lib/discordrb/permissions.rb +16 -12
- data/lib/discordrb/token_cache.rb +3 -0
- data/lib/discordrb/version.rb +3 -1
- data/lib/discordrb/voice/encoder.rb +2 -0
- data/lib/discordrb/voice/network.rb +21 -14
- data/lib/discordrb/voice/voice_bot.rb +26 -3
- data/lib/discordrb/websocket.rb +69 -0
- metadata +15 -26
- data/lib/discordrb/events/channel_create.rb +0 -44
- data/lib/discordrb/events/channel_delete.rb +0 -44
- data/lib/discordrb/events/channel_update.rb +0 -46
- data/lib/discordrb/events/guild_role_create.rb +0 -35
- data/lib/discordrb/events/guild_role_delete.rb +0 -36
- data/lib/discordrb/events/guild_role_update.rb +0 -35
@@ -0,0 +1,208 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'discordrb/api'
|
4
|
+
require 'discordrb/data'
|
5
|
+
|
6
|
+
module Discordrb
|
7
|
+
# This mixin module does caching stuff for the library. It conveniently separates the logic behind
|
8
|
+
# the caching (like, storing the user hashes or making API calls to retrieve things) from the Bot that
|
9
|
+
# actually uses it.
|
10
|
+
module Cache
|
11
|
+
# Initializes this cache
|
12
|
+
def init_cache
|
13
|
+
@users = {}
|
14
|
+
|
15
|
+
@servers = {}
|
16
|
+
|
17
|
+
@channels = {}
|
18
|
+
@private_channels = {}
|
19
|
+
|
20
|
+
@restricted_channels = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets a channel given its ID. This queries the internal channel cache, and if the channel doesn't
|
24
|
+
# exist in there, it will get the data from Discord.
|
25
|
+
# @param id [Integer] The channel ID for which to search for.
|
26
|
+
# @param server [Server] The server for which to search the channel for. If this isn't specified, it will be
|
27
|
+
# inferred using the API
|
28
|
+
# @return [Channel] The channel identified by the ID.
|
29
|
+
def channel(id, server = nil)
|
30
|
+
id = id.resolve_id
|
31
|
+
|
32
|
+
raise Discordrb::Errors::NoPermission if @restricted_channels.include? id
|
33
|
+
|
34
|
+
debug("Obtaining data for channel with id #{id}")
|
35
|
+
return @channels[id] if @channels[id]
|
36
|
+
|
37
|
+
begin
|
38
|
+
response = API.channel(token, id)
|
39
|
+
channel = Channel.new(JSON.parse(response), self, server)
|
40
|
+
@channels[id] = channel
|
41
|
+
rescue Discordrb::Errors::NoPermission
|
42
|
+
debug "Tried to get access to restricted channel #{id}, blacklisting it"
|
43
|
+
@restricted_channels << id
|
44
|
+
raise
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Gets a user by its ID.
|
49
|
+
# @note This can only resolve users known by the bot (i.e. that share a server with the bot).
|
50
|
+
# @param id [Integer] The user ID that should be resolved.
|
51
|
+
# @return [User, nil] The user identified by the ID, or `nil` if it couldn't be found.
|
52
|
+
def user(id)
|
53
|
+
id = id.resolve_id
|
54
|
+
return @users[id] if @users[id]
|
55
|
+
|
56
|
+
LOGGER.out("Resolving user #{id}")
|
57
|
+
response = API.user(token, id)
|
58
|
+
user = User.new(JSON.parse(response), self)
|
59
|
+
@users[id] = user
|
60
|
+
end
|
61
|
+
|
62
|
+
# Gets a server by its ID.
|
63
|
+
# @note This can only resolve servers the bot is currently in.
|
64
|
+
# @param id [Integer] The server ID that should be resolved.
|
65
|
+
# @return [Server, nil] The server identified by the ID, or `nil` if it couldn't be found.
|
66
|
+
def server(id)
|
67
|
+
id = id.resolve_id
|
68
|
+
return @servers[id] if @servers[id]
|
69
|
+
|
70
|
+
LOGGER.out("Resolving server #{id}")
|
71
|
+
response = API.server(token, id)
|
72
|
+
server = Server.new(JSON.parse(response), self)
|
73
|
+
@servers[id] = server
|
74
|
+
end
|
75
|
+
|
76
|
+
# Gets a member by both IDs
|
77
|
+
# @param server_id [Integer] The server ID for which a member should be resolved
|
78
|
+
# @param user_id [Integer] The ID of the user that should be resolved
|
79
|
+
# @return [Member, nil] The member identified by the IDs, or `nil` if none could be found
|
80
|
+
def member(server_id, user_id)
|
81
|
+
server_id = server_id.resolve_id
|
82
|
+
user_id = user_id.resolve_id
|
83
|
+
|
84
|
+
server = self.server(server_id)
|
85
|
+
return server.member(user_id) if server.member_cached?(user_id)
|
86
|
+
|
87
|
+
LOGGER.out("Resolving member #{server_id} on server #{user_id}")
|
88
|
+
response = API.member(token, server_id, user_id)
|
89
|
+
member = Member.new(JSON.parse(response), server, self)
|
90
|
+
server.cache_member(member)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Creates a private channel for the given user ID, or if one exists already, returns that one.
|
94
|
+
# It is recommended that you use {User#pm} instead, as this is mainly for internal use. However,
|
95
|
+
# usage of this method may be unavoidable if only the user ID is known.
|
96
|
+
# @param id [Integer] The user ID to generate a private channel for.
|
97
|
+
# @return [Channel] A private channel for that user.
|
98
|
+
def private_channel(id)
|
99
|
+
id = id.resolve_id
|
100
|
+
debug("Creating private channel with user id #{id}")
|
101
|
+
return @private_channels[id] if @private_channels[id]
|
102
|
+
|
103
|
+
response = API.create_private(token, @profile.id, id)
|
104
|
+
channel = Channel.new(JSON.parse(response), self)
|
105
|
+
@private_channels[id] = channel
|
106
|
+
end
|
107
|
+
|
108
|
+
# Ensures a given user object is cached and if not, cache it from the given data hash.
|
109
|
+
# @param data [Hash] A data hash representing a user.
|
110
|
+
# @return [User] the user represented by the data hash.
|
111
|
+
def ensure_user(data)
|
112
|
+
if @users.include?(data['id'].to_i)
|
113
|
+
@users[data['id'].to_i]
|
114
|
+
else
|
115
|
+
@users[data['id'].to_i] = User.new(data, self)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Ensures a given server object is cached and if not, cache it from the given data hash.
|
120
|
+
# @param data [Hash] A data hash representing a server.
|
121
|
+
# @return [Server] the server represented by the data hash.
|
122
|
+
def ensure_server(data)
|
123
|
+
if @servers.include?(data['id'].to_i)
|
124
|
+
@servers[data['id'].to_i]
|
125
|
+
else
|
126
|
+
@servers[data['id'].to_i] = Server.new(data, self)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# Ensures a given channel object is cached and if not, cache it from the given data hash.
|
131
|
+
# @param data [Hash] A data hash representing a channel.
|
132
|
+
# @return [Channel] the channel represented by the data hash.
|
133
|
+
def ensure_channel(data)
|
134
|
+
if @channels.include?(data['id'].to_i)
|
135
|
+
@channels[data['id'].to_i]
|
136
|
+
else
|
137
|
+
@channels[data['id'].to_i] = Channel.new(data, self)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Requests member chunks for a given server ID.
|
142
|
+
# @param id [Integer] The server ID to request chunks for.
|
143
|
+
def request_chunks(id)
|
144
|
+
chunk_packet = {
|
145
|
+
op: 8,
|
146
|
+
d: {
|
147
|
+
guild_id: id,
|
148
|
+
query: '',
|
149
|
+
limit: 0
|
150
|
+
}
|
151
|
+
}.to_json
|
152
|
+
@ws.send(chunk_packet)
|
153
|
+
end
|
154
|
+
|
155
|
+
# Gets the code for an invite.
|
156
|
+
# @param invite [String, Invite] The invite to get the code for. Possible formats are:
|
157
|
+
#
|
158
|
+
# * An {Invite} object
|
159
|
+
# * The code for an invite
|
160
|
+
# * A fully qualified invite URL (e. g. `https://discordapp.com/invite/0A37aN7fasF7n83q`)
|
161
|
+
# * A short invite URL with protocol (e. g. `https://discord.gg/0A37aN7fasF7n83q`)
|
162
|
+
# * A short invite URL without protocol (e. g. `discord.gg/0A37aN7fasF7n83q`)
|
163
|
+
# @return [String] Only the code for the invite.
|
164
|
+
def resolve_invite_code(invite)
|
165
|
+
invite = invite.code if invite.is_a? Discordrb::Invite
|
166
|
+
invite = invite[invite.rindex('/') + 1..-1] if invite.start_with?('http', 'discord.gg')
|
167
|
+
invite
|
168
|
+
end
|
169
|
+
|
170
|
+
# Gets information about an invite.
|
171
|
+
# @param invite [String, Invite] The invite to join. For possible formats see {#resolve_invite_code}.
|
172
|
+
# @return [Invite] The invite with information about the given invite URL.
|
173
|
+
def invite(invite)
|
174
|
+
code = resolve_invite_code(invite)
|
175
|
+
Invite.new(JSON.parse(API.resolve_invite(token, code)), self)
|
176
|
+
end
|
177
|
+
|
178
|
+
# Finds a channel given its name and optionally the name of the server it is in.
|
179
|
+
# @param channel_name [String] The channel to search for.
|
180
|
+
# @param server_name [String] The server to search for, or `nil` if only the channel should be searched for.
|
181
|
+
# @param type [String, nil] The type of channel to search for (`'text'` or `'voice'`), or `nil` if either type of
|
182
|
+
# channel should be searched for
|
183
|
+
# @return [Array<Channel>] The array of channels that were found. May be empty if none were found.
|
184
|
+
def find_channel(channel_name, server_name = nil, type: nil)
|
185
|
+
results = []
|
186
|
+
|
187
|
+
if /<#(?<id>\d+)>?/ =~ channel_name
|
188
|
+
# Check for channel mentions separately
|
189
|
+
return [channel(id)]
|
190
|
+
end
|
191
|
+
|
192
|
+
@servers.values.each do |server|
|
193
|
+
server.channels.each do |channel|
|
194
|
+
results << channel if channel.name == channel_name && (server_name || server.name) == server.name && (!type || (channel.type == type))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
results
|
199
|
+
end
|
200
|
+
|
201
|
+
# Finds a user given its username.
|
202
|
+
# @param username [String] The username to look for.
|
203
|
+
# @return [Array<User>] The array of users that were found. May be empty if none were found.
|
204
|
+
def find_user(username)
|
205
|
+
@users.values.find_all { |e| e.username == username }
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'discordrb/bot'
|
2
4
|
require 'discordrb/data'
|
3
5
|
require 'discordrb/commands/parser'
|
@@ -19,22 +21,22 @@ module Discordrb::Commands
|
|
19
21
|
include CommandContainer
|
20
22
|
|
21
23
|
# Creates a new CommandBot and logs in to Discord.
|
22
|
-
# @param
|
23
|
-
# @
|
24
|
-
# @
|
24
|
+
# @param attributes [Hash] The attributes to initialize the CommandBot with.
|
25
|
+
# @see {Discordrb::Bot#initialize} for other attributes that should be used to create the underlying regular bot.
|
26
|
+
# @option attributes [String] :prefix The prefix that should trigger this bot's commands. Can be any string (including the empty
|
25
27
|
# string), but note that it will be literal - if the prefix is "hi" then the corresponding trigger string for
|
26
28
|
# a command called "test" would be "hitest". Don't forget to put spaces in if you need them!
|
27
|
-
# @param attributes [Hash] The attributes to initialize the CommandBot with.
|
28
|
-
# @param debug [true, false] Whether or not debug mode should be used - debug mode logs tons of extra stuff to the
|
29
|
-
# console that may be useful in development.
|
30
29
|
# @option attributes [true, false] :advanced_functionality Whether to enable advanced functionality (very powerful
|
31
30
|
# way to nest commands into chains, see https://github.com/meew0/discordrb/wiki/Commands#command-chain-syntax
|
32
31
|
# for info. Default is true.
|
33
|
-
# @option attributes [Symbol, Array<Symbol
|
34
|
-
# commands. Use an array if you want to have aliases. Default is "help".
|
32
|
+
# @option attributes [Symbol, Array<Symbol>, false] :help_command The name of the command that displays info for
|
33
|
+
# other commands. Use an array if you want to have aliases. Default is "help". If none should be created, use
|
34
|
+
# `false` as the value.
|
35
35
|
# @option attributes [String] :command_doesnt_exist_message The message that should be displayed if a user attempts
|
36
36
|
# to use a command that does not exist. If none is specified, no message will be displayed. In the message, you
|
37
37
|
# can use the string '%command%' that will be replaced with the name of the command.
|
38
|
+
# @option attributes [true, false] :spaces_allowed Whether spaces are allowed to occur between the prefix and the
|
39
|
+
# command. Default is false.
|
38
40
|
# @option attributes [String] :previous Character that should designate the result of the previous command in
|
39
41
|
# a command chain (see :advanced_functionality). Default is '~'.
|
40
42
|
# @option attributes [String] :chain_delimiter Character that should designate that a new command begins in the
|
@@ -49,20 +51,34 @@ module Discordrb::Commands
|
|
49
51
|
# :advanced_functionality). Default is '"'.
|
50
52
|
# @option attributes [String] :quote_end Character that should end a quoted string (see
|
51
53
|
# :advanced_functionality). Default is '"'.
|
52
|
-
def initialize(
|
53
|
-
super(
|
54
|
-
|
54
|
+
def initialize(attributes = {})
|
55
|
+
super(
|
56
|
+
email: attributes[:email],
|
57
|
+
password: attributes[:password],
|
58
|
+
log_mode: attributes[:log_mode],
|
59
|
+
token: attributes[:token],
|
60
|
+
application_id: attributes[:application_id],
|
61
|
+
type: attributes[:type],
|
62
|
+
name: attributes[:name],
|
63
|
+
fancy_log: attributes[:fancy_log],
|
64
|
+
suppress_ready: attributes[:suppress_ready],
|
65
|
+
parse_self: attributes[:parse_self])
|
66
|
+
|
67
|
+
@prefix = attributes[:prefix]
|
55
68
|
@attributes = {
|
56
69
|
# Whether advanced functionality such as command chains are enabled
|
57
|
-
advanced_functionality: attributes[:advanced_functionality].nil? ?
|
70
|
+
advanced_functionality: attributes[:advanced_functionality].nil? ? false : attributes[:advanced_functionality],
|
58
71
|
|
59
|
-
# The name of the help command (that displays information to other commands).
|
60
|
-
help_command: attributes[:help_command] || :help,
|
72
|
+
# The name of the help command (that displays information to other commands). False if none should exist
|
73
|
+
help_command: (attributes[:help_command].is_a? FalseClass) ? nil : (attributes[:help_command] || :help),
|
61
74
|
|
62
75
|
# The message to display for when a command doesn't exist, %command% to get the command name in question and nil for no message
|
63
76
|
# No default value here because it may not be desired behaviour
|
64
77
|
command_doesnt_exist_message: attributes[:command_doesnt_exist_message],
|
65
78
|
|
79
|
+
# Spaces allowed between prefix and command
|
80
|
+
spaces_allowed: attributes[:spaces_allowed].nil? ? false : attributes[:spaces_allowed],
|
81
|
+
|
66
82
|
# All of the following need to be one character
|
67
83
|
# String to designate previous result in command chain
|
68
84
|
previous: attributes[:previous] || '~',
|
@@ -99,7 +115,8 @@ module Discordrb::Commands
|
|
99
115
|
desc = command.attributes[:description] || '*No description available*'
|
100
116
|
usage = command.attributes[:usage]
|
101
117
|
result = "**`#{command_name}`**: #{desc}"
|
102
|
-
result
|
118
|
+
result += "\nUsage: `#{usage}`" if usage
|
119
|
+
result
|
103
120
|
else
|
104
121
|
available_commands = @commands.values.reject { |c| !c.attributes[:help_available] }
|
105
122
|
case available_commands.length
|
@@ -133,10 +150,10 @@ module Discordrb::Commands
|
|
133
150
|
event.respond @attributes[:command_doesnt_exist_message].gsub('%command%', name.to_s) if @attributes[:command_doesnt_exist_message]
|
134
151
|
return
|
135
152
|
end
|
136
|
-
if permission?(
|
153
|
+
if permission?(event.user, command.attributes[:permission_level], event.server)
|
137
154
|
event.command = command
|
138
155
|
result = command.call(event, arguments, chained)
|
139
|
-
result
|
156
|
+
stringify(result)
|
140
157
|
else
|
141
158
|
event.respond "You don't have permission to execute command `#{name}`!"
|
142
159
|
end
|
@@ -172,7 +189,7 @@ module Discordrb::Commands
|
|
172
189
|
# @param server [Server] The server on which to check
|
173
190
|
# @return [true, false] whether or not the user has the given permission
|
174
191
|
def permission?(user, level, server)
|
175
|
-
determined_level = server.nil? ? 0 : user.roles
|
192
|
+
determined_level = server.nil? ? 0 : user.roles.reduce(0) do |memo, role|
|
176
193
|
[@permissions[:roles][role.id] || 0, memo].max
|
177
194
|
end
|
178
195
|
[@permissions[:users][user.id] || 0, determined_level].max >= level
|
@@ -183,11 +200,19 @@ module Discordrb::Commands
|
|
183
200
|
# Internal handler for MESSAGE_CREATE that is overwritten to allow for command handling
|
184
201
|
def create_message(data)
|
185
202
|
message = Discordrb::Message.new(data, self)
|
203
|
+
return if message.from_bot? && !@should_parse_self
|
204
|
+
|
186
205
|
event = CommandEvent.new(message, self)
|
187
206
|
|
188
207
|
return unless message.content.start_with? @prefix
|
189
208
|
chain = message.content[@prefix.length..-1]
|
190
209
|
|
210
|
+
# Don't allow spaces between the prefix and the command
|
211
|
+
if chain.start_with?(' ') && !@attributes[:spaces_allowed]
|
212
|
+
debug('Chain starts with a space')
|
213
|
+
return
|
214
|
+
end
|
215
|
+
|
191
216
|
if chain.strip.empty?
|
192
217
|
debug('Chain is empty')
|
193
218
|
return
|
@@ -212,5 +237,12 @@ module Discordrb::Commands
|
|
212
237
|
end
|
213
238
|
end
|
214
239
|
end
|
240
|
+
|
241
|
+
# Turns the object into a string, using to_s by default
|
242
|
+
def stringify(object)
|
243
|
+
return '' if object.is_a? Discordrb::Message
|
244
|
+
|
245
|
+
object.to_s
|
246
|
+
end
|
215
247
|
end
|
216
248
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'discordrb/container'
|
2
4
|
require 'discordrb/commands/rate_limiter'
|
3
5
|
|
@@ -34,8 +36,13 @@ module Discordrb::Commands
|
|
34
36
|
def command(name, attributes = {}, &block)
|
35
37
|
@commands ||= {}
|
36
38
|
if name.is_a? Array
|
37
|
-
new_command =
|
38
|
-
|
39
|
+
new_command = nil
|
40
|
+
|
41
|
+
name.each do |e|
|
42
|
+
new_command = Command.new(e, attributes, &block)
|
43
|
+
@commands[e] = new_command
|
44
|
+
end
|
45
|
+
|
39
46
|
new_command
|
40
47
|
else
|
41
48
|
@commands[name] = Command.new(name, attributes, &block)
|
@@ -53,6 +60,8 @@ module Discordrb::Commands
|
|
53
60
|
# @param container [Module] A module that `extend`s {CommandContainer} from which the commands will be added.
|
54
61
|
def include_commands(container)
|
55
62
|
handlers = container.instance_variable_get '@commands'
|
63
|
+
return unless handlers
|
64
|
+
|
56
65
|
@commands ||= {}
|
57
66
|
@commands.merge! handlers
|
58
67
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Discordrb::Commands
|
2
4
|
# Command that can be called in a chain
|
3
5
|
class Command
|
@@ -136,14 +138,14 @@ module Discordrb::Commands
|
|
136
138
|
b_level += 1
|
137
139
|
end
|
138
140
|
|
139
|
-
result
|
141
|
+
result += char if b_level <= 0
|
140
142
|
|
141
143
|
next unless char == @attributes[:sub_chain_end] && !quoted
|
142
144
|
b_level -= 1
|
143
145
|
next unless b_level == 0
|
144
146
|
nested = @chain[b_start + 1..index - 1]
|
145
147
|
subchain = CommandChain.new(nested, @bot, true)
|
146
|
-
result
|
148
|
+
result += subchain.execute(event)
|
147
149
|
end
|
148
150
|
|
149
151
|
event.respond("Your subchains are mismatched! Make sure you don't have any extra #{@attributes[:sub_chain_start]}'s or #{@attributes[:sub_chain_end]}'s") unless b_level == 0
|
@@ -165,21 +167,21 @@ module Discordrb::Commands
|
|
165
167
|
command = @attributes[:chain_delimiter] + command if first && @chain.start_with?(@attributes[:chain_delimiter])
|
166
168
|
first = false
|
167
169
|
|
168
|
-
command.strip
|
170
|
+
command = command.strip
|
169
171
|
|
170
172
|
# Replace the hacky delimiter that was used inside quotes with actual delimiters
|
171
|
-
command.gsub
|
173
|
+
command = command.gsub hacky_delim, @attributes[:chain_delimiter]
|
172
174
|
|
173
175
|
first_space = command.index ' '
|
174
176
|
command_name = first_space ? command[0..first_space - 1] : command
|
175
177
|
arguments = first_space ? command[first_space + 1..-1] : ''
|
176
178
|
|
177
179
|
# Append a previous sign if none is present
|
178
|
-
arguments
|
179
|
-
arguments.gsub
|
180
|
+
arguments += @attributes[:previous] unless arguments.include? @attributes[:previous]
|
181
|
+
arguments = arguments.gsub @attributes[:previous], prev
|
180
182
|
|
181
183
|
# Replace hacky previous signs with actual ones
|
182
|
-
arguments.gsub
|
184
|
+
arguments = arguments.gsub hacky_prev, @attributes[:previous]
|
183
185
|
|
184
186
|
arguments = arguments.split ' '
|
185
187
|
|
@@ -214,7 +216,7 @@ module Discordrb::Commands
|
|
214
216
|
executed_chain = divide_chain(old_chain).last
|
215
217
|
|
216
218
|
arg[1].to_i.times do
|
217
|
-
new_result
|
219
|
+
new_result += CommandChain.new(executed_chain, @bot).execute(event)
|
218
220
|
end
|
219
221
|
|
220
222
|
result = new_result
|