discorb 0.15.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/build_main.yml +2 -2
  4. data/.github/workflows/build_version.yml +1 -1
  5. data/.github/workflows/codeql-analysis.yml +1 -1
  6. data/.github/workflows/lint-push.yml +3 -5
  7. data/.github/workflows/lint.yml +1 -1
  8. data/.github/workflows/spec.yml +30 -0
  9. data/.lefthook/commit-msg/validator.rb +5 -0
  10. data/.rspec +2 -0
  11. data/.rspec_parallel +2 -0
  12. data/.rubocop.yml +49 -8
  13. data/Changelog.md +32 -1
  14. data/Gemfile +14 -8
  15. data/Rakefile +46 -25
  16. data/bin/console +3 -3
  17. data/docs/Examples.md +1 -1
  18. data/docs/application_command.md +138 -46
  19. data/docs/cli/irb.md +2 -2
  20. data/docs/cli/new.md +14 -9
  21. data/docs/cli/run.md +7 -11
  22. data/docs/cli.md +17 -10
  23. data/docs/events.md +257 -193
  24. data/docs/extension.md +1 -2
  25. data/docs/faq.md +0 -1
  26. data/docs/tutorial.md +12 -12
  27. data/docs/voice_events.md +106 -106
  28. data/examples/commands/message.rb +63 -0
  29. data/examples/commands/permission.rb +18 -0
  30. data/examples/commands/slash.rb +44 -0
  31. data/examples/commands/user.rb +51 -0
  32. data/examples/components/authorization_button.rb +2 -2
  33. data/examples/components/select_menu.rb +2 -2
  34. data/examples/extension/main.rb +1 -1
  35. data/examples/extension/message_expander.rb +5 -2
  36. data/examples/simple/eval.rb +2 -2
  37. data/examples/simple/ping_pong.rb +1 -1
  38. data/examples/simple/rolepanel.rb +2 -2
  39. data/examples/simple/shard.rb +17 -0
  40. data/examples/simple/wait_for_message.rb +1 -1
  41. data/exe/discorb +31 -16
  42. data/lefthook.yml +45 -0
  43. data/lib/discorb/allowed_mentions.rb +8 -0
  44. data/lib/discorb/app_command/command.rb +184 -60
  45. data/lib/discorb/app_command/common.rb +25 -0
  46. data/lib/discorb/app_command/handler.rb +116 -34
  47. data/lib/discorb/app_command.rb +2 -1
  48. data/lib/discorb/application.rb +17 -7
  49. data/lib/discorb/asset.rb +10 -2
  50. data/lib/discorb/attachment.rb +17 -2
  51. data/lib/discorb/audit_logs.rb +53 -12
  52. data/lib/discorb/channel/base.rb +108 -0
  53. data/lib/discorb/channel/category.rb +32 -0
  54. data/lib/discorb/channel/container.rb +44 -0
  55. data/lib/discorb/channel/dm.rb +28 -0
  56. data/lib/discorb/channel/guild.rb +245 -0
  57. data/lib/discorb/channel/stage.rb +140 -0
  58. data/lib/discorb/channel/text.rb +345 -0
  59. data/lib/discorb/channel/thread.rb +321 -0
  60. data/lib/discorb/channel/voice.rb +79 -0
  61. data/lib/discorb/channel.rb +2 -1126
  62. data/lib/discorb/client.rb +160 -64
  63. data/lib/discorb/common.rb +18 -3
  64. data/lib/discorb/components/button.rb +7 -7
  65. data/lib/discorb/components/select_menu.rb +6 -18
  66. data/lib/discorb/components/text_input.rb +12 -2
  67. data/lib/discorb/components.rb +1 -1
  68. data/lib/discorb/dictionary.rb +2 -0
  69. data/lib/discorb/embed.rb +55 -14
  70. data/lib/discorb/emoji.rb +59 -5
  71. data/lib/discorb/emoji_table.rb +4970 -4
  72. data/lib/discorb/error.rb +7 -1
  73. data/lib/discorb/event.rb +56 -21
  74. data/lib/discorb/exe/about.rb +1 -0
  75. data/lib/discorb/exe/irb.rb +2 -4
  76. data/lib/discorb/exe/new.rb +95 -28
  77. data/lib/discorb/exe/run.rb +9 -37
  78. data/lib/discorb/exe/setup.rb +25 -12
  79. data/lib/discorb/exe/show.rb +4 -3
  80. data/lib/discorb/extend.rb +1 -0
  81. data/lib/discorb/extension.rb +6 -3
  82. data/lib/discorb/flag.rb +11 -0
  83. data/lib/discorb/gateway.rb +312 -169
  84. data/lib/discorb/gateway_requests.rb +4 -7
  85. data/lib/discorb/guild.rb +255 -89
  86. data/lib/discorb/guild_template.rb +34 -7
  87. data/lib/discorb/http.rb +23 -11
  88. data/lib/discorb/integration.rb +27 -9
  89. data/lib/discorb/intents.rb +8 -8
  90. data/lib/discorb/interaction/autocomplete.rb +31 -19
  91. data/lib/discorb/interaction/command.rb +70 -17
  92. data/lib/discorb/interaction/components.rb +20 -4
  93. data/lib/discorb/interaction/modal.rb +0 -1
  94. data/lib/discorb/interaction/response.rb +73 -22
  95. data/lib/discorb/interaction/root.rb +29 -14
  96. data/lib/discorb/interaction.rb +1 -0
  97. data/lib/discorb/invite.rb +16 -9
  98. data/lib/discorb/member.rb +46 -5
  99. data/lib/discorb/message.rb +56 -15
  100. data/lib/discorb/message_meta.rb +39 -9
  101. data/lib/discorb/modules.rb +56 -14
  102. data/lib/discorb/permission.rb +14 -5
  103. data/lib/discorb/presence.rb +43 -10
  104. data/lib/discorb/rate_limit.rb +13 -3
  105. data/lib/discorb/reaction.rb +10 -4
  106. data/lib/discorb/role.rb +31 -4
  107. data/lib/discorb/shard.rb +74 -0
  108. data/lib/discorb/sticker.rb +30 -21
  109. data/lib/discorb/user.rb +13 -1
  110. data/lib/discorb/utils/colored_puts.rb +1 -0
  111. data/lib/discorb/voice_state.rb +30 -8
  112. data/lib/discorb/webhook.rb +88 -25
  113. data/lib/discorb.rb +10 -6
  114. data/po/yard.pot +9 -9
  115. data/sig/discorb.rbs +7232 -5837
  116. metadata +23 -6
  117. data/examples/commands/bookmarker.rb +0 -42
  118. data/examples/commands/hello.rb +0 -10
  119. data/examples/commands/inspect.rb +0 -25
  120. data/lib/discorb/log.rb +0 -81
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Discorb
3
4
  #
