discordrb 3.4.3 → 3.6.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.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +13 -0
  3. data/.devcontainer/devcontainer.json +29 -0
  4. data/.devcontainer/postcreate.sh +4 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
  6. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
  7. data/.github/workflows/ci.yml +78 -0
  8. data/.github/workflows/codeql.yml +65 -0
  9. data/.github/workflows/deploy.yml +54 -0
  10. data/.github/workflows/release.yml +45 -0
  11. data/.markdownlint.json +4 -0
  12. data/.rubocop.yml +58 -2
  13. data/CHANGELOG.md +485 -225
  14. data/LICENSE.txt +1 -1
  15. data/README.md +38 -26
  16. data/discordrb-webhooks.gemspec +4 -1
  17. data/discordrb.gemspec +18 -10
  18. data/lib/discordrb/api/application.rb +278 -0
  19. data/lib/discordrb/api/channel.rb +222 -18
  20. data/lib/discordrb/api/interaction.rb +63 -0
  21. data/lib/discordrb/api/invite.rb +2 -2
  22. data/lib/discordrb/api/server.rb +123 -66
  23. data/lib/discordrb/api/user.rb +20 -5
  24. data/lib/discordrb/api/webhook.rb +72 -0
  25. data/lib/discordrb/api.rb +35 -25
  26. data/lib/discordrb/bot.rb +437 -66
  27. data/lib/discordrb/cache.rb +41 -22
  28. data/lib/discordrb/commands/command_bot.rb +13 -21
  29. data/lib/discordrb/commands/container.rb +1 -1
  30. data/lib/discordrb/commands/parser.rb +7 -7
  31. data/lib/discordrb/commands/rate_limiter.rb +1 -1
  32. data/lib/discordrb/container.rb +178 -3
  33. data/lib/discordrb/data/activity.rb +1 -1
  34. data/lib/discordrb/data/application.rb +1 -0
  35. data/lib/discordrb/data/attachment.rb +38 -3
  36. data/lib/discordrb/data/audit_logs.rb +3 -3
  37. data/lib/discordrb/data/avatar_decoration.rb +26 -0
  38. data/lib/discordrb/data/call.rb +22 -0
  39. data/lib/discordrb/data/channel.rb +299 -30
  40. data/lib/discordrb/data/collectibles.rb +45 -0
  41. data/lib/discordrb/data/component.rb +229 -0
  42. data/lib/discordrb/data/embed.rb +10 -3
  43. data/lib/discordrb/data/emoji.rb +20 -1
  44. data/lib/discordrb/data/integration.rb +45 -3
  45. data/lib/discordrb/data/interaction.rb +937 -0
  46. data/lib/discordrb/data/invite.rb +1 -1
  47. data/lib/discordrb/data/member.rb +236 -44
  48. data/lib/discordrb/data/message.rb +278 -51
  49. data/lib/discordrb/data/overwrite.rb +15 -7
  50. data/lib/discordrb/data/primary_server.rb +60 -0
  51. data/lib/discordrb/data/profile.rb +2 -7
  52. data/lib/discordrb/data/reaction.rb +2 -1
  53. data/lib/discordrb/data/recipient.rb +1 -1
  54. data/lib/discordrb/data/role.rb +204 -18
  55. data/lib/discordrb/data/server.rb +194 -118
  56. data/lib/discordrb/data/server_preview.rb +68 -0
  57. data/lib/discordrb/data/snapshot.rb +110 -0
  58. data/lib/discordrb/data/user.rb +132 -12
  59. data/lib/discordrb/data/voice_region.rb +1 -0
  60. data/lib/discordrb/data/webhook.rb +99 -9
  61. data/lib/discordrb/data.rb +9 -0
  62. data/lib/discordrb/errors.rb +47 -3
  63. data/lib/discordrb/events/await.rb +1 -1
  64. data/lib/discordrb/events/channels.rb +38 -1
  65. data/lib/discordrb/events/generic.rb +2 -0
  66. data/lib/discordrb/events/guilds.rb +6 -1
  67. data/lib/discordrb/events/interactions.rb +575 -0
  68. data/lib/discordrb/events/invites.rb +2 -0
  69. data/lib/discordrb/events/members.rb +19 -2
  70. data/lib/discordrb/events/message.rb +42 -8
  71. data/lib/discordrb/events/presence.rb +23 -14
  72. data/lib/discordrb/events/raw.rb +1 -0
  73. data/lib/discordrb/events/reactions.rb +2 -1
  74. data/lib/discordrb/events/roles.rb +2 -0
  75. data/lib/discordrb/events/threads.rb +100 -0
  76. data/lib/discordrb/events/typing.rb +1 -0
  77. data/lib/discordrb/events/voice_server_update.rb +1 -0
  78. data/lib/discordrb/events/voice_state_update.rb +1 -0
  79. data/lib/discordrb/events/webhooks.rb +1 -0
  80. data/lib/discordrb/gateway.rb +57 -28
  81. data/lib/discordrb/paginator.rb +3 -3
  82. data/lib/discordrb/permissions.rb +71 -35
  83. data/lib/discordrb/version.rb +1 -1
  84. data/lib/discordrb/voice/encoder.rb +2 -2
  85. data/lib/discordrb/voice/network.rb +18 -7
  86. data/lib/discordrb/voice/sodium.rb +3 -1
  87. data/lib/discordrb/voice/voice_bot.rb +3 -3
  88. data/lib/discordrb/webhooks.rb +2 -0
  89. data/lib/discordrb/websocket.rb +0 -10
  90. data/lib/discordrb.rb +54 -5
  91. metadata +87 -25
  92. data/.circleci/config.yml +0 -126
  93. data/.codeclimate.yml +0 -16
  94. data/.travis.yml +0 -32
  95. data/bin/travis_build_docs.sh +0 -17
