discordrb 3.5.0 → 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 (78) 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/workflows/ci.yml +78 -0
  6. data/.github/workflows/codeql.yml +3 -3
  7. data/.github/workflows/deploy.yml +54 -0
  8. data/.github/workflows/release.yml +45 -0
  9. data/.rubocop.yml +52 -2
  10. data/CHANGELOG.md +95 -0
  11. data/README.md +5 -5
  12. data/discordrb-webhooks.gemspec +1 -1
  13. data/discordrb.gemspec +16 -11
  14. data/lib/discordrb/api/application.rb +84 -8
  15. data/lib/discordrb/api/channel.rb +51 -13
  16. data/lib/discordrb/api/interaction.rb +15 -6
  17. data/lib/discordrb/api/invite.rb +1 -1
  18. data/lib/discordrb/api/server.rb +96 -60
  19. data/lib/discordrb/api/user.rb +12 -2
  20. data/lib/discordrb/api/webhook.rb +20 -5
  21. data/lib/discordrb/api.rb +16 -20
  22. data/lib/discordrb/bot.rb +139 -53
  23. data/lib/discordrb/cache.rb +15 -1
  24. data/lib/discordrb/commands/command_bot.rb +7 -17
  25. data/lib/discordrb/commands/parser.rb +7 -7
  26. data/lib/discordrb/container.rb +46 -0
  27. data/lib/discordrb/data/activity.rb +1 -1
  28. data/lib/discordrb/data/application.rb +1 -0
  29. data/lib/discordrb/data/attachment.rb +23 -3
  30. data/lib/discordrb/data/avatar_decoration.rb +26 -0
  31. data/lib/discordrb/data/call.rb +22 -0
  32. data/lib/discordrb/data/channel.rb +140 -15
  33. data/lib/discordrb/data/collectibles.rb +45 -0
  34. data/lib/discordrb/data/embed.rb +10 -3
  35. data/lib/discordrb/data/emoji.rb +20 -1
  36. data/lib/discordrb/data/integration.rb +3 -0
  37. data/lib/discordrb/data/interaction.rb +164 -27
  38. data/lib/discordrb/data/member.rb +145 -28
  39. data/lib/discordrb/data/message.rb +198 -51
  40. data/lib/discordrb/data/overwrite.rb +2 -0
  41. data/lib/discordrb/data/primary_server.rb +60 -0
  42. data/lib/discordrb/data/profile.rb +2 -7
  43. data/lib/discordrb/data/reaction.rb +2 -1
  44. data/lib/discordrb/data/recipient.rb +1 -1
  45. data/lib/discordrb/data/role.rb +151 -22
  46. data/lib/discordrb/data/server.rb +115 -41
  47. data/lib/discordrb/data/server_preview.rb +68 -0
  48. data/lib/discordrb/data/snapshot.rb +110 -0
  49. data/lib/discordrb/data/user.rb +68 -8
  50. data/lib/discordrb/data/voice_region.rb +1 -0
  51. data/lib/discordrb/data/webhook.rb +2 -5
  52. data/lib/discordrb/data.rb +6 -0
  53. data/lib/discordrb/errors.rb +5 -2
  54. data/lib/discordrb/events/await.rb +1 -1
  55. data/lib/discordrb/events/channels.rb +37 -0
  56. data/lib/discordrb/events/generic.rb +2 -0
  57. data/lib/discordrb/events/guilds.rb +6 -1
  58. data/lib/discordrb/events/interactions.rb +135 -42
  59. data/lib/discordrb/events/invites.rb +2 -0
  60. data/lib/discordrb/events/members.rb +19 -2
  61. data/lib/discordrb/events/message.rb +39 -8
  62. data/lib/discordrb/events/presence.rb +2 -0
  63. data/lib/discordrb/events/raw.rb +1 -0
  64. data/lib/discordrb/events/reactions.rb +2 -0
  65. data/lib/discordrb/events/roles.rb +2 -0
  66. data/lib/discordrb/events/threads.rb +10 -6
  67. data/lib/discordrb/events/typing.rb +1 -0
  68. data/lib/discordrb/events/voice_server_update.rb +1 -0
  69. data/lib/discordrb/events/voice_state_update.rb +1 -0
  70. data/lib/discordrb/events/webhooks.rb +1 -0
  71. data/lib/discordrb/gateway.rb +29 -13
  72. data/lib/discordrb/paginator.rb +3 -3
  73. data/lib/discordrb/permissions.rb +54 -43
  74. data/lib/discordrb/version.rb +1 -1
  75. data/lib/discordrb/websocket.rb +0 -10
  76. data/lib/discordrb.rb +17 -1
  77. metadata +53 -25
  78. data/.circleci/config.yml +0 -152
