discorb 0.13.1 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
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