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
@@ -32,6 +32,8 @@ module Discordrb
32
32
  # @option attributes [Time] :after Matches a time after the time the message was sent at.
33
33
  # @option attributes [Time] :before Matches a time before the time the message was sent at.
34
34
  # @option attributes [Boolean] :private Matches whether or not the channel is private.
35
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was sent.
36
+ # @option attributes [Server, Integer, String] :server Matches the server the message was sent in.
35
37
  # @yield The block is executed when the event is raised.
36
38
  # @yieldparam event [MessageEvent] The event that was raised.
37
39
  # @return [MessageEventHandler] the event handler that was registered.
@@ -93,6 +95,8 @@ module Discordrb
93
95
  # @param attributes [Hash] The event's attributes.
94
96
  # @option attributes [String, Integer] :id Matches the ID of the message that was edited.
95
97
  # @option attributes [String, Integer, Channel] :in Matches the channel the message was edited in.
98
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was edited.
99
+ # @option attributes [Server, Integer, String] :server Matches the server the message was edited in.
96
100
  # @yield The block is executed when the event is raised.
97
101
  # @yieldparam event [MessageEditEvent] The event that was raised.
98
102
  # @return [MessageEditEventHandler] the event handler that was registered.
@@ -104,6 +108,7 @@ module Discordrb
104
108
  # @param attributes [Hash] The event's attributes.
105
109
  # @option attributes [String, Integer] :id Matches the ID of the message that was deleted.
106
110
  # @option attributes [String, Integer, Channel] :in Matches the channel the message was deleted in.
111
+ # @option attributes [Server, Integer, String] :server Matches the server the message was deleted in.
107
112
  # @yield The block is executed when the event is raised.
108
113
  # @yieldparam event [MessageDeleteEvent] The event that was raised.
109
114
  # @return [MessageDeleteEventHandler] the event handler that was registered.
@@ -118,6 +123,8 @@ module Discordrb
118
123
  # @param attributes [Hash] The event's attributes.
119
124
  # @option attributes [String, Integer] :id Matches the ID of the message that was updated.
120
125
  # @option attributes [String, Integer, Channel] :in Matches the channel the message was updated in.
126
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was updated.
127
+ # @option attributes [Server, Integer, String] :server Matches the server the message was updated in.
121
128
  # @yield The block is executed when the event is raised.
122
129
  # @yieldparam event [MessageUpdateEvent] The event that was raised.
123
130
  # @return [MessageUpdateEventHandler] the event handler that was registered.
@@ -200,6 +207,7 @@ module Discordrb
200
207
  # @option attributes [Time] :after Matches a time after the time the message was sent at.
201
208
  # @option attributes [Time] :before Matches a time before the time the message was sent at.
202
209
  # @option attributes [Boolean] :private Matches whether or not the channel is private.
210
+ # @option attributes [Integer, String, Symbol] :type Matches the type of the message that was sent.
203
211
  # @yield The block is executed when the event is raised.
204
212
  # @yieldparam event [MentionEvent] The event that was raised.
205
213
  # @return [MentionEventHandler] the event handler that was registered.
@@ -635,6 +643,44 @@ module Discordrb
635
643
  register_event(ChannelSelectEvent, attributes, block)
636
644
  end
637
645
 
