discordrb 3.7.2 → 3.8.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.
@@ -14,41 +14,70 @@ module Discordrb
14
14
  ActionRow.new(data, bot)
15
15
  when Webhooks::View::COMPONENT_TYPES[:button]
16
16
  Button.new(data, bot)
17
- when Webhooks::View::COMPONENT_TYPES[:string_select]
17
+ when Webhooks::View::COMPONENT_TYPES[:string_select], Webhooks::View::COMPONENT_TYPES[:user_select], Webhooks::View::COMPONENT_TYPES[:role_select], Webhooks::View::COMPONENT_TYPES[:mentionable_select], Webhooks::View::COMPONENT_TYPES[:channel_select]
18
18
  SelectMenu.new(data, bot)
19
19
  when Webhooks::Modal::COMPONENT_TYPES[:text_input]
20
20
  TextInput.new(data, bot)
21
+ when Webhooks::View::COMPONENT_TYPES[:section]
22
+ Section.new(data, bot)
23
+ when Webhooks::View::COMPONENT_TYPES[:text_display]
24
+ TextDisplay.new(data, bot)
25
+ when Webhooks::View::COMPONENT_TYPES[:thumbnail]
26
+ Thumbnail.new(data, bot)
27
+ when Webhooks::View::COMPONENT_TYPES[:media_gallery]
28
+ MediaGallery.new(data, bot)
29
+ when Webhooks::View::COMPONENT_TYPES[:file]
30
+ File.new(data, bot)
31
+ when Webhooks::View::COMPONENT_TYPES[:separator]
32
+ Separator.new(data, bot)
33
+ when Webhooks::View::COMPONENT_TYPES[:container]
34
+ Container.new(data, bot)
35
+ when Webhooks::Modal::COMPONENT_TYPES[:label]
36
+ Label.new(data, bot)
37
+ when Webhooks::Modal::COMPONENT_TYPES[:file_upload]
38
+ FileUpload.new(data, bot)
39
+ when Webhooks::Modal::COMPONENT_TYPES[:radio_group]
40
+ RadioGroup.new(data, bot)
41
+ when Webhooks::Modal::COMPONENT_TYPES[:checkbox_group]
42
+ CheckboxGroup.new(data, bot)
43
+ when Webhooks::Modal::COMPONENT_TYPES[:checkbox]
44
+ Checkbox.new(data, bot)
21
45
  end
22
46
  end
23
47
 
24
- # Represents a row of components
48
+ # Represents a row of components.
25
49
  class ActionRow
26
50
  include Enumerable
27
51
 
28
- # @return [Array<Button>]
52
+ # @return [Integer] the numeric identifier of the action row.
53
+ attr_reader :id
54
+
55
+ # @return [Array<Button>] the components contained within this action row.
29
56
  attr_reader :components
30
57
 
31
58
  # @!visibility private
32
59
  def initialize(data, bot)
33
60
  @bot = bot
61
+ @id = data['id']
34
62
  @components = data['components'].map { |component_data| Components.from_data(component_data, @bot) }
35
63
  end
36
64
 
37
65
  # Iterate over each component in the row.
66
+ # @return [Array, Enumerator]
38
67
  def each(&block)
39
68
  @components.each(&block)
40
69
  end
41
70
 
42
- # Get all buttons in this row
43
- # @return [Array<Button>]
71
+ # Get all the buttons in this action row.
72
+ # @return [Array<Button>] All of the buttons in this action row.
44
73
  def buttons
45
- select { |component| component.is_a? Button }
74
+ select { |component| component.is_a?(Button) }
46
75
  end
47
76
 
48
- # Get all buttons in this row
49
- # @return [Array<Button>]
77
+ # Get all the text inputs in this action row.
78
+ # @return [Array<TextInput>] All of the text inputs in this action row.
50
79
  def text_inputs
51
- select { |component| component.is_a? TextInput }
80
+ select { |component| component.is_a?(TextInput) }
52
81
  end
53
82
 
54
83
  # @!visibility private
@@ -59,28 +88,31 @@ module Discordrb
59
88
 
60
89
  # An interactable button component.
61
90
  class Button
