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
@@ -5,17 +5,80 @@ module Discordrb
5
5
  class Message
6
6
  include IDObject
7
7
 
8
+ # Map of message flags.
9
+ FLAGS = {
10
+ crossposted: 1 << 0,
11
+ crosspost: 1 << 1,
12
+ suppress_embeds: 1 << 2,
13
+ source_message_deleted: 1 << 3,
14
+ urgent: 1 << 4,
15
+ thread: 1 << 5,
16
+ ephemeral: 1 << 6,
17
+ loading: 1 << 7,
18
+ failed_to_mention_roles: 1 << 8,
19
+ suppress_notifications: 1 << 12,
20
+ voice_message: 1 << 13,
21
+ snapshot: 1 << 14,
22
+ uikit_components: 1 << 15
23
+ }.freeze
24
+
25
+ # Map of message types.
26
+ TYPES = {
27
+ default: 0,
28
+ recipient_add: 1,
29
+ recipient_remove: 2,
30
+ call: 3,
31
+ channel_name_change: 4,
32
+ channel_icon_change: 5,
33
+ channel_pinned_message: 6,
34
+ server_member_join: 7,
35
+ server_boost: 8,
36
+ server_boost_tier_one: 9,
37
+ server_boost_tier_two: 10,
38
+ server_boost_tier_three: 11,
39
+ channel_follow_add: 12,
40
+ server_discovery_disqualified: 14,
41
+ server_discovery_requalified: 15,
42
+ server_discovery_grace_period_initial_warning: 16,
43
+ server_discovery_grace_period_final_warning: 17,
44
+ thread_created: 18,
45
+ reply: 19,
46
+ chat_input_command: 20,
47
+ thread_starter_message: 21,
48
+ server_invite_reminder: 22,
49
+ context_menu_command: 23,
50
+ automod_action: 24,
51
+ role_subscription_purchase: 25,
52
+ interaction_premium_upsell: 26,
53
+ stage_start: 27,
54
+ stage_end: 28,
55
+ stage_speaker: 29,
56
+ stage_raise_hand: 30,
57
+ stage_topic: 31,
58
+ server_application_premium_subscription: 32,
59
+ server_incident_alert_mode_enabled: 36,
60
+ server_incident_alert_mode_disabled: 37,
61
+ server_incident_report_raid: 38,
62
+ server_incident_report_false_alarm: 39,
63
+ purchase_notification: 44,
64
+ poll_result: 46,
65
+ changelog: 47,
66
+ server_join_request_accepted: 52,
67
+ server_join_request_rejected: 53,
68
+ server_join_request_withdrawn: 54,
69
+ report_to_mod_deleted_message: 58,
70
+ report_to_mod_timeout_user: 59,
71
+ report_to_mod_kick_user: 60,
72
+ report_to_mod_ban_user: 61,
73
+ report_to_mod_closed_report: 62,
74
+ server_emoji_added: 63
75
+ }.freeze
76
+
8
77
  # @return [String] the content of this message.
9
78
  attr_reader :content
10
79
  alias_method :text, :content
11
80
  alias_method :to_s, :content
12
81
 
13
- # @return [Member, User] the user that sent this message. (Will be a {Member} most of the time, it should only be a
14
- # {User} for old messages when the author has left the server since then)
15
- attr_reader :author
16
- alias_method :user, :author
17
- alias_method :writer, :author
18
-
19
82
  # @return [Channel] the channel in which this message was sent.
20
83
  attr_reader :channel
21
84
 
@@ -70,9 +133,24 @@ module Discordrb
70
133
  # @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
71
134
  attr_reader :webhook_id
72
135
 
73
- # @return [Array<Component>]
136
+ # @return [Array<Component>] Interaction components for this message.
74
137
  attr_reader :components
75
138
 