646
+ # This **event** is raised whenever a message is pinned or unpinned.
647
+ # @param attributes [Hash] The event's attributes.
648
+ # @option attributes [String, Integer, Channel] :channel A channel to match against.
649
+ # @option attributes [String, Integer, Server] :server A server to match against.
650
+ # @yield The block is executed when the event is raised.
651
+ # @yieldparam event [ChannelPinsUpdateEvent] The event that was raised.
652
+ # @return [ChannelPinsUpdateEventHandler] The event handler that was registered.
653
+ def channel_pins_update(attributes = {}, &block)
654
+ register_event(ChannelPinsUpdateEvent, attributes, block)
655
+ end
656
+
657
+ # This **event** is raised whenever an autocomplete interaction is created.
658
+ # @param name [String, Symbol, nil] An option name to match against.
659
+ # @param attributes [Hash] The event's attributes.
660
+ # @option attributes [String, Integer] :command_id A command ID to match against.
661
+ # @option attributes [String, Symbol] :subcommand A subcommand name to match against.
662
+ # @option attributes [String, Symbol] :subcommand_group A subcommand group to match against.
663
+ # @option attributes [String, Symbol] :command_name A command name to match against.
664
+ # @option attributes [String, Integer, Server] :server A server to match against.
665
+ # @yield The block is executed when the event is raised.
666
+ # @yieldparam event [AutocompleteEvent] The event that was raised.
667
+ # @return [AutocompleteEventHandler] The event handler that was registered.
668
+ def autocomplete(name = nil, attributes = {}, &block)
669
+ register_event(AutocompleteEvent, attributes.merge!({ name: name }), block)
670
+ end
671
+
672
+ # This **event** is raised whenever an application command's permissions are updated.
673
+ # @param attributes [Hash] The event's attributes.
674
+ # @option attributes [String, Integer] :command_id A command ID to match against.
675
+ # @option attributes [String, Integer] :application_id An application ID to match against.
676
+ # @option attributes [String, Integer, Server] :server A server to match against.
677
+ # @yield The block is executed when the event is raised.
678
+ # @yieldparam event [ApplicationCommandPermissionsUpdateEvent] The event that was raised.
679
+ # @return [ApplicationCommandPermissionsUpdateEventHandler] The event handler that was registered.
680
+ def application_command_permissions_update(attributes = {}, &block)
681
+ register_event(ApplicationCommandPermissionsUpdateEvent, attributes, block)
682
+ end
683
+
638
684
  # This **event** is raised for every dispatch received over the gateway, whether supported by discordrb or not.
639
685
  # @param attributes [Hash] The event's attributes.
640
686
  # @option attributes [String, Symbol, Regexp] :type Matches the event type of the dispatch.
@@ -121,7 +121,7 @@ module Discordrb
121
121
 
122
122
  # @!visibility private
123
123
  def flag_set?(sym)
124
- !(@flags & FLAGS[sym]).zero?
124
+ !@flags.nobits?(FLAGS[sym])
125
125
  end
126
126
 
127
127
  # Timestamps for the start and end of instanced activities
@@ -22,6 +22,7 @@ module Discordrb
22
22
  # @return [User] the user object of the owner
23
23
  attr_reader :owner
24
24
 
25
+ # @!visibility private
25
26
  def initialize(data, bot)
26
27
  @bot = bot
27
28
 
@@ -5,9 +5,6 @@ module Discordrb
5
5
  class Attachment
6
6
  include IDObject
7
7
 
8
- # @return [Message] the message this attachment belongs to.
9
- attr_reader :message
10
-
11
8
  # @return [String] the CDN URL this attachment can be downloaded at.
12
9
  attr_reader :url
13
10
 
@@ -37,6 +34,15 @@ module Discordrb
37
34
  attr_reader :ephemeral
38
35
  alias_method :ephemeral?, :ephemeral
39
36
 
37
+ # @return [Float, nil] the duration of the voice message in seconds.
38
+ attr_reader :duration_seconds
39
+
40
+ # @return [String, nil] the base64 encoded bytearray representing a sampled waveform for a voice message.
41
+ attr_reader :waveform
42
+
43
+ # @return [Integer] the flags set on this attachment combined as a bitfield.
44
+ attr_reader :flags
45
+
40
46
  # @!visibility private
41
47
  def initialize(data, message, bot)
42
48
  @bot = bot
@@ -56,6 +62,10 @@ module Discordrb
56
62
  @content_type = data['content_type']
57
63
 
58
64
  @ephemeral = data['ephemeral']