62
- # @return [String]
91
+ # @return [Integer] the numeric identifier of the button.
92
+ attr_reader :id
93
+
94
+ # @return [String] the label of the button.
63
95
  attr_reader :label
64
96
 
65
- # @return [Integer]
97
+ # @return [Integer] the style of the button.
66
98
  attr_reader :style
67
99
 
68
- # @return [String]
100
+ # @return [String] the custom ID of the button.
69
101
  attr_reader :custom_id
70
102
 
71
- # @return [true, false]
103
+ # @return [true, false] whether or not the button is disabled.
72
104
  attr_reader :disabled
73
105
 
74
- # @return [String, nil]
106
+ # @return [String, nil] the URL of the button if applicable.
75
107
  attr_reader :url
76
108
 
77
- # @return [Emoji, nil]
109
+ # @return [Emoji, nil] the custom emoji of the button component.
78
110
  attr_reader :emoji
79
111
 
80
112
  # @!visibility private
81
113
  def initialize(data, bot)
82
114
  @bot = bot
83
-
115
+ @id = data['id']
84
116
  @label = data['label']
85
117
  @style = data['style']
86
118
  @custom_id = data['custom_id']
@@ -89,30 +121,30 @@ module Discordrb
89
121
  @emoji = Emoji.new(data['emoji'], @bot) if data['emoji']
90
122
  end
91
123
 
92
- # @method primary?
93
- # @return [true, false]
94
- # @method secondary?
95
- # @return [true, false]
96
- # @method success?
97
- # @return [true, false]
98
- # @method danger?
99
- # @return [true, false]
100
- # @method link?
101
- # @return [true, false]
124
+ # @!method primary?
125
+ # @return [true, false] whether the button is a primary option in the group.
126
+ # @!method secondary?
127
+ # @return [true, false] whether the button denotes a secondary option in the group.
128
+ # @!method success?
129
+ # @return [true, false] whether the button denotes a success action in the group.
130
+ # @!method danger?
131
+ # @return [true, false] whether the button denotes a dangerous action in the group.
132
+ # @!method link?
133
+ # @return [true, false] whether the button is a container for a URL that will open upon click.
102
134
  Webhooks::View::BUTTON_STYLES.each do |name, value|
103
135
  define_method("#{name}?") do
104
136
  @style == value
105
137
  end
106
138
  end
107
139
 
108
- # Await a button click
140
+ # Await a button click.
109
141
  def await_click(key, **attributes, &block)
110
- @bot.add_await(key, Discordrb::Events::ButtonEvent, { custom_id: @custom_id }.merge(attributes), &block)
142
+ @bot.add_await(key, Discordrb::Events::ButtonEvent, { custom_id: @custom_id }.merge!(attributes), &block)
111
143
  end
112
144
 
113
145
  # Await a button click, blocking.
114
146
  def await_click!(**attributes, &block)
115
- @bot.add_await!(Discordrb::Events::ButtonEvent, { custom_id: @custom_id }.merge(attributes), &block)
147
+ @bot.add_await!(Discordrb::Events::ButtonEvent, { custom_id: @custom_id }.merge!(attributes), &block)
116
148
  end
117
149
  end
118
150
 
@@ -120,19 +152,19 @@ module Discordrb
120
152
  class SelectMenu
121
153
  # A select menu option.
122
154
  class Option
123
- # @return [String]
155
+ # @return [String] the label of the option.
124
156
  attr_reader :label
125
157
 
126
- # @return [String]
158
+ # @return [String] the value of the option.
127
159
  attr_reader :value
128
160
 
129
- # @return [String, nil]
161
+ # @return [String, nil] the description of the option.
130
162
  attr_reader :description
131
163
 
132
- # @return [Emoji, nil]
164
+ # @return [Emoji, nil] the emoji of the option, or `nil`.
133
165
  attr_reader :emoji
134
166
 
135
- # @!visibility hidden
167
+ # @!visibility private
136
168
  def initialize(data)
137
169
  @label = data['label']
138
170
  @value = data['value']
@@ -141,88 +173,405 @@ module Discordrb
141
173
  end
142
174
  end
143
175
 
