discordrb 3.4.3 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +44 -18
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
  5. data/.github/workflows/codeql.yml +65 -0
  6. data/.markdownlint.json +4 -0
  7. data/.rubocop.yml +8 -2
  8. data/CHANGELOG.md +390 -225
  9. data/LICENSE.txt +1 -1
  10. data/README.md +37 -25
  11. data/discordrb-webhooks.gemspec +4 -1
  12. data/discordrb.gemspec +9 -6
  13. data/lib/discordrb/api/application.rb +202 -0
  14. data/lib/discordrb/api/channel.rb +177 -11
  15. data/lib/discordrb/api/interaction.rb +54 -0
  16. data/lib/discordrb/api/invite.rb +2 -2
  17. data/lib/discordrb/api/server.rb +40 -19
  18. data/lib/discordrb/api/user.rb +8 -3
  19. data/lib/discordrb/api/webhook.rb +57 -0
  20. data/lib/discordrb/api.rb +19 -5
  21. data/lib/discordrb/bot.rb +317 -32
  22. data/lib/discordrb/cache.rb +27 -22
  23. data/lib/discordrb/commands/command_bot.rb +6 -4
  24. data/lib/discordrb/commands/container.rb +1 -1
  25. data/lib/discordrb/commands/parser.rb +2 -2
  26. data/lib/discordrb/commands/rate_limiter.rb +1 -1
  27. data/lib/discordrb/container.rb +132 -3
  28. data/lib/discordrb/data/attachment.rb +15 -0
  29. data/lib/discordrb/data/audit_logs.rb +3 -3
  30. data/lib/discordrb/data/channel.rb +167 -23
  31. data/lib/discordrb/data/component.rb +229 -0
  32. data/lib/discordrb/data/integration.rb +42 -3
  33. data/lib/discordrb/data/interaction.rb +800 -0
  34. data/lib/discordrb/data/invite.rb +1 -1
  35. data/lib/discordrb/data/member.rb +108 -33
  36. data/lib/discordrb/data/message.rb +99 -19
  37. data/lib/discordrb/data/overwrite.rb +13 -7
  38. data/lib/discordrb/data/role.rb +58 -1
  39. data/lib/discordrb/data/server.rb +82 -80
  40. data/lib/discordrb/data/user.rb +69 -9
  41. data/lib/discordrb/data/webhook.rb +97 -4
  42. data/lib/discordrb/data.rb +3 -0
  43. data/lib/discordrb/errors.rb +44 -3
  44. data/lib/discordrb/events/channels.rb +1 -1
  45. data/lib/discordrb/events/interactions.rb +482 -0
  46. data/lib/discordrb/events/message.rb +9 -6
  47. data/lib/discordrb/events/presence.rb +21 -14
  48. data/lib/discordrb/events/reactions.rb +0 -1
  49. data/lib/discordrb/events/threads.rb +96 -0
  50. data/lib/discordrb/gateway.rb +30 -17
  51. data/lib/discordrb/permissions.rb +59 -34
  52. data/lib/discordrb/version.rb +1 -1
  53. data/lib/discordrb/voice/encoder.rb +2 -2
  54. data/lib/discordrb/voice/network.rb +18 -7
  55. data/lib/discordrb/voice/sodium.rb +3 -1
  56. data/lib/discordrb/voice/voice_bot.rb +3 -3
  57. data/lib/discordrb/webhooks.rb +2 -0
  58. data/lib/discordrb.rb +37 -4
  59. metadata +48 -14
  60. data/.codeclimate.yml +0 -16
  61. data/.travis.yml +0 -32
  62. data/bin/travis_build_docs.sh +0 -17
@@ -70,7 +70,9 @@ module Discordrb::Commands
70
70
  # @option attributes [String] :quote_end Character that should end a quoted string (see
71
71
  # :advanced_functionality). Default is '"' or the same as :quote_start. Set to an empty string to disable.
72
72
  # @option attributes [true, false] :ignore_bots Whether the bot should ignore bot accounts or not. Default is false.