139
+ # @return [Integer] flags set on the message.
140
+ attr_reader :flags
141
+
142
+ # @return [Channel, nil] The thread that was started from this message, or nil.
143
+ attr_reader :thread
144
+
145
+ # @return [Time, nil] the time at when this message was pinned. Only present on messages fetched via {Channel#pins}.
146
+ attr_reader :pinned_at
147
+
148
+ # @return [Call, nil] the call in a private channel that prompted this message.
149
+ attr_reader :call
150
+
151
+ # @return [Array<Snapshot>] the message snapshots included in this message.
152
+ attr_reader :snapshots
153
+
76
154
  # @!visibility private
77
155
  def initialize(data, bot)
78
156
  @bot = bot
@@ -91,35 +169,21 @@ module Discordrb
91
169
 
92
170
  @webhook_id = data['webhook_id']&.to_i
93
171
 
94
- @author = if data['author']
95
- if @webhook_id
96
- # This is a webhook user! It would be pointless to try to resolve a member here, so we just create
97
- # a User and return that instead.
98
- Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
99
- User.new(data['author'].merge({ '_webhook' => true }), @bot)
100
- elsif @channel.private?
101
- # Turn the message user into a recipient - we can't use the channel recipient
102
- # directly because the bot may also send messages to the channel
103
- Recipient.new(bot.user(data['author']['id'].to_i), @channel, bot)
104
- else
105
- member = @channel.server.member(data['author']['id'].to_i)
106
-
107
- if member
108
- member.update_data(data['member']) if data['member']
109
- member.update_global_name(data['author']['global_name']) if data['author']['global_name']
110
- else
111
- Discordrb::LOGGER.debug("Member with ID #{data['author']['id']} not cached (possibly left the server).")
112
- member = if data['member']
113
- member_data = data['author'].merge(data['member'])
114
- Member.new(member_data, @server, bot)
115
- else
116
- @bot.ensure_user(data['author'])
117
- end
118
- end
119
-
120
- member
121
- end
122
- end
172
+ if data['author']
173
+ if @webhook_id
174
+ # This is a webhook user! It would be pointless to try to resolve a member here, so we just create
175
+ # a User and return that instead.
176
+ Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
177
+ @author = User.new(data['author'].merge({ '_webhook' => true }), @bot)
178
+ elsif @channel.private?
179
+
180
+ # Turn the message user into a recipient - we can't use the channel recipient
181
+ # directly because the bot may also send messages to the channel
182
+ @author = Recipient.new(bot.user(data['author']['id'].to_i), @channel, bot)
183
+ else
184
+ @author_id = data['author']['id'].to_i
185
+ end
186
+ end
123
187
 
124
188
  @timestamp = Time.parse(data['timestamp']) if data['timestamp']
125
189
  @edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
@@ -157,8 +221,34 @@ module Discordrb
157
221
 
158
222
  @components = []
159
223
  @components = data['components'].map { |component_data| Components.from_data(component_data, @bot) } if data['components']
224
+
225
+ @flags = data['flags'] || 0
226
+
227
+ @thread = data['thread'] ? @bot.ensure_channel(data['thread'], @server) : nil
228
+
229
+ @pinned_at = data['pinned_at'] ? Time.parse(data['pinned_at']) : nil
230
+
231
+ @call = data['call'] ? Call.new(data['call'], @bot) : nil
232
+
233
+ @snapshots = data['message_snapshots']&.map { |snapshot| Snapshot.new(snapshot['message'], @bot) } || []
160
234
  end
161
235
 
236
+ # @return [Member, User] the user that sent this message. (Will be a {Member} most of the time, it should only be a
237
+ # {User} for old messages when the author has left the server since then)
238
+ def author
239
+ return @author if @author
240
+
241
+ if @channel.server
242
+ @author = @channel.server.member(@author_id)
243
+ Discordrb::LOGGER.debug("Member with ID #{@author_id} not cached (possibly left the server).") if @author.nil?
244
+ end
245
+
246
+ @author ||= @bot.user(@author_id)
247
+ end
248
+
249
+ alias_method :user, :author
250
+ alias_method :writer, :author
251
+
162
252
  # Replies to this message with the specified content.