144
- # @return [String]
176
+ # @return [Integer] the numeric identifier of the select menu.
177
+ attr_reader :id
178
+
179
+ # @return [Array<String>] the submitted values from the modal.
180
+ attr_reader :values
181
+
182
+ # @return [String] the custom ID used to identify the select menu.
145
183
  attr_reader :custom_id
146
184
 
147
- # @return [Integer, nil]
185
+ # @return [Integer, nil] the minimum amount of values that be selected.
148
186
  attr_reader :max_values
149
187
 
150
- # @return [Integer, nil]
188
+ # @return [Integer, nil] the maximum amount of values that can be selected.
151
189
  attr_reader :min_values
152
190
 
153
- # @return [String, nil]
191
+ # @return [String, nil] the default placeholder text shown on the select menu.
154
192
  attr_reader :placeholder
155
193
 
156
- # @return [Array<Option>]
194
+ # @return [Array<Option>] the options in the select menu, or the selected options.
157
195
  attr_reader :options
158
196
 
159
197
  # @!visibility private
160
198
  def initialize(data, bot)
161
199
  @bot = bot
162
-
200
+ @id = data['id']
201
+ @values = data['values'] || []
202
+ @custom_id = data['custom_id']
163
203
  @max_values = data['max_values']
164
204
  @min_values = data['min_values']
165
205
  @placeholder = data['placeholder']
166
- @custom_id = data['custom_id']
167
- @emoji = Emoji.new(data['emoji'], @bot) if data['emoji']
168
- @options = data['options'].map { |opt| Option.new(opt) }
206
+ @options = data['options']&.map { |option| Option.new(option) } || []
169
207
  end
170
208
  end
171
209
 
172
- # Text input component for use in modals. Can be either a line (`short`), or a multi line (`paragraph`) block.
210
+ # A free-form text input bar in a modal.
173
211
  class TextInput
174
- # Single line text input
212
+ # @!visibility private
213
+ PLACEHOLDERS = %i[
214
+ label
215
+ min_length
216
+ max_length
217
+ required
218
+ required?
219
+ placeholder
220
+ ].freeze
221
+
222
+ # @!visibility private
175
223
  SHORT = 1
176
- # Multi-line text input
224
+
225
+ # @!visibility private
177
226
  PARAGRAPH = 2
178
227
 
179
- # @return [String]
180
- attr_reader :custom_id
228
+ # @return [Integer] the numeric identifier of the text input.
229
+ attr_reader :id
181
230
 
182
- # @return [Symbol]
231
+ # @return [Symbol] This is deprecated and not accurate. This will
232
+ # be removed in the next major version (4.0.0).
183
233
  attr_reader :style
184
234
 
