discordrb 3.4.0 → 3.5.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +44 -18
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
  5. data/.github/workflows/codeql.yml +65 -0
  6. data/.markdownlint.json +4 -0
  7. data/.rubocop.yml +8 -2
  8. data/CHANGELOG.md +419 -222
  9. data/LICENSE.txt +1 -1
  10. data/README.md +37 -25
  11. data/discordrb-webhooks.gemspec +4 -1
  12. data/discordrb.gemspec +9 -6
  13. data/lib/discordrb/api/application.rb +202 -0
  14. data/lib/discordrb/api/channel.rb +182 -11
  15. data/lib/discordrb/api/interaction.rb +54 -0
  16. data/lib/discordrb/api/invite.rb +2 -2
  17. data/lib/discordrb/api/server.rb +42 -19
  18. data/lib/discordrb/api/user.rb +9 -3
  19. data/lib/discordrb/api/webhook.rb +57 -0
  20. data/lib/discordrb/api.rb +19 -5
  21. data/lib/discordrb/bot.rb +328 -33
  22. data/lib/discordrb/cache.rb +27 -22
  23. data/lib/discordrb/commands/command_bot.rb +14 -7
  24. data/lib/discordrb/commands/container.rb +1 -1
  25. data/lib/discordrb/commands/parser.rb +2 -2
  26. data/lib/discordrb/commands/rate_limiter.rb +1 -1
  27. data/lib/discordrb/container.rb +132 -3
  28. data/lib/discordrb/data/activity.rb +8 -1
  29. data/lib/discordrb/data/attachment.rb +15 -0
  30. data/lib/discordrb/data/audit_logs.rb +3 -3
  31. data/lib/discordrb/data/channel.rb +167 -23
  32. data/lib/discordrb/data/component.rb +229 -0
  33. data/lib/discordrb/data/integration.rb +42 -3
  34. data/lib/discordrb/data/interaction.rb +800 -0
  35. data/lib/discordrb/data/invite.rb +2 -2
  36. data/lib/discordrb/data/member.rb +108 -33
  37. data/lib/discordrb/data/message.rb +100 -20
  38. data/lib/discordrb/data/overwrite.rb +13 -7
  39. data/lib/discordrb/data/role.rb +58 -1
  40. data/lib/discordrb/data/server.rb +82 -80
  41. data/lib/discordrb/data/user.rb +69 -9
  42. data/lib/discordrb/data/webhook.rb +97 -4
  43. data/lib/discordrb/data.rb +3 -0
  44. data/lib/discordrb/errors.rb +44 -3
  45. data/lib/discordrb/events/channels.rb +1 -1
  46. data/lib/discordrb/events/interactions.rb +482 -0
  47. data/lib/discordrb/events/message.rb +9 -6
  48. data/lib/discordrb/events/presence.rb +21 -14
  49. data/lib/discordrb/events/reactions.rb +0 -1
  50. data/lib/discordrb/events/threads.rb +96 -0
  51. data/lib/discordrb/gateway.rb +30 -17
  52. data/lib/discordrb/permissions.rb +59 -34
  53. data/lib/discordrb/version.rb +1 -1
  54. data/lib/discordrb/voice/encoder.rb +13 -4
  55. data/lib/discordrb/voice/network.rb +18 -7
  56. data/lib/discordrb/voice/sodium.rb +3 -1
  57. data/lib/discordrb/voice/voice_bot.rb +3 -3
  58. data/lib/discordrb/webhooks.rb +2 -0
  59. data/lib/discordrb.rb +37 -4
  60. metadata +53 -19
  61. data/.codeclimate.yml +0 -16
  62. data/.travis.yml +0 -32
  63. data/bin/travis_build_docs.sh +0 -17
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'discordrb/webhooks/view'
4
+ require 'time'
5
+
3
6
  module Discordrb
4
7
  # A Discord channel, including data like the topic
5
8
  class Channel
@@ -13,23 +16,28 @@ module Discordrb
13
16
  group: 3,
14
17
  category: 4,
15
18
  news: 5,
16
- store: 6
19
+ store: 6,
20
+ news_thread: 10,
21
+ public_thread: 11,
22
+ private_thread: 12,
23
+ stage_voice: 13,
24
+ directory: 14,
25
+ forum: 15
17
26
  }.freeze