163
253
  # @deprecated Please use {#respond}.
164
254
  # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
@@ -176,18 +266,19 @@ module Discordrb
176
266
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
177
267
  # @param mention_user [true, false] Whether the user that is being replied to should be pinged by the reply.
178
268
  # @param components [View, Array<Hash>] Interaction components to associate with this message.
269
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
179
270
  # @return (see #respond)
180
- def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false, components: nil)
271
+ def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false, components: nil, flags: 0)
181
272
  allowed_mentions = { parse: [] } if allowed_mentions == false
182
273
  allowed_mentions = allowed_mentions.to_hash.transform_keys(&:to_sym)
183
274
  allowed_mentions[:replied_user] = mention_user
184
275
 
185
- respond(content, tts, embed, attachments, allowed_mentions, self, components)
276
+ respond(content, tts, embed, attachments, allowed_mentions, self, components, flags)
186
277
  end
187
278
 
188
279
  # (see Channel#send_message)
189
- def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
190
- @channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components)
280
+ def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
281
+ @channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
191
282
  end
192
283
 
193
284
  # Edits this message to have the specified content instead.
@@ -195,12 +286,13 @@ module Discordrb
195
286
  # @param new_content [String] the new content the message should have.
196
287
  # @param new_embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed>, nil] The new embeds the message should have. If `nil` the message will be changed to have no embeds.
197
288
  # @param new_components [View, Array<Hash>] The new components the message should have. If `nil` the message will be changed to have no components.
289
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) can be edited.
198
290
  # @return [Message] the resulting message.
199
- def edit(new_content, new_embeds = nil, new_components = nil)
291
+ def edit(new_content, new_embeds = nil, new_components = nil, flags = 0)
200
292
  new_embeds = (new_embeds.instance_of?(Array) ? new_embeds.map(&:to_hash) : [new_embeds&.to_hash]).compact
201
- new_components = new_components&.to_a || []
293
+ new_components = new_components.to_a
202
294
 
203
- response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embeds, new_components)
295
+ response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embeds, new_components, flags)
204
296
  Message.new(JSON.parse(response), @bot)
205
297
  end
206
298
 
@@ -224,6 +316,12 @@ module Discordrb
224
316
  nil
225
317
  end
226
318
 
319
+ # Crossposts a message in a news channel.
320
+ def crosspost
321
+ response = API::Channel.crosspost_message(@bot.token, @channel.id, @id)
322
+ Message.new(JSON.parse(response), @bot)
323
+ end
324
+
227
325
  # Add an {Await} for a message with the same user and channel.
228
326
  # @see Bot#add_await
229
327
  # @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
@@ -286,6 +384,25 @@ module Discordrb
286
384
  @reactions.select(&:me)
287
385
  end
288
386
 
387
+ # Removes embeds from the message
388
+ # @return [Message] the resulting message.
389
+ def suppress_embeds
390
+ flags = @flags | (1 << 2)
391
+ response = API::Channel.edit_message(@bot.token, @channel.id, @id, :undef, :undef, :undef, :undef, flags)
392
+ Message.new(JSON.parse(response), @bot)
393
+ end
394
+
395
+ # Check if this message mentions a specific user or role.
396
+ # @param target [Role, User, Member, Integer, String] The mention to match against.
397
+ # @return [true, false] whether or not this message mentions the target.
398
+ def mentions?(target)
399
+ mentions = (@mentions + role_mentions)
400
+
401
+ mentions << server if @mention_everyone
402
+
403
+ mentions.any?(target.resolve_id)
404
+ end
405
+
289
406
  # Reacts to a message.
290
407
  # @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
291
408
  def create_reaction(reaction)
@@ -308,7 +425,7 @@ module Discordrb
308
425
 
309
426
  get_reactions = proc do |fetch_limit, after_id = nil|
310
427
  resp = API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction, nil, after_id, fetch_limit)