185
- # @return [String]
186
- attr_reader :label
235
+ # @return [String, nil] the value the user typed into the text input.
236
+ attr_reader :value
237
+
238
+ # @return [String] the developer-defined identifier for the text input.
239
+ attr_reader :custom_id
240
+
241
+ # @!visibility private
242
+ def initialize(data, bot)
243
+ @bot = bot
244
+ @id = data['id']
245
+ @style = :paragraph
246
+ @value = data['value']
247
+ @custom_id = data['custom_id']
248
+ end
249
+
250
+ # @!visibility private
251
+ PLACEHOLDERS.each { |name| define_method(name) { nil } }
252
+ end
253
+
254
+ # A grouping of components with a contextual accessory.
255
+ class Section
256
+ # @return [Integer] the numeric identifier of the section.
257
+ attr_reader :id
258
+
259
+ # @return [Component] the contextual accessory of the section.
260
+ attr_reader :accessory
261
+
262
+ # @return [Array<Component>] the child components of the section.
263
+ attr_reader :components
264
+
265
+ # @!visibility private
266
+ def initialize(data, bot)
267
+ @bot = bot
268
+ @id = data['id']
269
+ @accessory = Components.from_data(data['accessory'], @bot)
270
+ @components = data['components'].map { |component| Components.from_data(component, @bot) }
271
+ end
272
+ end
273
+
274
+ # A content component representing message content.
275
+ class TextDisplay
276
+ # @return [Integer] the numeric identifier of the text display.
277
+ attr_reader :id
278
+
279
+ # @return [String] the content to be displayed for the text display.
280
+ attr_reader :content
281
+
282
+ # @!visibility private
283
+ def initialize(data, bot)
284
+ @bot = bot
285
+ @id = data['id']
286
+ @content = data['content']
287
+ end
288
+ end
289
+
290
+ # A content component that compactly displays media.
291
+ class Thumbnail
292
+ # @return [Integer] the numeric identifier of the thumbnail.
293
+ attr_reader :id
294
+
295
+ # @return [MediaItem] the unfurled media content of the thumbnail.
296
+ attr_reader :media
297
+
298
+ # @return [true, false] whether or not the thumbnail's media should
299
+ # be blurred out.
300
+ attr_reader :spoiler
301
+ alias spoiler? spoiler
302
+
303
+ # @return [String, nil] the alternative text for the thumbnail's media.
304
+ attr_reader :description
305
+
306
+ # @!visibility private
307
+ def initialize(data, bot)
308
+ @bot = bot
309
+ @id = data['id']
310
+ @spoiler = data['spoiler']
311
+ @description = data['description']
312
+ @media = MediaItem.new(data['media'], @bot)
313
+ end
314
+ end
315
+
316
+ # A grouping of media attachments in an organized gallery format.
317
+ class MediaGallery
318
+ # @return [Integer] the numeric identifier of the media gallery.
319
+ attr_reader :id
320
+
321
+ # @return [Array<Item>] the media items contained within the media gallery.
322
+ attr_reader :items
323
+
324
+ # @!visibility private
325
+ def initialize(data, bot)
326
+ @bot = bot
327
+ @id = data['id']
328
+ @items = data['items'].map { |item| Item.new(item, @bot) }
329
+ end
330
+
331
+ # A singular media attachment.
332
+ class Item
333
+ # @return [MediaItem] the unfurled media content of the gallery item.
334
+ attr_reader :media
335
+
336
+ # @return [true, false] whether or not the gallery item's media should
337
+ # be blurred out.
338
+ attr_reader :spoiler
339
+ alias spoiler? spoiler
340
+
341
+ # @return [String, nil] the alternative text for the gallery item's media.
342
+ attr_reader :description
343
+
344
+ # @!visibility private
345
+ def initialize(data, bot)
346
+ @bot = bot
347
+ @spoiler = data['spoiler']
348
+ @description = data['description']
349
+ @media = MediaItem.new(data['media'], @bot)
350
+ end
351
+ end
352
+ end
353
+
354
+ # A component that adds vertical padding and visual division.
355
+ class Separator
356
+ # @return [Integer] the numeric identifier of the separator.
357
+ attr_reader :id
358
+
359
+ # @return [true, false] whether or not a visual divider should be displayed.
360
+ attr_reader :divider
361
+ alias divider? divider
362
+
363
+ # @return [Integer] the size of the separator's padding. `1` for little padding,
364
+ # and `2` for big padding.
365
+ attr_reader :spacing
366
+
367
+ # @!visibility private
368
+ def initialize(data, bot)
369
+ @bot = bot
370
+ @id = data['id']
371
+ @divider = data['divider']
372
+ @spacing = data['spacing']
373
+ end
374
+ end
375
+
376
+ # A collection of components in an embed-like format.
377
+ class Container
378
+ # @return [Integer] the numeric identifier of the container.
379
+ attr_reader :id
187
380
 
188
- # @return [Integer, nil]
189
- attr_reader :min_length
381
+ # @return [ColourRGB, nil] the accent colour of the container.
382
+ attr_reader :color
383
+ alias colour color
190
384
 
191
- # @return [Integer, nil]
192
- attr_reader :max_length
385
+ # @return [true, false] whether or not the container should be
386
+ # blurred out.
387
+ attr_reader :spoiler
388
+ alias spoiler? spoiler
193
389
 
194
- # @return [true, false]
195
- attr_reader :required
390
+ # @return [Array<Component>] the child components of the container.
391
+ attr_reader :components
196
392
 
