discordrb 2.1.3 → 3.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.

@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'discordrb/api'
4
+ require 'discordrb/api/server'
5
+ require 'discordrb/api/invite'
6
+ require 'discordrb/api/user'
4
7
  require 'discordrb/data'
5
8
 
6
9
  module Discordrb
@@ -15,7 +18,7 @@ module Discordrb
15
18
  @servers = {}
16
19
 
17
20
  @channels = {}
18
- @private_channels = {}
21
+ @pm_channels = {}
19
22
 
20
23
  @restricted_channels = []
21
24
  end
@@ -36,7 +39,7 @@ module Discordrb
36
39
 
37
40
  begin
38
41
  begin
39
- response = API.channel(token, id)
42
+ response = API::Channel.resolve(token, id)
40
43
  rescue RestClient::ResourceNotFound
41
44
  return nil
42
45
  end
@@ -49,6 +52,8 @@ module Discordrb
49
52
  end
50
53
  end
51
54
 
55
+ alias_method :group_channel, :channel
56
+
52
57
  # Gets a user by its ID.
53
58
  # @note This can only resolve users known by the bot (i.e. that share a server with the bot).
54
59
  # @param id [Integer] The user ID that should be resolved.
@@ -59,7 +64,7 @@ module Discordrb
59
64
 
60
65
  LOGGER.out("Resolving user #{id}")
61
66
  begin
62
- response = API.user(token, id)
67
+ response = API::User.resolve(token, id)
63
68
  rescue RestClient::ResourceNotFound
64
69
  return nil
65
70
  end
@@ -77,7 +82,7 @@ module Discordrb
77
82
 
78
83
  LOGGER.out("Resolving server #{id}")
79
84
  begin
80
- response = API.server(token, id)
85
+ response = API::Server.resolve(token, id)
81
86
  rescue RestClient::ResourceNotFound
82
87
  return nil
83
88
  end
@@ -85,20 +90,21 @@ module Discordrb
85
90
  @servers[id] = server
86
91
  end
87
92
 
88
- # Gets a member by both IDs
89
- # @param server_id [Integer] The server ID for which a member should be resolved
93
+ # Gets a member by both IDs, or `Server` and user ID.
94
+ # @param server_or_id [Server, Integer] The `Server` or server ID for which a member should be resolved
90
95
  # @param user_id [Integer] The ID of the user that should be resolved
91
96
  # @return [Member, nil] The member identified by the IDs, or `nil` if none could be found
92
- def member(server_id, user_id)
93
- server_id = server_id.resolve_id
97
+ def member(server_or_id, user_id)
98
+ server_id = server_or_id.resolve_id
94
99
  user_id = user_id.resolve_id
95
100
 
96
- server = self.server(server_id)
101
+ server = server_or_id.is_a?(Server) ? server_or_id : self.server(server_id)
102
+
97
103
  return server.member(user_id) if server.member_cached?(user_id)
98
104
 
99
105
  LOGGER.out("Resolving member #{server_id} on server #{user_id}")
100
106
  begin
101
- response = API.member(token, server_id, user_id)
107
+ response = API::Server.resolve_member(token, server_id, user_id)
102
108
  rescue RestClient::ResourceNotFound
103
109
  return nil
104
110
  end
@@ -106,21 +112,22 @@ module Discordrb
106
112
  server.cache_member(member)
107
113
  end
108
114
 
109
- # Creates a private channel for the given user ID, or if one exists already, returns that one.
115
+ # Creates a PM channel for the given user ID, or if one exists already, returns that one.
110
116
  # It is recommended that you use {User#pm} instead, as this is mainly for internal use. However,
111
117
  # usage of this method may be unavoidable if only the user ID is known.
112
118
  # @param id [Integer] The user ID to generate a private channel for.
113
119
  # @return [Channel] A private channel for that user.
114
- def private_channel(id)
120
+ def pm_channel(id)
115
121
  id = id.resolve_id