@@ -21,8 +21,8 @@ module Discordrb
21
21
 
22
22
  @channels = {}
23
23
  @pm_channels = {}
24
-
25
- @restricted_channels = []
24
+ @thread_members = {}
25
+ @server_previews = {}
26
26
  end
27
27
 
28
28
  # Returns or caches the available voice regions
@@ -42,28 +42,21 @@ module Discordrb
42
42
  # @param id [Integer] The channel ID for which to search for.
43
43
  # @param server [Server] The server for which to search the channel for. If this isn't specified, it will be
44
44
  # inferred using the API
45
- # @return [Channel] The channel identified by the ID.
45
+ # @return [Channel, nil] The channel identified by the ID.
46
+ # @raise Discordrb::Errors::NoPermission
46
47
  def channel(id, server = nil)
47
48
  id = id.resolve_id
48
49
 
49
- raise Discordrb::Errors::NoPermission if @restricted_channels.include? id
50
-
51
50
  debug("Obtaining data for channel with id #{id}")
52
51
  return @channels[id] if @channels[id]
53
52
 
54
53
  begin
55
- begin
56
- response = API::Channel.resolve(token, id)
57
- rescue RestClient::ResourceNotFound
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
54
+ response = API::Channel.resolve(token, id)
55
+ rescue Discordrb::Errors::UnknownChannel
56
+ return nil
66
57
  end
58
+ channel = Channel.new(JSON.parse(response), self, server)
59
+ @channels[id] = channel
67
60
  end
68
61
 
69
62
  alias_method :group_channel, :channel
@@ -79,7 +72,7 @@ module Discordrb
79
72
  LOGGER.out("Resolving user #{id}")
80
73
  begin
81
74
  response = API::User.resolve(token, id)
82
- rescue RestClient::ResourceNotFound
75
+ rescue Discordrb::Errors::UnknownUser
83
76
  return nil
84
77
  end
85
78
  user = User.new(JSON.parse(response), self)
@@ -111,7 +104,6 @@ module Discordrb
111
104
  def member(server_or_id, user_id)
112
105
  server_id = server_or_id.resolve_id
113
106
  user_id = user_id.resolve_id
114
-
115
107
  server = server_or_id.is_a?(Server) ? server_or_id : self.server(server_id)
116
108
 
117
109
  return server.member(user_id) if server.member_cached?(user_id)
@@ -119,7 +111,7 @@ module Discordrb
119
111
  LOGGER.out("Resolving member #{server_id} on server #{user_id}")
120
112
  begin
121
113
  response = API::Server.resolve_member(token, server_id, user_id)
122
- rescue RestClient::ResourceNotFound
114
+ rescue Discordrb::Errors::UnknownUser, Discordrb::Errors::UnknownMember
123
115
  return nil
124
116
  end
125
117
  member = Member.new(JSON.parse(response), server, self)
@@ -143,6 +135,19 @@ module Discordrb
143
135
 
144
136
  alias_method :private_channel, :pm_channel
145
137
 
138
+ # Get a server preview. If the bot isn't a member of the server, the server must be discoverable.
139
+ # @param id [Integer, String, Server] the ID of the server preview to get.
140
+ # @return [ServerPreview, nil] the server preview, or `nil` if the server isn't accessible.
141
+ def server_preview(id)
142
+ id = id.resolve_id
143
+ return @server_previews[id] if @server_previews[id]
144
+
145
+ response = JSON.parse(API::Server.preview(token, id))
146
+ @server_previews[id] = ServerPreview.new(response, self)
147
+ rescue StandardError
148
+ nil
149
+ end
150
+
146
151
  # Ensures a given user object is cached and if not, cache it from the given data hash.