311
- return JSON.parse(resp).map { |d| User.new(d, @bot) }
428
+ JSON.parse(resp).map { |d| User.new(d, @bot) }
312
429
  end
313
430
 
314
431
  # Can be done without pagination
@@ -374,19 +491,13 @@ module Discordrb
374
491
  !@referenced_message.nil?
375
492
  end
376
493
 
377
- # Whether or not this message was of type "CHAT_INPUT_COMMAND"
378
- # @return [true, false]
379
- def chat_input_command?
380
- @type == 20
381
- end
382
-
383
494
  # @return [Message, nil] the Message this Message was sent in reply to.
384
495
  def referenced_message
385
496
  return @referenced_message if @referenced_message
386
497
  return nil unless @message_reference
387
498
 
388
499
  referenced_channel = @bot.channel(@message_reference['channel_id'])
389
- @referenced_message = referenced_channel.message(@message_reference['message_id'])
500
+ @referenced_message = referenced_channel&.message(@message_reference['message_id'])
390
501
  end
391
502
 
392
503
  # @return [Array<Components::Button>]
@@ -410,5 +521,41 @@ module Discordrb
410
521
  end
411
522
 
412
523
  alias_method :message, :to_message
524
+
525
+ FLAGS.each do |name, value|
526
+ define_method("#{name}?") do
527
+ @flags.anybits?(value)
528
+ end
529
+ end
530
+
531
+ TYPES.each do |name, value|
532
+ define_method("#{name}?") do
533
+ @type == value
534
+ end
535
+ end
536
+
537
+ # Convert this message to a hash that can be used to reference this message in a forward or a reply.
538
+ # @param type [Integer, Symbol] The reference type to set. Can either be one of `:reply` or `:forward`.
539
+ # @param must_exist [true, false] Whether to raise an error if this message was deleted when sending it.
540
+ # @return [Hash] the message as a hash representation that can be used in a forwarded message or a reply.
541
+ def to_reference(type: :reply, must_exist: true)
542
+ type = (type == :reply ? 0 : 1) if type.is_a?(Symbol)
543
+
544
+ { type: type, message_id: @id, channel_id: @channel.id, fail_if_not_exists: must_exist }
545
+ end
546
+
547
+ # Forward this message to another channel.
548
+ # @param channel [Integer, String, Channel] The target channel to forward this message to.
549
+ # @param must_exist [true, false] Whether to raise an error if this message was deleted when sending it.
550
+ # @param timeout [Float, nil] The amount of time in seconds after which the message sent will be deleted.
551
+ # @param flags [Integer, Symbol, Array<Integer, Symbol>] The message flags to set on the forwarded message.
552
+ # @param nonce [String, Integer, nil] The 25 character optional nonce that should be used when forwarding this message.
553
+ # @param enforce_nonce [true, false] Whether the provided nonce should be enforced and used for message de-duplication.
554
+ # @return [Message, nil] the message that was created from forwarding this one, or `nil` if this is a temporary message.
555
+ def forward(channel, must_exist: true, timeout: nil, flags: 0, nonce: nil, enforce_nonce: false)
556
+ reference = to_reference(type: :forward, must_exist: must_exist)
557
+
558
+ @bot.channel(channel).send_message!(reference: reference, timeout: timeout, flags: flags, nonce: nonce, enforce_nonce: enforce_nonce)
559
+ end
413
560
  end
414
561
  end
@@ -65,7 +65,9 @@ module Discordrb
65
65
 
66
66
  # Comparison by attributes [:id, :type, :allow, :deny]
67
67
  def ==(other)
68
+ # rubocop:disable Lint/Void
68
69
  false unless other.is_a? Discordrb::Overwrite
70
+ # rubocop:enable Lint/Void
69
71
  id == other.id &&
70
72
  type == other.type &&