116
- debug("Creating private channel with user id #{id}")
117
- return @private_channels[id] if @private_channels[id]
118
-
119
- response = API.create_private(token, @profile.id, id)
122
+ return @pm_channels[id] if @pm_channels[id]
123
+ debug("Creating pm channel with user id #{id}")
124
+ response = API::User.create_pm(token, id)
120
125
  channel = Channel.new(JSON.parse(response), self)
121
- @private_channels[id] = channel
126
+ @pm_channels[id] = channel
122
127
  end
123
128
 
129
+ alias_method :private_channel, :pm_channel
130
+
124
131
  # Ensures a given user object is cached and if not, cache it from the given data hash.
125
132
  # @param data [Hash] A data hash representing a user.
126
133
  # @return [User] the user represented by the data hash.
@@ -158,15 +165,7 @@ module Discordrb
158
165
  # Requests member chunks for a given server ID.
159
166
  # @param id [Integer] The server ID to request chunks for.
160
167
  def request_chunks(id)
161
- chunk_packet = {
162
- op: 8,
163
- d: {
164
- guild_id: id,
165
- query: '',
166
- limit: 0
167
- }
168
- }.to_json
169
- @ws.send(chunk_packet)
168
+ @gateway.send_request_members(id, '', 0)
170
169
  end
171
170
 
172
171
  # Gets the code for an invite.
@@ -189,13 +188,13 @@ module Discordrb
189
188
  # @return [Invite] The invite with information about the given invite URL.
190
189
  def invite(invite)
191
190
  code = resolve_invite_code(invite)
192
- Invite.new(JSON.parse(API.resolve_invite(token, code)), self)
191
+ Invite.new(JSON.parse(API::Invite.resolve(token, code)), self)
193
192
  end
194
193
 
195
194
  # Finds a channel given its name and optionally the name of the server it is in.
196
195
  # @param channel_name [String] The channel to search for.
197
196
  # @param server_name [String] The server to search for, or `nil` if only the channel should be searched for.
198
- # @param type [String, nil] The type of channel to search for (`'text'` or `'voice'`), or `nil` if either type of
197
+ # @param type [Integer, nil] The type of channel to search for (0: text, 1: private, 2: voice, 3: group), or `nil` if any type of
199
198
  # channel should be searched for
200
199
  # @return [Array<Channel>] The array of channels that were found. May be empty if none were found.
201
200
  def find_channel(channel_name, server_name = nil, type: nil)
@@ -32,18 +32,20 @@ module Discordrb::Commands
32
32
  # "hitest". Don't forget to put spaces in if you need them!
33
33
  # * An array of prefixes. Those will behave similarly to setting one string as a prefix, but instead of only one
34
34
  # string, any of the strings in the array can be used.
35
- # * Something Proc-like (responds to :call) that takes a string as an argument (the message) and returns either
36
- # the command chain in raw form or `nil` if the given string shouldn't be parsed. This can be used to make more
37
- # complicated dynamic prefixes, or even something else entirely (suffixes, or most adventurous, infixes).
35
+ # * Something Proc-like (responds to :call) that takes a {Message} object as an argument and returns either
36
+ # the command chain in raw form or `nil` if the given message shouldn't be parsed. This can be used to make more
37
+ # complicated dynamic prefixes (e. g. based on server), or even something else entirely (suffixes, or most
38
+ # adventurous, infixes).
38
39
  # @option attributes [true, false] :advanced_functionality Whether to enable advanced functionality (very powerful
39
40
  # way to nest commands into chains, see https://github.com/meew0/discordrb/wiki/Commands#command-chain-syntax
40
- # for info. Default is true.
41
+ # for info. Default is false.
41
42
  # @option attributes [Symbol, Array<Symbol>, false] :help_command The name of the command that displays info for
42
43
  # other commands. Use an array if you want to have aliases. Default is "help". If none should be created, use
43
44
  # `false` as the value.
44
45
  # @option attributes [String] :command_doesnt_exist_message The message that should be displayed if a user attempts