4
5
  # Represents an interaction of Discord.
@@ -19,12 +20,18 @@ module Discorb
19
20
  #
20
21
  def defer_source(ephemeral: false)
21
22
  Async do
22
- @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), {
23
- type: 5,
24
- data: {
25
- flags: (ephemeral ? 1 << 6 : 0),
26
- },
27
- }).wait
23
+ @client.http.request(
24
+ Route.new(
25
+ "/interactions/#{@id}/#{@token}/callback",
26
+ "//interactions/:interaction_id/:token/callback",
27
+ :post
28
+ ), {
29
+ type: 5,
30
+ data: {
31
+ flags: (ephemeral ? 1 << 6 : 0),
32
+ },
33
+ }
34
+ ).wait
28
35
  @defered = true
29
36
  end
30
37
  end
@@ -42,27 +49,45 @@ module Discorb
42
49
  # @param [Array<Discorb::Component>, Array<Array<Discorb::Component>>] components The components to send.
43
50
  # @param [Boolean] ephemeral Whether to make the response ephemeral.
44
51
  #
45
- # @return [Discorb::Interaction::SourceResponder::CallbackMessage, Discorb::Webhook::Message] The callback message.
52
+ # @return [Discorb::Interaction::SourceResponder::CallbackMessage, Discorb::Webhook::Message]
53
+ # The callback message.
46
54
  #