65
+
66
+ @duration_seconds = data['duration_secs']&.to_f
67
+ @waveform = data['waveform']
68
+ @flags = data['flags'] || 0
59
69
  end
60
70
 
61
71
  # @return [true, false] whether this file is an image file.
@@ -67,5 +77,15 @@ module Discordrb
67
77
  def spoiler?
68
78
  @filename.start_with? 'SPOILER_'
69
79
  end
80
+
81
+ # @return [Message, nil] the message this attachment object belongs to.
82
+ def message
83
+ @message unless @message.is_a?(Snapshot)
84
+ end
85
+
86
+ # @return [Snapshot, nil] the message snapshot this attachment object belongs to.
87
+ def snapshot
88
+ @message unless @message.is_a?(Message)
89
+ end
70
90
  end
71
91
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A decoration displayed on a user's avatar.
5
+ class AvatarDecoration
6
+ # @return [Integer] ID of the avatar decoration's SKU.
7
+ attr_reader :sku_id
8
+
9
+ # @return [String] ID that can be used to generate an avatar decoration URL.
10
+ # @see #url
11
+ attr_reader :decoration_id
12
+
13
+ # @!visibility private
14
+ def initialize(data, bot)
15
+ @bot = bot
16
+ @sku_id = data['sku_id']&.to_i
17
+ @decoration_id = data['asset']
18
+ end
19
+
20
+ # Utility method to get an avatar decoration URL.
21
+ # @return [String] the URL to the avatar decoration.
22
+ def url
23
+ API.avatar_decoration_url(@decoration_id)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A call in a private channel.
5
+ class Call
6
+ # @return [Time, nil] the time at when the call ended.
7
+ attr_reader :ended_at
8
+
9
+ # @!visibility private
10
+ def initialize(data, bot)
11
+ @bot = bot
12
+ @participant_ids = data['participants'] || []
13
+ @ended_at = Time.iso8601(data['ended_timestamp']) if data['ended_timestamp']
14
+ end
15
+
16
+ # Get the users that participated in this call.
17
+ # @return [Array<User>] the participants of this call.
18
+ def participants
19
+ @participants ||= @participant_ids.map { |participant| @bot.user(participant) }
20
+ end
21
+ end
22
+ end
@@ -64,7 +64,7 @@ module Discordrb
64
64
  attr_reader :rate_limit_per_user
65
65
  alias_method :slowmode_rate, :rate_limit_per_user
66
66
 
67
- # @return [Integer, nil] An approximate count of messages sent in a thread. Stops counting at 50.
67
+ # @return [Integer, nil] An approximate count of messages sent in a thread, excluding deleted messages.
68
68
  attr_reader :message_count
69
69
 
70
70
  # @return [Integer, nil] An approximate count of members in a thread. Stops counting at 50.
@@ -72,6 +72,7 @@ module Discordrb
72
72
 
73
73
  # @return [true, false, nil] Whether or not this thread is archived.
74
74
  attr_reader :archived
75
+ alias_method :archived?, :archived
75
76
 
76
77
  # @return [Integer, nil] How long after the last message before a thread is automatically archived.
77
78
  attr_reader :auto_archive_duration
@@ -89,9 +90,13 @@ module Discordrb
89
90
  # @return [Integer, nil] Member flags for this thread, used for notifications.
90
91
  attr_reader :member_flags
91
92
 
92
- # @return [true, false] For private threads, determines whether non-moderators can add other non-moderators to
93
+ # @return [true, false, nil] For private threads, determines whether non-moderators can add other non-moderators to
93
94
  # a thread.
94
95
  attr_reader :invitable
96
+ alias_method :invitable?, :invitable
97
+
98
+ # @return [Time, nil] The time at when the last pinned message was pinned in this channel.
99
+ attr_reader :last_pin_timestamp
95
100
 
96
101
  # @return [true, false] whether or not this channel is a PM or group channel.