18
27
 
19
28
  # @return [String] this channel's name.
20
29
  attr_reader :name
21
30
 
22
- # @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
23
- attr_reader :server
24
-
25
- # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category
31
+ # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category. If this channel is a
32
+ # thread, this is the text channel it is a child to.
26
33
  attr_reader :parent_id
27
34
 
28
35
  # @return [Integer] the type of this channel
29
36
  # @see TYPES
30
37
  attr_reader :type
31
38
 
32
- # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel.
39
+ # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel. If this
40
+ # channel is a thread, this is the member that started the thread.
33
41
  attr_reader :owner_id
34
42
 
35
43
  # @return [Array<Recipient>, nil] the array of recipients of the private messages, or nil if this is not a Private channel
@@ -56,6 +64,35 @@ module Discordrb
56
64
  attr_reader :rate_limit_per_user
57
65
  alias_method :slowmode_rate, :rate_limit_per_user
58
66
 
67
+ # @return [Integer, nil] An approximate count of messages sent in a thread. Stops counting at 50.
68
+ attr_reader :message_count
69
+
70
+ # @return [Integer, nil] An approximate count of members in a thread. Stops counting at 50.
71
+ attr_reader :member_count
72
+
73
+ # @return [true, false, nil] Whether or not this thread is archived.
74
+ attr_reader :archived
75
+
76
+ # @return [Integer, nil] How long after the last message before a thread is automatically archived.
77
+ attr_reader :auto_archive_duration
78
+
79
+ # @return [Time, nil] The timestamp of when this threads status last changed.
80
+ attr_reader :archive_timestamp
81
+
82
+ # @return [true, false, nil] Whether this thread is locked or not.
83
+ attr_reader :locked
84
+ alias_method :locked?, :locked
85
+
86
+ # @return [Time, nil] When the current user joined this thread.
87
+ attr_reader :join_timestamp
88
+
89
+ # @return [Integer, nil] Member flags for this thread, used for notifications.
90
+ attr_reader :member_flags
91
+
92
+ # @return [true, false] For private threads, determines whether non-moderators can add other non-moderators to
93
+ # a thread.
94
+ attr_reader :invitable
95
+
59
96
  # @return [true, false] whether or not this channel is a PM or group channel.
60
97
  def private?
61
98
  pm? || group?
@@ -99,15 +136,44 @@ module Discordrb
99
136
  end
100
137
  else
101
138
  @name = data['name']
102
- @server = server || bot.server(data['guild_id'].to_i)
139
+ @server_id = server&.id || data['guild_id'].to_i
140
+ @server = server
103
141
  end
104
142
 
105
143
  @nsfw = data['nsfw'] || false
106
144
  @rate_limit_per_user = data['rate_limit_per_user'] || 0
145
+ @message_count = data['message_count']
146
+ @member_count = data['member_count']
147
+
148
+ if (metadata = data['thread_metadata'])
149
+ @archived = metadata['archived']
150
+ @auto_archive_duration = metadata['auto_archive_duration']
151
+ @archive_timestamp = Time.iso8601(metadata['archive_timestamp'])
152
+ @locked = metadata['locked']
153
+ @invitable = metadata['invitable']
154
+ end
155
+
156
+ if (member = data['member'])
157
+ @member_join = Time.iso8601(member['join_timestamp'])
158
+ @member_flags = member['flags']
159
+ end
107
160
 
108
161
  process_permission_overwrites(data['permission_overwrites'])
109
162
  end
110
163
 
164
+ # @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
165
+ # @raise [Discordrb::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
166
+ # authorized with the `bot` scope.
167
+ def server
168
+ return @server if @server
169
+ return nil if private?
170
+
171
+ @server = @bot.server(@server_id)
172
+ raise Discordrb::Errors::NoPermission, 'The bot does not have access to this server' unless @server
173
+
174
+ @server
175
+ end
176
+
111
177
  # @return [true, false] whether or not this channel is a text channel
112
178
  def text?
113
179
  @type.zero?
@@ -143,6 +209,26 @@ module Discordrb
143
209
  @type == 6
144
210
  end
145
211
 