47
- def post(content = nil, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
55
+ def post(
56
+ content = nil,
57
+ tts: false,
58
+ embed: nil,
59
+ embeds: nil,
60
+ allowed_mentions: nil,
61
+ components: nil,
62
+ ephemeral: false
63
+ )
48
64
  Async do
49
65
  payload = {}
50
66
  payload[:content] = content if content
51
67
  payload[:tts] = tts
52
68
  payload[:embeds] = (embeds || [embed]).map { |e| e&.to_hash }.filter { _1 }
53
- payload[:allowed_mentions] = allowed_mentions&.to_hash(@client.allowed_mentions) || @client.allowed_mentions.to_hash
69
+ payload[:allowed_mentions] =
70
+ allowed_mentions&.to_hash(@client.allowed_mentions) || @client.allowed_mentions.to_hash
54
71
  payload[:components] = Component.to_payload(components) if components
55
72
  payload[:flags] = (ephemeral ? 1 << 6 : 0)
56
73
 
57
74
  ret = if @responded
58
- _resp, data = @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}", "//webhooks/:webhook_id/:token", :post), payload).wait
75
+ _resp, data = @client.http.request(
76
+ Route.new("/webhooks/#{@application_id}/#{@token}", "//webhooks/:webhook_id/:token", :post), payload
77
+ ).wait
59
78
  webhook = Webhook::URLWebhook.new("/webhooks/#{@application_id}/#{@token}")
60
79
  Webhook::Message.new(webhook, data, @client)
61
80
  elsif @defered
62
- @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original", "//webhooks/:webhook_id/:token/messages/@original", :patch), payload).wait
81
+ @client.http.request(
82
+ Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original",
83
+ "//webhooks/:webhook_id/:token/messages/@original", :patch), payload
84
+ ).wait
63
85
  CallbackMessage.new(@client, payload, @application_id, @token)
64
86
  else
65
- @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), { type: 4, data: payload }).wait
87
+ @client.http.request(
88
+ Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback",
89
+ :post), { type: 4, data: payload }
90
+ ).wait
66
91
  CallbackMessage.new(@client, payload, @application_id, @token)
67
92
  end
68
93
  @responded = true
@@ -74,7 +99,15 @@ module Discorb
74
99
  # Represents of a callback message of interaction.
75
100
  #
76
101
  class CallbackMessage
102
+ #
103
+ # Initializes a new instance of CallbackMessage.
77
104
  # @private
105
+ #
106
+ # @param [Client] client The client.
107
+ # @param [Hash] data The payload.
108
+ # @param [String] application_id The application ID.
109
+ # @param [String] token The token.
110
+ #
78
111
  def initialize(client, data, application_id, token)
79
112
  @client = client
80
113
  @data = data
@@ -110,7 +143,10 @@ module Discorb
110
143
  payload[:attachments] = attachments.map(&:to_hash) if attachments != Discorb::Unset
111
144
  files = [file] if file != Discorb::Unset
112
145
  files = [] if files == Discorb::Unset
113
- @client.http.multipart_request(Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original", "//webhooks/:webhook_id/:token/messages/@original", :patch), payload, files, headers: headers).wait
146
+ @client.http.multipart_request(
147
+ Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original",
148
+ "//webhooks/:webhook_id/:token/messages/@original", :patch), payload, files, headers: headers,
149
+ ).wait
114
150
  end
115
151
  end
116
152
 
@@ -125,9 +161,14 @@ module Discorb
125
161
  #
126
162
  def delete!
127
163
  Async do
128
- @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original", "//webhooks/:webhook_id/:token/messages/@original", :delete)).wait
164
+ @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original",
165
+ "//webhooks/:webhook_id/:token/messages/@original", :delete)).wait
129
166
  end
130
167
  end
168
+
169
+ def inspect
170
+ "#<#{self.class.name} application_id=#{@application_id}"
171
+ end
131
172
  end
132
173
  end
133
174
 
@@ -145,12 +186,18 @@ module Discorb
145
186
  #
146
187
  def defer_update(ephemeral: false)
147
188
  Async do
148
- @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), {
149
- type: 6,
150
- data: {
151
- flags: (ephemeral ? 1 << 6 : 0),
152
- },
153
- }).wait
189
+ @client.http.request(
190
+ Route.new(
191
+ "/interactions/#{@id}/#{@token}/callback",
192
+ "//interactions/:interaction_id/:token/callback",
193
+ :post
194
+ ), {
195
+ type: 6,
196
+ data: {
197
+ flags: (ephemeral ? 1 << 6 : 0),
198
+ },
199
+ }
200
+ ).wait
154
201
  end
155
202
  end
156
203
 
@@ -180,10 +227,14 @@ module Discorb
180
227
  embeds
181
228
  end
182
229
  payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
183
- payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
230
+ payload[:allowed_mentions] =
231
+ allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
184
232
  payload[:components] = Component.to_payload(components) if components
185
233
  payload[:flags] = (ephemeral ? 1 << 6 : 0)