97
102
  def private?
@@ -158,6 +163,7 @@ module Discordrb
158
163
  @member_flags = member['flags']
159
164
  end
160
165
 
166
+ process_last_pin_timestamp(data['last_pin_timestamp'])
161
167
  process_permission_overwrites(data['permission_overwrites'])
162
168
  end
163
169
 
@@ -428,9 +434,10 @@ module Discordrb
428
434
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
429
435
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
430
436
  # @param components [View, Array<Hash>] Interaction components to associate with this message.
437
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
431
438
  # @return [Message] the message that was sent.
432
- def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
433
- @bot.send_message(@id, content, tts, embed, attachments, allowed_mentions, message_reference, components)
439
+ def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
440
+ @bot.send_message(@id, content, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
434
441
  end
435
442
 
436
443
  alias_method :send, :send_message
@@ -444,8 +451,9 @@ module Discordrb
444
451
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
445
452
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
446
453
  # @param components [View, Array<Hash>] Interaction components to associate with this message.
447
- def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
448
- @bot.send_temporary_message(@id, content, timeout, tts, embed, attachments, allowed_mentions, message_reference, components)
454
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
455
+ def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
456
+ @bot.send_temporary_message(@id, content, timeout, tts, embed, attachments, allowed_mentions, message_reference, components, flags)
449
457
  end
450
458
 
451
459
  # Convenience method to send a message with an embed.
@@ -461,22 +469,68 @@ module Discordrb
461
469
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
462
470
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
463
471
  # @param components [View, Array<Hash>] Interaction components to associate with this message.
472
+ # @param flags [Integer] Flags for this message. Currently only SUPPRESS_EMBEDS (1 << 2) and SUPPRESS_NOTIFICATIONS (1 << 12) can be set.
464
473
  # @yield [embed] Yields the embed to allow for easy building inside a block.
465
474
  # @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
466
475
  # @return [Message] The resulting message.
467
- def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil)
476
+ def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil, flags = 0)
468
477
  embed ||= Discordrb::Webhooks::Embed.new
469
478
  view = Discordrb::Webhooks::View.new
470
479
 
471
480
  yield(embed, view) if block_given?
472
481
 
