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
@@ -7,6 +7,7 @@ module Discordrb::Events
7
7
  class Negated
8
8
  attr_reader :object
9
9
 
10
+ # @!visibility private
10
11
  def initialize(object)
11
12
  @object = object
12
13
  end
@@ -75,6 +76,7 @@ module Discordrb::Events
75
76
 
76
77
  # Generic event handler that can be extended
77
78
  class EventHandler
79
+ # @!visibility private
78
80
  def initialize(attributes, block)
79
81
  @attributes = attributes
80
82
  @block = block
@@ -9,6 +9,7 @@ module Discordrb::Events
9
9
  # @return [Server] the server in question.
10
10
  attr_reader :server
11
11
 
12
+ # @!visibility private
12
13
  def initialize(data, bot)
13
14
  @bot = bot
14
15
 
@@ -64,7 +65,8 @@ module Discordrb::Events
64
65
  # @return [Integer] The ID of the server that was left.
65
66
  attr_reader :server
66
67
 
67
- # Override init_server to account for the deleted server
68
+ # @!visibility private
69
+ # @note Override init_server to account for the deleted server
68
70
  def init_server(data, _bot)
69
71
  @server = data['id'].to_i
70
72
  end
@@ -81,6 +83,7 @@ module Discordrb::Events
81
83
  # @return [Array<Emoji>] array of emojis.
82
84
  attr_reader :emoji
83
85
 
86
+ # @!visibility private
84
87
  def initialize(server, data, bot)
85
88
  @bot = bot
86
89
  @server = server
@@ -103,6 +106,7 @@ module Discordrb::Events
103
106
  # @return [Emoji] the emoji data.
104
107
  attr_reader :emoji
105
108
 
109
+ # @!visibility private
106
110
  def initialize(server, emoji, bot)
107
111
  @bot = bot
108
112
  @emoji = emoji
@@ -127,6 +131,7 @@ module Discordrb::Events
127
131
  # @return [Emoji, nil] the updated emoji data.
128
132
  attr_reader :emoji
129
133
 
134
+ # @!visibility private
130
135
  def initialize(server, old_emoji, emoji, bot)
131
136
  @bot = bot
132
137
  @old_emoji = old_emoji