186
- @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), { type: 7, data: payload }).wait
234
+ @client.http.request(
235
+ Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback",
236
+ :post), { type: 7, data: payload }
237
+ ).wait
187
238
  end
188
239
  end
189
240
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Discorb
3
4
  #
4
5
  # Represents a user interaction with the bot.
@@ -10,10 +11,9 @@ module Discorb
10
11
  attr_reader :application_id
11
12
  # @return [Symbol] The type of interaction.
12
13
  attr_reader :type
13
- # @return [Discorb::Member] The member that created the interaction.
14
- attr_reader :member
15
- # @return [Discorb::User] The user that created the interaction.
14
+ # @return [Discorb::User, Discorb::Member] The user or member that created the interaction.
16
15
  attr_reader :user
16
+ alias member user
17
17
  # @return [Integer] The type of interaction.
18
18
  # @note This is always `1` for now.
19
19
  attr_reader :version
@@ -38,7 +38,13 @@ module Discorb
38
38
  @interaction_type = nil
39
39
  @interaction_name = nil
40
40
 
41
+ #
42
+ # Initialize a new interaction.
41
43
  # @private
44
+ #
45
+ # @param [Discorb::Client] client The client this interaction belongs to.
46
+ # @param [Hash] data The data of the interaction.
47
+ #
42
48
  def initialize(client, data)
43
49
  @client = client
44
50
  @id = Snowflake.new(data[:id])
@@ -47,8 +53,12 @@ module Discorb
47
53
  @type_id = self.class.interaction_type
48
54
  @guild_id = data[:guild_id] && Snowflake.new(data[:guild_id])
49
55
  @channel_id = data[:channel_id] && Snowflake.new(data[:channel_id])
50
- @member = guild.members[data[:member][:id]] || Member.new(@client, @guild_id, data[:member][:user], data[:member]) if data[:member]
51
- @user = @client.users[data[:user][:id]] || User.new(@client, data[:user]) if data[:user]
56
+ if data[:member]
57
+ @user = guild.members[data[:member][:id]] || Member.new(@client, @guild_id, data[:member][:user],
58
+ data[:member])
59
+ elsif data[:user]
60
+ @user = @client.users[data[:user][:id]] || User.new(@client, data[:user])
61
+ end
52
62
  @token = data[:token]
53
63
  @locale = data[:locale].to_s.gsub("-", "_").to_sym
54
64
  @guild_locale = data[:guild_locale].to_s.gsub("-", "_").to_sym
@@ -66,13 +76,6 @@ module Discorb
66
76
  @client.channels[@channel_id]
67
77
  end
68
78
 
69
- def target
70
- @member || @user
71
- end
72
-
73
- alias fired_by target
74
- alias from target
75
-
76
79
  def inspect
77
80
  "#<#{self.class} id=#{@id}>"
78
81
  end
@@ -81,20 +84,32 @@ module Discorb
81
84
  # @private
82
85
  attr_reader :interaction_type, :interaction_name, :event_name
83
86
 
87
+ #
88
+ # Create a new Interaction instance from the data.
84
89
  # @private
90
+ #
91
+ # @param [Discorb::Client] client The client this interaction belongs to.
92
+ # @param [Hash] data The data of the interaction.
93
+ #
85
94
  def make_interaction(client, data)
86
95
  interaction = nil
87
96
  descendants.each do |klass|
88
- interaction = klass.make_interaction(client, data) if !klass.interaction_type.nil? && klass.interaction_type == data[:type]
97
+ if !klass.interaction_type.nil? && klass.interaction_type == data[:type]
98
+ interaction = klass.make_interaction(client,
99
+ data)
100
+ end
89
101
  end
90
102
  if interaction.nil?
91
- client.log.warn("Unknown interaction type #{data[:type]}, initialized Interaction")
103
+ client.logger.warn("Unknown interaction type #{data[:type]}, initialized Interaction")
92
104
  interaction = Interaction.new(client, data)
93
105
  end
94
106
  interaction
95
107
  end
96
108
 
109
+ #
110
+ # Returns the descendants of the class.
97
111
  # @private
112
+ #
98
113
  def descendants
99
114
  ObjectSpace.each_object(Class).select { |klass| klass < self }
100
115
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  %w[root response command components autocomplete modal].each do |file|
3
4
  require_relative "interaction/#{file}.rb"
4
5
  end
@@ -66,13 +66,22 @@ module Discorb
66
66
  # Whether the invite is temporary.