73
- def initialize(attributes = {})
73
+ def initialize(**attributes)
74
+ # TODO: This needs to be revisited. undefined attributes are treated
75
+ # as explicitly passed nils.
74
76
  super(
75
77
  log_mode: attributes[:log_mode],
76
78
  token: attributes[:token],
@@ -85,7 +87,7 @@ module Discordrb::Commands
85
87
  redact_token: attributes.key?(:redact_token) ? attributes[:redact_token] : true,
86
88
  ignore_bots: attributes[:ignore_bots],
87
89
  compress_mode: attributes[:compress_mode],
88
- intents: attributes[:intents]
90
+ intents: attributes[:intents] || :all
89
91
  )
90
92
 
91
93
  @prefix = attributes[:prefix]
@@ -335,7 +337,7 @@ module Discordrb::Commands
335
337
  return nil if chain.empty?
336
338
 
337
339
  args = chain.split(' ')
338
- execute_command(args[0].to_sym, event, args[1..-1])
340
+ execute_command(args[0].to_sym, event, args[1..])
339
341
  end
340
342
 
341
343
  # Sets the permission level of a user
@@ -443,7 +445,7 @@ module Discordrb::Commands
443
445
  def standard_prefix_trigger(message, prefix)
444
446
  return nil unless message.start_with? prefix
445
447
 
446
- message[prefix.length..-1]
448
+ message[prefix.length..]
447
449
  end
448
450
 
449
451
  def required_permissions?(member, required, channel = nil)
@@ -86,7 +86,7 @@ module Discordrb::Commands
86
86
  # Adds all commands from another container into this one. Existing commands will be overwritten.
87
87
  # @param container [Module] A module that `extend`s {CommandContainer} from which the commands will be added.
88
88
  def include_commands(container)
89
- handlers = container.instance_variable_get '@commands'
89
+ handlers = container.instance_variable_get :@commands
90
90
  return unless handlers
91
91
 
92
92
  @commands ||= {}
@@ -246,7 +246,7 @@ module Discordrb::Commands
246
246
 
247
247
  first_space = command.index ' '
248
248
  command_name = first_space ? command[0..first_space - 1] : command
249
- arguments = first_space ? command[first_space + 1..-1] : ''
249
+ arguments = first_space ? command[first_space + 1..] : ''
250
250
 
251
251
  # Append a previous sign if none is present
252
252
  arguments += @attributes[:previous] unless arguments.include? @attributes[:previous]
@@ -318,7 +318,7 @@ module Discordrb::Commands
318
318
  arg.split ' '
319
319
  end
320
320
 
321
- chain = chain[chain_args_index + 1..-1]
321
+ chain = chain[chain_args_index + 1..]
322
322
  end
323
323
 
324
324
  [chain_args, chain]
@@ -125,7 +125,7 @@ module Discordrb::Commands
125
125
  # Adds all the buckets from another RateLimiter onto this one.
126
126
  # @param limiter [Module] Another {RateLimiter} module
127
127
  def include_buckets(limiter)
128
- buckets = limiter.instance_variable_get('@buckets') || {}
128
+ buckets = limiter.instance_variable_get(:@buckets) || {}
129
129
  @buckets ||= {}
130
130
  @buckets.merge! buckets
131
131
  end
@@ -13,6 +13,7 @@ require 'discordrb/events/guilds'
13
13
  require 'discordrb/events/await'
14
14
  require 'discordrb/events/bans'
15
15
  require 'discordrb/events/reactions'
16
+ require 'discordrb/events/interactions'
16
17
 
17
18
  require 'discordrb/await'
18
19
 
@@ -522,6 +523,118 @@ module Discordrb
522
523
  register_event(InviteDeleteEvent, attributes, block)
523
524
  end
524
525
 