@@ -0,0 +1,575 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'discordrb/events/generic'
4
+ require 'discordrb/data'
5
+
6
+ module Discordrb::Events
7
+ # Generic subclass for interaction events
8
+ class InteractionCreateEvent < Event
9
+ # @return [Interaction] The interaction for this event.
10
+ attr_reader :interaction
11
+
12
+ # @!attribute [r] type
13
+ # @return [Integer]
14
+ # @see Interaction#type
15
+ # @!attribute [r] server
16
+ # @return [Server, nil]
17
+ # @see Interaction#server
18
+ # @!attribute [r] server_id
19
+ # @return [Integer]
20
+ # @see Interaction#server_id
21
+ # @!attribute [r] channel
22
+ # @return [Channel]
23
+ # @see Interaction#channel
24
+ # @!attribute [r] channel_id
25
+ # @return [Integer]
26
+ # @see Interaction#channel_id
27
+ # @!attribute [r] user
28
+ # @return [User]
29
+ # @see Interaction#user
30
+ delegate :type, :server, :server_id, :channel, :channel_id, :user, to: :interaction
31
+
32
+ # @!visibility private
33
+ def initialize(data, bot)
34
+ @interaction = Discordrb::Interaction.new(data, bot)
35
+ @bot = bot
36
+ end
37
+
38
+ # @see Interaction#respond
39
+ def respond(...)
40
+ @interaction.respond(...)
41
+ end
42
+
43
+ # @see Interaction#defer
44
+ def defer(...)
45
+ @interaction.defer(...)
46
+ end
47
+
48
+ # @see Interaction#update_message
49
+ def update_message(...)
50
+ @interaction.update_message(...)
51
+ end
52
+
53
+ # @see Interaction#show_modal
54
+ def show_modal(...)
55
+ @interaction.show_modal(...)
56
+ end
57
+
58
+ # @see Interaction#edit_response
59
+ def edit_response(...)
60
+ @interaction.edit_response(...)
61
+ end
62
+
63
+ # @see Interaction#delete_response
64
+ def delete_response
65
+ @interaction.delete_response
66
+ end
67
+
68
+ # @see Interaction#send_message
69
+ def send_message(...)
70
+ @interaction.send_message(...)
71
+ end
72
+
73
+ # @see Interaction#edit_message
74
+ def edit_message(...)
75
+ @interaction.edit_message(...)
76
+ end
77
+
78
+ # @see Interaction#delete_message
79
+ def delete_message(...)
80
+ @interaction.delete_message(...)
81
+ end
82
+
83
+ # @see Interaction#defer_update
84
+ def defer_update
85
+ @interaction.defer_update
86
+ end
87
+
88
+ # @see Interaction#get_component
89
+ def get_component(...)
90
+ @interaction.get_component(...)
91
+ end
92
+ end
93
+
94
+ # Event handler for INTERACTION_CREATE events.
95
+ class InteractionCreateEventHandler < EventHandler
96
+ # @!visibility private
97
+ def matches?(event)
98
+ return false unless event.is_a? InteractionCreateEvent
99
+
100
+ [
101
+ matches_all(@attributes[:type], event.type) do |a, e|
102
+ a == case a
103
+ when String, Symbol
104
+ Discordrb::Interactions::TYPES[e.to_sym]
105
+ else
106
+ e
107
+ end
108
+ end,
109
+
110
+ matches_all(@attributes[:server], event.interaction) do |a, e|
111
+ a.resolve_id == e.server_id
112
+ end,
113
+
114
+ matches_all(@attributes[:channel], event.interaction) do |a, e|
115
+ a.resolve_id == e.channel_id
116
+ end,
117
+
118
+ matches_all(@attributes[:user], event.user) do |a, e|
119
+ a.resolve_id == e.id
120
+ end
121
+ ].reduce(true, &:&)
122
+ end
123
+ end
124
+
125
+ # Event for ApplicationCommand interactions.
126
+ class ApplicationCommandEvent < InteractionCreateEvent
127
+ # Struct to allow accessing data via [] or methods.
128
+ Resolved = Struct.new('Resolved', :channels, :members, :messages, :roles, :users, :attachments) # rubocop:disable Lint/StructNewOverride
129
+
130
+ # @return [Symbol] The name of the command.
131
+ attr_reader :command_name
132
+
133
+ # @return [Integer] The ID of the command.
134
+ attr_reader :command_id
135
+
136
+ # @return [Symbol, nil] The name of the subcommand group relevant to this event.
137
+ attr_reader :subcommand_group
138
+
139
+ # @return [Symbol, nil] The name of the subcommand relevant to this event.
140
+ attr_reader :subcommand
141
+
142
+ # @return [Resolved]
143
+ attr_reader :resolved
144
+
145
+ # @return [Hash<Symbol, Object>] Arguments provided to the command, mapped as `Name => Value`.
146
+ attr_reader :options
147
+
148
+ # @return [Integer, nil] The target of this command when it is a context command.
149
+ attr_reader :target_id
150
+
151
+ # @!visibility private
152
+ def initialize(data, bot)
153
+ super
154
+
155
+ command_data = data['data']
156
+
157
+ @command_id = command_data['id']
158
+ @command_name = command_data['name'].to_sym
159
+
160
+ @target_id = command_data['target_id']&.to_i
161
+ @resolved = Resolved.new({}, {}, {}, {}, {}, {})
162
+ process_resolved(command_data['resolved']) if command_data['resolved']
163
+
164
+ options = command_data['options'] || []
165
+
166
+ if options.empty?
167
+ @options = {}
168
+ return
169
+ end
170
+
171
+ case options[0]['type']
172
+ when 2
173
+ options = options[0]
174
+ @subcommand_group = options['name'].to_sym
175
+ @subcommand = options['options'][0]['name'].to_sym
176
+ options = options['options'][0]['options']
177
+ when 1
178
+ options = options[0]
179
+ @subcommand = options['name'].to_sym
180
+ options = options['options']
181
+ end
182
+
183
+ @options = transform_options_hash(options || {})
184
+ end
185
+
186
+ # @return [Message, User, nil] The target of this command, for context commands.
187
+ def target
188
+ return nil unless @target_id
189
+
190
+ @resolved.find { |data| data.key?(@target_id) }[@target_id]
191
+ end
192
+
193
+ private
194
+
195
+ def process_resolved(resolved_data)
196
+ resolved_data['users']&.each do |id, data|
197
+ @resolved[:users][id.to_i] = @bot.ensure_user(data)
198
+ end
199
+
200
+ resolved_data['roles']&.each do |id, data|
201
+ @resolved[:roles][id.to_i] = Discordrb::Role.new(data, @bot)
202
+ end
203
+
204
+ resolved_data['channels']&.each do |id, data|
205
+ data['guild_id'] = @interaction.server_id
206
+ @resolved[:channels][id.to_i] = Discordrb::Channel.new(data, @bot)
207
+ end
208
+
209
+ resolved_data['members']&.each do |id, data|
210
+ data['user'] = resolved_data['users'][id]
211
+ data['guild_id'] = @interaction.server_id
212
+ @resolved[:members][id.to_i] = Discordrb::Member.new(data, nil, @bot)
213
+ end
214
+
215
+ resolved_data['messages']&.each do |id, data|
216
+ @resolved[:messages][id.to_i] = Discordrb::Message.new(data, @bot)
217
+ end
218
+
219
+ resolved_data['attachments']&.each do |id, data|
220
+ @resolved[:attachments][id.to_i] = Discordrb::Attachment.new(data, nil, @bot)
221
+ end
222
+ end
223
+
224
+ def transform_options_hash(hash)
225
+ hash.to_h { |opt| [opt['name'], opt['options'] || opt['value']] }
226
+ end
227
+ end
228
+
229
+ # Event handler for ApplicationCommandEvents.
230
+ class ApplicationCommandEventHandler < EventHandler
231
+ # @return [Hash]
232
+ attr_reader :subcommands
233
+
234
+ # @!visibility private
235
+ def initialize(attributes, block)
236
+ super
237
+
238
+ @subcommands = {}
239
+ end
240
+
241
+ # @param name [Symbol, String]
242
+ # @yieldparam [SubcommandBuilder]
243
+ # @return [ApplicationCommandEventHandler]
244
+ def group(name)
245
+ raise ArgumentError, 'Unable to mix subcommands and groups' if @subcommands.any? { |n, v| n == name && v.is_a?(Proc) }
246
+
247
+ builder = SubcommandBuilder.new(name)
248
+ yield builder
249
+
250
+ @subcommands.merge!(builder.to_h)
251
+ self
252
+ end
253
+
254
+ # @param name [String, Symbol]
255
+ # @yieldparam [SubcommandBuilder]
256
+ # @return [ApplicationCommandEventHandler]
257
+ def subcommand(name, &block)
258
+ raise ArgumentError, 'Unable to mix subcommands and groups' if @subcommands.any? { |n, v| n == name && v.is_a?(Hash) }
259
+
260
+ @subcommands[name.to_sym] = block
261
+
262
+ self
263
+ end
264
+
265
+ # @!visibility private
266
+ # @param event [Event]
267
+ def call(event)
268
+ return unless matches?(event)
269
+
270
+ if event.subcommand_group
271
+ unless (cmd = @subcommands.dig(event.subcommand_group, event.subcommand))
272
+ Discordrb::LOGGER.debug("Received an event for an unhandled subcommand `#{event.command_name} #{event.subcommand_group} #{event.subcommand}'")
273
+ return
274
+ end
275
+
276
+ cmd.call(event)
277
+ elsif event.subcommand
278
+ unless (cmd = @subcommands[event.subcommand])
279
+ Discordrb::LOGGER.debug("Received an event for an unhandled subcommand `#{event.command_name} #{event.subcommand}'")
280
+ return
281
+ end
282
+
283
+ cmd.call(event)
284
+ else
285
+ @block.call(event)
286
+ end
287
+ end
288
+
289
+ # @!visibility private
290
+ def matches?(event)
291
+ return false unless event.is_a? ApplicationCommandEvent
292
+
293
+ [
294
+ matches_all(@attributes[:name], event.command_name) do |a, e|
295
+ a.to_sym == e.to_sym
296
+ end
297
+ ].reduce(true, &:&)
298
+ end
299
+ end
300
+
301
+ # Builder for adding subcommands to an ApplicationCommandHandler
302
+ class SubcommandBuilder
303
+ # @!visibility private
304
+ # @param group [String, Symbol, nil]
305
+ def initialize(group = nil)
306
+ @group = group&.to_sym
307
+ @subcommands = {}
308
+ end
309
+
310
+ # @param name [Symbol, String]
311
+ # @yieldparam [ApplicationCommandEvent]
312
+ def subcommand(name, &block)
313
+ @subcommands[name.to_sym] = block
314
+ end
315
+
316
+ # @!visibility private
317
+ def to_h
318
+ @group ? { @group => @subcommands } : @subcommands
319
+ end
320
+ end
321
+
322
+ # An event for when a user interacts with a component.
323
+ class ComponentEvent < InteractionCreateEvent
324
+ # @return [String] User provided data for this button.
325
+ attr_reader :custom_id
326
+
327
+ # @return [Interactions::Message, nil] The message the button originates from.
328
+ attr_reader :message
329
+
330
+ # @!visibility private
331
+ def initialize(data, bot)
332
+ super
333
+
334
+ @message = Discordrb::Interactions::Message.new(data['message'], bot, @interaction) if data['message']
335
+ @custom_id = data['data']['custom_id']
336
+ end
337
+ end
338
+
339
+ # Generic handler for component events.
340
+ class ComponentEventHandler < InteractionCreateEventHandler
341
+ def matches?(event)
342
+ return false unless super
343
+ return false unless event.is_a? ComponentEvent
344
+
345
+ [
346
+ matches_all(@attributes[:custom_id], event.custom_id) do |a, e|
347
+ # Match regexp and strings
348
+ case a
349
+ when Regexp
350
+ a.match?(e)
351
+ else
352
+ a == e
353
+ end
354
+ end,
355
+ matches_all(@attributes[:message], event.message) do |a, e|
356
+ case a
357
+ when String, Integer
358
+ a.resolve_id == e.id
359
+ else
360
+ a.id == e.id
361
+ end
362
+ end
363
+ ].reduce(&:&)
364
+ end
365
+ end
366
+
367
+ # An event for when a user interacts with a button component.
368
+ class ButtonEvent < ComponentEvent
369
+ end
370
+
371
+ # Event handler for a Button interaction event.
372
+ class ButtonEventHandler < ComponentEventHandler
373
+ end
374
+
375
+ # Event for when a user interacts with a select string component.
376
+ class StringSelectEvent < ComponentEvent
377
+ # @return [Array<String>] Selected values.
378
+ attr_reader :values
379
+
380
+ # @!visibility private
381
+ def initialize(data, bot)
382
+ super
383
+
384
+ @values = data['data']['values']
385
+ end
386
+ end
387
+
388
+ # Event handler for a select string component.
389
+ class StringSelectEventHandler < ComponentEventHandler
390
+ end
391
+
392
+ # An event for when a user submits a modal.
393
+ class ModalSubmitEvent < ComponentEvent
394
+ # @return [Array<TextInputComponent>]
395
+ attr_reader :components
396
+
397
+ # Get the value of an input passed to the modal.
398
+ # @param custom_id [String] The custom ID of the component to look for.
399
+ # @return [String, nil]
400
+ def value(custom_id)
401
+ get_component(custom_id)&.value
402
+ end
403
+ end
404
+
405
+ # Event handler for a modal submission.
406
+ class ModalSubmitEventHandler < ComponentEventHandler
407
+ end
408
+
409
+ # Event for when a user interacts with a select user component.
410
+ class UserSelectEvent < ComponentEvent
411
+ # @return [Array<User>] Selected values.
412
+ attr_reader :values
413
+
414
+ # @!visibility private
415
+ def initialize(data, bot)
416
+ super
417
+
418
+ @values = data['data']['values'].map { |e| bot.user(e) }
419
+ end
420
+ end
421
+
422
+ # Event handler for a select user component.
423
+ class UserSelectEventHandler < ComponentEventHandler
424
+ end
425
+
426
+ # Event for when a user interacts with a select role component.
427
+ class RoleSelectEvent < ComponentEvent
428
+ # @return [Array<Role>] Selected values.
429
+ attr_reader :values
430
+
431
+ # @!visibility private
432
+ def initialize(data, bot)
433
+ super
434
+
435
+ @values = data['data']['values'].map { |e| bot.server(data['guild_id']).role(e) }
436
+ end
437
+ end
438
+
439
+ # Event handler for a select role component.
440
+ class RoleSelectEventHandler < ComponentEventHandler
441
+ end
442
+
443
+ # Event for when a user interacts with a select mentionable component.
444
+ class MentionableSelectEvent < ComponentEvent
445
+ # @return [Hash<Symbol => Array<User>, Symbol => Array<Role>>] Selected values.
446
+ attr_reader :values
447
+
448
+ # @!visibility private
449
+ def initialize(data, bot)
450
+ super
451
+
452
+ users = data['data']['resolved']['users'].keys.map { |e| bot.user(e) }
453
+ roles = data['data']['resolved']['roles'] ? data['data']['resolved']['roles'].keys.map { |e| bot.server(data['guild_id']).role(e) } : []
454
+ @values = { users: users, roles: roles }
455
+ end
456
+ end
457
+
458
+ # Event handler for a select mentionable component.
459
+ class MentionableSelectEventHandler < ComponentEventHandler
460
+ end
461
+
462
+ # Event for when a user interacts with a select channel component.
463
+ class ChannelSelectEvent < ComponentEvent
464
+ # @return [Array<Channel>] Selected values.
465
+ attr_reader :values
466
+
467
+ # @!visibility private
468
+ def initialize(data, bot)
469
+ super
470
+
471
+ @values = data['data']['values'].map { |e| bot.channel(e, bot.server(data['guild_id'])) }
472
+ end
473
+ end
474
+
475
+ # Event handler for a select channel component.
476
+ class ChannelSelectEventHandler < ComponentEventHandler
477
+ end
478
+
479
+ # Event handler for an autocomplete option choices.
480
+ class AutocompleteEventHandler < InteractionCreateEventHandler
481
+ def matches?(event)
482
+ return false unless super
483
+ return false unless event.is_a?(AutocompleteEvent)
484
+
485
+ [
486
+ matches_all(@attributes[:name], event.focused) { |a, e| a&.to_s == e },
487
+ matches_all(@attributes[:command_id], event.command_id) { |a, e| a&.to_i == e },
488
+ matches_all(@attributes[:subcommand], event.subcommand) { |a, e| a&.to_sym == e },
489
+ matches_all(@attributes[:command_name], event.command_name) { |a, e| a&.to_sym == e },
490
+ matches_all(@attributes[:subcommand_group], event.subcommand_group) { |a, e| a&.to_sym == e },
491
+ matches_all(@attributes[:server], event.server_id) { |a, e| a&.resolve_id == e }
492
+ ].reduce(&:&)
493
+ end
494
+ end
495
+
496
+ # An event for an autocomplete option choice.
497
+ class AutocompleteEvent < ApplicationCommandEvent
498
+ # @return [String] Name of the currently focused option.
499
+ attr_reader :focused
500
+
501
+ # @return [Hash] An empty hash that can be used to return choices by adding K/V pairs.
502
+ attr_reader :choices
503
+
504
+ # @!visibility private
505
+ def initialize(data, bot)
506
+ super
507
+
508
+ @choices = {}
509
+
510
+ options = data['data']['options']
511
+
512
+ options = case options[0]['type']
513
+ when 1
514
+ options[0]['options']
515
+ when 2
516
+ options[0]['options'][0]['options']
517
+ else
518
+ options
519
+ end
520
+
521
+ @focused = options.find { |opt| opt.key?('focused') }['name']
522
+ end
523
+
524
+ # Respond to this interaction with autocomplete choices.
525
+ # @param choices [Array<Hash>, Hash, nil] Autocomplete choices to return.
526
+ def respond(choices:)
527
+ @interaction.show_autocomplete_choices(choices || [])
528
+ end
529
+ end
530
+
531
+ # An event for whenever an application command's permissions are updated.
532
+ class ApplicationCommandPermissionsUpdateEvent < Event
533
+ # @return [Integer] the ID of the server where the command permissions were updated.
534
+ attr_reader :server_id
535
+
536
+ # @return [Integer, nil] the ID of the application command that was updated.
537
+ attr_reader :command_id
538
+
539
+ # @return [Array<ApplicationCommand::Permission>] the permissions that were updated.
540
+ attr_reader :permissions
541
+
542
+ # @return [Integer] the ID of the application whose commands were updated.
543
+ attr_reader :application_id
544
+
545
+ # @!visibility private
546
+ def initialize(data, bot)
547
+ @bot = bot
548
+ @server_id = data['guild_id'].to_i
549
+ @application_id = data['application_id'].to_i
550
+ @command_id = data['id'].to_i if data['id'].to_i != @application_id
551
+ @permissions = data['permissions'].map do |permission|
552
+ Discordrb::ApplicationCommand::Permission.new(permission, data, bot)
553
+ end
554
+ end
555
+
556
+ # @return [Server] the server where the command's permissions were updated.
557
+ def server
558
+ @bot.server(@server_id)
559
+ end
560
+ end
561
+
562
+ # Event handler for the APPLICATION_COMMAND_PERMISSIONS_UPDATE event.
563
+ class ApplicationCommandPermissionsUpdateEventHandler < EventHandler
564
+ # @!visibility private
565
+ def matches?(event)
566
+ return false unless event.is_a?(ApplicationCommandPermissionsUpdateEvent)
567
+
568
+ [
569
+ matches_all(@attributes[:server], event.server_id) { |a, e| a.resolve_id == e },
570
+ matches_all(@attributes[:command_id], event.command_id) { |a, e| a.resolve_id == e },
571
+ matches_all(@attributes[:application_id], event.application_id) { |a, e| a.resolve_id == e }
572
+ ].reduce(&:&)
573
+ end
574
+ end
575
+ end
@@ -34,6 +34,7 @@ module Discordrb::Events
34
34
 