@@ -16,6 +16,7 @@ module Discordrb
16
16
 
17
17
  # @return [true, false] whether or not this role should be displayed separately from other users
18
18
  attr_reader :hoist
19
+ alias_method :hoist?, :hoist
19
20
 
20
21
  # @return [true, false] whether or not this role is managed by an integration or a bot
21
22
  attr_reader :managed
@@ -25,7 +26,7 @@ module Discordrb
25
26
  attr_reader :mentionable
26
27
  alias_method :mentionable?, :mentionable
27
28
 
28
- # @return [ColourRGB] the role colour
29
+ # @return [ColourRGB] the primary colour of this role.
29
30
  attr_reader :colour
30
31
  alias_method :color, :colour
31
32
 
@@ -35,9 +36,23 @@ module Discordrb
35
36
  # @return [String, nil] The icon hash for this role.
36
37
  attr_reader :icon
37
38
 
38
- # @return [Tags, nil] The role tags
39
+ # @return [Tags, nil] The role tags.
39
40
  attr_reader :tags
40
41
 
42
+ # @return [Integer] The flags for this role.
43
+ attr_reader :flags
44
+
45
+ # @return [String, nil] The unicode emoji of this role, or nil.
46
+ attr_reader :unicode_emoji
47
+
48
+ # @return [ColourRGB, nil] the secondary colour of this role.
49
+ attr_reader :secondary_colour
50
+ alias_method :secondary_color, :secondary_colour
51
+
52
+ # @return [ColourRGB, nil] the tertiary colour of this role.
53
+ attr_reader :tertiary_colour
54
+ alias_method :tertiary_color, :tertiary_colour
55
+
41
56
  # Wrapper for the role tags
42
57
  class Tags
43
58
  # @return [Integer, nil] The ID of the bot this role belongs to
@@ -48,16 +63,21 @@ module Discordrb
48
63
 
49
64
  # @return [true, false] Whether this is the guild's Booster role
50
65
  attr_reader :premium_subscriber
66
+ alias_method :premium_subscriber?, :premium_subscriber
51
67
 
52
68
  # @return [Integer, nil] The id of this role's subscription sku and listing
53
69
  attr_reader :subscription_listing_id
54
70
 
55
71
  # @return [true, false] Whether this role is available for purchase
56
72
  attr_reader :available_for_purchase
73
+ alias_method :available_for_purchase?, :available_for_purchase
57
74
 
58
75
  # @return [true, false] Whether this role is a guild's linked role
59
76
  attr_reader :guild_connections
77
+ alias_method :guild_connections?, :guild_connections
78
+ alias_method :server_connections?, :guild_connections
60
79
 
80
+ # @!visibility private
61
81
  def initialize(data)
62
82
  @bot_id = data['bot_id']&.resolve_id
63
83
  @integration_id = data['integration_id']&.resolve_id
@@ -92,7 +112,7 @@ module Discordrb
92
112
  def initialize(data, bot, server = nil)
93
113
  @bot = bot
94
114
  @server = server
95
- @permissions = Permissions.new(data['permissions'], RoleWriter.new(self, @bot.token))
115
+ @permissions = Permissions.new(data['permissions'].to_i, RoleWriter.new(self, @bot.token))
96
116
  @name = data['name']
97
117
  @id = data['id'].to_i
98
118
 
@@ -102,11 +122,19 @@ module Discordrb
102
122
  @mentionable = data['mentionable']
103
123
  @managed = data['managed']
104
124
 
105
- @colour = ColourRGB.new(data['color'])
125
+ colours = data['colors']
126
+ @colour = ColourRGB.new(colours['primary_color'])
106
127
 
107
128
  @icon = data['icon']
108
129
 
109
130
  @tags = Tags.new(data['tags']) if data['tags']
131
+
132
+ @flags = data['flags']
133
+
134
+ @unicode_emoji = data['unicode_emoji']
135
+
136
+ @tertiary_colour = ColourRGB.new(colours['tertiary_color']) if colours['tertiary_color']
137
+ @secondary_colour = ColourRGB.new(colours['secondary_color']) if colours['secondary_color']
110
138
  end
111
139
 
112
140
  # @return [String] a string that will mention this role, if it is mentionable.
@@ -133,16 +161,28 @@ module Discordrb
133
161
  @position = other.position
134
162
  @managed = other.managed
135
163
  @icon = other.icon