71
73
  allow == other.allow &&
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A server tag that a user has chosen to display on their profile.
5
+ class PrimaryServer
6
+ # @return [Integer] the ID of the server this primary server is for.
7
+ attr_reader :server_id
8
+
9
+ # @return [Boolean] if the user is displaying the primary server's tag.
10
+ attr_reader :enabled
11
+ alias_method :enabled?, :enabled
12
+
13
+ # @return [String] the text of the primary server's tag. Limited to four characters.
14
+ attr_reader :name
15
+ alias_method :text, :name
16
+
17
+ # @return [String] the ID of the server tag's badge. can be used to generate a badge URL.
18
+ # @see #badge_url
19
+ attr_reader :badge_id
20
+
21
+ # @!visibility private
22
+ def initialize(data, bot)
23
+ @bot = bot
24
+ @server_id = data['identity_guild_id']&.to_i
25
+ @enabled = data['identity_enabled']
26
+ @name = data['tag']
27
+ @badge_id = data['badge']
28
+ end
29
+
30
+ # Get the server associated with this primary server.
31
+ # @return [Server] the server associated with this primary server.
32
+ # @raise [Discordrb::Errors::NoPermission] this can happen when the bot is not in the associated server.
33
+ def server
34
+ @bot.server(@server_id)
35
+ end
36
+
37
+ # Get the server preview associated with this primary server.
38
+ # @return [ServerPreview, nil] the server preview associated with this primary server, or `nil` if it can't be accessed.
39
+ def server_preview
40
+ @bot.server_preview(@server_id)
41
+ end
42
+
43
+ # Utility method to get a server tag's badge URL.
44
+ # @param format [String] the URL will default to `webp`. You can otherwise specify one of `jpg` or `png` to override this.
45
+ # @return [String] the URL to the server tag's badge image.
46
+ def badge_url(format = 'webp')
47
+ API.server_tag_badge_url(@server_id, @badge_id, format)
48
+ end
49
+
50
+ # Comparison based off of server ID.
51
+ # @return [true, false] if the other object is equal to this primary server.
52
+ def ==(other)
53
+ return false unless other.is_a?(PrimaryServer)
54
+
55
+ Discordrb.id_compare(other.server_id, @server_id)
56
+ end
57
+
58
+ alias_method :eql?, :==
59
+ end
60
+ end
@@ -22,13 +22,8 @@ module Discordrb
22
22
  # @param avatar [String, #read] A JPG file to be used as the avatar, either
23
23
  # something readable (e.g. File Object) or as a data URL.
24
24
  def avatar=(avatar)
25
- if avatar.respond_to? :read
26
- # Set the file to binary mode if supported, so we don't get problems with Windows
27
- avatar.binmode if avatar.respond_to?(:binmode)
28
-
29
- avatar_string = 'data:image/jpg;base64,'
30
- avatar_string += Base64.strict_encode64(avatar.read)
31
- update_profile_data(avatar: avatar_string)
25
+ if avatar.respond_to?(:read)
26
+ update_profile_data(avatar: Discordrb.encode64(avatar))
32
27
  else
33
28
  update_profile_data(avatar: avatar)
34
29
  end
@@ -16,10 +16,11 @@ module Discordrb
16
16
  # @return [String] the name or unicode representation of the emoji
17
17
  attr_reader :name
18
18
 
19
+ # @!visibility private
19
20
  def initialize(data)
20
21
  @count = data['count']
21
22
  @me = data['me']
22
- @id = data['emoji']['id'].nil? ? nil : data['emoji']['id'].to_i
23
+ @id = data['emoji']['id']&.to_i
23
24
  @name = data['emoji']['name']
24
25
  end
25
26
 
@@ -16,7 +16,7 @@ module Discordrb
16
16
  raise ArgumentError, 'Tried to create a recipient for a public channel!' unless @channel.private?
17
17
 
18
18
  @user = user
19
- super @user
19
+ super(@user)
20
20
 
21
21
  # Member attributes
22
22
  @mute = @deaf = @self_mute = @self_deaf = false