526
+ # This **event** is raised whenever an interaction event is received.
527
+ # @param attributes [Hash] The event's attributes.
528
+ # @option attributes [Integer, Symbol, String] :type The interaction type, can be the integer value or the name
529
+ # of the key in {Discordrb::Interaction::TYPES}.
530
+ # @option attributes [String, Integer, Server, nil] :server The server where this event was created. `nil` for DM channels.
531
+ # @option attributes [String, Integer, Channel] :channel The channel where this event was created.
532
+ # @option attributes [String, Integer, User] :user The user that triggered this event.
533
+ # @yield The block is executed when the event is raised.
534
+ # @yieldparam event [InteractionCreateEvent] The event that was raised.
535
+ # @return [InteractionCreateEventHandler] The event handler that was registered.
536
+ def interaction_create(attributes = {}, &block)
537
+ register_event(InteractionCreateEvent, attributes, block)
538
+ end
539
+
540
+ # This **event** is raised whenever an application command (slash command) is executed.
541
+ # @param name [Symbol] The name of the application command this handler is for.
542
+ # @param attributes [Hash] The event's attributes.
543
+ # @yield The block is executed when the event is raised.
544
+ # @yieldparam event [ApplicationCommandEvent] The event that was raised.
545
+ # @return [ApplicationCommandEventHandler] The event handler that was registered.
546
+ def application_command(name, attributes = {}, &block)
547
+ @application_commands ||= {}
548
+
549
+ unless block
550
+ @application_commands[name] ||= ApplicationCommandEventHandler.new(attributes, nil)
551
+ return @application_commands[name]
552
+ end
553
+
554
+ @application_commands[name] = ApplicationCommandEventHandler.new(attributes, block)
555
+ end
556
+
557
+ # This **event** is raised whenever an button interaction is created.
558
+ # @param attributes [Hash] The event's attributes.
559
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
560
+ # @option attributes [String, Integer, Message] :message The message to filter for.
561
+ # @yield The block is executed when the event is raised.
562
+ # @yieldparam event [ButtonEvent] The event that was raised.
563
+ # @return [ButtonEventHandler] The event handler that was registered.
564
+ def button(attributes = {}, &block)
565
+ register_event(ButtonEvent, attributes, block)
566
+ end
567
+
568
+ # This **event** is raised whenever an select string interaction is created.
569
+ # @param attributes [Hash] The event's attributes.
570
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
571
+ # @option attributes [String, Integer, Message] :message The message to filter for.
572
+ # @yield The block is executed when the event is raised.
573
+ # @yieldparam event [StringSelectEvent] The event that was raised.
574
+ # @return [StringSelectEventHandler] The event handler that was registered.
575
+ def string_select(attributes = {}, &block)
576
+ register_event(StringSelectEvent, attributes, block)
577
+ end
578
+
579
+ alias_method :select_menu, :string_select
580
+
581
+ # This **event** is raised whenever a modal is submitted.
582
+ # @param attributes [Hash] The event's attributes.
583
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
584
+ # @option attributes [String, Integer, Message] :message The message to filter for.
585
+ # @option attributes [String, Integer, Server, nil] :server The server where this event was created. `nil` for DM channels.
586
+ # @option attributes [String, Integer, Channel] :channel The channel where this event was created.
587
+ # @option attributes [String, Integer, User] :user The user that triggered this event. # @yield The block is executed when the event is raised.
588
+ # @yieldparam event [ModalSubmitEvent] The event that was raised.
589
+ # @return [ModalSubmitEventHandler] The event handler that was registered.
590
+ def modal_submit(attributes = {}, &block)
591
+ register_event(ModalSubmitEvent, attributes, block)
592
+ end
593
+
594
+ # This **event** is raised whenever an select user interaction is created.
595
+ # @param attributes [Hash] The event's attributes.
596
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
597
+ # @option attributes [String, Integer, Message] :message The message to filter for.
598
+ # @yield The block is executed when the event is raised.
599
+ # @yieldparam event [UserSelectEvent] The event that was raised.
600
+ # @return [UserSelectEventHandler] The event handler that was registered.
601
+ def user_select(attributes = {}, &block)
602
+ register_event(UserSelectEvent, attributes, block)
603
+ end
604
+
605
+ # This **event** is raised whenever an select role interaction is created.
606
+ # @param attributes [Hash] The event's attributes.
607
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
608
+ # @option attributes [String, Integer, Message] :message The message to filter for.
609
+ # @yield The block is executed when the event is raised.
610
+ # @yieldparam event [RoleSelectEvent] The event that was raised.
611
+ # @return [RoleSelectEventHandler] The event handler that was registered.
612
+ def role_select(attributes = {}, &block)
613
+ register_event(RoleSelectEvent, attributes, block)
614
+ end
615
+
616
+ # This **event** is raised whenever an select mentionable interaction is created.
617
+ # @param attributes [Hash] The event's attributes.
618
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
619
+ # @option attributes [String, Integer, Message] :message The message to filter for.
620
+ # @yield The block is executed when the event is raised.
621
+ # @yieldparam event [MentionableSelectEvent] The event that was raised.
622
+ # @return [MentionableSelectEventHandler] The event handler that was registered.
623
+ def mentionable_select(attributes = {}, &block)
624
+ register_event(MentionableSelectEvent, attributes, block)
625
+ end
626
+
627
+ # This **event** is raised whenever an select channel interaction is created.
628
+ # @param attributes [Hash] The event's attributes.
629
+ # @option attributes [String, Regexp] :custom_id A custom_id to match against.
630
+ # @option attributes [String, Integer, Message] :message The message to filter for.
631
+ # @yield The block is executed when the event is raised.
632
+ # @yieldparam event [ChannelSelectEvent] The event that was raised.
633
+ # @return [ChannelSelectEventHandler] The event handler that was registered.
634
+ def channel_select(attributes = {}, &block)
635
+ register_event(ChannelSelectEvent, attributes, block)
636
+ end
637
+
525
638
  # This **event** is raised for every dispatch received over the gateway, whether supported by discordrb or not.