45
46
  # to use a command that does not exist. If none is specified, no message will be displayed. In the message, you
46
47
  # can use the string '%command%' that will be replaced with the name of the command.
48
+ # @option attributes [String] :no_permission_message The message to be displayed when `NoPermission` error is raised.
47
49
  # @option attributes [true, false] :spaces_allowed Whether spaces are allowed to occur between the prefix and the
48
50
  # command. Default is false.
49
51
  # @option attributes [String] :previous Character that should designate the result of the previous command in
@@ -62,18 +64,18 @@ module Discordrb::Commands
62
64
  # :advanced_functionality). Default is '"'.
63
65
  def initialize(attributes = {})
64
66
  super(
65
- email: attributes[:email],
66
- password: attributes[:password],
67
67
  log_mode: attributes[:log_mode],
68
68
  token: attributes[:token],
69
69
  application_id: attributes[:application_id],
70
+ client_id: attributes[:client_id],
70
71
  type: attributes[:type],
71
72
  name: attributes[:name],
72
73
  fancy_log: attributes[:fancy_log],
73
74
  suppress_ready: attributes[:suppress_ready],
74
75
  parse_self: attributes[:parse_self],
75
76
  shard_id: attributes[:shard_id],
76
- num_shards: attributes[:num_shards])
77
+ num_shards: attributes[:num_shards],
78
+ redact_token: attributes[:redact_token])
77
79
 
78
80
  @prefix = attributes[:prefix]