164
+ @flags = other.flags
165
+ @unicode_emoji = other.unicode_emoji
166
+ @secondary_colour = other.secondary_colour
167
+ @tertiary_colour = other.tertiary_colour
136
168
  end
137
169
 
138
170
  # Updates the data cache from a hash containing data
139
171
  # @note For internal use only
140
172
  # @!visibility private
141
173
  def update_data(new_data)
142
- @name = new_data[:name] || new_data['name'] || @name
143
- @hoist = new_data['hoist'] unless new_data['hoist'].nil?
144
- @hoist = new_data[:hoist] unless new_data[:hoist].nil?
145
- @colour = new_data[:colour] || (new_data['color'] ? ColourRGB.new(new_data['color']) : @colour)
174
+ @name = new_data['name']
175
+ @hoist = new_data['hoist']
176
+ @icon = new_data['icon']
177
+ @unicode_emoji = new_data['unicode_emoji']
178
+ @position = new_data['position']
179
+ @mentionable = new_data['mentionable']
180
+ @flags = new_data['flags']
181
+ colours = new_data['colors']
182
+ @permissions.bits = new_data['permissions'].to_i
183
+ @colour = ColourRGB.new(colours['primary_color'])
184
+ @secondary_color = ColourRGB.new(colours['secondary_color']) if colours['secondary_color']
185
+ @tertiary_colour = ColourRGB.new(colours['tertiary_color']) if colours['tertiary_color']
146
186
  end
147
187
 
148
188
  # Sets the role name to something new
@@ -163,18 +203,42 @@ module Discordrb
163
203
  update_role_data(mentionable: mentionable)
164
204
  end
165
205
 
166
- # Sets the role colour to something new
167
- # @param colour [ColourRGB] The new colour
206
+ # Sets the primary role colour to something new.
207
+ # @param colour [ColourRGB, Integer, nil] The new colour.
168
208
  def colour=(colour)
169
- update_role_data(colour: colour)
209
+ update_colors(primary: colour)
210
+ end
211
+
212
+ # Sets the secondary role colour to something new.
213
+ # @param colour [ColourRGB, Integer, nil] The new secondary colour.
214
+ def secondary_colour=(colour)
215
+ update_colours(secondary: colour)
216
+ end
217
+
218
+ # Sets the tertiary role colour to something new.
219
+ # @param colour [ColourRGB, Integer, nil] The new tertiary colour.
220
+ def tertiary_colour=(colour)
221
+ update_colours(tertiary: colour)
222
+ end
223
+
224
+ # Sets whether the role colour should be a holographic style.
225
+ # @param holographic [true, false] whether the role colour should be a holographic style.
226
+ def holographic=(holographic)
227
+ update_colours(holographic: holographic)
170
228
  end
171
229
 
172
230
  # Upload a role icon for servers with the ROLE_ICONS feature.
173
- # @param file [File]
231
+ # @param file [File, nil] File like object that responds to #read, or nil.
174
232
  def icon=(file)
175
233
  update_role_data(icon: file)
176
234
  end
177
235
 
236
+ # Set a role icon to a unicode emoji for servers with the ROLE_ICONS feature.
237
+ # @param emoji [String, nil] The new unicode emoji for this role, or nil.
238
+ def unicode_emoji=(emoji)
239
+ update_role_data(unicode_emoji: emoji)
240
+ end
241
+
178
242
  # @param format ['webp', 'png', 'jpeg']
179
243
  # @return [String] URL to the icon on Discord's CDN.
180
244
  def icon_url(format = 'webp')
@@ -183,7 +247,44 @@ module Discordrb
183
247
  Discordrb::API.role_icon_url(@id, @icon, format)
184
248
  end
185
249
 
250
+ # Get the icon that a role has displayed.
251
+ # @return [String, nil] Icon URL, the unicode emoji, or nil if this role doesn't have any icon.
252
+ # @note A role can have a unicode emoji, and an icon, but only the icon will be shown in the UI.
253
+ def display_icon
254
+ icon_url || unicode_emoji
255
+ end
256
+
257
+ # Set the icon this role is displaying.
258
+ # @param icon [File, String, nil] File like object that responds to #read, unicode emoji, or nil.
259
+ # @note Setting the icon to nil will remove the unicode emoji **and** the custom icon.
260
+ def display_icon=(icon)
261
+ if icon.nil?
262
+ update_role_data(unicode_emoji: nil, icon: nil)
263
+ return
264
+ end
265
+
266
+ if icon.respond_to?(:read)
267
+ update_role_data(unicode_emoji: nil, icon: icon)
268
+ else
269
+ update_role_data(unicode_emoji: icon, icon: nil)
270
+ end
271
+ end
272
+
273
+ # Whether or not the role is of the holographic style.
274
+ # @return [true, false]
275
+ def holographic?
276
+ !@tertiary_colour.nil?
277
+ end
278
+
279
+ # Whether or not the role has a two-point gradient.
280
+ # @return [true, false]
281
+ def gradient?
282
+ !@secondary_colour.nil? && @tertiary_colour.nil?
283
+ end
284
+
186
285
  alias_method :color=, :colour=