197
- # @return [String, nil]
393
+ # @!visibility private
394
+ def initialize(data, bot)
395
+ @bot = bot
396
+ @id = data['id']
397
+ @spoiler = data['spoiler']
398
+ @color = ColourRGB.new(data['accent_color']) if data['accent_color']
399
+ @components = data['components'].map { |component| Components.from_data(component, @bot) }
400
+ end
401
+
402
+ # Get the buttons contained within the container.
403
+ # @return [Array<Button>] The buttons within the container.
404
+ def buttons
405
+ @components.flat_map do |component|
406
+ case component
407
+ when ActionRow
408
+ component.buttons
409
+ when Section
410
+ component.accessory if component.accessory.is_a?(Button)
411
+ end
412
+ end.compact
413
+ end
414
+ end
415
+
416
+ # A component that allows you to display an attachment.
417
+ class File
418
+ # @return [Integer] the numeric identifier of the file.
419
+ attr_reader :id
420
+
421
+ # @return [String] the name of the file that was uploaded.
422
+ attr_reader :name
423
+
424
+ # @return [Integer] the size of the file that was uploaded
425
+ # in bytes.
426
+ attr_reader :size
427
+
428
+ # @return [MediaItem] the unfurled media item of the file.
429
+ attr_reader :media
430
+
431
+ # @return [true, false] whether or not the file should be
432
+ # blurred out.
433
+ attr_reader :spoiler
434
+ alias spoiler? spoiler
435
+
436
+ # @!visibility private
437
+ def initialize(data, bot)
438
+ @bot = bot
439
+ @id = data['id']
440
+ @name = data['name']
441
+ @size = data['size']
442
+ @spoiler = data['spoiler']
443
+ @media = MediaItem.new(data['file'], @bot)
444
+ end
445
+ end
446
+
447
+ # Resolved metadata about a piece of media.
448
+ class MediaItem
449
+ # @return [String] the URL to the media item.
450
+ attr_reader :url
451
+
452
+ # @return [Integer, nil] the width of the media item.
453
+ attr_reader :width
454
+
455
+ # @return [Integer, nil] the height of the media item.
456
+ attr_reader :height
457
+
458
+ # @return [String, nil] the proxied URL to the media item.
459
+ attr_reader :proxy_url
460
+
461
+ # @return [String, nil] the content type of the media item.
462
+ attr_reader :content_type
463
+
464
+ # @return [Integer, nil] the ID of the uploaded attachment. Only present
465
+ # when the media item was uploaded via an `attachment://<filename>` reference.
466
+ attr_reader :attachment_id
467
+
468
+ # @!visibility private
469
+ def initialize(data, bot)
470
+ @bot = bot
471
+ @url = data['url']
472
+ @width = data['width']
473
+ @height = data['height']
474
+ @proxy_url = data['proxy_url']
475
+ @content_type = data['content_type']
476
+ @attachment_id = data['attachment_id']&.to_i
477
+ end
478
+ end
479
+
480
+ # A parent component for interactive modal components.
481
+ class Label
482
+ # @return [Integer] the numeric identifier of the label.
483
+ attr_reader :id
484
+
485
+ # @return [Component] the interactive component of the label.
486
+ attr_reader :component
487
+
488
+ # @!visibility private
489
+ def initialize(data, bot)
490
+ @bot = bot
491
+ @id = data['id']
492
+ @component = Components.from_data(data['component'], @bot)
493
+ end
494
+ end
495
+
496
+ # A surface that allows users to upload files in a modal.
497
+ class FileUpload
498
+ # @return [Integer] the numeric identifier of the file upload.
499
+ attr_reader :id
500
+
501
+ # @return [Array<Integer>] the IDs of the uploaded attachments.
502
+ attr_reader :values
503
+
504
+ # @return [String] the developer-defined identifier for the file upload.
505
+ attr_reader :custom_id
506
+
507
+ # @!visibility private
508
+ def initialize(data, bot)
509
+ @bot = bot
510
+ @id = data['id']
511
+ @custom_id = data['custom_id']
512
+ @values = data['values'].map(&:to_i)
513
+ end
514
+ end
515
+
516
+ # A grouping of radio buttons in a modal.
517
+ class RadioGroup
518
+ # @return [Integer] the numeric identifier of the radio group.
519
+ attr_reader :id
520
+
521
+ # @return [true, false] whether or not a radio button was selected.
198
522
  attr_reader :value