473
- send_message(message, tts, embed, attachments, allowed_mentions, message_reference, components || view.to_a)
482
+ send_message(message, tts, embed, attachments, allowed_mentions, message_reference, components || view.to_a, flags)
483
+ end
484
+
485
+ # Send a message to this channel.
486
+ # @example This sends a silent message with an embed.
487
+ # channel.send_message!(content: 'Hi <@171764626755813376>', flags: :suppress_notifications) do |builder|
488
+ # builder.add_embed do |embed|
489
+ # embed.title = 'The Ruby logo'
490
+ # embed.image = Discordrb::Webhooks::EmbedImage.new(url: 'https://www.ruby-lang.org/images/header-ruby-logo.png')
491
+ # end
492
+ # end
493
+ # @param content [String] The content of the message. Should not be longer than 2000 characters or it will result in an error.
494
+ # @param timeout [Float, nil] The amount of time in seconds after which the message sent will be deleted, or `nil` if the message should not be deleted.
495
+ # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
496
+ # @param embeds [Array<Hash, Webhooks::Embed>] The embeds that should be attached to the message.
497
+ # @param attachments [Array<File>] Files that can be referenced in embeds and components via `attachment://file.png`.
498
+ # @param allowed_mentions [Hash, Discordrb::AllowedMentions, nil] Mentions that are allowed to ping on this message.
499
+ # @param reference [Message, String, Integer, Hash, nil] The optional message, or message ID, to reply to or forward.
500
+ # @param components [View, Array<#to_h>] Interaction components to associate with this message.
501
+ # @param flags [Integer, Symbol, Array<Symbol, Integer>] Flags for this message. Currently only `:suppress_embeds` (1 << 2), `:suppress_notifications` (1 << 12), and `:uikit_components` (1 << 15) can be set.
502
+ # @param has_components [true, false] Whether this message includes any V2 components. Enabling this disables sending content and embeds.
503
+ # @param nonce [nil, String, Integer, false] The 25 character nonce that should be used when sending this message.
504
+ # @param enforce_nonce [true, false] Whether the provided nonce should be enforced and used for message de-duplication.
505
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the builder overwrite method data.
506
+ # @yieldparam view [Webhooks::View] An optional component builder. Arguments passed to the builder overwrite method data.
507
+ # @return [Message, nil] The resulting message that was created, or `nil` if the `timeout` parameter was set to a non `nil` value.
508
+ def send_message!(content: '', timeout: nil, tts: false, embeds: [], attachments: nil, allowed_mentions: nil, reference: nil, components: nil, flags: 0, has_components: false, nonce: nil, enforce_nonce: false)
509
+ builder = Discordrb::Webhooks::Builder.new
510
+ view = Discordrb::Webhooks::View.new
511
+
512
+ builder.tts = tts
513
+ builder.content = content
514
+ embeds&.each { |embed| builder << embed }
515
+ builder.allowed_mentions = allowed_mentions
516
+
517
+ yield(builder, view) if block_given?
518
+
519
+ flags = Array(flags).map { |flag| Discordrb::Message::FLAGS[flag] || flag }.reduce(&:|)
520
+ flags |= (1 << 15) if has_components
521
+ builder = builder.to_json_hash
522
+
523
+ if timeout
524
+ @bot.send_temporary_message(@id, builder[:content], timeout, builder[:tts], builder[:embeds], attachments, builder[:allowed_mentions], reference, components&.to_a || view.to_a, flags, nonce, enforce_nonce)
525
+ else
526
+ @bot.send_message(@id, builder[:content], builder[:tts], builder[:embeds], attachments, builder[:allowed_mentions], reference, components&.to_a || view.to_a, flags, nonce, enforce_nonce)
527
+ end
474
528
  end
475
529
 
476
530
  # Sends multiple messages to a channel
477
531
  # @param content [Array<String>] The messages to send.
478
532
  def send_multiple(content)
479
- content.each { |e| send_message(e) }
533
+ content.each { |text| send_message!(content: text) }
480
534
  end
481
535
 
482
536
  # Splits a message into chunks whose length is at most the Discord character limit, then sends them individually.
@@ -594,6 +648,7 @@ module Discordrb
594
648
  # @!visibility private
595
649
  def update_from(other)
596
650
  @name = other.name
651
+ @type = other.type
597
652
  @position = other.position
598
653
  @topic = other.topic
599
654
  @recipients = other.recipients
@@ -603,6 +658,13 @@ module Discordrb
603
658
  @nsfw = other.nsfw
604
659
  @parent_id = other.parent_id
605
660
  @rate_limit_per_user = other.rate_limit_per_user
661
+ @archived = other.archived?
662
+ @auto_archive_duration = other.auto_archive_duration
663
+ @archive_timestamp = other.archive_timestamp
664
+ @locked = other.locked?
665
+ @invitable = other.invitable?
666
+ @message_count = other.message_count
667
+ @last_pin_timestamp = other.last_pin_timestamp
606
668
  end
607
669
 
608
670
  # The list of users currently in this channel. For a voice channel, it will return all the members currently
@@ -656,11 +718,27 @@ module Discordrb
656
718
 
657
719
  alias_method :message, :load_message
658
720
 
