discordrb 3.4.3 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.devcontainer/Dockerfile +13 -0
  3. data/.devcontainer/devcontainer.json +29 -0
  4. data/.devcontainer/postcreate.sh +4 -0
  5. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
  6. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
  7. data/.github/workflows/ci.yml +78 -0
  8. data/.github/workflows/codeql.yml +65 -0
  9. data/.github/workflows/deploy.yml +54 -0
  10. data/.github/workflows/release.yml +45 -0
  11. data/.markdownlint.json +4 -0
  12. data/.rubocop.yml +58 -2
  13. data/CHANGELOG.md +485 -225
  14. data/LICENSE.txt +1 -1
  15. data/README.md +38 -26
  16. data/discordrb-webhooks.gemspec +4 -1
  17. data/discordrb.gemspec +18 -10
  18. data/lib/discordrb/api/application.rb +278 -0
  19. data/lib/discordrb/api/channel.rb +222 -18
  20. data/lib/discordrb/api/interaction.rb +63 -0
  21. data/lib/discordrb/api/invite.rb +2 -2
  22. data/lib/discordrb/api/server.rb +123 -66
  23. data/lib/discordrb/api/user.rb +20 -5
  24. data/lib/discordrb/api/webhook.rb +72 -0
  25. data/lib/discordrb/api.rb +35 -25
  26. data/lib/discordrb/bot.rb +437 -66
  27. data/lib/discordrb/cache.rb +41 -22
  28. data/lib/discordrb/commands/command_bot.rb +13 -21
  29. data/lib/discordrb/commands/container.rb +1 -1
  30. data/lib/discordrb/commands/parser.rb +7 -7
  31. data/lib/discordrb/commands/rate_limiter.rb +1 -1
  32. data/lib/discordrb/container.rb +178 -3
  33. data/lib/discordrb/data/activity.rb +1 -1
  34. data/lib/discordrb/data/application.rb +1 -0
  35. data/lib/discordrb/data/attachment.rb +38 -3
  36. data/lib/discordrb/data/audit_logs.rb +3 -3
  37. data/lib/discordrb/data/avatar_decoration.rb +26 -0
  38. data/lib/discordrb/data/call.rb +22 -0
  39. data/lib/discordrb/data/channel.rb +299 -30
  40. data/lib/discordrb/data/collectibles.rb +45 -0
  41. data/lib/discordrb/data/component.rb +229 -0
  42. data/lib/discordrb/data/embed.rb +10 -3
  43. data/lib/discordrb/data/emoji.rb +20 -1
  44. data/lib/discordrb/data/integration.rb +45 -3
  45. data/lib/discordrb/data/interaction.rb +937 -0
  46. data/lib/discordrb/data/invite.rb +1 -1
  47. data/lib/discordrb/data/member.rb +236 -44
  48. data/lib/discordrb/data/message.rb +278 -51
  49. data/lib/discordrb/data/overwrite.rb +15 -7
  50. data/lib/discordrb/data/primary_server.rb +60 -0
  51. data/lib/discordrb/data/profile.rb +2 -7
  52. data/lib/discordrb/data/reaction.rb +2 -1
  53. data/lib/discordrb/data/recipient.rb +1 -1
  54. data/lib/discordrb/data/role.rb +204 -18
  55. data/lib/discordrb/data/server.rb +194 -118
  56. data/lib/discordrb/data/server_preview.rb +68 -0
  57. data/lib/discordrb/data/snapshot.rb +110 -0
  58. data/lib/discordrb/data/user.rb +132 -12
  59. data/lib/discordrb/data/voice_region.rb +1 -0
  60. data/lib/discordrb/data/webhook.rb +99 -9
  61. data/lib/discordrb/data.rb +9 -0
  62. data/lib/discordrb/errors.rb +47 -3
  63. data/lib/discordrb/events/await.rb +1 -1
  64. data/lib/discordrb/events/channels.rb +38 -1
  65. data/lib/discordrb/events/generic.rb +2 -0
  66. data/lib/discordrb/events/guilds.rb +6 -1
  67. data/lib/discordrb/events/interactions.rb +575 -0
  68. data/lib/discordrb/events/invites.rb +2 -0
  69. data/lib/discordrb/events/members.rb +19 -2
  70. data/lib/discordrb/events/message.rb +42 -8
  71. data/lib/discordrb/events/presence.rb +23 -14
  72. data/lib/discordrb/events/raw.rb +1 -0
  73. data/lib/discordrb/events/reactions.rb +2 -1
  74. data/lib/discordrb/events/roles.rb +2 -0
  75. data/lib/discordrb/events/threads.rb +100 -0
  76. data/lib/discordrb/events/typing.rb +1 -0
  77. data/lib/discordrb/events/voice_server_update.rb +1 -0
  78. data/lib/discordrb/events/voice_state_update.rb +1 -0
  79. data/lib/discordrb/events/webhooks.rb +1 -0
  80. data/lib/discordrb/gateway.rb +57 -28
  81. data/lib/discordrb/paginator.rb +3 -3
  82. data/lib/discordrb/permissions.rb +71 -35
  83. data/lib/discordrb/version.rb +1 -1
  84. data/lib/discordrb/voice/encoder.rb +2 -2
  85. data/lib/discordrb/voice/network.rb +18 -7
  86. data/lib/discordrb/voice/sodium.rb +3 -1
  87. data/lib/discordrb/voice/voice_bot.rb +3 -3
  88. data/lib/discordrb/webhooks.rb +2 -0
  89. data/lib/discordrb/websocket.rb +0 -10
  90. data/lib/discordrb.rb +54 -5
  91. metadata +87 -25
  92. data/.circleci/config.yml +0 -126
  93. data/.codeclimate.yml +0 -16
  94. data/.travis.yml +0 -32
  95. data/bin/travis_build_docs.sh +0 -17