147
152
  # @param data [Hash] A data hash representing a user.
148
153
  # @return [User] the user represented by the data hash.
@@ -156,10 +161,14 @@ module Discordrb
156
161
 
157
162
  # Ensures a given server object is cached and if not, cache it from the given data hash.
158
163
  # @param data [Hash] A data hash representing a server.
164
+ # @param force_cache [true, false] Whether the object in cache should be updated with the given
165
+ # data if it already exists.
159
166
  # @return [Server] the server represented by the data hash.
160
- def ensure_server(data)
167
+ def ensure_server(data, force_cache = false)
161
168
  if @servers.include?(data['id'].to_i)
162
- @servers[data['id'].to_i]
169
+ server = @servers[data['id'].to_i]
170
+ server.update_data(data) if force_cache
171
+ server
163
172
  else
164
173
  @servers[data['id'].to_i] = Server.new(data, self)
165
174
  end
@@ -177,6 +186,16 @@ module Discordrb
177
186
  end
178
187
  end
179
188
 
189
+ # Ensures a given thread member object is cached.
190
+ # @param data [Hash] Thread member data.
191
+ def ensure_thread_member(data)
192
+ thread_id = data['id'].to_i
193
+ user_id = data['user_id'].to_i
194
+
195
+ @thread_members[thread_id] ||= {}
196
+ @thread_members[thread_id][user_id] = data.slice('join_timestamp', 'flags')
197
+ end
198
+
180
199
  # Requests member chunks for a given server ID.
181
200
  # @param id [Integer] The server ID to request chunks for.
182
201
  def request_chunks(id)
@@ -194,7 +213,7 @@ module Discordrb
194
213
  # @return [String] Only the code for the invite.
195
214
  def resolve_invite_code(invite)
196
215
  invite = invite.code if invite.is_a? Discordrb::Invite
197
- invite = invite[invite.rindex('/') + 1..-1] if invite.start_with?('http', 'discord.gg')
216
+ invite = invite[(invite.rindex('/') + 1)..] if invite.start_with?('http', 'discord.gg')
198
217
  invite
199
218
  end
200
219
 
@@ -70,7 +70,9 @@ module Discordrb::Commands
70
70
  # @option attributes [String] :quote_end Character that should end a quoted string (see
71
71
  # :advanced_functionality). Default is '"' or the same as :quote_start. Set to an empty string to disable.
72
72
  # @option attributes [true, false] :ignore_bots Whether the bot should ignore bot accounts or not. Default is false.
73
- def initialize(attributes = {})
73
+ def initialize(**attributes)
74
+ # TODO: This needs to be revisited. undefined attributes are treated
75
+ # as explicitly passed nils.
74
76
  super(
75
77
  log_mode: attributes[:log_mode],
76
78
  token: attributes[:token],
@@ -85,7 +87,7 @@ module Discordrb::Commands
85
87
  redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true,
86
88
  ignore_bots: attributes[:ignore_bots],
87
89
  compress_mode: attributes[:compress_mode],
88
- intents: attributes[:intents]
90
+ intents: attributes[:intents] || :all
89
91
  )
90
92
 
91
93
  @prefix = attributes[:prefix]
@@ -107,7 +109,7 @@ module Discordrb::Commands
107
109
  spaces_allowed: attributes[:spaces_allowed].nil? ? false : attributes[:spaces_allowed],
108
110
 
109
111
  # Webhooks allowed to trigger commands
110
- webhook_commands: attributes[:webhook_commands].nil? ? true : attributes[:webhook_commands],
112
+ webhook_commands: attributes[:webhook_commands].nil? || attributes[:webhook_commands],
111
113
 
112
114
  channels: attributes[:channels] || [],
113
115
 
@@ -151,7 +153,9 @@ module Discordrb::Commands
151
153
  command = command.aliased_command
152
154
  command_name = command.name
153
155
  end
156
+ # rubocop:disable Lint/ReturnInVoidContext
154
157
  return "The command `#{command_name}` does not exist!" unless command
158
+ # rubocop:enable Lint/ReturnInVoidContext
155
159
 
156
160
  desc = command.attributes[:description] || '*No description available*'
157
161
  usage = command.attributes[:usage]
@@ -255,24 +259,16 @@ module Discordrb::Commands
255
259
  next arg if types[i].nil? || types[i] == String
256
260
 
257
261
  if types[i] == Integer
258
- begin
259
- Integer(arg, 10)
260
- rescue ArgumentError
261
- nil
262
- end
262
+ Integer(arg, 10, exception: false)
263
263
  elsif types[i] == Float
264
- begin
265
- Float(arg)
266
- rescue ArgumentError
267
- nil
268
- end
264
+ Float(arg, exception: false)
269
265
  elsif types[i] == Time