212
+ # @return [true, false] whether or not this channel is a news thread.
213
+ def news_thread?
214
+ @type == 10
215
+ end
216
+
217
+ # @return [true, false] whether or not this channel is a public thread.
218
+ def public_thread?
219
+ @type == 11
220
+ end
221
+
222
+ # @return [true, false] whether or not this channel is a private thread.
223
+ def private_thread?
224
+ @type == 12
225
+ end
226
+
227
+ # @return [true, false] whether or not this channel is a thread.
228
+ def thread?
229
+ news_thread? || public_thread? || private_thread?
230
+ end
231
+
146
232
  # @return [Channel, nil] the category channel, if this channel is in a category
147
233
  def category
148
234
  @bot.channel(@parent_id) if @parent_id
@@ -199,7 +285,7 @@ module Discordrb
199
285
  ids = if parent
200
286
  parent.children
201
287
  else
202
- @server.channels.reject(&:parent_id).select { |c| c.type == @type }
288
+ server.channels.reject(&:parent_id).select { |c| c.type == @type }
203
289
  end.sort_by(&:position).map(&:id)
204
290
 
205
291
  # Move our channel ID after the target ID by deleting it,
@@ -225,7 +311,7 @@ module Discordrb
225
311
  move_argument << hash
226
312
  end
227
313
 
228
- API::Server.update_channel_positions(@bot.token, @server.id, move_argument)
314
+ API::Server.update_channel_positions(@bot.token, @server_id, move_argument)
229
315
  end
230
316
 
231
317
  # Sets whether this channel is NSFW
@@ -262,9 +348,9 @@ module Discordrb
262
348
 
263
349
  # Sets the amount of time (in seconds) users have to wait in between sending messages.
264
350
  # @param rate [Integer]
265
- # @raise [ArgumentError] if value isn't between 0 and 120
351
+ # @raise [ArgumentError] if value isn't between 0 and 21600
266
352
  def rate_limit_per_user=(rate)
267
- raise ArgumentError, 'rate_limit_per_user must be between 0 and 120' unless rate.between?(0, 120)
353
+ raise ArgumentError, 'rate_limit_per_user must be between 0 and 21600' unless rate.between?(0, 21_600)
268
354
 
269
355
  update_channel_data(rate_limit_per_user: rate)
270
356
  end
@@ -341,9 +427,10 @@ module Discordrb
341
427
  # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
342
428
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
343
429
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
430
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
344
431
  # @return [Message] the message that was sent.
345
- def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
346
- @bot.send_message(@id, content, tts, embed, attachments, allowed_mentions, message_reference)
432
+ def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
433
+ @bot.send_message(@id, content, tts, embed, attachments, allowed_mentions, message_reference, components)
347
434
  end
348
435
 
349
436
  alias_method :send, :send_message
@@ -356,8 +443,9 @@ module Discordrb
356
443
  # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
357
444
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
358
445
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
359
- def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
360
- @bot.send_temporary_message(@id, content, timeout, tts, embed, attachments, allowed_mentions, message_reference)
446
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
447
+ def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
448
+ @bot.send_temporary_message(@id, content, timeout, tts, embed, attachments, allowed_mentions, message_reference, components)
361
449
  end
362
450
 
363
451
  # Convenience method to send a message with an embed.
@@ -372,13 +460,17 @@ module Discordrb
372
460
  # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
373
461
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
374
462
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
463
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
375
464
  # @yield [embed] Yields the embed to allow for easy building inside a block.
376
465
  # @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
377
466
  # @return [Message] The resulting message.
378
- def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil)
467
+ def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil)
379
468
  embed ||= Discordrb::Webhooks::Embed.new
380
- yield(embed) if block_given?
381
- send_message(message, tts, embed, attachments, allowed_mentions, message_reference)
469
+ view = Discordrb::Webhooks::View.new
470
+
471
+ yield(embed, view) if block_given?
472
+
473
+ send_message(message, tts, embed, attachments, allowed_mentions, message_reference, components || view.to_a)
382
474
  end
383
475
 
384
476
  # Sends multiple messages to a channel
@@ -518,9 +610,9 @@ module Discordrb
518
610
  # @return [Array<Member>] the users in this channel
519
611
  def users
520
612
  if text?
521
- @server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
613
+ server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
522
614
  elsif voice?