526
639
  # @param attributes [Hash] The event's attributes.
527
640
  # @option attributes [String, Symbol, Regexp] :type Matches the event type of the dispatch.
@@ -552,9 +665,16 @@ module Discordrb
552
665
  @event_handlers[clazz].delete(handler)
553
666
  end
554
667
 
668
+ # Remove an application command handler
669
+ # @param name [String, Symbol] The name of the command handler to remove.
670
+ def remove_application_command_handler(name)
671
+ @application_commands.delete(name)
672
+ end
673
+
555
674
  # Removes all events from this event handler.
556
675
  def clear!
557
676
  @event_handlers&.clear
677
+ @application_commands&.clear
558
678
  end
559
679
 
560
680
  # Adds an event handler to this container. Usually, it's more expressive to just use one of the shorthand adder
@@ -570,11 +690,19 @@ module Discordrb
570
690
  # Adds all event handlers from another container into this one. Existing event handlers will be overwritten.
571
691
  # @param container [Module] A module that `extend`s {EventContainer} from which the handlers will be added.
572
692
  def include_events(container)
573
- handlers = container.instance_variable_get '@event_handlers'
574
- return unless handlers
693
+ application_command_handlers = container.instance_variable_get(:@application_commands)
694
+ handlers = container.instance_variable_get :@event_handlers
695
+ return unless handlers || application_command_handlers
575
696
 
576
697
  @event_handlers ||= {}
577
- @event_handlers.merge!(handlers) { |_, old, new| old + new }
698
+ @event_handlers.merge!(handlers || {}) { |_, old, new| old + new }
699
+
700
+ @application_commands ||= {}
701
+
702
+ @application_commands.merge!(application_command_handlers || {}) do |_, old, new|
703
+ old.subcommands.merge!(new.subcommands)
704
+ old
705
+ end
578
706
  end
579
707
 
580
708
  alias_method :include!, :include_events
@@ -612,6 +740,7 @@ module Discordrb
612
740
 
613
741
  include Discordrb::Events
614
742
 
743
+ # @return [EventHandler]
615
744
  def register_event(clazz, attributes, block)