@@ -64,38 +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
-
70
- process_channels(data['channels'])
71
- update_data(data)
72
-
73
- @large = data['large']
74
- @member_count = data['member_count']
75
- @splash_id = nil
76
- @banner_id = nil
77
- @features = data['features'].map { |element| element.downcase.to_sym }
78
68
  @members = {}
79
69
  @voice_states = {}
80
70
  @emoji = {}
81
71
 
82
- process_roles(data['roles'])
83
- process_emoji(data['emojis'])
84
- process_members(data['members'])
85
- process_presences(data['presences'])
86
- process_voice_states(data['voice_states'])
72
+ update_data(data)
87
73
 
88
74
  # Whether this server's members have been chunked (resolved using op 8 and GUILD_MEMBERS_CHUNK) yet
89
75
  @chunked = false
90
- @processed_chunk_members = 0
91
-
92
- @booster_count = data['premium_subscription_count'] || 0
93
- @boost_level = data['premium_tier']
94
76
  end
95
77
 
96
78
  # @return [Member] The server owner.
97
79
  def owner
98
- @owner ||= member(@owner_id)
80
+ member(@owner_id)
99
81
  end
100
82
 
101
83
  # The default channel is the text channel on this server with the highest position
@@ -143,10 +125,15 @@ module Discordrb
143
125
  end
144
126
 
145
127
  # @return [Array<Member>] an array of all the members on this server.
128
+ # @raise [RuntimeError] if the bot was not started with the :server_member intent
146
129
  def members
147
130
  return @members.values if @chunked
148
131
 
149
132
  @bot.debug("Members for server #{@id} not chunked yet - initiating")
133
+
134
+ # If the SERVER_MEMBERS intent flag isn't set, the gateway won't respond when we ask for members.
135
+ raise 'The :server_members intent is required to get server members' if @bot.gateway.intents.nobits?(INTENTS[:server_members])
136
+
150
137
  @bot.request_chunks(@id)
151
138
  sleep 0.05 until @chunked
152
139
  @members.values
@@ -189,78 +176,73 @@ module Discordrb
189
176
  AuditLogs.new(self, @bot, JSON.parse(API::Server.audit_logs(@bot.token, @id, limit, user, action, before)))
190
177
  end
191
178
 
192
- # Cache @embed
179
+ # Cache @widget
193
180
  # @note For internal use only
194
181
  # @!visibility private
