discordrb 3.3.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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +152 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  5. data/.github/pull_request_template.md +37 -0
  6. data/.github/workflows/codeql.yml +65 -0
  7. data/.markdownlint.json +4 -0
  8. data/.rubocop.yml +39 -36
  9. data/CHANGELOG.md +874 -552
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +80 -86
  13. data/Rakefile +2 -0
  14. data/bin/console +1 -0
  15. data/discordrb-webhooks.gemspec +9 -6
  16. data/discordrb.gemspec +21 -18
  17. data/lib/discordrb/allowed_mentions.rb +36 -0
  18. data/lib/discordrb/api/application.rb +202 -0
  19. data/lib/discordrb/api/channel.rb +236 -47
  20. data/lib/discordrb/api/interaction.rb +54 -0
  21. data/lib/discordrb/api/invite.rb +5 -5
  22. data/lib/discordrb/api/server.rb +94 -66
  23. data/lib/discordrb/api/user.rb +17 -11
  24. data/lib/discordrb/api/webhook.rb +63 -6
  25. data/lib/discordrb/api.rb +55 -16
  26. data/lib/discordrb/await.rb +0 -1
  27. data/lib/discordrb/bot.rb +480 -93
  28. data/lib/discordrb/cache.rb +31 -24
  29. data/lib/discordrb/colour_rgb.rb +43 -0
  30. data/lib/discordrb/commands/command_bot.rb +35 -12
  31. data/lib/discordrb/commands/container.rb +21 -24
  32. data/lib/discordrb/commands/parser.rb +20 -20
  33. data/lib/discordrb/commands/rate_limiter.rb +4 -3
  34. data/lib/discordrb/container.rb +209 -20
  35. data/lib/discordrb/data/activity.rb +271 -0
  36. data/lib/discordrb/data/application.rb +50 -0
  37. data/lib/discordrb/data/attachment.rb +71 -0
  38. data/lib/discordrb/data/audit_logs.rb +345 -0
  39. data/lib/discordrb/data/channel.rb +993 -0
  40. data/lib/discordrb/data/component.rb +229 -0
  41. data/lib/discordrb/data/embed.rb +251 -0
  42. data/lib/discordrb/data/emoji.rb +82 -0
  43. data/lib/discordrb/data/integration.rb +122 -0
  44. data/lib/discordrb/data/interaction.rb +800 -0
  45. data/lib/discordrb/data/invite.rb +137 -0
  46. data/lib/discordrb/data/member.rb +372 -0
  47. data/lib/discordrb/data/message.rb +414 -0
  48. data/lib/discordrb/data/overwrite.rb +108 -0
  49. data/lib/discordrb/data/profile.rb +91 -0
  50. data/lib/discordrb/data/reaction.rb +33 -0
  51. data/lib/discordrb/data/recipient.rb +34 -0
  52. data/lib/discordrb/data/role.rb +248 -0
  53. data/lib/discordrb/data/server.rb +1004 -0
  54. data/lib/discordrb/data/user.rb +264 -0
  55. data/lib/discordrb/data/voice_region.rb +45 -0
  56. data/lib/discordrb/data/voice_state.rb +41 -0
  57. data/lib/discordrb/data/webhook.rb +238 -0
  58. data/lib/discordrb/data.rb +28 -4180
  59. data/lib/discordrb/errors.rb +46 -4
  60. data/lib/discordrb/events/bans.rb +7 -5
  61. data/lib/discordrb/events/channels.rb +3 -1
  62. data/lib/discordrb/events/guilds.rb +16 -9
  63. data/lib/discordrb/events/interactions.rb +482 -0
  64. data/lib/discordrb/events/invites.rb +125 -0
  65. data/lib/discordrb/events/members.rb +6 -2
  66. data/lib/discordrb/events/message.rb +72 -27
  67. data/lib/discordrb/events/presence.rb +35 -18
  68. data/lib/discordrb/events/raw.rb +1 -3
  69. data/lib/discordrb/events/reactions.rb +49 -4
  70. data/lib/discordrb/events/threads.rb +96 -0
  71. data/lib/discordrb/events/typing.rb +6 -4
  72. data/lib/discordrb/events/voice_server_update.rb +47 -0
  73. data/lib/discordrb/events/voice_state_update.rb +15 -10
  74. data/lib/discordrb/events/webhooks.rb +9 -6
  75. data/lib/discordrb/gateway.rb +99 -71
  76. data/lib/discordrb/id_object.rb +39 -0
  77. data/lib/discordrb/light/integrations.rb +1 -1
  78. data/lib/discordrb/light/light_bot.rb +1 -1
  79. data/lib/discordrb/logger.rb +4 -4
  80. data/lib/discordrb/paginator.rb +57 -0
  81. data/lib/discordrb/permissions.rb +159 -39
  82. data/lib/discordrb/version.rb +1 -1
  83. data/lib/discordrb/voice/encoder.rb +16 -7
  84. data/lib/discordrb/voice/network.rb +99 -47
  85. data/lib/discordrb/voice/sodium.rb +98 -0
  86. data/lib/discordrb/voice/voice_bot.rb +33 -25
  87. data/lib/discordrb/webhooks.rb +2 -0
  88. data/lib/discordrb.rb +107 -1
  89. metadata +126 -54
  90. data/.codeclimate.yml +0 -16
  91. data/.travis.yml +0 -33
  92. data/bin/travis_build_docs.sh +0 -17
  93. /data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