286
+ alias_method :secondary_color=, :secondary_colour=
287
+ alias_method :tertiary_color=, :tertiary_colour=
187
288
 
188
289
  # Changes this role's permissions to a fixed bitfield. This allows setting multiple permissions at once with just
189
290
  # one API call.
@@ -226,23 +327,51 @@ module Discordrb
226
327
  @server.delete_role(@id)
227
328
  end
228
329
 
330
+ # A rich interface designed to make working with role colours simple.
331
+ # @param primary [ColourRGB, Integer, nil] The new primary/base colour of this role, or nil to clear the primary colour.
332
+ # @param secondary [ColourRGB, Integer, nil] The new secondary colour of this role, or nil to clear the secondary colour.
333
+ # @param tertiary [ColourRGB, Integer,nil] The new tertiary colour of this role, or nil to clear the tertiary colour.
334
+ # @param holographic [true, false] Whether to apply or remove the holographic style to the role colour, overriding any other
335
+ # arguments that were passed. Using this argument is recommended over passing individual colours.
336
+ def update_colours(primary: :undef, secondary: :undef, tertiary: :undef, holographic: :undef)
337
+ colours = {
338
+ primary_color: (primary == :undef ? @colour : primary)&.to_i,
339
+ tertiary_color: (tertiary == :undef ? @tertiary_colour : tertiary)&.to_i,
340
+ secondary_color: (secondary == :undef ? @secondary_colour : secondary)&.to_i
341
+ }
342
+
343
+ holographic_colours = {
344
+ primary_color: 11_127_295,
345
+ tertiary_color: 16_761_760,
346
+ secondary_color: 16_759_788
347
+ }
348
+
349
+ # Only set the tertiary_color to `nil` if holographic is explicitly set to false.
350
+ colours[:tertiary_color] = nil if holographic.is_a?(FalseClass) && holographic?
351
+
352
+ update_role_data(colours: holographic == true ? holographic_colours : colours)
353
+ end
354
+
355
+ alias_method :update_colors, :update_colours
356
+
229
357
  # The inspect method is overwritten to give more useful output
230
358
  def inspect
231
- "<Role name=#{@name} permissions=#{@permissions.inspect} hoist=#{@hoist} colour=#{@colour.inspect} server=#{@server.inspect} position=#{@position} mentionable=#{@mentionable}>"
359
+ "<Role name=#{@name} permissions=#{@permissions.inspect} hoist=#{@hoist} colour=#{@colour.inspect} server=#{@server.inspect} position=#{@position} mentionable=#{@mentionable} unicode_emoji=#{@unicode_emoji} flags=#{@flags}>"
232
360
  end
233
361
 
234
362
  private
235
363
 
236
364
  def update_role_data(new_data)
237
- API::Server.update_role(@bot.token, @server.id, @id,
238
- new_data[:name] || @name,
239
- (new_data[:colour] || @colour).combined,
240
- new_data[:hoist].nil? ? @hoist : new_data[:hoist],
241
- new_data[:mentionable].nil? ? @mentionable : new_data[:mentionable],
242
- new_data[:permissions] || @permissions.bits,
243
- nil,
244
- new_data.key?(:icon) ? new_data[:icon] : :undef)
245
- update_data(new_data)
365
+ update_data(JSON.parse(API::Server.update_role(@bot.token, @server.id, @id,
366
+ new_data[:name] || @name,
367
+ :undef,
368
+ new_data.key?(:hoist) ? new_data[:hoist] : :undef,
369
+ new_data.key?(:mentionable) ? new_data[:mentionable] : :undef,
370
+ new_data[:permissions] || @permissions.bits,
371
+ nil,
372
+ new_data.key?(:icon) ? new_data[:icon] : :undef,
373
+ new_data.key?(:unicode_emoji) ? new_data[:unicode_emoji] : :undef,
374
+ new_data.key?(:colours) ? new_data[:colours] : :undef)))
246
375
  end
247
376
  end
248
377
  end
@@ -64,25 +64,20 @@ module Discordrb
64
64
  # @!visibility private