659
- # Requests all pinned messages in a channel.
660
- # @return [Array<Message>] the received messages.
661
- def pins
662
- msgs = API::Channel.pinned_messages(@bot.token, @id)
663
- JSON.parse(msgs).map { |msg| Message.new(msg, @bot) }
721
+ # Requests the pinned messages in a channel.
722
+ # @param limit [Integer, nil] the limit of how many pinned messages to retrieve. `nil` will return all the pinned messages.
723
+ # @return [Array<Message>] the messages pinned in the channel.
724
+ def pins(limit: 50)
725
+ get_pins = proc do |fetch_limit, before = nil|
726
+ resp = API::Channel.pinned_messages(@bot.token, @id, fetch_limit, before&.iso8601)
727
+ JSON.parse(resp)['items'].map { |pin| Message.new(pin['message'].merge({ 'pinned_at' => pin['pinned_at'] }), @bot) }
728
+ end
729
+
730
+ # Can be done without pagination.
731
+ return get_pins.call(limit) if limit && limit <= 50
732
+
733
+ paginator = Paginator.new(limit, :down) do |last_page|
734
+ if last_page && last_page.count < 50
735
+ []
736
+ else
737
+ get_pins.call(50, last_page&.last&.pinned_at)
738
+ end
739
+ end
740
+
741
+ paginator.to_a
664
742
  end
665
743
 
666
744
  # Delete the last N messages on this channel.
@@ -856,6 +934,43 @@ module Discordrb
856
934
  Channel.new(JSON.parse(data), @bot, @server)
857
935
  end
858
936
 
937
+ # Start a thread in a forum or media channel.
938
+ # @param name [String] The name of the forum post to create.
939
+ # @param auto_archive_duration [Integer, nil] How long before the post is automatically archived.
940
+ # @param rate_limit_per_user [Integer, nil] The slowmode rate of the forum post to create.
941
+ # @param tags [Array<#resolve_id>, nil] The tags of the forum channel to apply onto the forum post.
942
+ # @param content [String, nil] The content of the forum post's starter message.
943
+ # @param embeds [Array<Hash, Webhooks::Embed>, nil] The embeds that should be attached to the forum post's starter message.
944
+ # @param allowed_mentions [Hash, Discordrb::AllowedMentions, nil] Mentions that are allowed to ping on this forum post's starter message.
945
+ # @param components [Webhooks::View, Array<#to_h>, nil] The interaction components to associate with this forum post's starter message.
946
+ # @param stickers [Array<#resolve_id>, nil] The stickers to include in the forum post's starter message.
947
+ # @param attachments [Array<File>, nil] Files that can be referenced in embeds and components via `attachment://file.png`.
948
+ # @param flags [Integer, Symbol, Array<Symbol, Integer>, nil] The flags to set on the forum post's starter message. Currently only `:suppress_embeds` (1 << 2), `:suppress_notifications` (1 << 12), and `:uikit_components` (1 << 15) can be set.
949
+ # @param has_components [true, false] Whether the starter message for this forum post includes any V2 components. Enabling this disables sending content and embeds.
950
+ # @param reason [String, nil] The reason for creating this forum post.
951
+ # @yieldparam builder [Webhooks::Builder] An optional message builder. Arguments passed to the builder overwrite method data.
952
+ # @yieldparam view [Webhooks::View] An optional component builder. Arguments passed to the builder overwrite method data.
953
+ # @return [Message] the starter message of the forum post. The forum post that was created can be accessed via {Message#thread}.
954
+ def start_forum_thread(name:, auto_archive_duration: nil, rate_limit_per_user: nil, tags: nil, content: nil, embeds: nil, allowed_mentions: nil, components: nil, stickers: nil, attachments: nil, flags: nil, has_components: false, reason: nil)
955
+ builder = Discordrb::Webhooks::Builder.new
956
+ view = Discordrb::Webhooks::View.new
957
+
958
+ builder.content = content
959
+ embeds&.each { |embed| builder << embed }
960
+ builder.allowed_mentions = allowed_mentions
961
+
962
+ yield(builder, view) if block_given?
963
+
964
+ flags = Array(flags).map { |flag| Discordrb::Message::FLAGS[flag] || flag }.reduce(&:|)
965
+ flags |= (1 << 15) if has_components
966
+ builder = builder.to_json_hash
967
+
968
+ message = { content: builder[:content], embeds: builder[:embeds], allowed_mentions: builder[:allowed_mentions], components: components&.to_a || view.to_a, sticker_ids: stickers&.map(&:resolve_id), flags: flags }
969
+ response = JSON.parse(API::Channel.start_thread_in_forum_or_media_channel(@bot.token, @id, name, message.compact, attachments, rate_limit_per_user, auto_archive_duration, tags&.map(&:resolve_id), reason))
970
+
971
+ Message.new(response['message'].merge!('channel_id' => response['id'], 'thread' => response), @bot)
972
+ end
973
+
859
974
  # @!group Threads