35
35
  alias temporary? temporary
36
36
 
37
+ # @!visibility private
37
38
  def initialize(data, invite, bot)
38
39
  @bot = bot
39
40
  @invite = invite
@@ -53,6 +54,7 @@ module Discordrb::Events
53
54
  # @return [String] The code of the deleted invite.
54
55
  attr_reader :code
55
56
 
57
+ # @!visibility private
56
58
  def initialize(data, bot)
57
59
  @bot = bot
58
60
  @channel = bot.channel(data['channel_id'])
@@ -16,6 +16,7 @@ module Discordrb::Events
16
16
  # @return [Server] the server on which the event happened.
17
17
  attr_reader :server
18
18
 
19
+ # @!visibility private
19
20
  def initialize(data, bot)
20
21
  @bot = bot
21
22
 
@@ -28,11 +29,13 @@ module Discordrb::Events
28
29
 
29
30
  private
30
31
 
32
+ # @!visibility private
31
33
  def init_user(data, _)
32
34
  user_id = data['user']['id'].to_i
33
35
  @user = @server.member(user_id)
34
36
  end
35
37
 
38
+ # @!visibility private
36
39
  def init_roles(data, _)
37
40
  @roles = [@server.role(@server.id)]
38
41
  return unless data['roles']
@@ -71,7 +74,20 @@ module Discordrb::Events
71
74
 