65
65
  def initialize(data, bot)
66
66
  @bot = bot
67
- @owner_id = data['owner_id'].to_i
68
67
  @id = data['id'].to_i
69
68
  @members = {}
70
69
  @voice_states = {}
71
70
  @emoji = {}
72
71
 
73
- process_channels(data['channels'])
74
72
  update_data(data)
75
73
 
76
74
  # Whether this server's members have been chunked (resolved using op 8 and GUILD_MEMBERS_CHUNK) yet
77
75
  @chunked = false
78
-
79
- @booster_count = data['premium_subscription_count'] || 0
80
- @boost_level = data['premium_tier']
81
76
  end
82
77
 
83
78
  # @return [Member] The server owner.
84
79
  def owner
85
- @owner ||= member(@owner_id)
80
+ member(@owner_id)
86
81
  end
87
82
 
88
83
  # The default channel is the text channel on this server with the highest position
@@ -137,7 +132,7 @@ module Discordrb
137
132
  @bot.debug("Members for server #{@id} not chunked yet - initiating")
138
133
 
139
134
  # If the SERVER_MEMBERS intent flag isn't set, the gateway won't respond when we ask for members.
140
- raise 'The :server_members intent is required to get server members' if (@bot.gateway.intents & INTENTS[:server_members]).zero?
135
+ raise 'The :server_members intent is required to get server members' if @bot.gateway.intents.nobits?(INTENTS[:server_members])
141
136
 
142
137
  @bot.request_chunks(@id)
143
138
  sleep 0.05 until @chunked
@@ -325,6 +320,11 @@ module Discordrb
325
320
  @channels.reject { |c| c.parent || c.category? }
326
321
  end
327
322
 
323
+ # @return [ServerPreview] the preview of this server shown in the discovery page.
324
+ def preview
325
+ @bot.server_preview(@id)
326
+ end
327
+
328
328
  # @return [String, nil] the widget URL to the server that displays the amount of online members in a
329
329
  # stylish way. `nil` if the widget is not enabled.
330
330
  def widget_url
@@ -397,7 +397,7 @@ module Discordrb
397
397
  # @!visibility private
398
398
  def delete_role(role_id)
399
399
  @roles.reject! { |r| r.id == role_id }
400
- @members.each do |_, member|
400
+ @members.each_value do |member|
401
401
  new_roles = member.roles.reject { |r| r.id == role_id }
402
402
  member.update_roles(new_roles)
403
403
  end
@@ -507,14 +507,19 @@ module Discordrb
507
507
  # Creates a role on this server which can then be modified. It will be initialized
508
508
  # with the regular role defaults the client uses unless specified, i.e. name is "new role",
509
509
  # permissions are the default, colour is the default etc.
510
- # @param name [String] Name of the role to create
511
- # @param colour [Integer, ColourRGB, #combined] The roles colour
512
- # @param hoist [true, false]
513
- # @param mentionable [true, false]
510
+ # @param name [String] Name of the role to create.
511
+ # @param colour [Integer, ColourRGB, #combined] The primary colour of the role to create.
512
+ # @param hoist [true, false] whether members of this role should be displayed seperately in the members list.
513
+ # @param mentionable [true, false] whether this role can mentioned by anyone in the server.
514
514
  # @param permissions [Integer, Array<Symbol>, Permissions, #bits] The permissions to write to the new role.
515
+ # @param icon [String, #read, nil] The base64 encoded image data, or a file like object that responds to #read.
516
+ # @param unicode_emoji [String, nil] The unicode emoji of the role to create, or nil.
517
+ # @param display_icon [String, File, #read, nil] The icon to display for the role. Overrides the **icon** and **unicode_emoji** parameters if passed.
515
518
  # @param reason [String] The reason the for the creation of this role.
519
+ # @param secondary_colour [Integer, ColourRGB, nil] The secondary colour of the role to create.
520
+ # @param tertiary_colour [Integer, ColourRGB, nil] The tertiary colour of the role to create.
516
521
  # @return [Role] the created role.
517
- def create_role(name: 'new role', colour: 0, hoist: false, mentionable: false, permissions: 104_324_161, reason: nil)
522
+ def create_role(name: 'new role', colour: 0, hoist: false, mentionable: false, permissions: 104_324_161, secondary_colour: nil, tertiary_colour: nil, icon: nil, unicode_emoji: nil, display_icon: nil, reason: nil)
518
523
  colour = colour.respond_to?(:combined) ? colour.combined : colour
519
524
 
520
525
  permissions = if permissions.is_a?(Array)