270
266
  begin
271
267
  Time.parse arg
272
268
  rescue ArgumentError
273
269
  nil
274
270
  end
275
- elsif types[i] == TrueClass || types[i] == FalseClass
271
+ elsif [TrueClass, FalseClass].include?(types[i])
276
272
  if arg.casecmp('true').zero? || arg.downcase.start_with?('y')
277
273
  true
278
274
  elsif arg.casecmp('false').zero? || arg.downcase.start_with?('n')
@@ -293,11 +289,7 @@ module Discordrb::Commands
293
289
  nil
294
290
  end
295
291
  elsif types[i] == Rational
296
- begin
297
- Rational(arg)
298
- rescue ArgumentError
299
- nil
300
- end
292
+ Rational(arg, exception: false)
301
293
  elsif types[i] == Range
302
294
  begin
303
295
  if arg.include? '...'
@@ -335,7 +327,7 @@ module Discordrb::Commands
335
327
  return nil if chain.empty?
336
328
 
337
329
  args = chain.split(' ')
338
- execute_command(args[0].to_sym, event, args[1..-1])
330
+ execute_command(args[0].to_sym, event, args[1..])
339
331
  end
340
332
 
341
333
  # Sets the permission level of a user
@@ -443,7 +435,7 @@ module Discordrb::Commands
443
435
  def standard_prefix_trigger(message, prefix)
444
436
  return nil unless message.start_with? prefix
445
437
 
446
- message[prefix.length..-1]
438
+ message[prefix.length..]
447
439
  end
448
440
 
449
441
  def required_permissions?(member, required, channel = nil)
@@ -86,7 +86,7 @@ module Discordrb::Commands
86
86
  # Adds all commands from another container into this one. Existing commands will be overwritten.
87
87
  # @param container [Module] A module that `extend`s {CommandContainer} from which the commands will be added.
88
88
  def include_commands(container)
89
- handlers = container.instance_variable_get '@commands'
89
+ handlers = container.instance_variable_get :@commands
90
90
  return unless handlers
91
91
 
92
92
  @commands ||= {}
@@ -32,10 +32,10 @@ module Discordrb::Commands
32
32
  channels: attributes[:channels] || nil,
33
33
 
34
34
  # Whether this command is usable in a command chain
35
- chain_usable: attributes[:chain_usable].nil? ? true : attributes[:chain_usable],
35
+ chain_usable: attributes[:chain_usable].nil? || attributes[:chain_usable],
36
36
 
37
37
  # Whether this command should show up in the help command
38
- help_available: attributes[:help_available].nil? ? true : attributes[:help_available],
38
+ help_available: attributes[:help_available].nil? || attributes[:help_available],
39
39
 
40
40
  # Description (for help command)
41
41
  description: attributes[:description] || nil,
@@ -159,7 +159,7 @@ module Discordrb::Commands
159
159
  escaped = false
160
160
  hacky_delim, hacky_space, hacky_prev, hacky_newline = [0xe001, 0xe002, 0xe003, 0xe004].pack('U*').chars
161
161
 
162
- @chain.each_char.each_with_index do |char, index|
162
+ @chain.each_char.with_index do |char, index|
163
163
  # Escape character
164
164
  if char == '\\' && !escaped
165
165
  escaped = true
@@ -211,7 +211,7 @@ module Discordrb::Commands
211
211
  b_level -= 1
212
212
  next unless b_level.zero?
213
213
 
214
- nested = @chain[b_start + 1..index - 1]
214
+ nested = @chain[(b_start + 1)..(index - 1)]
215
215
  subchain = CommandChain.new(nested, @bot, true)
216
216
  result += subchain.execute(event)
217
217
  end
@@ -245,8 +245,8 @@ module Discordrb::Commands
245
245
  command = command.gsub hacky_delim, @attributes[:chain_delimiter]
246
246
 
247
247
  first_space = command.index ' '
248
- command_name = first_space ? command[0..first_space - 1] : command
249
- arguments = first_space ? command[first_space + 1..-1] : ''
248
+ command_name = first_space ? command[0..(first_space - 1)] : command
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..-1]
321
+ chain = chain[(chain_args_index + 1)..]
322
322
  end
323
323
 
324
324
  [chain_args, chain]
@@ -125,7 +125,7 @@ module Discordrb::Commands
125
125
  # Adds all the buckets from another RateLimiter onto this one.
126
126
  # @param limiter [Module] Another {RateLimiter} module
127
127
  def include_buckets(limiter)
128
- buckets = limiter.instance_variable_get('@buckets') || {}
128
+ buckets = limiter.instance_variable_get(:@buckets) || {}
129
129
  @buckets ||= {}