79
81
  @attributes = {
@@ -81,12 +83,15 @@ module Discordrb::Commands
81
83
  advanced_functionality: attributes[:advanced_functionality].nil? ? false : attributes[:advanced_functionality],
82
84
 
83
85
  # The name of the help command (that displays information to other commands). False if none should exist
84
- help_command: (attributes[:help_command].is_a? FalseClass) ? nil : (attributes[:help_command] || :help),
86
+ help_command: attributes[:help_command].is_a?(FalseClass) ? nil : (attributes[:help_command] || :help),
85
87
 
86
88
  # The message to display for when a command doesn't exist, %command% to get the command name in question and nil for no message
87
89
  # No default value here because it may not be desired behaviour
88
90
  command_doesnt_exist_message: attributes[:command_doesnt_exist_message],
89
91
 
92
+ # The message to be displayed when `NoPermission` error is raised.
93
+ no_permission_message: attributes[:no_permission_message],
94
+
90
95
  # Spaces allowed between prefix and command
91
96
  spaces_allowed: attributes[:spaces_allowed].nil? ? false : attributes[:spaces_allowed],
92
97
 
@@ -125,8 +130,13 @@ module Discordrb::Commands
125
130
  return "The command `#{command_name}` does not exist!" unless command
126
131
  desc = command.attributes[:description] || '*No description available*'
127
132
  usage = command.attributes[:usage]
133
+ parameters = command.attributes[:parameters]
128
134
  result = "**`#{command_name}`**: #{desc}"
129
135
  result += "\nUsage: `#{usage}`" if usage
136
+ if parameters
137
+ result += "\nAccepted Parameters:"
138
+ parameters.each { |p| result += "\n `#{p}`" }
139
+ end
130
140
  result
131
141
  else
132
142
  available_commands = @commands.values.reject { |c| !c.attributes[:help_available] }
@@ -162,7 +172,8 @@ module Discordrb::Commands
162
172
  return
163
173
  end
164
174
  if permission?(event.author, command.attributes[:permission_level], event.server) &&
165
- required_permissions?(event.author, command.attributes[:required_permissions], event.channel)
175
+ required_permissions?(event.author, command.attributes[:required_permissions], event.channel) &&
176
+ required_roles?(event.author, command.attributes[:required_roles])
166
177
  event.command = command
167
178
  result = command.call(event, arguments, chained)
168
179
  stringify(result)
@@ -170,6 +181,9 @@ module Discordrb::Commands
170
181
  event.respond command.attributes[:permission_message].gsub('%name%', name.to_s) if command.attributes[:permission_message]
171
182
  nil
172
183
  end
184
+ rescue Discordrb::Errors::NoPermission
185
+ event.respond @attributes[:no_permission_message] unless @attributes[:no_permission_message].nil?
186
+ raise
173
187
  end
174
188
 
175
189
  # Executes a command in a simple manner, without command chains or permissions.
@@ -222,7 +236,7 @@ module Discordrb::Commands
222
236
 
223
237
  event = CommandEvent.new(message, self)
224
238
 
225
- chain = trigger?(message.content)
239
+ chain = trigger?(message)
226
240
  return unless chain
227
241
 
228
242
  # Don't allow spaces between the prefix and the command
@@ -242,9 +256,9 @@ module Discordrb::Commands
242
256
  # Check whether a message should trigger command execution, and if it does, return the raw chain
243
257
  def trigger?(message)
244
258
  if @prefix.is_a? String
245
- standard_prefix_trigger(message, @prefix)
259
+ standard_prefix_trigger(message.content, @prefix)
246
260
  elsif @prefix.is_a? Array
247
- @prefix.map { |e| standard_prefix_trigger(message, e) }.reduce { |a, e| a || e }
261
+ @prefix.map { |e| standard_prefix_trigger(message.content, e) }.reduce { |a, e| a || e }
248
262
  elsif @prefix.respond_to? :call
249
263
  @prefix.call(message)
250
264
  end
@@ -261,15 +275,30 @@ module Discordrb::Commands
261
275
  end
262
276
  end
263
277
 
278
+ def required_roles?(member, required)
279
+ if required.is_a? Array
280
+ required.all? do |role|
281
+ member.role?(role)
282
+ end
283
+ else
284
+ member.role?(role)
285
+ end
286
+ end
287
+
264
288
  def execute_chain(chain, event)
265
289
  t = Thread.new do
266
290
  @event_threads << t
267
291
  Thread.current[:discordrb_name] = "ct-#{@current_thread += 1}"
268
292
  begin
269
293
  debug("Parsing command chain #{chain}")
270
- result = (@attributes[:advanced_functionality]) ? CommandChain.new(chain, self).execute(event) : simple_execute(chain, event)
271
- result = event.saved_message + (result || '')
272
- event.respond result unless result.nil? || result.empty?
294
+ result = @attributes[:advanced_functionality] ? CommandChain.new(chain, self).execute(event) : simple_execute(chain, event)
295
+ result = event.drain_into(result)
296
+
297
+ if event.file
298
+ event.send_file(event.file, caption: result)
299
+ else
300
+ event.respond result unless result.nil? || result.empty?
301
+ end
273
302
  rescue => e
274
303
  log_exception(e)
275
304
  ensure
@@ -9,11 +9,11 @@ module Discordrb::Commands
9
9
  module CommandContainer
10
10
  include RateLimiter
11
11
 
12
- # @return [Array<Command>] the list of commands this container has.
12
+ # @return [Hash<Symbol, Command>] 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] The name of the command to add.
16
+ # @param name [Symbol, Array<Symbol>] The name of the command to add, or an array of multiple names for the command
17
17
  # @param attributes [Hash] The attributes to initialize the command with.
18
18
  # @option attributes [Integer] :permission_level The minimum permission level that can use this command, inclusive.
19
19
  # See {CommandBot#set_user_permission} and {CommandBot#set_role_permission}.
@@ -22,6 +22,7 @@ module Discordrb::Commands
22
22
  # the message by setting this option to false.
23
23
  # @option attributes [Array<Symbol>] :required_permissions Discord action permissions (e.g. `:kick_members`) that
24
24
  # should be required to use this command. See {Discordrb::Permissions::Flags} for a list.
25
+ # @option attributes [Array<Role>, Array<#resolve_id>] :required_roles Roles that user should have to use this command.
25
26
  # @option attributes [true, false] :chain_usable Whether this command is able to be used inside of a command chain
26
27
  # or sub-chain. Typically used for administrative commands that shouldn't be done carelessly.
27
28
  # @option attributes [true, false] :help_available Whether this command is visible in the help command. See the
@@ -17,11 +17,14 @@ module Discordrb::Commands
17
17
  permission_level: attributes[:permission_level] || 0,
18
18
 
19
19
  # Message to display when a user does not have sufficient permissions to execute a command
20
- permission_message: (attributes[:permission_message].is_a? FalseClass) ? nil : (attributes[:permission_message] || "You don't have permission to execute command %name%!"),
20
+ permission_message: attributes[:permission_message].is_a?(FalseClass) ? nil : (attributes[:permission_message] || "You don't have permission to execute command %name%!"),
21
21
 
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
26
+ required_roles: attributes[:required_roles] || [],
27
+
25
28
  # Whether this command is usable in a command chain
26
29
  chain_usable: attributes[:chain_usable].nil? ? true : attributes[:chain_usable],
27
30
 
@@ -34,6 +37,9 @@ module Discordrb::Commands
34
37
  # Usage description (for help command and error messages)
35
38
  usage: attributes[:usage] || nil,
36
39
 
40
+ # Parameter list (for help command and error messages)
41
+ parameters: attributes[:parameters] || nil,
42
+
37
43
  # Minimum number of arguments
38
44
  min_args: attributes[:min_args] || 0,
39
45
 
@@ -82,7 +88,8 @@ module Discordrb::Commands
82
88
  return
83
89
  end
84
90
 
85
- @block.call(event, *arguments)
91
+ result = @block.call(event, *arguments)
92
+ event.drain_into(result)
86
93
  rescue LocalJumpError # occurs when breaking
87
94
  nil
88
95
  end
@@ -140,7 +147,7 @@ module Discordrb::Commands
140
147
  end
141
148
 
142
149
  if char == @attributes[:sub_chain_start] && !quoted
143
- b_start = index if b_level == 0
150
+ b_start = index if b_level.zero?
144
151
  b_level += 1
145
152
  end
146
153
 
@@ -148,13 +155,13 @@ module Discordrb::Commands
148
155
 
149
156
  next unless char == @attributes[:sub_chain_end] && !quoted
150
157
  b_level -= 1
151
- next unless b_level == 0
158
+ next unless b_level.zero?
152
159
  nested = @chain[b_start + 1..index - 1]
153
160
  subchain = CommandChain.new(nested, @bot, true)
154
161
  result += subchain.execute(event)
155
162
  end
156
163
 
157
- 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
164
+ 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.zero?
158
165
 
159
166
  @chain = result
160
167
 
@@ -222,7 +229,8 @@ module Discordrb::Commands
222
229
  executed_chain = divide_chain(old_chain).last
223
230
 
224
231
  arg[1].to_i.times do
225
- new_result += CommandChain.new(executed_chain, @bot).execute(event)
232
+ chain_result = CommandChain.new(executed_chain, @bot).execute(event)
233
+ new_result += chain_result if chain_result
226
234
  end
227
235
 
228
236
  result = new_result
@@ -123,6 +123,7 @@ module Discordrb
123
123
  # @param attributes [Hash] The event's attributes.
124
124
  # @option attributes [String, Integer, User] :from Matches the user whose playing game changes.
125
125
  # @option attributes [String] :game Matches the game the user is now playing.
126
+ # @option attributes [Integer] :type Matches the type of game object (0 game, 1 Twitch stream)
126
127
  # @yield The block is executed when the event is raised.
127
128
  # @yieldparam event [PlayingEvent] The event that was raised.
128
129
  # @return [PlayingEventHandler] The event handler that was registered.
@@ -150,7 +151,7 @@ module Discordrb
150
151
 
151
152
  # This **event** is raised when a channel is created.
152
153
  # @param attributes [Hash] The event's attributes.
153
- # @option attributes [String] :type Matches the type of channel that is being created (text or voice)
154
+ # @option attributes [Integer] :type Matches the type of channel that is being created (0: text, 1: private, 2: voice, 3: group)
154
155
  # @option attributes [String] :name Matches the name of the created channel.
155
156
  # @yield The block is executed when the event is raised.
156
157
  # @yieldparam event [ChannelCreateEvent] The event that was raised.
@@ -161,7 +162,7 @@ module Discordrb
161
162
 
162
163
  # This **event** is raised when a channel is updated.
163
164
  # @param attributes [Hash] The event's attributes.
164
- # @option attributes [String] :type Matches the type of channel that is being updated (text or voice)
165
+ # @option attributes [Integer] :type Matches the type of channel that is being updated (0: text, 1: private, 2: voice, 3: group).
165
166
  # @option attributes [String] :name Matches the new name of the channel.
166
167
  # @yield The block is executed when the event is raised.
167
168
  # @yieldparam event [ChannelUpdateEvent] The event that was raised.
@@ -172,7 +173,7 @@ module Discordrb
172
173
 
173
174
  # This **event** is raised when a channel is deleted.
174
175
  # @param attributes [Hash] The event's attributes.
175
- # @option attributes [String] :type Matches the type of channel that is being deleted (text or voice)
176
+ # @option attributes [Integer] :type Matches the type of channel that is being deleted (0: text, 1: private, 2: voice, 3: group).
176
177
  # @option attributes [String] :name Matches the name of the deleted channel.
177
178
  # @yield The block is executed when the event is raised.
178
179
  # @yieldparam event [ChannelDeleteEvent] The event that was raised.
@@ -181,6 +182,30 @@ module Discordrb
181
182
  register_event(ChannelDeleteEvent, attributes, block)
182
183
  end
183
184
 
185
+ # This **event** is raised when a recipient is added to a group channel.
186
+ # @param attributes [Hash] The event's attributes.
187
+ # @option attributes [String] :name Matches the name of the group channel that the recipient is added to.
188
+ # @option attributes [#resolve_id] :owner_id Matches the id of the group channel's owner.
189
+ # @option attributes [#resolve_id] :id Matches the id of the recipient added to the group channel.
190
+ # @yield The block is executed when the event is raised.
191
+ # @yieldparam event [ChannelRecipientAddEvent] The event that was raised.
192
+ # @return [ChannelRecipientAddHandler] The event handler that was registered.
193
+ def channel_recipient_add(attributes = {}, &block)
194
+ register_event(ChannelRecipientAddEvent, attributes, block)
195
+ end
196
+
197
+ # This **event** is raised when a recipient is removed from a group channel.
198
+ # @param attributes [Hash] The event's attributes.
199
+ # @option attributes [String] :name Matches the name of the group channel that the recipient is added to.
200
+ # @option attributes [#resolve_id] :owner_id Matches the id of the group channel's owner.
201
+ # @option attributes [#resolve_id] :id Matches the id of the recipient removed from the group channel.
202
+ # @yield The block is executed when the event is raised.
203
+ # @yieldparam event [ChannelRecipientRemoveEvent] The event that was raised.
204
+ # @return [ChannelRecipientRemoveHandler] The event handler that was registered.
205
+ def channel_recipient_remove(attributes = {}, &block)
206
+ register_event(ChannelRecipientRemoveEvent, attributes, block)
207
+ end
208
+
184
209
  # This **event** is raised when a user's voice state changes.
185
210
  # @param attributes [Hash] The event's attributes.
186
211
  # @option attributes [String, Integer, User] :from Matches the user that sent the message.
@@ -312,6 +337,8 @@ module Discordrb
312
337
  end
313
338
 
314
339
  alias_method :private_message, :pm
340
+ alias_method :direct_message, :pm
341
+ alias_method :dm, :pm
315
342
 
316
343
  # Removes an event handler from this container. If you're looking for a way to do temporary events, I recommend
317
344
  # {Await}s instead of this.