523
- @server.voice_states.map { |id, voice_state| @server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }.compact
615
+ server.voice_states.filter_map { |id, voice_state| server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }
524
616
  end
525
617
  end
526
618
 
@@ -554,9 +646,11 @@ module Discordrb
554
646
  # @param message_id [Integer] The ID of the message to retrieve.
555
647
  # @return [Message, nil] the retrieved message, or `nil` if it couldn't be found.
556
648
  def load_message(message_id)
649
+ raise ArgumentError, 'message_id cannot be nil' if message_id.nil?
650
+
557
651
  response = API::Channel.message(@bot.token, @id, message_id)
558
652
  Message.new(JSON.parse(response), @bot)
559
- rescue RestClient::ResourceNotFound
653
+ rescue Discordrb::Errors::UnknownMessage
560
654
  nil
561
655
  end
562
656
 
@@ -742,9 +836,59 @@ module Discordrb
742
836
  invites.map { |invite_data| Invite.new(invite_data, @bot) }
743
837
  end
744
838
 
839
+ # Start a thread.
840
+ # @param name [String] The name of the thread.
841
+ # @param auto_archive_duration [60, 1440, 4320, 10080] How long before a thread is automatically
842
+ # archived.
843
+ # @param message [Message, Integer, String] The message to reference when starting this thread.
844
+ # @param type [Symbol, Integer] The type of thread to create. Can be a key from {TYPES} or the value.
845
+ # @return [Channel]
846
+ def start_thread(name, auto_archive_duration, message: nil, type: 11)
847
+ message_id = message&.id || message
848
+ type = TYPES[type] || type
849
+
850
+ data = if message
851
+ API::Channel.start_thread_with_message(@bot.token, @id, message_id, name, auto_archive_duration)
852
+ else
853
+ API::Channel.start_thread_without_message(@bot.token, @id, name, auto_archive_duration, type)
854
+ end
855
+
856
+ Channel.new(JSON.parse(data), @bot, @server)
857
+ end
858
+
859
+ # @!group Threads
860
+
861
+ # Join this thread.
862
+ def join_thread
863
+ @bot.join_thread(@id)
864
+ end
865
+
866
+ # Leave this thread
867
+ def leave_thread
868
+ @bot.leave_thread(@id)
869
+ end
870
+
871
+ # Members in the thread.
872
+ def members
873
+ @bot.thread_members[@id].collect { |id| @server_id ? @bot.member(@server_id, id) : @bot.user(id) }
874
+ end
875
+
876
+ # Add a member to the thread
877
+ # @param member [Member, Integer, String] The member, or ID of the member, to add to this thread.
878
+ def add_member(member)
879
+ @bot.add_thread_member(@id, member)
880
+ end
881
+
882
+ # @param member [Member, Integer, String] The member, or ID of the member, to remove from a thread.
883
+ def remove_member(member)
884
+ @bot.remove_thread_member(@id, member)
885
+ end
886
+
887
+ # @!endgroup
888
+
745
889
  # The default `inspect` method is overwritten to give more useful output.
746
890
  def inspect
747
- "<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server}>"
891
+ "<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server || @server_id}>"
748
892
  end
749
893
 
750
894
  # Adds a recipient to a group channel.
@@ -790,7 +934,7 @@ module Discordrb
790
934
 
791
935
  # @return [String] a URL that a user can use to navigate to this channel in the client
792
936
  def link
793
- "https://discord.com/channels/#{@server&.id || '@me'}/#{@channel.id}"
937
+ "https://discord.com/channels/#{@server_id || '@me'}/#{@channel.id}"
794
938
  end
795
939
 
796
940
  alias_method :jump_link, :link
