discorb 0.13.1 → 0.14.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 (89) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/build_version.yml +3 -3
  4. data/.github/workflows/codeql-analysis.yml +70 -0
  5. data/.github/workflows/lint-push.yml +18 -0
  6. data/.github/workflows/lint.yml +16 -0
  7. data/.rubocop.yml +70 -0
  8. data/CODE_OF_CONDUCT.md +128 -0
  9. data/Changelog.md +33 -0
  10. data/Gemfile +7 -3
  11. data/README.md +1 -1
  12. data/Rakefile +35 -35
  13. data/discorb.gemspec +13 -1
  14. data/examples/commands/bookmarker.rb +2 -1
  15. data/examples/commands/hello.rb +1 -0
  16. data/examples/commands/inspect.rb +3 -2
  17. data/examples/components/authorization_button.rb +2 -1
  18. data/examples/components/select_menu.rb +2 -1
  19. data/examples/extension/main.rb +1 -0
  20. data/examples/extension/message_expander.rb +1 -0
  21. data/examples/simple/eval.rb +3 -2
  22. data/examples/simple/ping_pong.rb +1 -0
  23. data/examples/simple/rolepanel.rb +1 -0
  24. data/examples/simple/wait_for_message.rb +4 -3
  25. data/exe/discorb +8 -7
  26. data/lib/discorb/allowed_mentions.rb +64 -0
  27. data/lib/discorb/app_command/command.rb +274 -0
  28. data/lib/discorb/app_command/handler.rb +168 -0
  29. data/lib/discorb/app_command.rb +2 -404
  30. data/lib/discorb/asset.rb +3 -1
  31. data/lib/discorb/audit_logs.rb +3 -3
  32. data/lib/discorb/channel.rb +89 -53
  33. data/lib/discorb/client.rb +36 -33
  34. data/lib/discorb/common.rb +28 -21
  35. data/lib/discorb/components/button.rb +106 -0
  36. data/lib/discorb/components/select_menu.rb +157 -0
  37. data/lib/discorb/components/text_input.rb +96 -0
  38. data/lib/discorb/components.rb +11 -276
  39. data/lib/discorb/dictionary.rb +13 -2
  40. data/lib/discorb/embed.rb +2 -2
  41. data/lib/discorb/emoji.rb +21 -5
  42. data/lib/discorb/emoji_table.rb +1 -1
  43. data/lib/discorb/error.rb +4 -6
  44. data/lib/discorb/event.rb +13 -11
  45. data/lib/discorb/exe/about.rb +1 -0
  46. data/lib/discorb/exe/irb.rb +4 -3
  47. data/lib/discorb/exe/new.rb +6 -7
  48. data/lib/discorb/exe/run.rb +2 -1
  49. data/lib/discorb/exe/setup.rb +8 -5
  50. data/lib/discorb/exe/show.rb +1 -0
  51. data/lib/discorb/extend.rb +19 -14
  52. data/lib/discorb/extension.rb +5 -1
  53. data/lib/discorb/gateway.rb +75 -27
  54. data/lib/discorb/guild.rb +58 -80
  55. data/lib/discorb/guild_template.rb +5 -5
  56. data/lib/discorb/http.rb +34 -169
  57. data/lib/discorb/integration.rb +32 -3
  58. data/lib/discorb/intents.rb +1 -1
  59. data/lib/discorb/interaction/autocomplete.rb +5 -4
  60. data/lib/discorb/interaction/command.rb +34 -9
  61. data/lib/discorb/interaction/components.rb +5 -2
  62. data/lib/discorb/interaction/modal.rb +33 -0
  63. data/lib/discorb/interaction/response.rb +41 -12
  64. data/lib/discorb/interaction/root.rb +1 -0
  65. data/lib/discorb/interaction.rb +2 -1
  66. data/lib/discorb/invite.rb +1 -1
  67. data/lib/discorb/log.rb +4 -3
  68. data/lib/discorb/member.rb +4 -6
  69. data/lib/discorb/message.rb +31 -282
  70. data/lib/discorb/message_meta.rb +205 -0
  71. data/lib/discorb/modules.rb +11 -11
  72. data/lib/discorb/permission.rb +2 -2
  73. data/lib/discorb/presence.rb +6 -3
  74. data/lib/discorb/rate_limit.rb +15 -21
  75. data/lib/discorb/role.rb +3 -3
  76. data/lib/discorb/sticker.rb +2 -2
  77. data/lib/discorb/user.rb +3 -3
  78. data/lib/discorb/utils/colored_puts.rb +1 -0
  79. data/lib/discorb/voice_state.rb +7 -2
  80. data/lib/discorb/webhook.rb +8 -5
  81. data/lib/discorb.rb +1 -0
  82. data/template-replace/scripts/arrow.rb +1 -0
  83. data/template-replace/scripts/favicon.rb +1 -0
  84. data/template-replace/scripts/index.rb +2 -1
  85. data/template-replace/scripts/locale_ja.rb +5 -4
  86. data/template-replace/scripts/sidebar.rb +1 -0
  87. data/template-replace/scripts/version.rb +7 -10
  88. data/template-replace/scripts/yard_replace.rb +5 -4
  89. metadata +29 -4