860
975
 
861
976
  # Join this thread.
@@ -915,6 +1030,14 @@ module Discordrb
915
1030
  @recipients.delete(recipient)
916
1031
  end
917
1032
 
1033
+ # Set the last pin timestamp of a channel.
1034
+ # @param time [String, nil] the time of the last pinned message in the channel
1035
+ # @note For internal use only
1036
+ # @!visibility private
1037
+ def process_last_pin_timestamp(time)
1038
+ @last_pin_timestamp = time ? Time.parse(time) : time
1039
+ end
1040
+
918
1041
  # Updates the cached data with new data
919
1042
  # @note For internal use only
920
1043
  # @!visibility private
@@ -941,8 +1064,10 @@ module Discordrb
941
1064
 
942
1065
  private
943
1066
 
1067
+ # rubocop:disable Lint/UselessConstantScoping
944
1068
  # For bulk_delete checking
945
1069
  TWO_WEEKS = 86_400 * 14
1070
+ # rubocop:enable Lint/UselessConstantScoping
946
1071
 
947
1072
  # Deletes a list of messages on this channel using bulk delete.
948
1073
  def bulk_delete(ids, strict = false, reason = nil)
@@ -965,7 +1090,7 @@ module Discordrb
965
1090
  def update_channel_data(new_data)
966
1091
  new_nsfw = new_data[:nsfw].is_a?(TrueClass) || new_data[:nsfw].is_a?(FalseClass) ? new_data[:nsfw] : @nsfw
967
1092
  # send permission_overwrite only when explicitly set