@@ -0,0 +1,229 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Components are interactable interfaces that can be attached to messages.
5
+ module Components
6
+ # @deprecated This alias will be removed in future releases.
7
+ class View < Webhooks::View
8
+ end
9
+
10
+ # @!visibility private
11
+ def self.from_data(data, bot)
12
+ case data['type']
13
+ when Webhooks::View::COMPONENT_TYPES[:action_row]
14
+ ActionRow.new(data, bot)
15
+ when Webhooks::View::COMPONENT_TYPES[:button]
16
+ Button.new(data, bot)
17
+ when Webhooks::View::COMPONENT_TYPES[:string_select]
18
+ SelectMenu.new(data, bot)
19
+ when Webhooks::Modal::COMPONENT_TYPES[:text_input]
20
+ TextInput.new(data, bot)
21
+ end
22
+ end
23
+
24
+ # Represents a row of components
25
+ class ActionRow
26
+ include Enumerable
27
+
28
+ # @return [Array<Button>]
29
+ attr_reader :components
30
+
31
+ # @!visibility private
32
+ def initialize(data, bot)
33
+ @bot = bot
34
+ @components = data['components'].map { |component_data| Components.from_data(component_data, @bot) }
35
+ end
36
+
37
+ # Iterate over each component in the row.
38
+ def each(&block)
39
+ @components.each(&block)
40
+ end
41
+
42
+ # Get all buttons in this row
43
+ # @return [Array<Button>]
44
+ def buttons
45
+ select { |component| component.is_a? Button }
46
+ end
47
+
48
+ # Get all buttons in this row
49
+ # @return [Array<Button>]
50
+ def text_inputs
51
+ select { |component| component.is_a? TextInput }
52
+ end
53
+
54
+ # @!visibility private
55
+ def to_a
56
+ @components
57
+ end
58
+ end
59
+
60
+ # An interactable button component.
61
+ class Button
62
+ # @return [String]
63
+ attr_reader :label
64
+
65
+ # @return [Integer]
66
+ attr_reader :style
67
+
68
+ # @return [String]
69
+ attr_reader :custom_id
70
+
71
+ # @return [true, false]
72
+ attr_reader :disabled
73
+
74
+ # @return [String, nil]
75
+ attr_reader :url
76
+
77
+ # @return [Emoji, nil]
78
+ attr_reader :emoji
79
+
80
+ # @!visibility private
81
+ def initialize(data, bot)
82
+ @bot = bot
83
+
84
+ @label = data['label']
85
+ @style = data['style']
86
+ @custom_id = data['custom_id']
87
+ @disabled = data['disabled']
88
+ @url = data['url']
89
+ @emoji = Emoji.new(data['emoji'], @bot) if data['emoji']
90
+ end
91
+
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]
102
+ Webhooks::View::BUTTON_STYLES.each do |name, value|
103
+ define_method("#{name}?") do
104
+ @style == value
105
+ end
106
+ end
107
+
108
+ # Await a button click
109
+ def await_click(key, **attributes, &block)
110
+ @bot.add_await(key, Discordrb::Events::ButtonEvent, { custom_id: @custom_id }.merge(attributes), &block)
111
+ end
112
+
113
+ # Await a button click, blocking.
114
+ def await_click!(**attributes, &block)
115
+ @bot.add_await!(Discordrb::Events::ButtonEvent, { custom_id: @custom_id }.merge(attributes), &block)
116
+ end
117
+ end
118
+
119
+ # An interactable select menu component.
120
+ class SelectMenu
121
+ # A select menu option.
122
+ class Option
123
+ # @return [String]
124
+ attr_reader :label
125
+
126
+ # @return [String]
127
+ attr_reader :value
128
+
129
+ # @return [String, nil]
130
+ attr_reader :description
131
+
132
+ # @return [Emoji, nil]
133
+ attr_reader :emoji
134
+
135
+ # @!visibility hidden
136
+ def initialize(data)
137
+ @label = data['label']
138
+ @value = data['value']
139
+ @description = data['description']
140
+ @emoji = Emoji.new(data['emoji'], @bot) if data['emoji']
141
+ end
142
+ end
143
+
144
+ # @return [String]
145
+ attr_reader :custom_id
146
+
147
+ # @return [Integer, nil]
148
+ attr_reader :max_values
149
+
150
+ # @return [Integer, nil]
151
+ attr_reader :min_values
152
+
153
+ # @return [String, nil]
154
+ attr_reader :placeholder
155
+
156
+ # @return [Array<Option>]
157
+ attr_reader :options
158
+
159
+ # @!visibility private
160
+ def initialize(data, bot)
161
+ @bot = bot
162
+
163
+ @max_values = data['max_values']
164
+ @min_values = data['min_values']
165
+ @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) }
169
+ end
170
+ end
171
+
172
+ # Text input component for use in modals. Can be either a line (`short`), or a multi line (`paragraph`) block.
173
+ class TextInput
174
+ # Single line text input
175
+ SHORT = 1
176
+ # Multi-line text input
177
+ PARAGRAPH = 2
178
+
179
+ # @return [String]
180
+ attr_reader :custom_id
181
+
182
+ # @return [Symbol]
183
+ attr_reader :style
184
+
185
+ # @return [String]
186
+ attr_reader :label
187
+
188
+ # @return [Integer, nil]
189
+ attr_reader :min_length
190
+
191
+ # @return [Integer, nil]
192
+ attr_reader :max_length
193
+
194
+ # @return [true, false]
195
+ attr_reader :required
196
+
197
+ # @return [String, nil]
198
+ attr_reader :value
199
+
200
+ # @return [String, nil]
201
+ attr_reader :placeholder
202
+
203
+ # @!visibility private
204
+ def initialize(data, bot)
205
+ @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']
211
+ @value = data['value']
212
+ @placeholder = data['placeholder']
213
+ @custom_id = data['custom_id']
214
+ end
215
+
216
+ def short?
217
+ @style == :short
218
+ end
219
+
220
+ def paragraph?
221
+ @style == :paragraph
222
+ end
223
+
224
+ def required?
225
+ @required
226
+ end
227
+ end
228
+ end
229
+ end
@@ -15,6 +15,36 @@ module Discordrb
15
15
  end