195
- def cache_embed_data
196
- data = JSON.parse(API::Server.embed(@bot.token, @id))
197
- @embed_enabled = data['enabled']
198
- @embed_channel_id = data['channel_id']
182
+ def cache_widget_data
183
+ data = JSON.parse(API::Server.widget(@bot.token, @id))
184
+ @widget_enabled = data['enabled']
185
+ @widget_channel_id = data['channel_id']
199
186
  end
200
187
 
201
188
  # @return [true, false] whether or not the server has widget enabled
202
- def embed_enabled?
203
- cache_embed_data if @embed_enabled.nil?
204
- @embed_enabled
189
+ def widget_enabled?
190
+ cache_widget_data if @widget_enabled.nil?
191
+ @widget_enabled
205
192
  end
206
- alias_method :widget_enabled, :embed_enabled?
207
- alias_method :widget?, :embed_enabled?
208
- alias_method :embed?, :embed_enabled?
193
+ alias_method :widget?, :widget_enabled?
194
+ alias_method :embed_enabled, :widget_enabled?
195
+ alias_method :embed?, :widget_enabled?
209
196
 
210
- # @return [Channel, nil] the channel the server embed will make an invite for.
211
- def embed_channel
212
- cache_embed_data if @embed_enabled.nil?
213
- @bot.channel(@embed_channel_id) if @embed_channel_id
197
+ # @return [Channel, nil] the channel the server widget will make an invite for.
198
+ def widget_channel
199
+ cache_widget_data if @widget_enabled.nil?
200
+ @bot.channel(@widget_channel_id) if @widget_channel_id
214
201
  end
215
- alias_method :widget_channel, :embed_channel
202
+ alias_method :embed_channel, :widget_channel
216
203
 
217
- # Sets whether this server's embed (widget) is enabled
204
+ # Sets whether this server's widget is enabled
218
205
  # @param value [true, false]
219
- def embed_enabled=(value)
220
- modify_embed(value, embed_channel)
206
+ def widget_enabled=(value)
207
+ modify_widget(value, widget_channel)
221
208
  end
209
+ alias_method :embed_enabled=, :widget_enabled=
222
210
 
223
- alias_method :widget_enabled=, :embed_enabled=
224
-
225
- # Sets whether this server's embed (widget) is enabled
211
+ # Sets whether this server's widget is enabled
226
212
  # @param value [true, false]
227
213
  # @param reason [String, nil] the reason to be shown in the audit log for this action
228
- def set_embed_enabled(value, reason = nil)
229
- modify_embed(value, embed_channel, reason)
214
+ def set_widget_enabled(value, reason = nil)
215
+ modify_widget(value, widget_channel, reason)
230
216
  end
217
+ alias_method :set_embed_enabled, :set_widget_enabled
231
218
 
232
- alias_method :set_widget_enabled, :set_embed_enabled
233
-
234
- # Changes the channel on the server's embed (widget)
235
- # @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the embed
236
- def embed_channel=(channel)
237
- modify_embed(embed?, channel)
219
+ # Changes the channel on the server's widget
220
+ # @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
221
+ def widget_channel=(channel)
222
+ modify_widget(widget?, channel)
238
223
  end
224
+ alias_method :embed_channel=, :widget_channel=
239
225
 
240
- alias_method :widget_channel=, :embed_channel=
241
-
242
- # Changes the channel on the server's embed (widget)
243
- # @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the embed
226
+ # Changes the channel on the server's widget
227
+ # @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
244
228
  # @param reason [String, nil] the reason to be shown in the audit log for this action
245
- def set_embed_channel(channel, reason = nil)
246
- modify_embed(embed?, channel, reason)
229
+ def set_widget_channel(channel, reason = nil)
230
+ modify_widget(widget?, channel, reason)
247
231
  end
232
+ alias_method :set_embed_channel, :set_widget_channel
248
233
 
249
- alias_method :set_widget_channel, :set_embed_channel
250
-
251
- # Changes the channel on the server's embed (widget), and sets whether it is enabled.
252
- # @param enabled [true, false] whether the embed (widget) is enabled
253
- # @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the embed
234
+ # Changes the channel on the server's widget, and sets whether it is enabled.
235
+ # @param enabled [true, false] whether the widget is enabled
236
+ # @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
254
237
  # @param reason [String, nil] the reason to be shown in the audit log for this action