130
130
  @buckets.merge! buckets
131
131
  end
@@ -13,6 +13,7 @@ require 'discordrb/events/guilds'
13
13
  require 'discordrb/events/await'
14
14
  require 'discordrb/events/bans'
15
15
  require 'discordrb/events/reactions'
16
+ require 'discordrb/events/interactions'
16
17
 
17
18
  require 'discordrb/await'
18
19
 
@@ -31,6 +32,8 @@ module Discordrb
31
32
  # @option attributes [Time] :after Matches a time after the time the message was sent at.
32
33
  # @option attributes [Time] :before Matches a time before the time the message was sent at.
33
34
  # @option attributes [Boolean] :private Matches whether or not the channel is private.
35
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was sent.
36
+ # @option attributes [Server, Integer, String] :server Matches the server the message was sent in.
34
37
  # @yield The block is executed when the event is raised.
35
38
  # @yieldparam event [MessageEvent] The event that was raised.
36
39
  # @return [MessageEventHandler] the event handler that was registered.
@@ -92,6 +95,8 @@ module Discordrb
92
95
  # @param attributes [Hash] The event's attributes.
93
96
  # @option attributes [String, Integer] :id Matches the ID of the message that was edited.
94
97
  # @option attributes [String, Integer, Channel] :in Matches the channel the message was edited in.
98
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was edited.
99
+ # @option attributes [Server, Integer, String] :server Matches the server the message was edited in.
95
100
  # @yield The block is executed when the event is raised.
96
101
  # @yieldparam event [MessageEditEvent] The event that was raised.
97
102
  # @return [MessageEditEventHandler] the event handler that was registered.
@@ -103,6 +108,7 @@ module Discordrb
103
108
  # @param attributes [Hash] The event's attributes.
104
109
  # @option attributes [String, Integer] :id Matches the ID of the message that was deleted.
105
110
  # @option attributes [String, Integer, Channel] :in Matches the channel the message was deleted in.
111
+ # @option attributes [Server, Integer, String] :server Matches the server the message was deleted in.
106
112
  # @yield The block is executed when the event is raised.
107
113
  # @yieldparam event [MessageDeleteEvent] The event that was raised.
108
114
  # @return [MessageDeleteEventHandler] the event handler that was registered.
@@ -117,6 +123,8 @@ module Discordrb
117
123
  # @param attributes [Hash] The event's attributes.
118
124
  # @option attributes [String, Integer] :id Matches the ID of the message that was updated.
119
125
  # @option attributes [String, Integer, Channel] :in Matches the channel the message was updated in.
126
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was updated.
127
+ # @option attributes [Server, Integer, String] :server Matches the server the message was updated in.
120
128
  # @yield The block is executed when the event is raised.
121
129
  # @yieldparam event [MessageUpdateEvent] The event that was raised.
122
130
  # @return [MessageUpdateEventHandler] the event handler that was registered.
@@ -199,6 +207,7 @@ module Discordrb
199
207
  # @option attributes [Time] :after Matches a time after the time the message was sent at.
200
208
  # @option attributes [Time] :before Matches a time before the time the message was sent at.
201
209
  # @option attributes [Boolean] :private Matches whether or not the channel is private.
210
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was sent.
202
211
  # @yield The block is executed when the event is raised.
203
212
  # @yieldparam event [MentionEvent] The event that was raised.
204
213
  # @return [MentionEventHandler] the event handler that was registered.
@@ -522,6 +531,156 @@ module Discordrb
522
531
  register_event(InviteDeleteEvent, attributes, block)
523
532
  end
524
533
 