616
745
  handler = EventContainer.handler_class(clazz).new(attributes, block)
617
746
 
@@ -27,6 +27,16 @@ module Discordrb
27
27
  # @return [Integer, nil] the height of an image file, in pixels, or `nil` if the file is not an image.
28
28
  attr_reader :height
29
29
 
30
+ # @return [String, nil] the attachment's description.
31
+ attr_reader :description
32
+
33
+ # @return [String, nil] the attachment's media type.
34
+ attr_reader :content_type
35
+
36
+ # @return [true, false] whether this attachment is ephemeral.
37
+ attr_reader :ephemeral
38
+ alias_method :ephemeral?, :ephemeral
39
+
30
40
  # @!visibility private
31
41
  def initialize(data, message, bot)
32
42
  @bot = bot
@@ -41,6 +51,11 @@ module Discordrb
41
51
 
42
52
  @width = data['width']
43
53
  @height = data['height']
54
+
55
+ @description = data['description']
56
+ @content_type = data['content_type']
57
+
58
+ @ephemeral = data['ephemeral']
44
59
  end
45
60
 
46
61
  # @return [true, false] whether this file is an image file.
@@ -177,7 +177,7 @@ module Discordrb
177
177
 
178
178
  # The inspect method is overwritten to give more useful output
179
179
  def inspect
180
- "<AuditLogs::Entry id=#{@id} action=#{@action} reason=#{@reason} action_type=#{@action_type} target_type=#{@target_type} count=#{@count} days=#{@days} members_removed=#{@members_removed}>"
180
+ "<AuditLogs::Entry id=#{@id} key=#{@key} action=#{@action} reason=#{@reason} action_type=#{@action_type} target_type=#{@target_type} count=#{@count} days=#{@days} members_removed=#{@members_removed}>"
181
181
  end
182
182
 
183
183
  # Process action changes
@@ -219,8 +219,8 @@ module Discordrb
219
219
  @old = Permissions.new(@old) if @old && @key == 'permissions'
220
220
  @new = Permissions.new(@new) if @new && @key == 'permissions'
221
221
 