255
- def modify_embed(enabled, channel, reason = nil)
256
- cache_embed_data if @embed_enabled.nil?
257
- channel_id = channel ? channel.resolve_id : @embed_channel_id
258
- response = JSON.parse(API::Server.modify_embed(@bot.token, @id, enabled, channel_id, reason))
259
- @embed_enabled = response['enabled']
260
- @embed_channel_id = response['channel_id']
238
+ def modify_widget(enabled, channel, reason = nil)
239
+ cache_widget_data if @widget_enabled.nil?
240
+ channel_id = channel ? channel.resolve_id : @widget_channel_id
241
+ response = JSON.parse(API::Server.modify_widget(@bot.token, @id, enabled, channel_id, reason))
242
+ @widget_enabled = response['enabled']
243
+ @widget_channel_id = response['channel_id']
261
244
  end
262
-
263
- alias_method :modify_widget, :modify_embed
245
+ alias_method :modify_embed, :modify_widget
264
246
 
265
247
  # @param include_idle [true, false] Whether to count idle members as online.
266
248
  # @param include_bots [true, false] Whether to include bot accounts in the count.
@@ -338,6 +320,11 @@ module Discordrb
338
320
  @channels.reject { |c| c.parent || c.category? }
339
321
  end
340
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
+
341
328
  # @return [String, nil] the widget URL to the server that displays the amount of online members in a
342
329
  # stylish way. `nil` if the widget is not enabled.
343
330
  def widget_url
@@ -410,7 +397,7 @@ module Discordrb
410
397
  # @!visibility private
411
398
  def delete_role(role_id)
412
399
  @roles.reject! { |r| r.id == role_id }
413
- @members.each do |_, member|
400
+ @members.each_value do |member|
414
401
  new_roles = member.roles.reject { |r| r.id == role_id }
415
402
  member.update_roles(new_roles)
416
403
  end
@@ -444,7 +431,7 @@ module Discordrb
444
431
  # @!visibility private
445
432
  def delete_member(user_id)
446
433
  @members.delete(user_id)
447
- @member_count -= 1
434
+ @member_count -= 1 unless @member_count <= 0
448
435
  end
449
436
 
450
437
  # Checks whether a member is cached
@@ -520,14 +507,19 @@ module Discordrb
520
507
  # Creates a role on this server which can then be modified. It will be initialized
521
508
  # with the regular role defaults the client uses unless specified, i.e. name is "new role",
522
509
  # permissions are the default, colour is the default etc.
523
- # @param name [String] Name of the role to create
524
- # @param colour [Integer, ColourRGB, #combined] The roles colour
525
- # @param hoist [true, false]
526
- # @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.
527
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.
528
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.
529
521
  # @return [Role] the created role.
530
- 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)
531
523
  colour = colour.respond_to?(:combined) ? colour.combined : colour
532
524
 
533
525
  permissions = if permissions.is_a?(Array)
@@ -538,7 +530,21 @@ module Discordrb
538
530
  permissions
539
531
  end
540
532
 
541
- 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)
542
548
 
543
549
  role = Role.new(JSON.parse(response), @bot, self)
544
550
  @roles << role
@@ -552,13 +558,9 @@ module Discordrb
552
558
  # @param reason [String] The reason the for the creation of this emoji.
553
559
  # @return [Emoji] The emoji that has been added.
554
560
  def add_emoji(name, image, roles = [], reason: nil)
555
- image_string = image
556
- if image.respond_to? :read
557
- image_string = 'data:image/jpg;base64,'
558
- image_string += Base64.strict_encode64(image.read)
559
- end
561
+ image = image.respond_to?(:read) ? Discordrb.encode64(image) : image
560
562
 
561
- 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))
562
564
  new_emoji = Emoji.new(data, @bot, self)
563
565
  @emoji[new_emoji.id] = new_emoji
564
566
  end
@@ -586,7 +588,7 @@ module Discordrb
586
588
  # The amount of emoji the server can have, based on its current Nitro Boost Level.