968
- overwrites = new_data[:permission_overwrites] ? new_data[:permission_overwrites].map { |_, v| v.to_hash } : nil
1093
+ overwrites = new_data[:permission_overwrites] ? new_data[:permission_overwrites].map(&:to_hash) : nil
969
1094
  response = JSON.parse(API::Channel.update(@bot.token, @id,
970
1095
  new_data[:name] || @name,
971
1096
  new_data[:topic] || @topic,
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Collectibles are resources such as nameplates that can be collected by users.
5
+ class Collectibles
6
+ # @return [Nameplate, nil] the nameplate the user has collected or nil.
7
+ attr_reader :nameplate
8
+
9
+ # @!visibility private
10
+ def initialize(data, bot)
11
+ @bot = bot
12
+ @nameplate = Nameplate.new(data['nameplate'], bot) if data['nameplate']
13
+ end
14
+
15
+ # Collectable background images shown on a user's name in the member's tab.
16
+ class Nameplate
17
+ # @return [Integer] ID of the nameplate's SKU.
18
+ attr_reader :sku_id
19
+
20
+ # @return [String] the path to the nameplate asset.
21
+ attr_reader :asset
22
+
23
+ # @return [String] the label of the nameplate.
24
+ attr_reader :label
25
+
26
+ # @return [Symbol] the background color of the nameplate.
27
+ attr_reader :palette
28
+
29
+ # @!visibility private
30
+ def initialize(data, bot)
31
+ @bot = bot
32
+ @sku_id = data['sku_id']&.to_i
33
+ @asset = data['asset']
34
+ @label = data['label']
35
+ @palette = data['palette'].to_sym
36
+ end
37
+
38
+ # Utility method to get the URL of this nameplate.
39
+ # @return [String] CDN url of this nameplate.
40
+ def url
41
+ API.nameplate_url(@asset)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -5,9 +5,6 @@ module Discordrb
5
5
  # A freshly generated embed object will not appear in a message object
6
6
  # unless grabbed from its ID in a channel.
7
7
  class Embed
8
- # @return [Message] the message this embed object is contained in.
9
- attr_reader :message
10
-
11
8
  # @return [String] the URL this embed object is based on.
12
9
  attr_reader :url
13
10
 
@@ -70,6 +67,16 @@ module Discordrb
70
67
  @author = data['author'].nil? ? nil : EmbedAuthor.new(data['author'], self)
71
68
  @fields = data['fields'].nil? ? nil : data['fields'].map { |field| EmbedField.new(field, self) }
72
69
  end
70
+
71
+ # @return [Message, nil] the message this embed object is contained in.
72
+ def message
73
+ @message unless @message.is_a?(Snapshot)
74
+ end
75
+
76
+ # @return [Snapshot, nil] the message snapshot this embed object is contained in.
77
+ def snapshot
78
+ @message unless @message.is_a?(Message)
79
+ end
73
80
  end
74
81
 
75
82
  # An Embed footer for the embed object.
@@ -14,6 +14,21 @@ module Discordrb
14
14
  # @return [Array<Role>, nil] roles this emoji is active for, or nil if the emoji's server is unknown
15
15
  attr_reader :roles
16
16
 
17
+ # @return [User, nil] the user who uploaded this emoji, or nil if the emoji's server is unknown
18
+ attr_reader :creator
19
+
20
+ # @return [Boolean, nil] if the emoji requires colons to be used, or nil if the emoji's server is unknown
21
+ attr_reader :requires_colons
22
+ alias_method :requires_colons?, :requires_colons
23
+
24
+ # @return [Boolean, nil] whether this emoji is managed by an integration, or nil if the emoji's server is unknown
25
+ attr_reader :managed
26
+ alias_method :managed?, :managed
27
+
28
+ # @return [Boolean, nil] if this emoji is currently usable, or nil if the emoji's server is unknown
29
+ attr_reader :available
30
+ alias_method :available?, :available
31
+
17
32
  # @return [true, false] if the emoji is animated
18
33
  attr_reader :animated
19
34
  alias_method :animated?, :animated
@@ -25,8 +40,12 @@ module Discordrb
25
40
 
26
41
  @name = data['name']
27
42
  @server = server
28
- @id = data['id'].nil? ? nil : data['id'].to_i
43
+ @id = data['id']&.to_i
29
44
  @animated = data['animated']
45
+ @managed = data['managed']
46
+ @available = data['available']
47
+ @requires_colons = data['require_colons']
48
+ @creator = data['user'] ? bot.ensure_user(data['user']) : nil
30
49
 
31
50
  process_roles(data['roles']) if server
32
51
  end
@@ -9,6 +9,7 @@ module Discordrb
9
9
  # @return [Integer] this account's ID.
10
10
  attr_reader :id
11
11
 
12
+ # @!visibility private
12
13
  def initialize(data)
13
14
  @name = data['name']
14
15
  @id = data['id'].to_i
@@ -35,6 +36,7 @@ module Discordrb
35
36
  # @return [User, nil] the bot associated with this application.
36
37
  attr_reader :bot
37
38
 
39
+ # @!visibility private
38
40
  def initialize(data, bot)
39
41
  @id = data['id'].to_i
40
42
  @name = data['name']
@@ -93,6 +95,7 @@ module Discordrb
93
95
  # @return [true, false] has this integration been revoked.
94
96
  attr_reader :revoked
95
97
 
98
+ # @!visibility private
96
99
  def initialize(data, bot, server)
97
100
  @bot = bot
98
101