@@ -525,7 +530,21 @@ module Discordrb
525
530
  permissions
526
531
  end
527
532
 
528
- response = API::Server.create_role(@bot.token, @id, name, colour, hoist, mentionable, permissions, reason)
533
+ icon = icon.respond_to?(:read) ? Discordrb.encode64(icon) : icon
534
+
535
+ colours = {
536
+ primary_color: colour&.to_i,
537
+ tertiary_color: tertiary_colour&.to_i,
538
+ secondary_color: secondary_colour&.to_i
539
+ }
540
+
541
+ if display_icon.is_a?(String)
542
+ unicode_emoji = display_icon
543
+ elsif display_icon.respond_to?(:read)
544
+ icon = Discordrb.encode64(display_icon)
545
+ end
546
+
547
+ response = API::Server.create_role(@bot.token, @id, name, nil, hoist, mentionable, permissions&.to_s, reason, colours, icon, unicode_emoji)
529
548
 
530
549
  role = Role.new(JSON.parse(response), @bot, self)
531
550
  @roles << role
@@ -539,13 +558,9 @@ module Discordrb
539
558
  # @param reason [String] The reason the for the creation of this emoji.
540
559
  # @return [Emoji] The emoji that has been added.
541
560
  def add_emoji(name, image, roles = [], reason: nil)
542
- image_string = image
543
- if image.respond_to? :read
544
- image_string = 'data:image/jpg;base64,'
545
- image_string += Base64.strict_encode64(image.read)
546
- end
561
+ image = image.respond_to?(:read) ? Discordrb.encode64(image) : image
547
562
 
548
- data = JSON.parse(API::Server.add_emoji(@bot.token, @id, image_string, name, roles.map(&:resolve_id), reason))
563
+ data = JSON.parse(API::Server.add_emoji(@bot.token, @id, image, name, roles.map(&:resolve_id), reason))
549
564
  new_emoji = Emoji.new(data, @bot, self)
550
565
  @emoji[new_emoji.id] = new_emoji
551
566
  end
@@ -585,6 +600,17 @@ module Discordrb
585
600
  end
586
601
  end
587
602
 
603
+ # Searches a server for members that matches a username or a nickname.
604
+ # @param name [String] The username or nickname to search for.
605
+ # @param limit [Integer] The maximum number of members between 1-1000 to return. Returns 1 member by default.
606
+ # @return [Array<Member>, nil] An array of member objects that match the given parameters, or nil for no members.
607
+ def search_members(name:, limit: nil)
608
+ response = JSON.parse(API::Server.search_guild_members(@bot.token, @id, name, limit))
609
+ return nil if response.empty?
610
+
611
+ response.map { |mem| Member.new(mem, self, @bot) }
612
+ end
613
+
588
614
  # Retrieve banned users from this server.
589
615
  # @param limit [Integer] Number of users to return (up to maximum 1000, default 1000).
590
616
  # @param before_id [Integer] Consider only users before given user id.
@@ -599,10 +625,17 @@ module Discordrb
599
625
 
600
626
  # Bans a user from this server.
601
627
  # @param user [User, String, Integer] The user to ban.
602
- # @param message_days [Integer] How many days worth of messages sent by the user should be deleted.
628
+ # @param message_days [Integer] How many days worth of messages sent by the user should be deleted. This is deprecated and will be removed in 4.0.
629
+ # @param message_seconds [Integer] How many seconds of messages sent by the user should be deleted.
603
630
  # @param reason [String] The reason the user is being banned.
604
- def ban(user, message_days = 0, reason: nil)
605
- API::Server.ban_user(@bot.token, @id, user.resolve_id, message_days, reason)
631
+ def ban(user, message_days = 0, message_seconds: nil, reason: nil)
632
+ delete_messages = if message_days != 0 && message_days
633
+ message_days * 86_400
634
+ else
635
+ message_seconds || 0
636
+ end
637
+
638
+ API::Server.ban_user!(@bot.token, @id, user.resolve_id, delete_messages, reason)
606
639
  end
607
640
 
608
641
  # Unbans a previously banned user from this server.
@@ -612,6 +645,20 @@ module Discordrb
612
645
  API::Server.unban_user(@bot.token, @id, user.resolve_id, reason)
613
646
  end
614
647
 