67
67
  # @return [Boolean]
68
68
 
69
- @target_types = {
69
+ # @private
70
+ # @return [{Integer => Symbol}] The mapping of target types.
71
+ TARGET_TYPES = {
70
72
  nil => :voice,
71
73
  1 => :stream,
72
74
  2 => :guild,
73
75
  }.freeze
74
76
 
77
+ #
78
+ # Initialize a new invite.
75
79
  # @private
80
+ #
81
+ # @param [Discorb::Client] client The client.
82
+ # @param [Hash] data The data of invite.
83
+ # @param [Boolean] gateway Whether the data is from gateway.
84
+ #
76
85
  def initialize(client, data, gateway)
77
86
  @client = client
78
87
  @data = data[:data]
@@ -105,7 +114,7 @@ module Discorb
105
114
  #
106
115
  def delete!(reason: nil)
107
116
  Async do
108
- @client.http.request(Route.new("/invites/#{@code}", "//invites/:code", :delete), audit_log_reason: reason)
117
+ @client.http.request(Route.new("/invites/#{@code}", "//invites/:code", :delete), {}, audit_log_reason: reason)
109
118
  end
110
119
  end
111
120
 
@@ -124,8 +133,11 @@ module Discorb
124
133
  @expires_at = data[:expires_at] && Time.iso8601(data[:expires_at])
125
134
  end
126
135
  @inviter_data = data[:inviter]
127
- @target_type = self.class.target_types[data[:target_type]]
128
- @target_user = @client.users[data[:target_user][:id]] || User.new(@client, data[:target_user]) if data[:target_user]
136
+ @target_type = TARGET_TYPES[data[:target_type]]
137
+ if data[:target_user]
138
+ @target_user = @client.users[data[:target_user][:id]] || User.new(@client,
139
+ data[:target_user])
140
+ end
129
141
  # @target_application = nil
130
142
 
131
143
  # @stage_instance = data[:stage_instance] && Invite::StageInstance.new(self, data[:stage_instance])
@@ -137,10 +149,5 @@ module Discorb
137
149
  @temporary = data[:temporary]
138
150
  @created_at = Time.iso8601(data[:created_at])
139
151
  end
140
-
141
- class << self
142
- # @private
143
- attr_reader :target_types
144
- end
145
152
  end
146
153
  end
@@ -62,7 +62,15 @@ module Discorb
62
62
  # @!attribute [r] owner?
63
63
  # @return [Boolean] Whether the member is the owner of the guild.
64
64
 
65
+ #
66
+ # Initialize a new instance of the member.
65
67
  # @private
68
+ #
69
+ # @param [Discorb::Client] client The client.
70
+ # @param [Discorb::Snowflake] guild_id The ID of the guild.
71
+ # @param [Hash] user_data The data of the user.
72
+ # @param [Hash] member_data The data of the member.
73
+ #
66
74
  def initialize(client, guild_id, user_data, member_data)
67
75
  @guild_id = guild_id
68
76
  @client = client
@@ -106,6 +114,7 @@ module Discorb
106
114
 
107
115
  def permissions
108
116
  return Permission.new((1 << 38) - 1) if owner?
117
+
109
118
  roles.map(&:permissions).sum(Permission.new(0))
110
119
  end
111
120
 
@@ -150,7 +159,10 @@ module Discorb
150
159
  #
151
160
  def add_role(role, reason: nil)
152
161
  Async do
153
- @client.http.request(Route.new("/guilds/#{@guild_id}/members/#{@id}/roles/#{role.is_a?(Role) ? role.id : role}", "//guilds/:guild_id/members/:user_id/roles/:role_id", :put), nil, audit_log_reason: reason).wait
162
+ @client.http.request(
163
+ Route.new("/guilds/#{@guild_id}/members/#{@id}/roles/#{role.is_a?(Role) ? role.id : role}",
164
+ "//guilds/:guild_id/members/:user_id/roles/:role_id", :put), nil, audit_log_reason: reason,
165
+ ).wait
154
166
  end
155
167
  end
156
168
 
@@ -165,7 +177,10 @@ module Discorb
165
177
  #
166
178
  def remove_role(role, reason: nil)
167
179
  Async do
168
- @client.http.request(Route.new("/guilds/#{@guild_id}/members/#{@id}/roles/#{role.is_a?(Role) ? role.id : role}", "//guilds/:guild_id/members/:user_id/roles/:role_id", :delete), audit_log_reason: reason).wait
180
+ @client.http.request(
181
+ Route.new("/guilds/#{@guild_id}/members/#{@id}/roles/#{role.is_a?(Role) ? role.id : role}",
182
+ "//guilds/:guild_id/members/:user_id/roles/:role_id", :delete), {}, audit_log_reason: reason,
183
+ ).wait
169
184
  end
170
185
  end
171
186
 
@@ -186,7 +201,13 @@ module Discorb
186
201
  # @return [Async::Task<void>] The task.
187
202
  #
188
203
  def edit(
189
- nick: Discorb::Unset, role: Discorb::Unset, mute: Discorb::Unset, deaf: Discorb::Unset, channel: Discorb::Unset, communication_disabled_until: Discorb::Unset, timeout_until: Discorb::Unset,
204
+ nick: Discorb::Unset,
205
+ role: Discorb::Unset,
206
+ mute: Discorb::Unset,
207
+ deaf: Discorb::Unset,
208
+ channel: Discorb::Unset,
209
+ communication_disabled_until: Discorb::Unset,
210
+ timeout_until: Discorb::Unset,
190
211
  reason: nil
191
212
  )
192
213
  Async do
@@ -196,9 +217,15 @@ module Discorb
196
217
  payload[:mute] = mute if mute != Discorb::Unset
197
218
  payload[:deaf] = deaf if deaf != Discorb::Unset
198
219
  communication_disabled_until = timeout_until if timeout_until != Discorb::Unset
199
- payload[:communication_disabled_until] = communication_disabled_until&.iso8601 if communication_disabled_until != Discorb::Unset
220
+ if communication_disabled_until != Discorb::Unset
221
+ payload[:communication_disabled_until] =
222
+ communication_disabled_until&.iso8601
223
+ end
200
224
  payload[:channel_id] = channel&.id if channel != Discorb::Unset
201
- @client.http.request(Route.new("/guilds/#{@guild_id}/members/#{@id}", "//guilds/:guild_id/members/:user_id", :patch), payload, audit_log_reason: reason).wait
225
+ @client.http.request(
226
+ Route.new("/guilds/#{@guild_id}/members/#{@id}", "//guilds/:guild_id/members/:user_id",
227
+ :patch), payload, audit_log_reason: reason,
228
+ ).wait
202
229
  end
203
230
  end
204
231
 
@@ -248,6 +275,20 @@ module Discorb
248
275
  end
249
276
  end
250
277
 
278
+ #
279
+ # Checks if the member can manage the given role.
280
+ #
281
+ # @param [Discorb::Role] role The role.
282
+ #
283
+ # @return [Boolean] `true` if the member can manage the role.
284
+ #
285
+ def can_manage?(role)
286
+ return true if owner?
287
+
288
+ top_role = roles.max_by(&:position)
289
+ top_role.position > role.position
290
+ end
291
+
251
292
  private
252
293
 
253
294
  def _set_data(user_data, member_data)
@@ -85,7 +85,9 @@ module Discorb
85
85
  # @return [Boolean] Whether the message is pinned.
86
86
  attr_reader :pinned
87
87
  alias pinned? pinned
88
- @message_type = {
88
+ # @private
89
+ # @return [{Integer => Symbol}] The mapping of message type.
90
+ MESSAGE_TYPE = {
89
91
  0 => :default,
90
92
  1 => :recipient_add,
91
93
  2 => :recipient_remove,
@@ -152,7 +154,14 @@ module Discorb
152
154
  !@guild_id.nil?
153
155
  end
154
156
 
157
+ #
158
+ # Initialize a new message.
155
159
  # @private
160
+ #
161
+ # @param [Discorb::Client] client The client.
162
+ # @param [Hash] data The data of the welcome screen.
163
+ # @param [Boolean] no_cache Whether to disable caching.
164
+ #
156
165
  def initialize(client, data, no_cache: false)
157
166
  @client = client
158
167
  @data = {}
@@ -250,8 +259,15 @@ module Discorb
250
259
  #
251
260
  # @return [Async::Task<void>] The task.
252
261
  #
253
- def edit(content = Discorb::Unset, embed: Discorb::Unset, embeds: Discorb::Unset, allowed_mentions: Discorb::Unset,
254
- attachments: Discorb::Unset, components: Discorb::Unset, supress: Discorb::Unset)
262
+ def edit(
263
+ content = Discorb::Unset,
264
+ embed: Discorb::Unset,
265
+ embeds: Discorb::Unset,
266
+ allowed_mentions: Discorb::Unset,
267
+ attachments: Discorb::Unset,
268
+ components: Discorb::Unset,
269
+ supress: Discorb::Unset
270
+ )
255
271
  Async do
256
272
  channel.edit_message(@id, content, embed: embed, embeds: embeds, allowed_mentions: allowed_mentions,
257
273
  attachments: attachments, components: components, supress: supress).wait
@@ -324,7 +340,10 @@ module Discorb
324
340
  #
325
341
  def add_reaction(emoji)
326
342
  Async do
327
- @client.http.request(Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/@me", "//channels/:channel_id/messages/:message_id/reactions/:emoji/@me", :put), nil).wait
343
+ @client.http.request(
344
+ Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/@me",
345
+ "//channels/:channel_id/messages/:message_id/reactions/:emoji/@me", :put), nil
346
+ ).wait
328
347
  end
329
348
  end
330
349
 
@@ -340,7 +359,11 @@ module Discorb
340
359
  #
341
360
  def remove_reaction(emoji)
342
361
  Async do
343
- @client.http.request(Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/@me", "//channels/:channel_id/messages/:message_id/reactions/:emoji/@me", :delete)).wait
362
+ @client.http.request(
363
+ Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/@me",
364
+ "//channels/:channel_id/messages/:message_id/reactions/:emoji/@me",
365
+ :delete)
366
+ ).wait
344
367
  end
345
368
  end
346
369
 
@@ -357,7 +380,17 @@ module Discorb
357
380
  #
358
381
  def remove_reaction_of(emoji, member)
359
382
  Async do
360
- @client.http.request(Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/#{member.is_a?(Member) ? member.id : member}", "//channels/:channel_id/messages/:message_id/reactions/:emoji/:user_id", :delete)).wait
383
+ @client.http.request(
384
+ Route.new(
385
+ "/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}/#{if member.is_a?(Member)
386
+ member.id
387
+ else
388
+ member
389
+ end}",
390
+ "//channels/:channel_id/messages/:message_id/reactions/:emoji/:user_id",
391
+ :delete
392
+ )
393
+ ).wait
361
394
  end
362
395
  end
363
396
 
@@ -379,7 +412,13 @@ module Discorb
379
412
  after = 0
380
413
  users = []
381
414
  loop do
382
- _resp, data = @client.http.request(Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}?limit=100&after=#{after}", "//channels/:channel_id/messages/:message_id/reactions/:emoji", :get)).wait
415
+ _resp, data = @client.http.request(
416
+ Route.new(
417
+ "/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}?limit=100&after=#{after}",
418
+ "//channels/:channel_id/messages/:message_id/reactions/:emoji",
419
+ :get
420
+ )
421
+ ).wait
383
422
  break if data.empty?
384
423
 
385
424
  users += data.map { |r| guild&.members&.[](r[:id]) || @client.users[r[:id]] || User.new(@client, r) }
@@ -390,7 +429,13 @@ module Discorb
390
429
  end
391
430
  next users
392
431
  else
393
- _resp, data = @client.http.request(Route.new("/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}?limit=#{limit}&after=#{after}", "//channels/:channel_id/messages/:message_id/reactions/:emoji", :get)).wait
432
+ _resp, data = @client.http.request(
433
+ Route.new(
434
+ "/channels/#{@channel_id}/messages/#{@id}/reactions/#{emoji.to_uri}?limit=#{limit}&after=#{after}",
435
+ "//channels/:channel_id/messages/:message_id/reactions/:emoji",
436
+ :get
437
+ )
438
+ ).wait
394
439
  next data.map { |r| guild&.members&.[](r[:id]) || @client.users[r[:id]] || User.new(@client, r) }
395
440
  end
396
441
  end
@@ -446,7 +491,8 @@ module Discorb
446
491
  "#<#{self.class} #{@content.inspect} id=#{@id}>"
447
492
  end
448
493
 
449
- # @private
494
+ private
495
+
450
496
  def _set_data(data)
451
497
  @id = Snowflake.new(data[:id])
452
498
  @channel_id = data[:channel_id]
@@ -479,7 +525,7 @@ module Discorb
479
525
  @embeds = data[:embeds] ? data[:embeds].map { |e| Embed.from_hash(e) } : []
480
526
  @reactions = data[:reactions] ? data[:reactions].map { |r| Reaction.new(self, r) } : []
481
527
  @pinned = data[:pinned]
482
- @type = self.class.message_type[data[:type]]
528
+ @type = MESSAGE_TYPE[data[:type]]
483
529
  @activity = data[:activity] && Activity.new(data[:activity])
484
530
  @application_id = data[:application_id]
485
531
  @message_reference = data[:message_reference] && Reference.from_hash(data[:message_reference])
@@ -492,10 +538,5 @@ module Discorb
492
538
  @data.update(data)
493
539
  @deleted = false
494
540
  end
495
-
496
- class << self
497
- # @private
498
- attr_reader :message_type
499
- end
500
541
  end
501
542
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Discorb
3
4
  #
4
5
  # Represents a message in Discord.
@@ -88,18 +89,31 @@ module Discorb
88
89
  def self.from_hash(data)
89
90
  new(data[:guild_id], data[:channel_id], data[:message_id], fail_if_not_exists: data[:fail_if_not_exists])
90
91
  end
92
+
93
+ def inspect
94
+ "#<#{self.class.name} #{@channel_id}/#{@message_id}>"
95
+ end
91
96
  end
92
97
 
93
98
  #
94
99
  # Represents a sticker.
95
100
  #
96
101
  class Sticker
97
- attr_reader :id, :name, :format
102
+ # @return [Discorb::Snowflake] The sticker ID.
103
+ attr_reader :id
104
+ # @return [String] The sticker name.
105
+ attr_reader :name
106
+ # @return [Symbol] The sticker format.
107
+ attr_reader :format
98
108
 
99
109
  def initialize(data)
100
110
  @id = Snowflake.new(data[:id])
101
111
  @name = data[:name]
102
- @format = Discorb::Sticker.sticker_format[data[:format]]
112
+ @format = Discorb::Sticker::STICKER_FORMAT[data[:format]]
113
+ end
114
+
115
+ def inspect
116
+ "#<#{self.class.name} #{@id}: #{@name} format=#{@format}>"
103
117
  end
104
118
  end
105
119
 
@@ -107,7 +121,7 @@ module Discorb
107
121
  # Represents a interaction of message.
108
122
  #
109
123
  class Interaction < DiscordModel
110
- # @return [Discorb::Snowflake] The user ID.
124
+ # @return [Discorb::Snowflake] The interaction ID.
111
125
  attr_reader :id
112
126
  # @return [String] The name of command.
113
127
  # @return [nil] If the message is not a command.
@@ -117,13 +131,23 @@ module Discorb
117
131
  # @return [Discorb::User] The user.
118
132
  attr_reader :user
119
133
 
134
+ #
135
+ # Initialize a new interaction.
120
136
  # @private
137
+ #
138
+ # @param [Discorb::Client] client The client.
139
+ # @param [Hash] data The interaction data.
140
+ #
121
141
  def initialize(client, data)
122
142
  @id = Snowflake.new(data[:id])
123
143
  @name = data[:name]
124
144
  @type = Discorb::Interaction.descendants.find { |c| c.interaction_type == data[:type] }
125
145
  @user = client.users[data[:user][:id]] || User.new(client, data[:user])
126
146
  end
147
+
148
+ def inspect
149
+ "<#{self.class.name} #{@id}: #{@name} type=#{@type} #{@user}>"
150
+ end
127
151
  end
128
152
 
129
153
  #
@@ -135,22 +159,28 @@ module Discorb
135
159
  # @return [Symbol] The type of activity.
136
160
  attr_reader :type
137
161
 
138
- @type = {
162
+ # @private
163
+ # @return [{Integer => Symbol}] The mapping of activity type.
164
+ TYPES = {
139
165
  1 => :join,
140
166
  2 => :spectate,
141
167
  3 => :listen,
142
168
  5 => :join_request,
143
- }
169
+ }.freeze
144
170
 
171
+ #
172
+ # Initialize a new activity.
145
173
  # @private
174
+ #
175
+ # @param [Hash] data The activity data.
176
+ #
146
177
  def initialize(data)
147
178
  @name = data[:name]
148
- @type = self.class.type(data[:type])
179
+ @type = TYPES[data[:type]]
149
180
  end
150
181
 
151
- class << self
152
- # @private
153
- attr_reader :type
182
+ def inspect
183
+ "<#{self.class.name} #{@name} type=#{@type}>"
154
184
  end
155
185
  end
156
186
  end