587
589
  # @return [Integer] the max amount of emoji
588
590
  def max_emoji
589
- case @level
591
+ case @boost_level
590
592
  when 1
591
593
  100
592
594
  when 2
@@ -598,9 +600,24 @@ module Discordrb
598
600
  end
599
601
  end
600
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
+
614
+ # Retrieve banned users from this server.
615
+ # @param limit [Integer] Number of users to return (up to maximum 1000, default 1000).
616
+ # @param before_id [Integer] Consider only users before given user id.
617
+ # @param after_id [Integer] Consider only users after given user id.
601
618
  # @return [Array<ServerBan>] a list of banned users on this server and the reason they were banned.
602
- def bans
603
- response = JSON.parse(API::Server.bans(@bot.token, @id))
619
+ def bans(limit: nil, before_id: nil, after_id: nil)
620
+ response = JSON.parse(API::Server.bans(@bot.token, @id, limit, before_id, after_id))
604
621
  response.map do |e|
605
622
  ServerBan.new(self, User.new(e['user'], @bot), e['reason'])
606
623
  end
@@ -608,10 +625,17 @@ module Discordrb
608
625
 
609
626
  # Bans a user from this server.
610
627
  # @param user [User, String, Integer] The user to ban.
611
- # @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.
612
630
  # @param reason [String] The reason the user is being banned.
613
- def ban(user, message_days = 0, reason: nil)
614
- 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)
615
639
  end
616
640
 
617
641
  # Unbans a previously banned user from this server.
@@ -621,6 +645,20 @@ module Discordrb
621
645
  API::Server.unban_user(@bot.token, @id, user.resolve_id, reason)
622
646
  end
623
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
+
624
662
  # Kicks a user from this server.
625
663
  # @param user [User, String, Integer] The user to kick.
626
664
  # @param reason [String] The reason the user is being kicked.
@@ -628,16 +666,12 @@ module Discordrb
628
666
  API::Server.remove_member(@bot.token, @id, user.resolve_id, reason)
629
667
  end
630
668
 
631
- # Forcibly moves a user into a different voice channel. Only works if the bot has the permission needed.
669
+ # Forcibly moves a user into a different voice channel.
670
+ # Only works if the bot has the permission needed and if the user is already connected to some voice channel on this server.
632
671
  # @param user [User, String, Integer] The user to move.
633
- # @param channel [Channel, String, Integer] The voice channel to move into.
672
+ # @param channel [Channel, String, Integer, nil] The voice channel to move into. (If nil, the user is disconnected from the voice channel)
634
673
  def move(user, channel)
635
- API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel.resolve_id)
636
- end
637
-
638
- # Deletes this server. Be aware that this is permanent and impossible to undo, so be careful!
639
- def delete
640
- API::Server.delete(@bot.token, @id)
674
+ API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel&.resolve_id)
641
675
  end
642
676
 
643
677
  # Leave the server.
@@ -645,12 +679,6 @@ module Discordrb
645
679
  API::User.leave_server(@bot.token, @id)
646
680
  end
647
681
 
648
- # Transfers server ownership to another user.
649
- # @param user [User, String, Integer] The user who should become the new owner.
650
- def owner=(user)
651
- API::Server.transfer_ownership(@bot.token, @id, user.resolve_id)
652
- end
653
-
654
682
  # Sets the server's name.
655
683
  # @param name [String] The new server name.
656
684
  def name=(name)
@@ -682,10 +710,8 @@ module Discordrb
682
710
  # Sets the server's icon.
683
711
  # @param icon [String, #read] The new icon, in base64-encoded JPG format.
684
712
  def icon=(icon)
685
- if icon.respond_to? :read
686
- icon_string = 'data:image/jpg;base64,'
687
- icon_string += Base64.strict_encode64(icon.read)
688
- update_server_data(icon_id: icon_string)
713
+ if icon.respond_to?(:read)
714
+ update_server_data(icon_id: Discordrb.encode64(icon))
689
715
  else
690
716
  update_server_data(icon_id: icon)
691
717
  end
@@ -805,19 +831,16 @@ module Discordrb
805
831
  # Processes a GUILD_MEMBERS_CHUNK packet, specifically the members field