534
+ # This **event** is raised whenever an interaction event is received.
535
+ # @param attributes [Hash] The event's attributes.
536
+ # @option attributes [Integer, Symbol, String] :type The interaction type, can be the integer value or the name
537
+ # of the key in {Discordrb::Interaction::TYPES}.
538
+ # @option attributes [String, Integer, Server, nil] :server The server where this event was created. `nil` for DM channels.
539
+ # @option attributes [String, Integer, Channel] :channel The channel where this event was created.
540
+ # @option attributes [String, Integer, User] :user The user that triggered this event.
541
+ # @yield The block is executed when the event is raised.
542
+ # @yieldparam event [InteractionCreateEvent] The event that was raised.
543
+ # @return [InteractionCreateEventHandler] The event handler that was registered.
544
+ def interaction_create(attributes = {}, &block)
545
+ register_event(InteractionCreateEvent, attributes, block)
546
+ end
547
+
548
+ # This **event** is raised whenever an application command (slash command) is executed.
549
+ # @param name [Symbol] The name of the application command this handler is for.
550
+ # @param attributes [Hash] The event's attributes.
551
+ # @yield The block is executed when the event is raised.
552
+ # @yieldparam event [ApplicationCommandEvent] The event that was raised.
553
+ # @return [ApplicationCommandEventHandler] The event handler that was registered.
554
+ def application_command(name, attributes = {}, &block)
555
+ @application_commands ||= {}
556
+
557
+ unless block
558
+ @application_commands[name] ||= ApplicationCommandEventHandler.new(attributes, nil)
559
+ return @application_commands[name]
560
+ end
561
+
562
+ @application_commands[name] = ApplicationCommandEventHandler.new(attributes, block)
563
+ end
564
+
565
+ # This **event** is raised whenever an button interaction is created.
566
+ # @param attributes [Hash] The event's attributes.
567
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
568
+ # @option attributes [String, Integer, Message] :message The message to filter for.
569
+ # @yield The block is executed when the event is raised.
570
+ # @yieldparam event [ButtonEvent] The event that was raised.
571
+ # @return [ButtonEventHandler] The event handler that was registered.
572
+ def button(attributes = {}, &block)
573
+ register_event(ButtonEvent, attributes, block)
574
+ end
575
+
576
+ # This **event** is raised whenever an select string interaction is created.
577
+ # @param attributes [Hash] The event's attributes.
578
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
579
+ # @option attributes [String, Integer, Message] :message The message to filter for.
580
+ # @yield The block is executed when the event is raised.
581
+ # @yieldparam event [StringSelectEvent] The event that was raised.
582
+ # @return [StringSelectEventHandler] The event handler that was registered.
583
+ def string_select(attributes = {}, &block)
584
+ register_event(StringSelectEvent, attributes, block)
585
+ end
586
+
587
+ alias_method :select_menu, :string_select
588
+
589
+ # This **event** is raised whenever a modal is submitted.
590
+ # @param attributes [Hash] The event's attributes.
591
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
592
+ # @option attributes [String, Integer, Message] :message The message to filter for.
593
+ # @option attributes [String, Integer, Server, nil] :server The server where this event was created. `nil` for DM channels.
594
+ # @option attributes [String, Integer, Channel] :channel The channel where this event was created.
595
+ # @option attributes [String, Integer, User] :user The user that triggered this event. # @yield The block is executed when the event is raised.
596
+ # @yieldparam event [ModalSubmitEvent] The event that was raised.
597
+ # @return [ModalSubmitEventHandler] The event handler that was registered.
598
+ def modal_submit(attributes = {}, &block)
599
+ register_event(ModalSubmitEvent, attributes, block)
600
+ end
601
+
602
+ # This **event** is raised whenever an select user interaction is created.
603
+ # @param attributes [Hash] The event's attributes.
604
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
605
+ # @option attributes [String, Integer, Message] :message The message to filter for.
606
+ # @yield The block is executed when the event is raised.
607
+ # @yieldparam event [UserSelectEvent] The event that was raised.
608
+ # @return [UserSelectEventHandler] The event handler that was registered.
609
+ def user_select(attributes = {}, &block)
610
+ register_event(UserSelectEvent, attributes, block)
611
+ end
612
+
613
+ # This **event** is raised whenever an select role interaction is created.
614
+ # @param attributes [Hash] The event's attributes.
615
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
616
+ # @option attributes [String, Integer, Message] :message The message to filter for.
617
+ # @yield The block is executed when the event is raised.
618
+ # @yieldparam event [RoleSelectEvent] The event that was raised.
619
+ # @return [RoleSelectEventHandler] The event handler that was registered.
620
+ def role_select(attributes = {}, &block)
621
+ register_event(RoleSelectEvent, attributes, block)
622
+ end
623
+
624
+ # This **event** is raised whenever an select mentionable interaction is created.
625
+ # @param attributes [Hash] The event's attributes.
626
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
627
+ # @option attributes [String, Integer, Message] :message The message to filter for.
628
+ # @yield The block is executed when the event is raised.
629
+ # @yieldparam event [MentionableSelectEvent] The event that was raised.
630
+ # @return [MentionableSelectEventHandler] The event handler that was registered.
631
+ def mentionable_select(attributes = {}, &block)
632
+ register_event(MentionableSelectEvent, attributes, block)
633
+ end
634
+
635
+ # This **event** is raised whenever an select channel interaction is created.
636
+ # @param attributes [Hash] The event's attributes.
637
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
638
+ # @option attributes [String, Integer, Message] :message The message to filter for.
639
+ # @yield The block is executed when the event is raised.
640
+ # @yieldparam event [ChannelSelectEvent] The event that was raised.
641
+ # @return [ChannelSelectEventHandler] The event handler that was registered.
642
+ def channel_select(attributes = {}, &block)
643
+ register_event(ChannelSelectEvent, attributes, block)
644
+ end
645
+
646
+ # This **event** is raised whenever a message is pinned or unpinned.
647
+ # @param attributes [Hash] The event's attributes.
648
+ # @option attributes [String, Integer, Channel] :channel A channel to match against.
649
+ # @option attributes [String, Integer, Server] :server A server to match against.
650
+ # @yield The block is executed when the event is raised.
651
+ # @yieldparam event [ChannelPinsUpdateEvent] The event that was raised.
652
+ # @return [ChannelPinsUpdateEventHandler] The event handler that was registered.
653
+ def channel_pins_update(attributes = {}, &block)
654
+ register_event(ChannelPinsUpdateEvent, attributes, block)
655
+ end
656
+
657
+ # This **event** is raised whenever an autocomplete interaction is created.
658
+ # @param name [String, Symbol, nil] An option name to match against.
659
+ # @param attributes [Hash] The event's attributes.
660
+ # @option attributes [String, Integer] :command_id A command ID to match against.
661
+ # @option attributes [String, Symbol] :subcommand A subcommand name to match against.
662
+ # @option attributes [String, Symbol] :subcommand_group A subcommand group to match against.
663
+ # @option attributes [String, Symbol] :command_name A command name to match against.
664
+ # @option attributes [String, Integer, Server] :server A server to match against.
665
+ # @yield The block is executed when the event is raised.
666
+ # @yieldparam event [AutocompleteEvent] The event that was raised.
667
+ # @return [AutocompleteEventHandler] The event handler that was registered.
668
+ def autocomplete(name = nil, attributes = {}, &block)
669
+ register_event(AutocompleteEvent, attributes.merge!({ name: name }), block)
670
+ end
671
+
672
+ # This **event** is raised whenever an application command's permissions are updated.
673
+ # @param attributes [Hash] The event's attributes.
674
+ # @option attributes [String, Integer] :command_id A command ID to match against.
675
+ # @option attributes [String, Integer] :application_id An application ID to match against.
676
+ # @option attributes [String, Integer, Server] :server A server to match against.
677
+ # @yield The block is executed when the event is raised.
678
+ # @yieldparam event [ApplicationCommandPermissionsUpdateEvent] The event that was raised.
679
+ # @return [ApplicationCommandPermissionsUpdateEventHandler] The event handler that was registered.
680
+ def application_command_permissions_update(attributes = {}, &block)
681
+ register_event(ApplicationCommandPermissionsUpdateEvent, attributes, block)
682
+ end
683
+
525
684
  # This **event** is raised for every dispatch received over the gateway, whether supported by discordrb or not.