222
- @old = @old.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
223
- @new = @new.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, allow: o['allow'], deny: o['deny']) } if @new && @key == 'permission_overwrites'
222
+ @old = @old.map { |o| Overwrite.new(o['id'], type: o['type'], allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
223
+ @new = @new.map { |o| Overwrite.new(o['id'], type: o['type'], allow: o['allow'], deny: o['deny']) } if @new && @key == 'permission_overwrites'
224
224
  end
225
225
 
226
226
  # @return [Channel, nil] the channel that was previously used in the server widget. Only present if the key for this change is `widget_channel_id`.
@@ -1,5 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'discordrb/webhooks/view'
4
+ require 'time'
5
+
3
6
  module Discordrb
4
7
  # A Discord channel, including data like the topic
5
8
  class Channel
@@ -13,23 +16,28 @@ module Discordrb
13
16
  group: 3,
14
17
  category: 4,
15
18
  news: 5,
16
- store: 6
19
+ store: 6,
20
+ news_thread: 10,
21
+ public_thread: 11,
22
+ private_thread: 12,
23
+ stage_voice: 13,
24
+ directory: 14,
25
+ forum: 15
17
26
  }.freeze
18
27
 
19
28
  # @return [String] this channel's name.
20
29
  attr_reader :name
21
30
 
22
- # @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
23
- attr_reader :server
24
-
25
- # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category
31
+ # @return [Integer, nil] the ID of the parent channel, if this channel is inside a category. If this channel is a
32
+ # thread, this is the text channel it is a child to.
26
33
  attr_reader :parent_id
27
34
 
28
35
  # @return [Integer] the type of this channel
29
36
  # @see TYPES
30
37
  attr_reader :type
31
38
 
32
- # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel.
39
+ # @return [Integer, nil] the ID of the owner of the group channel or nil if this is not a group channel. If this
40
+ # channel is a thread, this is the member that started the thread.
33
41
  attr_reader :owner_id
34
42
 
35
43
  # @return [Array<Recipient>, nil] the array of recipients of the private messages, or nil if this is not a Private channel
@@ -56,6 +64,35 @@ module Discordrb
56
64
  attr_reader :rate_limit_per_user
57
65
  alias_method :slowmode_rate, :rate_limit_per_user
58
66
 
67
+ # @return [Integer, nil] An approximate count of messages sent in a thread. Stops counting at 50.
68
+ attr_reader :message_count
69
+
70
+ # @return [Integer, nil] An approximate count of members in a thread. Stops counting at 50.
71
+ attr_reader :member_count
72
+
73
+ # @return [true, false, nil] Whether or not this thread is archived.
74
+ attr_reader :archived
75
+
76
+ # @return [Integer, nil] How long after the last message before a thread is automatically archived.
77
+ attr_reader :auto_archive_duration
78
+
79
+ # @return [Time, nil] The timestamp of when this threads status last changed.
80
+ attr_reader :archive_timestamp
81
+
82
+ # @return [true, false, nil] Whether this thread is locked or not.
83
+ attr_reader :locked
84
+ alias_method :locked?, :locked
85
+
86
+ # @return [Time, nil] When the current user joined this thread.
87
+ attr_reader :join_timestamp
88
+
89
+ # @return [Integer, nil] Member flags for this thread, used for notifications.
90
+ attr_reader :member_flags
91
+
92
+ # @return [true, false] For private threads, determines whether non-moderators can add other non-moderators to
93
+ # a thread.
94
+ attr_reader :invitable
95
+
59
96
  # @return [true, false] whether or not this channel is a PM or group channel.
60
97
  def private?
61
98
  pm? || group?
@@ -99,15 +136,44 @@ module Discordrb
99
136
  end
100
137
  else
101
138
  @name = data['name']
102
- @server = server || bot.server(data['guild_id'].to_i)
139
+ @server_id = server&.id || data['guild_id'].to_i
140
+ @server = server
103
141
  end
104
142
 
105
143
  @nsfw = data['nsfw'] || false
106
144
  @rate_limit_per_user = data['rate_limit_per_user'] || 0
145
+ @message_count = data['message_count']
146
+ @member_count = data['member_count']
147
+
148
+ if (metadata = data['thread_metadata'])
149
+ @archived = metadata['archived']
150
+ @auto_archive_duration = metadata['auto_archive_duration']
151
+ @archive_timestamp = Time.iso8601(metadata['archive_timestamp'])
152
+ @locked = metadata['locked']
153
+ @invitable = metadata['invitable']
154
+ end
155
+
156
+ if (member = data['member'])
157
+ @member_join = Time.iso8601(member['join_timestamp'])
158
+ @member_flags = member['flags']
159
+ end
107
160
 
108
161
  process_permission_overwrites(data['permission_overwrites'])
109
162
  end
110
163
 
164
+ # @return [Server, nil] the server this channel is on. If this channel is a PM channel, it will be nil.
165
+ # @raise [Discordrb::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
166
+ # authorized with the `bot` scope.
167
+ def server
168
+ return @server if @server
169
+ return nil if private?
170
+
171
+ @server = @bot.server(@server_id)
172
+ raise Discordrb::Errors::NoPermission, 'The bot does not have access to this server' unless @server
173
+
174
+ @server
175
+ end
176
+
111
177
  # @return [true, false] whether or not this channel is a text channel
112
178
  def text?
113
179
  @type.zero?
@@ -143,6 +209,26 @@ module Discordrb
143
209
  @type == 6
144
210
  end
145
211
 
212
+ # @return [true, false] whether or not this channel is a news thread.
213
+ def news_thread?
214
+ @type == 10
215
+ end
216
+
217
+ # @return [true, false] whether or not this channel is a public thread.
218
+ def public_thread?
219
+ @type == 11
220
+ end
221
+
222
+ # @return [true, false] whether or not this channel is a private thread.
223
+ def private_thread?
224
+ @type == 12
225
+ end
226
+
227
+ # @return [true, false] whether or not this channel is a thread.
228
+ def thread?
229
+ news_thread? || public_thread? || private_thread?
230
+ end
231
+
146
232
  # @return [Channel, nil] the category channel, if this channel is in a category
147
233
  def category
148
234
  @bot.channel(@parent_id) if @parent_id
@@ -199,7 +285,7 @@ module Discordrb
199
285
  ids = if parent
200
286
  parent.children
201
287
  else
202
- @server.channels.reject(&:parent_id).select { |c| c.type == @type }
288
+ server.channels.reject(&:parent_id).select { |c| c.type == @type }
203
289
  end.sort_by(&:position).map(&:id)
204
290
 
205
291
  # Move our channel ID after the target ID by deleting it,
@@ -225,7 +311,7 @@ module Discordrb
225
311
  move_argument << hash
226
312
  end
227
313
 
228
- API::Server.update_channel_positions(@bot.token, @server.id, move_argument)
314
+ API::Server.update_channel_positions(@bot.token, @server_id, move_argument)
229
315
  end
230
316
 
231
317
  # Sets whether this channel is NSFW
@@ -262,9 +348,9 @@ module Discordrb
262
348
 
263
349
  # Sets the amount of time (in seconds) users have to wait in between sending messages.
264
350
  # @param rate [Integer]
265
- # @raise [ArgumentError] if value isn't between 0 and 120
351
+ # @raise [ArgumentError] if value isn't between 0 and 21600
266
352
  def rate_limit_per_user=(rate)
267
- raise ArgumentError, 'rate_limit_per_user must be between 0 and 120' unless rate.between?(0, 120)
353
+ raise ArgumentError, 'rate_limit_per_user must be between 0 and 21600' unless rate.between?(0, 21_600)
268
354
 
269
355
  update_channel_data(rate_limit_per_user: rate)
270
356
  end
@@ -341,9 +427,10 @@ module Discordrb
341
427
  # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
342
428
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
343
429
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
430
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
344
431
  # @return [Message] the message that was sent.
345
- def send_message(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
346
- @bot.send_message(@id, content, tts, embed, attachments, allowed_mentions, message_reference)
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)
347
434
  end
348
435
 
349
436
  alias_method :send, :send_message
@@ -356,8 +443,9 @@ module Discordrb
356
443
  # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
357
444
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
358
445
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
359
- def send_temporary_message(content, timeout, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
360
- @bot.send_temporary_message(@id, content, timeout, tts, embed, attachments, allowed_mentions, message_reference)
446
+ # @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)
361
449
  end
362
450
 
363
451
  # Convenience method to send a message with an embed.
@@ -372,13 +460,17 @@ module Discordrb
372
460
  # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
373
461
  # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
374
462
  # @param message_reference [Message, String, Integer, nil] The message, or message ID, to reply to if any.
463
+ # @param components [View, Array<Hash>] Interaction components to associate with this message.
375
464
  # @yield [embed] Yields the embed to allow for easy building inside a block.
376
465
  # @yieldparam embed [Discordrb::Webhooks::Embed] The embed from the parameters, or a new one.
377
466
  # @return [Message] The resulting message.
378
- def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil)
467
+ def send_embed(message = '', embed = nil, attachments = nil, tts = false, allowed_mentions = nil, message_reference = nil, components = nil)
379
468
  embed ||= Discordrb::Webhooks::Embed.new
380
- yield(embed) if block_given?
381
- send_message(message, tts, embed, attachments, allowed_mentions, message_reference)
469
+ view = Discordrb::Webhooks::View.new
470
+
471
+ yield(embed, view) if block_given?
472
+
473
+ send_message(message, tts, embed, attachments, allowed_mentions, message_reference, components || view.to_a)
382
474
  end
383
475
 
384
476
  # Sends multiple messages to a channel
@@ -518,9 +610,9 @@ module Discordrb
518
610
  # @return [Array<Member>] the users in this channel
519
611
  def users
520
612
  if text?
521
- @server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
613
+ server.online_members(include_idle: true).select { |u| u.can_read_messages? self }
522
614
  elsif voice?
523
- @server.voice_states.map { |id, voice_state| @server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }.compact
615
+ server.voice_states.filter_map { |id, voice_state| server.member(id) if !voice_state.voice_channel.nil? && voice_state.voice_channel.id == @id }
524
616
  end
525
617
  end
526
618
 
@@ -554,9 +646,11 @@ module Discordrb
554
646
  # @param message_id [Integer] The ID of the message to retrieve.
555
647
  # @return [Message, nil] the retrieved message, or `nil` if it couldn't be found.
556
648
  def load_message(message_id)
649
+ raise ArgumentError, 'message_id cannot be nil' if message_id.nil?
650
+
557
651
  response = API::Channel.message(@bot.token, @id, message_id)
558
652
  Message.new(JSON.parse(response), @bot)
559
- rescue RestClient::ResourceNotFound
653
+ rescue Discordrb::Errors::UnknownMessage
560
654
  nil
561
655
  end
562
656
 
@@ -742,9 +836,59 @@ module Discordrb
742
836
  invites.map { |invite_data| Invite.new(invite_data, @bot) }
743
837
  end
744
838
 
839
+ # Start a thread.
840
+ # @param name [String] The name of the thread.
841
+ # @param auto_archive_duration [60, 1440, 4320, 10080] How long before a thread is automatically
842
+ # archived.
843
+ # @param message [Message, Integer, String] The message to reference when starting this thread.
844
+ # @param type [Symbol, Integer] The type of thread to create. Can be a key from {TYPES} or the value.
845
+ # @return [Channel]
846
+ def start_thread(name, auto_archive_duration, message: nil, type: 11)
847
+ message_id = message&.id || message
848
+ type = TYPES[type] || type
849
+
850
+ data = if message
851
+ API::Channel.start_thread_with_message(@bot.token, @id, message_id, name, auto_archive_duration)
852
+ else
853
+ API::Channel.start_thread_without_message(@bot.token, @id, name, auto_archive_duration, type)
854
+ end
855
+
856
+ Channel.new(JSON.parse(data), @bot, @server)
857
+ end
858
+
859
+ # @!group Threads
860
+
861
+ # Join this thread.
862
+ def join_thread
863
+ @bot.join_thread(@id)
864
+ end
865
+
866
+ # Leave this thread
867
+ def leave_thread
868
+ @bot.leave_thread(@id)
869
+ end
870
+
871
+ # Members in the thread.
872
+ def members
873
+ @bot.thread_members[@id].collect { |id| @server_id ? @bot.member(@server_id, id) : @bot.user(id) }
874
+ end
875
+
876
+ # Add a member to the thread
877
+ # @param member [Member, Integer, String] The member, or ID of the member, to add to this thread.
878
+ def add_member(member)
879
+ @bot.add_thread_member(@id, member)
880
+ end
881
+
882
+ # @param member [Member, Integer, String] The member, or ID of the member, to remove from a thread.
883
+ def remove_member(member)
884
+ @bot.remove_thread_member(@id, member)
885
+ end
886
+
887
+ # @!endgroup
888
+
745
889
  # The default `inspect` method is overwritten to give more useful output.
746
890
  def inspect
747
- "<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server}>"
891
+ "<Channel name=#{@name} id=#{@id} topic=\"#{@topic}\" type=#{@type} position=#{@position} server=#{@server || @server_id}>"
748
892
  end
749
893
 
750
894
  # Adds a recipient to a group channel.
@@ -790,7 +934,7 @@ module Discordrb
790
934
 
791
935
  # @return [String] a URL that a user can use to navigate to this channel in the client
792
936
  def link
793
- "https://discord.com/channels/#{@server&.id || '@me'}/#{@channel.id}"
937
+ "https://discord.com/channels/#{@server_id || '@me'}/#{@channel.id}"
794
938
  end
795
939
 
796
940
  alias_method :jump_link, :link