648
+ # Ban up to 200 users from this server in one go.
649
+ # @param users [Array<User, String, Integer>] Array of up to 200 users to ban.
650
+ # @param message_seconds [Integer] How many seconds of messages sent by the users should be deleted.
651
+ # @param reason [String] The reason these users are being banned.
652
+ # @return [BulkBan]
653
+ def bulk_ban(users:, message_seconds: 0, reason: nil)
654
+ raise ArgumentError, 'Can only ban between 1 and 200 users!' unless users.size.between?(1, 200)
655
+
656
+ return ban(users.first, 0, message_seconds: message_seconds, reason: reason) if users.size == 1
657
+
658
+ response = API::Server.bulk_ban(@bot.token, @id, users.map(&:resolve_id), message_seconds, reason)
659
+ BulkBan.new(JSON.parse(response), self, reason)
660
+ end
661
+
615
662
  # Kicks a user from this server.
616
663
  # @param user [User, String, Integer] The user to kick.
617
664
  # @param reason [String] The reason the user is being kicked.
@@ -627,22 +674,11 @@ module Discordrb
627
674
  API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel&.resolve_id)
628
675
  end
629
676
 
630
- # Deletes this server. Be aware that this is permanent and impossible to undo, so be careful!
631
- def delete
632
- API::Server.delete(@bot.token, @id)
633
- end
634
-
635
677
  # Leave the server.
636
678
  def leave
637
679
  API::User.leave_server(@bot.token, @id)
638
680
  end
639
681
 
640
- # Transfers server ownership to another user.
641
- # @param user [User, String, Integer] The user who should become the new owner.
642
- def owner=(user)
643
- API::Server.transfer_ownership(@bot.token, @id, user.resolve_id)
644
- end
645
-
646
682
  # Sets the server's name.
647
683
  # @param name [String] The new server name.
648
684
  def name=(name)
@@ -674,10 +710,8 @@ module Discordrb
674
710
  # Sets the server's icon.
675
711
  # @param icon [String, #read] The new icon, in base64-encoded JPG format.
676
712
  def icon=(icon)
677
- if icon.respond_to? :read
678
- icon_string = 'data:image/jpg;base64,'
679
- icon_string += Base64.strict_encode64(icon.read)
680
- update_server_data(icon_id: icon_string)
713
+ if icon.respond_to?(:read)
714
+ update_server_data(icon_id: Discordrb.encode64(icon))
681
715
  else
682
716
  update_server_data(icon_id: icon)
683
717
  end
@@ -830,11 +864,11 @@ module Discordrb
830
864
  @afk_timeout = new_data[:afk_timeout] || new_data['afk_timeout'] || @afk_timeout
831
865
 
832
866
  afk_channel_id = new_data[:afk_channel_id] || new_data['afk_channel_id'] || @afk_channel
833
- @afk_channel_id = afk_channel_id.nil? ? nil : afk_channel_id.resolve_id
867
+ @afk_channel_id = afk_channel_id&.resolve_id
834
868
  widget_channel_id = new_data[:widget_channel_id] || new_data['widget_channel_id'] || @widget_channel
835
- @widget_channel_id = widget_channel_id.nil? ? nil : widget_channel_id.resolve_id
869
+ @widget_channel_id = widget_channel_id&.resolve_id
836
870
  system_channel_id = new_data[:system_channel_id] || new_data['system_channel_id'] || @system_channel
837
- @system_channel_id = system_channel_id.nil? ? nil : system_channel_id.resolve_id
871
+ @system_channel_id = system_channel_id&.resolve_id
838
872
 
839
873
  @widget_enabled = new_data[:widget_enabled] || new_data['widget_enabled']
840
874
  @splash = new_data[:splash_id] || new_data['splash_id'] || @splash_id
@@ -848,6 +882,9 @@ module Discordrb
848
882
  @splash_id = new_data['splash'] || @splash_id
849
883
  @banner_id = new_data['banner'] || @banner_id
850
884
  @features = new_data['features'] ? new_data['features'].map { |element| element.downcase.to_sym } : @features || []
885
+ @booster_count = new_data['premium_subscription_count'] || @booster_count || 0
886
+ @boost_level = new_data['premium_tier'] || @boost_level
887
+ @owner_id = new_data['owner_id'].to_i
851
888
 
852
889
  process_channels(new_data['channels']) if new_data['channels']
853
890
  process_roles(new_data['roles']) if new_data['roles']
@@ -855,6 +892,7 @@ module Discordrb
855
892
  process_members(new_data['members']) if new_data['members']
856
893
  process_presences(new_data['presences']) if new_data['presences']
857
894
  process_voice_states(new_data['voice_states']) if new_data['voice_states']
895
+ process_active_threads(new_data['threads']) if new_data['threads']
858
896
  end
859
897
 
860
898
  # Adds a channel to this server's cache