526
685
  # @param attributes [Hash] The event's attributes.
527
686
  # @option attributes [String, Symbol, Regexp] :type Matches the event type of the dispatch.
@@ -552,9 +711,16 @@ module Discordrb
552
711
  @event_handlers[clazz].delete(handler)
553
712
  end
554
713
 
714
+ # Remove an application command handler
715
+ # @param name [String, Symbol] The name of the command handler to remove.
716
+ def remove_application_command_handler(name)
717
+ @application_commands.delete(name)
718
+ end
719
+
555
720
  # Removes all events from this event handler.
556
721
  def clear!
557
722
  @event_handlers&.clear
723
+ @application_commands&.clear
558
724
  end
559
725
 
560
726
  # Adds an event handler to this container. Usually, it's more expressive to just use one of the shorthand adder
@@ -570,11 +736,19 @@ module Discordrb
570
736
  # Adds all event handlers from another container into this one. Existing event handlers will be overwritten.
571
737
  # @param container [Module] A module that `extend`s {EventContainer} from which the handlers will be added.
572
738
  def include_events(container)
573
- handlers = container.instance_variable_get '@event_handlers'
574
- return unless handlers
739
+ application_command_handlers = container.instance_variable_get(:@application_commands)
740
+ handlers = container.instance_variable_get :@event_handlers
741
+ return unless handlers || application_command_handlers
575
742
 
576
743
  @event_handlers ||= {}
577
- @event_handlers.merge!(handlers) { |_, old, new| old + new }
744
+ @event_handlers.merge!(handlers || {}) { |_, old, new| old + new }
745
+
746
+ @application_commands ||= {}
747
+
748
+ @application_commands.merge!(application_command_handlers || {}) do |_, old, new|
749
+ old.subcommands.merge!(new.subcommands)
750
+ old
751
+ end
578
752
  end
579
753
 
580
754
  alias_method :include!, :include_events
@@ -612,6 +786,7 @@ module Discordrb
612
786
 
613
787
  include Discordrb::Events
614
788
 
789
+ # @return [EventHandler]
615
790
  def register_event(clazz, attributes, block)