@@ -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
@@ -0,0 +1,251 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # An Embed object that is contained in a message
5
+ # A freshly generated embed object will not appear in a message object
6
+ # unless grabbed from its ID in a channel.
7
+ class Embed
8
+ # @return [Message] the message this embed object is contained in.
9
+ attr_reader :message
10
+
11
+ # @return [String] the URL this embed object is based on.
12
+ attr_reader :url
13
+
14
+ # @return [String, nil] the title of the embed object. `nil` if there is not a title
15
+ attr_reader :title
16
+
17
+ # @return [String, nil] the description of the embed object. `nil` if there is not a description
18
+ attr_reader :description
19
+
20
+ # @return [Symbol] the type of the embed object. Possible types are:
21
+ #
22
+ # * `:link`
23
+ # * `:video`
24
+ # * `:image`
25
+ attr_reader :type
26
+
27
+ # @return [Time, nil] the timestamp of the embed object. `nil` if there is not a timestamp
28
+ attr_reader :timestamp
29
+
30
+ # @return [String, nil] the color of the embed object. `nil` if there is not a color
31
+ attr_reader :color
32
+ alias_method :colour, :color
33
+
34
+ # @return [EmbedFooter, nil] the footer of the embed object. `nil` if there is not a footer
35
+ attr_reader :footer
36
+
37
+ # @return [EmbedProvider, nil] the provider of the embed object. `nil` if there is not a provider
38
+ attr_reader :provider
39
+
40
+ # @return [EmbedImage, nil] the image of the embed object. `nil` if there is not an image
41
+ attr_reader :image
42
+
43
+ # @return [EmbedThumbnail, nil] the thumbnail of the embed object. `nil` if there is not a thumbnail
44
+ attr_reader :thumbnail
45
+
46
+ # @return [EmbedVideo, nil] the video of the embed object. `nil` if there is not a video
47
+ attr_reader :video
48
+
49
+ # @return [EmbedAuthor, nil] the author of the embed object. `nil` if there is not an author
50
+ attr_reader :author
51
+
52
+ # @return [Array<EmbedField>, nil] the fields of the embed object. `nil` if there are no fields
53
+ attr_reader :fields
54
+
55
+ # @!visibility private
56
+ def initialize(data, message)
57
+ @message = message
58
+
59
+ @url = data['url']
60
+ @title = data['title']
61
+ @type = data['type'].to_sym
62
+ @description = data['description']
63
+ @timestamp = data['timestamp'].nil? ? nil : Time.parse(data['timestamp'])
64
+ @color = data['color']
65
+ @footer = data['footer'].nil? ? nil : EmbedFooter.new(data['footer'], self)
66
+ @image = data['image'].nil? ? nil : EmbedImage.new(data['image'], self)
67
+ @video = data['video'].nil? ? nil : EmbedVideo.new(data['video'], self)
68
+ @provider = data['provider'].nil? ? nil : EmbedProvider.new(data['provider'], self)
69
+ @thumbnail = data['thumbnail'].nil? ? nil : EmbedThumbnail.new(data['thumbnail'], self)
70
+ @author = data['author'].nil? ? nil : EmbedAuthor.new(data['author'], self)
71
+ @fields = data['fields'].nil? ? nil : data['fields'].map { |field| EmbedField.new(field, self) }
72
+ end
73
+ end
74
+
75
+ # An Embed footer for the embed object.
76
+ class EmbedFooter
77
+ # @return [Embed] the embed object this is based on.
78
+ attr_reader :embed
79
+
80
+ # @return [String] the footer text.
81
+ attr_reader :text
82
+
83
+ # @return [String] the URL of the footer icon.
84
+ attr_reader :icon_url
85
+
86
+ # @return [String] the proxied URL of the footer icon.
87
+ attr_reader :proxy_icon_url
88
+
89
+ # @!visibility private
90
+ def initialize(data, embed)
91
+ @embed = embed
92
+
93
+ @text = data['text']
94
+ @icon_url = data['icon_url']
95
+ @proxy_icon_url = data['proxy_icon_url']
96
+ end
97
+ end
98
+
99
+ # An Embed image for the embed object.
100
+ class EmbedImage
101
+ # @return [Embed] the embed object this is based on.
102
+ attr_reader :embed
103
+
104
+ # @return [String] the source URL of the image.
105
+ attr_reader :url
106
+
107
+ # @return [String] the proxy URL of the image.
108
+ attr_reader :proxy_url
109
+
110
+ # @return [Integer] the width of the image, in pixels.
111
+ attr_reader :width
112
+
113
+ # @return [Integer] the height of the image, in pixels.
114
+ attr_reader :height
115
+
116
+ # @!visibility private
117
+ def initialize(data, embed)
118
+ @embed = embed
119
+
120
+ @url = data['url']
121
+ @proxy_url = data['proxy_url']
122
+ @width = data['width']
123
+ @height = data['height']
124
+ end
125
+ end
126
+
127
+ # An Embed video for the embed object
128
+ class EmbedVideo
129
+ # @return [Embed] the embed object this is based on.
130
+ attr_reader :embed
131
+
132
+ # @return [String] the source URL of the video.
133
+ attr_reader :url
134
+
135
+ # @return [Integer] the width of the video, in pixels.
136
+ attr_reader :width
137
+
138
+ # @return [Integer] the height of the video, in pixels.
139
+ attr_reader :height
140
+
141
+ # @!visibility private
142
+ def initialize(data, embed)
143
+ @embed = embed
144
+
145
+ @url = data['url']
146
+ @width = data['width']
147
+ @height = data['height']
148
+ end
149
+ end
150
+
151
+ # An Embed thumbnail for the embed object
152
+ class EmbedThumbnail
153
+ # @return [Embed] the embed object this is based on.
154
+ attr_reader :embed
155
+
156
+ # @return [String] the CDN URL this thumbnail can be downloaded at.
157
+ attr_reader :url
158
+
159
+ # @return [String] the thumbnail's proxy URL - I'm not sure what exactly this does, but I think it has something to
160
+ # do with CDNs.
161
+ attr_reader :proxy_url
162
+
163
+ # @return [Integer] the width of this thumbnail file, in pixels.
164
+ attr_reader :width
165
+
166
+ # @return [Integer] the height of this thumbnail file, in pixels.
167
+ attr_reader :height
168
+
169
+ # @!visibility private
170
+ def initialize(data, embed)
171
+ @embed = embed
172
+
173
+ @url = data['url']
174
+ @proxy_url = data['proxy_url']
175
+ @width = data['width']
176
+ @height = data['height']
177
+ end
178
+ end
179
+
180
+ # An Embed provider for the embed object
181
+ class EmbedProvider
182
+ # @return [Embed] the embed object this is based on.
183
+ attr_reader :embed
184
+
185
+ # @return [String] the provider's name.
186
+ attr_reader :name
187
+
188
+ # @return [String, nil] the URL of the provider, or `nil` if there is no URL.
189
+ attr_reader :url
190
+
191
+ # @!visibility private
192
+ def initialize(data, embed)
193
+ @embed = embed
194
+
195
+ @name = data['name']
196
+ @url = data['url']
197
+ end
198
+ end
199
+
200
+ # An Embed author for the embed object
201
+ class EmbedAuthor
202
+ # @return [Embed] the embed object this is based on.
203
+ attr_reader :embed
204
+
205
+ # @return [String] the author's name.
206
+ attr_reader :name
207
+
208
+ # @return [String, nil] the URL of the author's website, or `nil` if there is no URL.
209
+ attr_reader :url
210
+
211
+ # @return [String, nil] the icon of the author, or `nil` if there is no icon.
212
+ attr_reader :icon_url
213
+
214
+ # @return [String, nil] the Discord proxy URL, or `nil` if there is no `icon_url`.
215
+ attr_reader :proxy_icon_url
216
+
217
+ # @!visibility private
218
+ def initialize(data, embed)
219
+ @embed = embed
220
+
221
+ @name = data['name']
222
+ @url = data['url']
223
+ @icon_url = data['icon_url']
224
+ @proxy_icon_url = data['proxy_icon_url']
225
+ end
226
+ end
227
+
228
+ # An Embed field for the embed object
229
+ class EmbedField
230
+ # @return [Embed] the embed object this is based on.
231
+ attr_reader :embed
232
+
233
+ # @return [String] the field's name.
234
+ attr_reader :name
235
+
236
+ # @return [String] the field's value.
237
+ attr_reader :value
238
+
239
+ # @return [true, false] whether this field is inline.
240
+ attr_reader :inline
241
+
242
+ # @!visibility private
243
+ def initialize(data, embed)
244
+ @embed = embed
245
+
246
+ @name = data['name']
247
+ @value = data['value']
248
+ @inline = data['inline']
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Server emoji
5
+ class Emoji
6
+ include IDObject
7
+
8
+ # @return [String] the emoji name
9
+ attr_reader :name
10
+
11
+ # @return [Server, nil] the server of this emoji
12
+ attr_reader :server
13
+
14
+ # @return [Array<Role>, nil] roles this emoji is active for, or nil if the emoji's server is unknown
15
+ attr_reader :roles
16
+
17
+ # @return [true, false] if the emoji is animated
18
+ attr_reader :animated
19
+ alias_method :animated?, :animated
20
+
21
+ # @!visibility private
22
+ def initialize(data, bot, server = nil)
23
+ @bot = bot
24
+ @roles = nil
25
+
26
+ @name = data['name']
27
+ @server = server
28
+ @id = data['id'].nil? ? nil : data['id'].to_i
29
+ @animated = data['animated']
30
+
31
+ process_roles(data['roles']) if server
32
+ end
33
+
34
+ # ID or name based comparison
35
+ def ==(other)
36
+ return false unless other.is_a? Emoji
37
+ return Discordrb.id_compare(@id, other) if @id
38
+
39
+ name == other.name
40
+ end
41
+
42
+ alias_method :eql?, :==
43
+
44
+ # @return [String] the layout to mention it (or have it used) in a message
45
+ def mention
46
+ return name if id.nil?
47
+
48
+ "<#{'a' if animated}:#{name}:#{id}>"
49
+ end
50
+
51
+ alias_method :use, :mention
52
+ alias_method :to_s, :mention
53
+
54
+ # @return [String] the layout to use this emoji in a reaction
55
+ def to_reaction
56
+ return name if id.nil?
57
+
58
+ "#{name}:#{id}"
59
+ end
60
+
61
+ # @return [String] the icon URL of the emoji
62
+ def icon_url
63
+ API.emoji_icon_url(id)
64
+ end
65
+
66
+ # The inspect method is overwritten to give more useful output
67
+ def inspect
68
+ "<Emoji name=#{name} id=#{id} animated=#{animated}>"
69
+ end
70
+
71
+ # @!visibility private
72
+ def process_roles(roles)
73
+ @roles = []
74
+ return unless roles
75
+
76
+ roles.each do |role_id|
77
+ role = server.role(role_id)
78
+ @roles << role
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Integration Account
5
+ class IntegrationAccount
6
+ # @return [String] this account's name.
7
+ attr_reader :name
8
+
9
+ # @return [Integer] this account's ID.
10
+ attr_reader :id
11
+
12
+ def initialize(data)
13
+ @name = data['name']
14
+ @id = data['id'].to_i
15
+ end
16
+ end
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
+
48
+ # Server integration
49
+ class Integration
50
+ include IDObject
51
+
52
+ # @return [String] the integration name
53
+ attr_reader :name
54
+
55
+ # @return [Server] the server the integration is linked to
56
+ attr_reader :server
57
+
58
+ # @return [User] the user the integration is linked to
59
+ attr_reader :user
60
+
61
+ # @return [Integer, nil] the role that this integration uses for "subscribers"
62
+ attr_reader :role_id
63
+
64
+ # @return [true, false] whether emoticons are enabled
65
+ attr_reader :emoticon
66
+ alias_method :emoticon?, :emoticon
67
+
68
+ # @return [String] the integration type (YouTube, Twitch, etc.)
69
+ attr_reader :type
70
+
71
+ # @return [true, false] whether the integration is enabled
72
+ attr_reader :enabled
73
+
74
+ # @return [true, false] whether the integration is syncing
75
+ attr_reader :syncing
76
+
77
+ # @return [IntegrationAccount] the integration account information
78
+ attr_reader :account
79
+
80
+ # @return [Time] the time the integration was synced at
81
+ attr_reader :synced_at
82
+
83
+ # @return [Symbol] the behaviour of expiring subscribers (:remove = Remove User from role; :kick = Kick User from server)
84
+ attr_reader :expire_behaviour
85
+ alias_method :expire_behavior, :expire_behaviour
86
+
87
+ # @return [Integer] the grace period before subscribers expire (in days)
88
+ attr_reader :expire_grace_period
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
+
96
+ def initialize(data, bot, server)
97
+ @bot = bot
98
+
99
+ @name = data['name']
100
+ @server = server
101
+ @id = data['id'].to_i
102
+ @enabled = data['enabled']
103
+ @syncing = data['syncing']
104
+ @type = data['type']
105
+ @account = IntegrationAccount.new(data['account'])
106
+ @synced_at = Time.parse(data['synced_at'])
107
+ @expire_behaviour = %i[remove kick][data['expire_behavior']]
108
+ @expire_grace_period = data['expire_grace_period']
109
+ @user = @bot.ensure_user(data['user'])
110
+ @role_id = data['role_id']&.to_i
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']
115
+ end
116
+
117
+ # The inspect method is overwritten to give more useful output
118
+ def inspect
119
+ "<Integration name=#{@name} id=#{@id} type=#{@type} enabled=#{@enabled}>"
120
+ end
121
+ end
122
+ end