523
+ alias value? value
199
524
 
200
- # @return [String, nil]
201
- attr_reader :placeholder
525
+ # @return [String] the developer-defined identifier for the radio group.
526
+ attr_reader :custom_id
202
527
 
203
528
  # @!visibility private
204
529
  def initialize(data, bot)
205
530
  @bot = bot
206
- @style = data['style'] == SHORT ? :short : :paragraph
207
- @label = data['label']
208
- @min_length = data['min_length']
209
- @max_length = data['max_length']
210
- @required = data['required']
531
+ @id = data['id']
211
532
  @value = data['value']
212
- @placeholder = data['placeholder']
213
533
  @custom_id = data['custom_id']
214
534
  end
535
+ end
215
536
 
216
- def short?
217
- @style == :short
218
- end
537
+ # A grouping of checkboxes in a modal.
538
+ class CheckboxGroup
539
+ # @return [Integer] the numeric identifier of the checkbox group.
540
+ attr_reader :id
541
+
542
+ # @return [Array<String>] the values of the selected checkbox buttons.
543
+ attr_reader :values
544
+
545
+ # @return [String] the developer-defined identifier for the checkbox group.
546
+ attr_reader :custom_id
219
547
 
220
- def paragraph?
221
- @style == :paragraph
548
+ # @!visibility private
549
+ def initialize(data, bot)
550
+ @bot = bot
551
+ @id = data['id']
552
+ @values = data['values']
553
+ @custom_id = data['custom_id']
222
554
  end
555
+ end
223
556
 
224
- def required?
225
- @required
557
+ # A checkbox that can be ticked in a modal.
558
+ class Checkbox
559
+ # @return [Integer] the numeric identifier of the checkbox.
560
+ attr_reader :id
561
+
562
+ # @return [true, false] whether or not the checkbox was selected.
563
+ attr_reader :value
564
+ alias value? value
565
+
566
+ # @return [String] the developer-defined identifier for the checkbox.
567
+ attr_reader :custom_id
568
+
569
+ # @!visibility private
570
+ def initialize(data, bot)
571
+ @bot = bot
572
+ @id = data['id']
573
+ @value = data['value']
574
+ @custom_id = data['custom_id']
226
575
  end
227
576
  end
228
577
  end
@@ -41,7 +41,7 @@ module Discordrb
41
41
  @name = data['name']
42
42
  @server = server
43
43
  @id = data['id']&.to_i
44
- @animated = data['animated']
44
+ @animated = data['animated'] || false
45
45
  @managed = data['managed']
46
46
  @available = data['available']
47
47
  @requires_colons = data['require_colons']
@@ -84,7 +84,13 @@ module Discordrb
84
84
 
85
85
  # The inspect method is overwritten to give more useful output
86
86
  def inspect
87
- "<Emoji name=#{name} id=#{id} animated=#{animated}>"
87
+ "<Emoji name=#{name.inspect} id=#{id.inspect} animated=#{animated}>"
88
+ end
89
+
90
+ # Converts this Emoji into a hash that can be sent back to Discord in other endpoints.
91
+ # @return [Hash] A hash representation of this emoji.
92
+ def to_h
93
+ id.nil? ? { name: name } : { id: id }
88
94
  end
89
95
 
90
96
  # @!visibility private
@@ -97,5 +103,21 @@ module Discordrb
97
103
  @roles << role
98
104
  end
99
105
  end
106
+
107
+ # @!visibility private
108
+ def self.build_emoji_hash(emoji, prefix: true)
109
+ data = { id: nil, name: nil }
110
+
111
+ case emoji
112
+ when Emoji, Reaction
113
+ emoji.id ? data[:id] = emoji.id : data[:name] = emoji.name
114
+ when Integer, String
115
+ emoji.to_i.zero? ? data[:name] = emoji : data[:id] = emoji
116
+ else
117
+ raise TypeError, "Invalid emoji type: #{emoji.class}" unless emoji.nil?
118
+ end
119
+
120
+ prefix ? data.transform_keys!({ id: :emoji_id, name: :emoji_name }) : data
121
+ end
100
122
  end
101
123
  end