616
791
  handler = EventContainer.handler_class(clazz).new(attributes, block)
617
792
 
@@ -121,7 +121,7 @@ module Discordrb
121
121
 
122
122
  # @!visibility private
123
123
  def flag_set?(sym)
124
- !(@flags & FLAGS[sym]).zero?
124
+ !@flags.nobits?(FLAGS[sym])
125
125
  end
126
126
 
127
127
  # Timestamps for the start and end of instanced activities
@@ -22,6 +22,7 @@ module Discordrb
22
22
  # @return [User] the user object of the owner
23
23
  attr_reader :owner
24
24
 
25
+ # @!visibility private
25
26
  def initialize(data, bot)
26
27
  @bot = bot
27
28
 
@@ -5,9 +5,6 @@ module Discordrb
5
5
  class Attachment
6
6
  include IDObject
7
7
 
8
- # @return [Message] the message this attachment belongs to.
9
- attr_reader :message
10
-
11
8
  # @return [String] the CDN URL this attachment can be downloaded at.
12
9
  attr_reader :url
13
10
 
@@ -27,6 +24,25 @@ module Discordrb
27
24
  # @return [Integer, nil] the height of an image file, in pixels, or `nil` if the file is not an image.
28
25
  attr_reader :height
29
26
 
27
+ # @return [String, nil] the attachment's description.
28
+ attr_reader :description
29
+
30
+ # @return [String, nil] the attachment's media type.
31
+ attr_reader :content_type
32
+
33
+ # @return [true, false] whether this attachment is ephemeral.
34
+ attr_reader :ephemeral
35
+ alias_method :ephemeral?, :ephemeral
36
+
37
+ # @return [Float, nil] the duration of the voice message in seconds.
38
+ attr_reader :duration_seconds
39
+
40
+ # @return [String, nil] the base64 encoded bytearray representing a sampled waveform for a voice message.
41
+ attr_reader :waveform
42
+
43
+ # @return [Integer] the flags set on this attachment combined as a bitfield.
44
+ attr_reader :flags
45
+
30
46
  # @!visibility private
31
47
  def initialize(data, message, bot)
32
48
  @bot = bot
@@ -41,6 +57,15 @@ module Discordrb
41
57
 
42
58
  @width = data['width']
43
59
  @height = data['height']
60
+
61
+ @description = data['description']
62
+ @content_type = data['content_type']
63
+
64
+ @ephemeral = data['ephemeral']
65
+
66
+ @duration_seconds = data['duration_secs']&.to_f
67
+ @waveform = data['waveform']
68
+ @flags = data['flags'] || 0
44
69
  end
45
70
 
46
71
  # @return [true, false] whether this file is an image file.
@@ -52,5 +77,15 @@ module Discordrb
52
77
  def spoiler?
53
78
  @filename.start_with? 'SPOILER_'
54
79
  end
80
+
81
+ # @return [Message, nil] the message this attachment object belongs to.
82
+ def message
83
+ @message unless @message.is_a?(Snapshot)
84
+ end
85
+
86
+ # @return [Snapshot, nil] the message snapshot this attachment object belongs to.
87
+ def snapshot
88
+ @message unless @message.is_a?(Message)
89
+ end
55
90
  end
56
91
  end
@@ -177,7 +177,7 @@ module Discordrb
177
177
 
178
178
  # The inspect method is overwritten to give more useful output
179
179
  def inspect
180
- "<AuditLogs::Entry id=#{@id} action=#{@action} reason=#{@reason} action_type=#{@action_type} target_type=#{@target_type} count=#{@count} days=#{@days} members_removed=#{@members_removed}>"
180
+ "<AuditLogs::Entry id=#{@id} key=#{@key} action=#{@action} reason=#{@reason} action_type=#{@action_type} target_type=#{@target_type} count=#{@count} days=#{@days} members_removed=#{@members_removed}>"
181
181
  end
182
182
 
183
183
  # Process action changes
@@ -219,8 +219,8 @@ module Discordrb
219
219
  @old = Permissions.new(@old) if @old && @key == 'permissions'
220
220
  @new = Permissions.new(@new) if @new && @key == 'permissions'
221
221
 
222
- @old = @old.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
223
- @new = @new.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, allow: o['allow'], deny: o['deny']) } if @new && @key == 'permission_overwrites'
222
+ @old = @old.map { |o| Overwrite.new(o['id'], type: o['type'], allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
223
+ @new = @new.map { |o| Overwrite.new(o['id'], type: o['type'], allow: o['allow'], deny: o['deny']) } if @new && @key == 'permission_overwrites'
224
224
  end
225
225
 
226
226
  # @return [Channel, nil] the channel that was previously used in the server widget. Only present if the key for this change is `widget_channel_id`.