@@ -972,6 +1010,19 @@ module Discordrb
972
1010
  update_voice_state(element)
973
1011
  end
974
1012
  end
1013
+
1014
+ def process_active_threads(threads)
1015
+ @channels ||= []
1016
+ @channels_by_id ||= {}
1017
+
1018
+ return unless threads
1019
+
1020
+ threads.each do |element|
1021
+ thread = @bot.ensure_channel(element, self)
1022
+ @channels << thread
1023
+ @channels_by_id[thread.id] = thread
1024
+ end
1025
+ end
975
1026
  end
976
1027
 
977
1028
  # A ban entry on a server
@@ -1001,4 +1052,27 @@ module Discordrb
1001
1052
  alias_method :unban, :remove
1002
1053
  alias_method :lift, :remove
1003
1054
  end
1055
+
1056
+ # A bulk ban entry on a server
1057
+ class BulkBan
1058
+ # @return [Server] The server this bulk ban belongs to.
1059
+ attr_reader :server
1060
+
1061
+ # @return [String, nil] The reason these users were banned.
1062
+ attr_reader :reason
1063
+
1064
+ # @return [Array<Integer>] Array of user IDs that were banned.
1065
+ attr_reader :banned_users
1066
+
1067
+ # @return [Array<Integer>] Array of user IDs that couldn't be banned.
1068
+ attr_reader :failed_users
1069
+
1070
+ # @!visibility private
1071
+ def initialize(data, server, reason)
1072
+ @server = server
1073
+ @reason = reason
1074
+ @banned_users = data['banned_users']&.map(&:resolve_id) || []
1075
+ @failed_users = data['failed_users']&.map(&:resolve_id) || []
1076
+ end
1077
+ end
1004
1078
  end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Publicly accessible information about a discoverable server.
5
+ class ServerPreview
6
+ include IDObject
7
+ include ServerAttributes
8
+
9
+ # @return [String, nil] the ID of the server's invite splash screen.
10
+ # @see #splash_url
11
+ attr_reader :splash_id
12
+
13
+ # @return [String, nil] the ID of the server's discovery splash screen.
14
+ # @see #discovery_splash_url
15
+ attr_reader :discovery_splash_id
16
+
17
+ # @return [Hash<Integer => Emoji>] a hash of all the emojis usable on this server.
18
+ attr_reader :emojis
19
+
20
+ # @return [Array<Symbol>] the features of this server, e.g. `:banner` or `:verified`.
21
+ attr_reader :features
22
+
23
+ # @return [Integer] the approximate number of members on this server, offline or not.
24
+ attr_reader :member_count
25
+
26
+ # @return [Integer] the approximate number of members that aren't offline on this server.
27
+ attr_reader :presence_count
28
+
29
+ # @return [String, nil] the description of this server that's shown in the discovery tab.
30
+ attr_reader :description
31
+
32
+ # @!visibility private
33
+ def initialize(data, bot)
34
+ @bot = bot
35
+ @id = data['id'].to_i
36
+ @name = data['name']
37
+ @icon_id = data['icon']
38
+ @splash_id = data['splash']
39
+ @description = data['description']
40
+ @discovery_splash_id = data['discovery_splash']
41
+ @member_count = data['approximate_member_count']
42
+ @presence_count = data['approximate_presence_count']
43
+ @features = data['features'].map { |feature| feature.downcase.to_sym }
44
+ @emojis = data['emojis'].to_h { |emoji| [emoji['id'].to_i, Emoji.new(emoji, bot)] }
45
+ end
46
+
47
+ # Get the server associated with this server preview.
48
+ # @return [Server] the server associated with this server preview.
49
+ # @raise [Discordrb::Errors::NoPermission] this can happen when the bot is not in the associated server.
50
+ def server
51
+ @bot.server(@id)
52
+ end
53
+
54
+ # Utility method to get a server preview's splash URL.
55
+ # @param format [String] the URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
56
+ # @return [String, nil] the URL to the server's splash image, or `nil` if the server doesn't have a splash image.
57
+ def splash_url(format = 'webp')
58
+ API.splash_url(@id, @splash_id, format) if @splash_id
59
+ end
60
+
61
+ # Utility method to get a server preview's discovery splash URL.
62
+ # @param format [String] the URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
63
+ # @return [String, nil] the URL to the server's discovery splash image, or `nil` if the server doesn't have a discovery splash image.
64
+ def discovery_splash_url(format = 'webp')
65
+ API.discovery_splash_url(@id, @discovery_splash_id, format) if @discovery_splash_id
66
+ end
67
+ end
68
+ end