806
832
  # @note For internal use only
807
833
  # @!visibility private
808
- def process_chunk(members)
834
+ def process_chunk(members, chunk_index, chunk_count)
809
835
  process_members(members)
810
- @processed_chunk_members += members.length
811
- LOGGER.debug("Processed one chunk on server #{@id} - length #{members.length}")
836
+ LOGGER.debug("Processed chunk #{chunk_index + 1}/#{chunk_count} server #{@id} - index #{chunk_index} - length #{members.length}")
812
837
 
813
- # Don't bother with the rest of the method if it's not truly the last packet
814
- return unless @processed_chunk_members == @member_count
838
+ return if chunk_index + 1 < chunk_count
815
839
 
816
840
  LOGGER.debug("Finished chunking server #{@id}")
817
841
 
818
842
  # Reset everything to normal
819
843
  @chunked = true
820
- @processed_chunk_members = 0
821
844
  end
822
845
 
823
846
  # @return [Channel, nil] the AFK voice channel of this server, or `nil` if none is set.
@@ -841,18 +864,35 @@ module Discordrb
841
864
  @afk_timeout = new_data[:afk_timeout] || new_data['afk_timeout'] || @afk_timeout
842
865
 
843
866
  afk_channel_id = new_data[:afk_channel_id] || new_data['afk_channel_id'] || @afk_channel
844
- @afk_channel_id = afk_channel_id.nil? ? nil : afk_channel_id.resolve_id
845
- embed_channel_id = new_data[:embed_channel_id] || new_data['embed_channel_id'] || @embed_channel
846
- @embed_channel_id = embed_channel_id.nil? ? nil : embed_channel_id.resolve_id
867
+ @afk_channel_id = afk_channel_id&.resolve_id
868
+ widget_channel_id = new_data[:widget_channel_id] || new_data['widget_channel_id'] || @widget_channel
869
+ @widget_channel_id = widget_channel_id&.resolve_id
847
870
  system_channel_id = new_data[:system_channel_id] || new_data['system_channel_id'] || @system_channel
848
- @system_channel_id = system_channel_id.nil? ? nil : system_channel_id.resolve_id
871
+ @system_channel_id = system_channel_id&.resolve_id
849
872
 
850
- @embed_enabled = new_data[:embed_enabled] || new_data['embed_enabled']
873
+ @widget_enabled = new_data[:widget_enabled] || new_data['widget_enabled']
851
874
  @splash = new_data[:splash_id] || new_data['splash_id'] || @splash_id
852
875
 
853
876
  @verification_level = new_data[:verification_level] || new_data['verification_level'] || @verification_level
854
877
  @explicit_content_filter = new_data[:explicit_content_filter] || new_data['explicit_content_filter'] || @explicit_content_filter
855
878
  @default_message_notifications = new_data[:default_message_notifications] || new_data['default_message_notifications'] || @default_message_notifications
879
+
880
+ @large = new_data.key?('large') ? new_data['large'] : @large
881
+ @member_count = new_data['member_count'] || @member_count || 0
882
+ @splash_id = new_data['splash'] || @splash_id
883
+ @banner_id = new_data['banner'] || @banner_id
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
888
+
889
+ process_channels(new_data['channels']) if new_data['channels']
890
+ process_roles(new_data['roles']) if new_data['roles']
891
+ process_emoji(new_data['emojis']) if new_data['emojis']
892
+ process_members(new_data['members']) if new_data['members']
893
+ process_presences(new_data['presences']) if new_data['presences']
894
+ process_voice_states(new_data['voice_states']) if new_data['voice_states']
895
+ process_active_threads(new_data['threads']) if new_data['threads']
856
896
  end
857
897
 
858
898
  # Adds a channel to this server's cache
@@ -970,6 +1010,19 @@ module Discordrb
970
1010
  update_voice_state(element)
971
1011
  end
972
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
973
1026
  end
974
1027
 
975
1028
  # A ban entry on a server
@@ -999,4 +1052,27 @@ module Discordrb
999
1052
  alias_method :unban, :remove
1000
1053
  alias_method :lift, :remove
1001
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
1002
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