72
75
  # Member is updated (roles added or deleted)
73
76
  # @see Discordrb::EventContainer#member_update
74
- class ServerMemberUpdateEvent < ServerMemberEvent; end
77
+ class ServerMemberUpdateEvent < ServerMemberEvent
78
+ # @!visibility private
79
+ # @note Override init_user so we don't make requests all the time on large servers
80
+ def init_user(data, _)
81
+ @user_id = data['user']['id']
82
+ end
83
+
84
+ # @return [Member] the member in question.
85
+ def user
86
+ @server&.member(@user_id)
87
+ end
88
+
89
+ alias_method :member, :user
90
+ end
75
91
 
76
92
  # Event handler for {ServerMemberUpdateEvent}
77
93
  class ServerMemberUpdateEventHandler < ServerMemberEventHandler; end
@@ -79,7 +95,8 @@ module Discordrb::Events
79
95
  # Member leaves
80
96
  # @see Discordrb::EventContainer#member_leave
81
97
  class ServerMemberDeleteEvent < ServerMemberEvent
82
- # Override init_user to account for the deleted user on the server
98
+ # @!visibility private
99
+ # @note Override init_user to account for the deleted user on the server
83
100
  def init_user(data, bot)
84
101
  @user = Discordrb::User.new(data['user'], bot)
85
102
  end