16
16
  end
17
17
 
18
+ # Bot/OAuth2 application for discord integrations
19
+ class IntegrationApplication
20
+ # @return [Integer] the ID of the application.
21
+ attr_reader :id
22
+
23
+ # @return [String] the name of the application.
24
+ attr_reader :name
25
+
26
+ # @return [String, nil] the icon hash of the application.
27
+ attr_reader :icon
28
+
29
+ # @return [String] the description of the application.
30
+ attr_reader :description
31
+
32
+ # @return [String] the summary of the application.
33
+ attr_reader :summary
34
+
35
+ # @return [User, nil] the bot associated with this application.
36
+ attr_reader :bot
37
+
38
+ def initialize(data, bot)
39
+ @id = data['id'].to_i
40
+ @name = data['name']
41
+ @icon = data['icon']
42
+ @description = data['description']
43
+ @summary = data['summary']
44
+ @bot = Discordrb::User.new(data['user'], bot) if data['user']
45
+ end
46
+ end
47
+
18
48
  # Server integration
19
49
  class Integration
20
50
  include IDObject
@@ -28,8 +58,8 @@ module Discordrb
28
58
  # @return [User] the user the integration is linked to
29
59
  attr_reader :user
30
60
 
31
- # @return [Role, nil] the role that this integration uses for "subscribers"
32
- attr_reader :role
61
+ # @return [Integer, nil] the role that this integration uses for "subscribers"
62
+ attr_reader :role_id
33
63
 
34
64
  # @return [true, false] whether emoticons are enabled
35
65
  attr_reader :emoticon
@@ -57,6 +87,12 @@ module Discordrb
57
87
  # @return [Integer] the grace period before subscribers expire (in days)
58
88
  attr_reader :expire_grace_period
59
89
 
90
+ # @return [Integer, nil] how many subscribers this integration has.
91
+ attr_reader :subscriber_count
92
+
93
+ # @return [true, false] has this integration been revoked.
94
+ attr_reader :revoked
95
+
60
96
  def initialize(data, bot, server)
61
97
  @bot = bot
62
98
 
@@ -71,8 +107,11 @@ module Discordrb
71
107
  @expire_behaviour = %i[remove kick][data['expire_behavior']]
72
108
  @expire_grace_period = data['expire_grace_period']
73
109
  @user = @bot.ensure_user(data['user'])
74
- @role = server.role(data['role_id']) || nil
110
+ @role_id = data['role_id']&.to_i
75
111
  @emoticon = data['enable_emoticons']
112
+ @subscriber_count = data['subscriber_count']&.to_i
113
+ @revoked = data['revoked']
114
+ @application = IntegrationApplication.new(data['application'], bot) if data['application']
76
115
  end
77
116
 
78
117
  # The inspect method is overwritten to give more useful output