data/lib/discorb/http.rb CHANGED
@@ -17,10 +17,11 @@ module Discorb
17
17
  end
18
18
 
19
19
  #
20
- # Execute a GET request.
20
+ # Execute a request.
21
21
  # @async
22
22
  #
23
- # @param [String] path The path to the resource.
23
+ # @param [Discorb::Route] path The path to the resource.
24
+ # @param [String, Hash] body The body of the request. Defaults to an empty string.
24
25
  # @param [Hash] headers The headers to send with the request.
25
26
  # @param [String] audit_log_reason The audit log reason to send with the request.
26
27
  # @param [Hash] kwargs The keyword arguments.
@@ -30,46 +31,25 @@ module Discorb
30
31
  #
31
32
  # @raise [Discorb::HTTPError] The request was failed.
32
33
  #
33
- def get(path, headers: nil, audit_log_reason: nil, **kwargs)
34
- Async do |task|
35
- @ratelimit_handler.wait("GET", path)
36
- resp = http.get(get_path(path), get_headers(headers, "", audit_log_reason), **kwargs)
34
+ def request(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
35
+ Async do |_task|
36
+ @ratelimit_handler.wait(path)
37
+ resp = if %i[post patch put].include? path.method
38
+ http.send(path.method, get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
39
+ else
40
+ http.send(path.method, get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
41
+ end
37
42
  data = get_response_data(resp)
38
- @ratelimit_handler.save("GET", path, resp)
39
- handle_response(:patch, resp, data, path, nil, headers, audit_log_reason, kwargs)
43
+ @ratelimit_handler.save(path, resp)
44
+ handle_response(resp, data, path, body, headers, audit_log_reason, kwargs)
40
45
  end
41
46
  end
42
47
 
43
48
  #
44
- # Execute a POST request.
49
+ # Execute a multipart request.
45
50
  # @async
46
51
  #
47
- # @param [String] path The path to the resource.
48
- # @param [String, Hash] body The body of the request.
49
- # @param [Hash] headers The headers to send with the request.
50
- # @param [String] audit_log_reason The audit log reason to send with the request.
51
- # @param [Hash] kwargs The keyword arguments.
52
- #
53
- # @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
54
- # @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
55
- #
56
- # @raise [Discorb::HTTPError] The request was failed.
57
- #
58
- def post(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
59
- Async do |task|
60
- @ratelimit_handler.wait("POST", path)
61
- resp = http.post(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
62
- data = get_response_data(resp)
63
- @ratelimit_handler.save("POST", path, resp)
64
- handle_response(:post, resp, data, path, body, headers, audit_log_reason, kwargs)
65
- end
66
- end
67
-
68
- #
69
- # Execute a multipart POST request.
70
- # @async
71
- #
72
- # @param [String] path The path to the resource.
52
+ # @param [Discorb::Route] path The path to the resource.
73
53
  # @param [String, Hash] body The body of the request.
74
54
  # @param [Array<Discorb::File>] files The files to upload.
75
55
  # @param [Hash] headers The headers to send with the request.
@@ -81,12 +61,12 @@ module Discorb
81
61
  #
82
62
  # @raise [Discorb::HTTPError] The request was failed.
83
63
  #
84
- def multipart_post(path, body = "", files, headers: nil, audit_log_reason: nil, **kwargs)
85
- Async do |task|
86
- @ratelimit_handler.wait("POST", path)
87
- req = Net::HTTP::Post.new(get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
64
+ def multipart_request(path, body, files, headers: nil, audit_log_reason: nil, **kwargs)
65
+ Async do |_task|
66
+ @ratelimit_handler.wait(path)
67
+ req = Net::HTTP.const_get(path.method.to_s.capitalize).new(get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
88
68
  data = [
89
- ["payload_json", get_body(body)],
69
+ ["payload_json", get_body(body)]
90
70
  ]
91
71
  files&.each_with_index do |file, i|
92
72
  next if file.nil?
@@ -97,117 +77,8 @@ module Discorb
97
77
  session.use_ssl = true
98
78
  resp = session.request(req)
99
79
  data = get_response_data(resp)
100
- @ratelimit_handler.save("POST", path, resp)
101
- handle_response(:post, resp, data, path, body, headers, audit_log_reason, kwargs)
102
- end
103
- end
104
-
105
- #
106
- # Execute a PATCH request.
107
- # @async
108
- #
109
- # @param [String] path The path to the resource.
110
- # @param [String, Hash] body The body of the request.
111
- # @param [Hash] headers The headers to send with the request.
112
- # @param [String] audit_log_reason The audit log reason to send with the request.
113
- # @param [Hash] kwargs The keyword arguments.
114
- #
115
- # @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
116
- # @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
117
- #
118
- # @raise [Discorb::HTTPError] The request was failed.
119
- #
120
- def patch(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
121
- Async do |task|
122
- @ratelimit_handler.wait("PATCH", path)
123
- resp = http.patch(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
124
- data = get_response_data(resp)
125
- @ratelimit_handler.save("PATCH", path, resp)
126
- handle_response(:patch, resp, data, path, body, headers, audit_log_reason, kwargs)
127
- end
128
- end
129
-
130
- #
131
- # Execute a PATCH request.
132
- # @async
133
- #
134
- # @param [String] path The path to the resource.
135
- # @param [String, Hash] body The body of the request.
136
- # @param [Hash] headers The headers to send with the request.
137
- # @param [String] audit_log_reason The audit log reason to send with the request.
138
- # @param [Hash] kwargs The keyword arguments.
139
- #
140
- # @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
141
- # @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
142
- #
143
- # @raise [Discorb::HTTPError] The request was failed.
144
- #
145
- def multipart_patch(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
146
- Async do |task|
147
- @ratelimit_handler.wait("PATCH", path)
148
- req = Net::HTTP::Patch.new(get_path(path), get_headers(headers, body, audit_log_reason), **kwargs)
149
- data = [
150
- ["payload_json", get_body(body)],
151
- ]
152
- files&.each_with_index do |file, i|
153
- data << ["files[#{i}]", file.io, { filename: file.filename, content_type: file.content_type }]
154
- end
155
- req.set_form(data, "multipart/form-data")
156
- session = Net::HTTP.new("discord.com", 443)
157
- session.use_ssl = true
158
- resp = session.request(req)
159
- data = get_response_data(resp)
160
- @ratelimit_handler.save("PATCH", path, resp)
161
- handle_response(:patch, resp, data, path, body, headers, audit_log_reason, kwargs)
162
- end
163
- end
164
-
165
- #
166
- # Execute a PUT request.
167
- # @async
168
- #
169
- # @param [String] path The path to the resource.
170
- # @param [String, Hash] body The body of the request.
171
- # @param [Hash] headers The headers to send with the request.
172
- # @param [String] audit_log_reason The audit log reason to send with the request.
173
- # @param [Hash] kwargs The keyword arguments.
174
- #
175
- # @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
176
- # @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
177
- #
178
- # @raise [Discorb::HTTPError] The request was failed.
179
- #
180
- def put(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
181
- Async do |task|
182
- @ratelimit_handler.wait("PUT", path)
183
- resp = http.put(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
184
- data = get_response_data(resp)
185
- @ratelimit_handler.save("PUT", path, resp)
186
- handle_response(:put, resp, data, path, body, headers, audit_log_reason, kwargs)
187
- end
188
- end
189
-
190
- #
191
- # Execute a DELETE request.
192
- # @async
193
- #
194
- # @param [String] path The path to the resource.
195
- # @param [Hash] headers The headers to send with the request.
196
- # @param [String] audit_log_reason The audit log reason to send with the request.
197
- # @param [Hash] kwargs The keyword arguments.
198
- #
199
- # @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
200
- # @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
201
- #
202
- # @raise [Discorb::HTTPError] The request was failed.
203
- #
204
- def delete(path, headers: nil, audit_log_reason: nil, **kwargs)
205
- Async do
206
- @ratelimit_handler.wait("DELETE", path)
207
- resp = http.delete(get_path(path), get_headers(headers, "", audit_log_reason))
208
- data = get_response_data(resp)
209
- @ratelimit_handler.save("DELETE", path, resp)
210
- handle_response(:delete, resp, data, path, nil, headers, audit_log_reason, kwargs)
80
+ @ratelimit_handler.save(path, resp)
81
+ handle_response(resp, data, path, body, headers, audit_log_reason, kwargs)
211
82
  end
212
83
  end
213
84
 
@@ -217,16 +88,12 @@ module Discorb
217
88
 
218
89
  private
219
90
 
220
- def handle_response(method, resp, data, path, body, headers, audit_log_reason, kwargs)
91
+ def handle_response(resp, data, path, body, headers, audit_log_reason, kwargs)
221
92
  case resp.code
222
93
  when "429"
223
- @client.log.info("Rate limit exceeded for #{method} #{path}, waiting #{data[:retry_after]} seconds")
94
+ @client.log.info("Rate limit exceeded for #{path.method} #{path.url}, waiting #{data[:retry_after]} seconds")
224
95
  sleep(data[:retry_after])
225
- if body
226
- __send__(method, path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
227
- else
228
- __send__(method, path, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
229
- end
96
+ request(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
230
97
  when "400"
231
98
  raise BadRequestError.new(resp, data)
232
99
  when "401"
@@ -263,10 +130,10 @@ module Discorb
263
130
  end
264
131
 
265
132
  def get_path(path)
266
- full_path = if path.start_with?("https://")
267
- path
133
+ full_path = if path.url.start_with?("https://")
134
+ path.url
268
135
  else
269
- API_BASE_URL + path
136
+ API_BASE_URL + path.url
270
137
  end
271
138
  uri = URI(full_path)
272
139
  full_path.sub(uri.scheme + "://" + uri.host, "")
@@ -276,15 +143,13 @@ module Discorb
276
143
  begin
277
144
  data = JSON.parse(resp.body, symbolize_names: true)
278
145
  rescue JSON::ParserError, TypeError
279
- if resp.body.nil? || resp.body.empty?
280
- data = nil
281
- else
282
- data = resp.body
283
- end
284
- end
285
- if resp["Via"].nil? && resp.code == "429" && data.is_a?(String)
286
- raise CloudFlareBanError.new(resp, @client)
146
+ data = if resp.body.nil? || resp.body.empty?
147
+ nil
148
+ else
149
+ resp.body
150
+ end
287
151
  end
152
+ raise CloudFlareBanError.new(resp, @client) if resp["Via"].nil? && resp.code == "429" && data.is_a?(String)
288
153
  data
289
154
  end
290
155
 
@@ -44,12 +44,11 @@ module Discorb
44
44
  }
45
45
 
46
46
  # @private
47
- def initialize(client, data, guild_id, no_cache: false)
47
+ def initialize(client, data, guild_id)
48
48
  @client = client
49
49
  @data = data
50
50
  @guild_id = guild_id
51
51
  _set_data(data)
52
- guild.integrations[@id] = self unless no_cache
53
52
  end
54
53
 
55
54
  def guild
@@ -66,7 +65,7 @@ module Discorb
66
65
  #
67
66
  def delete!(reason: nil)
68
67
  Async do
69
- @client.http.delete("/guilds/#{@guild}/integrations/#{@id}", audit_log_reason: reason).wait
68
+ @client.http.request(Route.new("/guilds/#{@guild}/integrations/#{@id}", "//guilds/:guild_id/integrations/:integration_id", :delete), audit_log_reason: reason).wait
70
69
  end
71
70
  end
72
71
 
@@ -110,5 +109,35 @@ module Discorb
110
109
  @name = data[:name]
111
110
  end
112
111
  end
112
+
113
+ #
114
+ # Represents an application for an integration.
115
+ #
116
+ class Application < DiscordModel
117
+ # @return [Discorb::Snowflake] The ID of the application.
118
+ attr_reader :id
119
+ # @return [String] The name of the application.
120
+ attr_reader :name
121
+ # @return [Asset] The icon of the application.
122
+ # @return [nil] If the application has no icon.
123
+ attr_reader :icon
124
+ # @return [String] The description of the application.
125
+ attr_reader :description
126
+ # @return [String] The summary of the application.
127
+ attr_reader :summary
128
+ # @return [Discorb::User] The bot user associated with the application.
129
+ # @return [nil] If the application has no bot user.
130
+ attr_reader :bot
131
+
132
+ # @private
133
+ def initialize(client, data)
134
+ @id = Snowflake.new(data[:id])
135
+ @name = data[:name]
136
+ @icon = data[:icon] && Asset.new(self, data[:icon])
137
+ @description = data[:description]
138
+ @summary = data[:summary]
139
+ @bot = data[:bot] and client.users[data[:bot][:id]] || Discorb::User.new(client, data[:bot])
140
+ end
141
+ end
113
142
  end
114
143
  end
@@ -86,7 +86,7 @@ module Discorb
86
86
  if @raw_value.key?(name)
87
87
  @raw_value[name]
88
88
  elsif name.end_with?("=") && @raw_value.key?(name[0..-2].to_sym)
89
- raise ArgumentError, "true/false expected" unless args.is_a? TrueClass or args.is_a?(FalseClass)
89
+ raise ArgumentError, "true/false expected" unless args.is_a?(TrueClass) || args.is_a?(FalseClass)
90
90
 
91
91
  @raw_value[name[0..-2].to_sym] = args
92
92
  else
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
  #
3
4
  # Represents auto complete interaction.
@@ -17,17 +18,17 @@ module Discorb
17
18
  next
18
19
  end
19
20
 
20
- option_map = command.options.map { |k, v| [k.to_s, v[:default]] }.to_h
21
- Discorb::CommandInteraction::SlashCommand.modify_option_map(option_map, options, guild)
21
+ option_map = command.options.to_h { |k, v| [k.to_s, v[:default]] }
22
+ Discorb::CommandInteraction::SlashCommand.modify_option_map(option_map, options, guild, {}, {})
22
23
  focused_index = options.find_index { |o| o[:focused] }
23
- val = command.options.values[focused_index][:autocomplete]&.call(self, *command.options.map { |k, v| option_map[k.to_s] })
24
+ val = command.options.values.filter { |option| option[:type] != :attachment }[focused_index][:autocomplete]&.call(self, *command.options.map { |k, _v| option_map[k.to_s] })
24
25
  send_complete_result(val)
25
26
  end
26
27
  end
27
28
 
28
29
  # @private
29
30
  def send_complete_result(val)
30
- @client.http.post("/interactions/#{@id}/#{@token}/callback", {
31
+ @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), {
31
32
  type: 8,
32
33
  data: {
33
34
  choices: val.map do |vk, vv|
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
  #
3
4
  # Represents a command interaction.
@@ -5,7 +6,8 @@ module Discorb
5
6
  class CommandInteraction < Interaction
6
7
  @interaction_type = 2
7
8
  @interaction_name = :application_command
8
- include Interaction::SourceResponse
9
+ include Interaction::SourceResponder
10
+ include Interaction::ModalResponder
9
11
 
10
12
  #
11
13
  # Represents a slash command interaction.
@@ -25,10 +27,10 @@ module Discorb
25
27
  return
26
28
  end
27
29
 
28
- option_map = command.options.map { |k, v| [k.to_s, v[:default]] }.to_h
29
- SlashCommand.modify_option_map(option_map, options, guild)
30
+ option_map = command.options.to_h { |k, v| [k.to_s, v[:default]] }
31
+ SlashCommand.modify_option_map(option_map, options, guild, @members, @attachments)
30
32
 
31
- command.block.call(self, *command.options.map { |k, v| option_map[k.to_s] })
33
+ command.block.call(self, *command.options.map { |k, _v| option_map[k.to_s] })
32
34
  end
33
35
 
34
36
  class << self
@@ -55,13 +57,13 @@ module Discorb
55
57
  options = data[:options]
56
58
  end
57
59
 
58
- return name, options
60
+ [name, options]
59
61
  end
60
62
 
61
63
  # @private
62
- def modify_option_map(option_map, options, guild)
64
+ def modify_option_map(option_map, options, guild, members, attachments)
63
65
  options ||= []
64
- options.each_with_index do |option|
66
+ options.each do |option|
65
67
  val = case option[:type]
66
68
  when 3, 4, 5, 10
67
69
  option[:value]
@@ -72,7 +74,9 @@ module Discorb
72
74
  when 8
73
75
  guild.roles[option[:value]] || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
74
76
  when 9
75
- guild.members[option[:value]] || guild.roles[option[:value]] || guild.fetch_member(option[:value]).wait || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
77
+ members[option[:value]] || guild.members[option[:value]] || guild.roles[option[:value]] || guild.fetch_member(option[:value]).wait || guild.fetch_roles.wait.find { |role| role.id == option[:value] }
78
+ when 11
79
+ attachments[option[:value]]
76
80
  end
77
81
  option_map[option[:name]] = val
78
82
  end
@@ -92,6 +96,7 @@ module Discorb
92
96
  private
93
97
 
94
98
  def _set_data(data)
99
+ super
95
100
  @target = guild.members[data[:target_id]] || Discorb::Member.new(@client, @guild_id, data[:resolved][:users][data[:target_id].to_sym], data[:resolved][:members][data[:target_id].to_sym])
96
101
  @client.commands.find { |c| c.name == data[:name] && c.type_raw == 2 }.block.call(self, @target)
97
102
  end
@@ -109,7 +114,8 @@ module Discorb
109
114
  private
110
115
 
111
116
  def _set_data(data)
112
- @target = Message.new(@client, data[:resolved][:messages][data[:target_id].to_sym].merge(guild_id: @guild_id.to_s))
117
+ super
118
+ @target = @messages[data[:target_id]]
113
119
  @client.commands.find { |c| c.name == data[:name] && c.type_raw == 3 }.block.call(self, @target)
114
120
  end
115
121
  end
@@ -117,7 +123,26 @@ module Discorb
117
123
  private
118
124
 
119
125
  def _set_data(data)
126
+ super
120
127
  @name = data[:name]
128
+ @messages, @attachments, @members = {}, {}, {}
129
+
130
+ if data[:resolved]
131
+ data[:resolved][:users]&.each do |id, user|
132
+ @client.users[id] = Discorb::User.new(@client, user)
133
+ end
134
+ data[:resolved][:members]&.each do |id, member|
135
+ @members[id] = Discorb::Member.new(
136
+ @client, @guild_id, data[:resolved][:users][id], member
137
+ )
138
+ end
139
+ data[:resolved][:messages]&.to_h do |id, _message|
140
+ @messages[id.to_i] = Message.new(@client, data[:resolved][:messages][data[:target_id].to_sym].merge(guild_id: @guild_id.to_s)).merge(guild_id: @guild_id.to_s)
141
+ end
142
+ data[:resolved][:attachments]&.to_h do |id, attachment|
143
+ @attachments[id.to_s] = Attachment.new(attachment)
144
+ end
145
+ end
121
146
  end
122
147
 
123
148
  class << self
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
 
3
4
  #
@@ -5,8 +6,10 @@ module Discorb
5
6
  # @abstract
6
7
  #
7
8
  class MessageComponentInteraction < Interaction
8
- include Interaction::SourceResponse
9
- include Interaction::UpdateResponse
9
+ include Interaction::SourceResponder
10
+ include Interaction::UpdateResponder
11
+ include Interaction::ModalResponder
12
+
10
13
  # @return [String] The content of the response.
11
14
  attr_reader :custom_id
12
15
  # @return [Discorb::Message] The target message.
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+
5
+ #
6
+ # Represents a modal interaction.
7
+ #
8
+ class ModalInteraction < Interaction
9
+ include Interaction::SourceResponder
10
+
11
+ @interaction_type = 5
12
+ @interaction_name = :modal_submit
13
+ @event_name = :modal_submit
14
+
15
+ # @return [String] The custom id of the modal.
16
+ attr_reader :custom_id
17
+ # @return [{String => String}] The contents of the modal.
18
+ attr_reader :contents
19
+
20
+ private
21
+
22
+ def _set_data(data)
23
+ @custom_id = data[:custom_id]
24
+ @contents = data[:components].to_h do |component|
25
+ [component[:components][0][:custom_id], component[:components][0][:value]]
26
+ end
27
+ end
28
+
29
+ class << self
30
+ alias make_interaction new
31
+ end
32
+ end
33
+ end
@@ -1,9 +1,13 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
3
+ #
4
+ # Represents an interaction of Discord.
5
+ #
2
6
  class Interaction
3
7
  #
4
8
  # A module for response with source.
5
9
  #
6
- module SourceResponse
10
+ module SourceResponder
7
11
  #
8
12
  # Response with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`(`5`).
9
13
  #
@@ -15,7 +19,7 @@ module Discorb
15
19
  #
16
20
  def defer_source(ephemeral: false)
17
21
  Async do
18
- @client.http.post("/interactions/#{@id}/#{@token}/callback", {
22
+ @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), {
19
23
  type: 5,
20
24
  data: {
21
25
  flags: (ephemeral ? 1 << 6 : 0),
@@ -38,27 +42,27 @@ module Discorb
38
42
  # @param [Array<Discorb::Component>, Array<Array<Discorb::Component>>] components The components to send.
39
43
  # @param [Boolean] ephemeral Whether to make the response ephemeral.
40
44
  #
41
- # @return [Discorb::Interaction::SourceResponse::CallbackMessage, Discorb::Webhook::Message] The callback message.
45
+ # @return [Discorb::Interaction::SourceResponder::CallbackMessage, Discorb::Webhook::Message] The callback message.
42
46
  #
43
47
  def post(content = nil, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, ephemeral: false)
44
48
  Async do
45
49
  payload = {}
46
50
  payload[:content] = content if content
47
51
  payload[:tts] = tts
48
- payload[:embeds] = (embeds || [embed])&.map { |e| e&.to_hash }.filter { _1 }
52
+ payload[:embeds] = (embeds || [embed]).map { |e| e&.to_hash }.filter { _1 }
49
53
  payload[:allowed_mentions] = allowed_mentions&.to_hash(@client.allowed_mentions) || @client.allowed_mentions.to_hash
50
54
  payload[:components] = Component.to_payload(components) if components
51
55
  payload[:flags] = (ephemeral ? 1 << 6 : 0)
52
56
 
53
57
  ret = if @responded
54
- _resp, data = @client.http.post("/webhooks/#{@application_id}/#{@token}", payload).wait
58
+ _resp, data = @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}", "//webhooks/:webhook_id/:token", :post), payload).wait
55
59
  webhook = Webhook::URLWebhook.new("/webhooks/#{@application_id}/#{@token}")
56
60
  Webhook::Message.new(webhook, data, @client)
57
61
  elsif @defered
58
- @client.http.patch("/webhooks/#{@application_id}/#{@token}/messages/@original", payload).wait
62
+ @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original", "//webhooks/:webhook_id/:token/messages/@original", :patch), payload).wait
59
63
  CallbackMessage.new(@client, payload, @application_id, @token)
60
64
  else
61
- @client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 4, data: payload }).wait
65
+ @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), { type: 4, data: payload }).wait
62
66
  CallbackMessage.new(@client, payload, @application_id, @token)
63
67
  end
64
68
  @responded = true
@@ -66,6 +70,9 @@ module Discorb
66
70
  end
67
71
  end
68
72
 
73
+ #
74
+ # Represents of a callback message of interaction.
75
+ #
69
76
  class CallbackMessage
70
77
  # @private
71
78
  def initialize(client, data, application_id, token)
@@ -103,7 +110,7 @@ module Discorb
103
110
  payload[:attachments] = attachments.map(&:to_hash) if attachments != Discorb::Unset
104
111
  files = [file] if file != Discorb::Unset
105
112
  files = [] if files == Discorb::Unset
106
- @client.http.multipart_patch("/webhooks/#{@application_id}/#{@token}/messages/@original", payload, files, headers: headers).wait
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
107
114
  end
108
115
  end
109
116
 
@@ -118,7 +125,7 @@ module Discorb
118
125
  #
119
126
  def delete!
120
127
  Async do
121
- @client.http.delete("/webhooks/#{@application_id}/#{@token}/messages/@original").wait
128
+ @client.http.request(Route.new("/webhooks/#{@application_id}/#{@token}/messages/@original", "//webhooks/:webhook_id/:token/messages/@original", :delete)).wait
122
129
  end
123
130
  end
124
131
  end
@@ -127,7 +134,7 @@ module Discorb
127
134
  #
128
135
  # A module for response with update.
129
136
  #
130
- module UpdateResponse
137
+ module UpdateResponder
131
138
  #
132
139
  # Response with `DEFERRED_UPDATE_MESSAGE`(`6`).
133
140
  # @async
@@ -138,7 +145,7 @@ module Discorb
138
145
  #
139
146
  def defer_update(ephemeral: false)
140
147
  Async do
141
- @client.http.post("/interactions/#{@id}/#{@token}/callback", {
148
+ @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), {
142
149
  type: 6,
143
150
  data: {
144
151
  flags: (ephemeral ? 1 << 6 : 0),
@@ -176,11 +183,33 @@ module Discorb
176
183
  payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
177
184
  payload[:components] = Component.to_payload(components) if components
178
185
  payload[:flags] = (ephemeral ? 1 << 6 : 0)
179
- @client.http.post("/interactions/#{@id}/#{@token}/callback", { type: 7, data: payload }).wait
186
+ @client.http.request(Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post), { type: 7, data: payload }).wait
180
187
  end
181
188
  end
182
189
  end
183
190
 
191
+ #
192
+ # A module for response with modal.
193
+ #
194
+ module ModalResponder
195
+ #
196
+ # Response with `MODAL`(`9`).
197
+ #
198
+ # @param [String] title The title of the modal.
199
+ # @param [String] custom_id The custom id of the modal.
200
+ # @param [Array<Discorb::TextInput>] components The text inputs to send.
201
+ #
202
+ # @return [Async::Task<void>] The task.
203
+ #
204
+ def show_modal(title, custom_id, components)
205
+ payload = { title: title, custom_id: custom_id, components: Component.to_payload(components) }
206
+ @client.http.request(
207
+ Route.new("/interactions/#{@id}/#{@token}/callback", "//interactions/:interaction_id/:token/callback", :post),
208
+ { type: 9, data: payload }
209
+ ).wait
210
+ end
211
+ end
212
+
184
213
  private
185
214
 
186
215
  def _set_data(*)
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  module Discorb
2
3
  #
3
4
  # Represents a user interaction with the bot.
@@ -1,3 +1,4 @@
1
- %w[root response command components autocomplete].each do |file|
1
+ # frozen_string_literal: true
2
+ %w[root response command components autocomplete modal].each do |file|
2
3
  require_relative "interaction/#{file}.rb"
3
4
  end
@@ -105,7 +105,7 @@ module Discorb
105
105
  #
106
106
  def delete!(reason: nil)
107
107
  Async do
108
- @client.http.delete("/invites/#{@code}", audit_log_reason: reason)
108
+ @client.http.request(Route.new("/invites/#{@code}", "//invites/:code", :delete), audit_log_reason: reason)
109
109
  end
110
110
  end
111
111