discorb 0.13.2 → 0.15.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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +2 -0
  3. data/.github/workflows/build_version.yml +1 -1
  4. data/.github/workflows/codeql-analysis.yml +70 -0
  5. data/.github/workflows/lint-push.yml +20 -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 +34 -0
  10. data/Gemfile +7 -3
  11. data/README.md +1 -1
  12. data/Rakefile +22 -22
  13. data/discorb.gemspec +13 -1
  14. data/docs/faq.md +8 -8
  15. data/examples/commands/bookmarker.rb +2 -1
  16. data/examples/commands/hello.rb +1 -0
  17. data/examples/commands/inspect.rb +3 -2
  18. data/examples/components/authorization_button.rb +2 -1
  19. data/examples/components/select_menu.rb +2 -1
  20. data/examples/extension/main.rb +1 -0
  21. data/examples/extension/message_expander.rb +1 -0
  22. data/examples/simple/eval.rb +3 -2
  23. data/examples/simple/ping_pong.rb +1 -0
  24. data/examples/simple/rolepanel.rb +1 -0
  25. data/examples/simple/wait_for_message.rb +4 -3
  26. data/exe/discorb +8 -7
  27. data/lib/discorb/allowed_mentions.rb +64 -0
  28. data/lib/discorb/app_command/command.rb +274 -0
  29. data/lib/discorb/app_command/handler.rb +168 -0
  30. data/lib/discorb/app_command.rb +2 -404
  31. data/lib/discorb/asset.rb +3 -1
  32. data/lib/discorb/{file.rb → attachment.rb} +42 -35
  33. data/lib/discorb/audit_logs.rb +3 -3
  34. data/lib/discorb/channel.rb +65 -61
  35. data/lib/discorb/client.rb +36 -33
  36. data/lib/discorb/common.rb +29 -22
  37. data/lib/discorb/components/button.rb +106 -0
  38. data/lib/discorb/components/select_menu.rb +157 -0
  39. data/lib/discorb/components/text_input.rb +96 -0
  40. data/lib/discorb/components.rb +11 -276
  41. data/lib/discorb/dictionary.rb +13 -2
  42. data/lib/discorb/embed.rb +40 -33
  43. data/lib/discorb/emoji.rb +21 -5
  44. data/lib/discorb/emoji_table.rb +1 -1
  45. data/lib/discorb/error.rb +4 -6
  46. data/lib/discorb/event.rb +13 -11
  47. data/lib/discorb/exe/about.rb +1 -0
  48. data/lib/discorb/exe/irb.rb +4 -3
  49. data/lib/discorb/exe/new.rb +6 -7
  50. data/lib/discorb/exe/run.rb +2 -1
  51. data/lib/discorb/exe/setup.rb +8 -5
  52. data/lib/discorb/exe/show.rb +1 -0
  53. data/lib/discorb/extend.rb +19 -14
  54. data/lib/discorb/extension.rb +5 -1
  55. data/lib/discorb/gateway.rb +82 -29
  56. data/lib/discorb/guild.rb +58 -80
  57. data/lib/discorb/guild_template.rb +5 -5
  58. data/lib/discorb/http.rb +52 -170
  59. data/lib/discorb/integration.rb +32 -3
  60. data/lib/discorb/intents.rb +9 -4
  61. data/lib/discorb/interaction/autocomplete.rb +5 -4
  62. data/lib/discorb/interaction/command.rb +34 -9
  63. data/lib/discorb/interaction/components.rb +5 -2
  64. data/lib/discorb/interaction/modal.rb +33 -0
  65. data/lib/discorb/interaction/response.rb +41 -12
  66. data/lib/discorb/interaction/root.rb +1 -0
  67. data/lib/discorb/interaction.rb +2 -1
  68. data/lib/discorb/invite.rb +1 -1
  69. data/lib/discorb/log.rb +4 -3
  70. data/lib/discorb/member.rb +4 -6
  71. data/lib/discorb/message.rb +38 -241
  72. data/lib/discorb/message_meta.rb +157 -0
  73. data/lib/discorb/modules.rb +47 -23
  74. data/lib/discorb/permission.rb +2 -2
  75. data/lib/discorb/presence.rb +6 -3
  76. data/lib/discorb/rate_limit.rb +15 -21
  77. data/lib/discorb/role.rb +3 -3
  78. data/lib/discorb/sticker.rb +2 -2
  79. data/lib/discorb/user.rb +3 -3
  80. data/lib/discorb/utils/colored_puts.rb +1 -0
  81. data/lib/discorb/voice_state.rb +7 -2
  82. data/lib/discorb/webhook.rb +9 -6
  83. data/lib/discorb.rb +2 -1
  84. data/sig/discorb.rbs +5836 -6714
  85. data/template-replace/scripts/arrow.rb +1 -0
  86. data/template-replace/scripts/favicon.rb +1 -0
  87. data/template-replace/scripts/index.rb +2 -1
  88. data/template-replace/scripts/locale_ja.rb +5 -4
  89. data/template-replace/scripts/sidebar.rb +1 -0
  90. data/template-replace/scripts/version.rb +7 -10
  91. data/template-replace/scripts/yard_replace.rb +5 -4
  92. metadata +30 -5
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,133 +61,35 @@ 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?
93
- data << ["files[#{i}]", file.io, { filename: file.filename, content_type: file.content_type }]
73
+ if file.created_by == :discord
74
+ request_io = StringIO.new(
75
+ cdn_http.get(URI.parse(file.url).path, {
76
+ "Content-Type" => nil,
77
+ "User-Agent" => Discorb::USER_AGENT,
78
+ }).body
79
+ )
80
+ data << ["files[#{i}]", request_io, { filename: file.filename, content_type: file.content_type }]
81
+ else
82
+ data << ["files[#{i}]", file.io, { filename: file.filename, content_type: file.content_type }]
83
+ end
94
84
  end
95
85
  req.set_form(data, "multipart/form-data")
96
86
  session = Net::HTTP.new("discord.com", 443)
97
87
  session.use_ssl = true
98
88
  resp = session.request(req)
89
+ files&.then { _1.filter(&:will_close).each { |f| f.io.close } }
99
90
  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)
91
+ @ratelimit_handler.save(path, resp)
92
+ handle_response(resp, data, path, body, headers, audit_log_reason, kwargs)
211
93
  end
212
94
  end
213
95
 
@@ -217,16 +99,12 @@ module Discorb
217
99
 
218
100
  private
219
101
 
220
- def handle_response(method, resp, data, path, body, headers, audit_log_reason, kwargs)
102
+ def handle_response(resp, data, path, body, headers, audit_log_reason, kwargs)
221
103
  case resp.code
222
104
  when "429"
223
- @client.log.info("Rate limit exceeded for #{method} #{path}, waiting #{data[:retry_after]} seconds")
105
+ @client.log.info("Rate limit exceeded for #{path.method} #{path.url}, waiting #{data[:retry_after]} seconds")
224
106
  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
107
+ request(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
230
108
  when "400"
231
109
  raise BadRequestError.new(resp, data)
232
110
  when "401"
@@ -263,10 +141,10 @@ module Discorb
263
141
  end
264
142
 
265
143
  def get_path(path)
266
- full_path = if path.start_with?("https://")
267
- path
144
+ full_path = if path.url.start_with?("https://")
145
+ path.url
268
146
  else
269
- API_BASE_URL + path
147
+ API_BASE_URL + path.url
270
148
  end
271
149
  uri = URI(full_path)
272
150
  full_path.sub(uri.scheme + "://" + uri.host, "")
@@ -276,15 +154,13 @@ module Discorb
276
154
  begin
277
155
  data = JSON.parse(resp.body, symbolize_names: true)
278
156
  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)
157
+ data = if resp.body.nil? || resp.body.empty?
158
+ nil
159
+ else
160
+ resp.body
161
+ end
287
162
  end
163
+ raise CloudFlareBanError.new(resp, @client) if resp["Via"].nil? && resp.code == "429" && data.is_a?(String)
288
164
  data
289
165
  end
290
166
 
@@ -294,6 +170,12 @@ module Discorb
294
170
  https
295
171
  end
296
172
 
173
+ def cdn_http
174
+ https = Net::HTTP.new("cdn.discordapp.com", 443)
175
+ https.use_ssl = true
176
+ https
177
+ end
178
+
297
179
  def recr_utf8(data)
298
180
  case data
299
181
  when Hash
@@ -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
@@ -19,6 +19,7 @@ module Discorb
19
19
  dm_messages: 1 << 12,
20
20
  dm_reactions: 1 << 13,
21
21
  dm_typing: 1 << 14,
22
+ message_content: 1 << 15,
22
23
  scheduled_events: 1 << 16,
23
24
  }.freeze
24
25
 
@@ -39,9 +40,13 @@ module Discorb
39
40
  # @param dm_messages [Boolean] Whether dm messages related events are enabled.
40
41
  # @param dm_reactions [Boolean] Whether dm reactions related events are enabled.
41
42
  # @param dm_typing [Boolean] Whether dm typing related events are enabled.
43
+ # @param message_content [Boolean] Whether message content will be sent with events.
42
44
  # @param scheduled_events [Boolean] Whether events related scheduled events are enabled.
43
45
  #
44
46
  # @note You must enable privileged intents to use `members` and/or `presences` intents.
47
+ # @note Message Content Intent is not required to use `message_content` intents for now,
48
+ # this will be required in April 30, 2022. [Learn More](https://support-dev.discord.com/hc/en-us/articles/4404772028055).
49
+ # You should specify `message_content` intent for preventing unexpected changes in the future.
45
50
  #
46
51
  def initialize(guilds: true,
47
52
  members: false,
@@ -58,6 +63,7 @@ module Discorb
58
63
  dm_messages: true,
59
64
  dm_reactions: true,
60
65
  dm_typing: true,
66
+ message_content: nil,
61
67
  scheduled_events: true)
62
68
  @raw_value = {
63
69
  guilds: guilds,
@@ -75,6 +81,7 @@ module Discorb
75
81
  dm_messages: dm_messages,
76
82
  dm_reactions: dm_reactions,
77
83
  dm_typing: dm_typing,
84
+ message_content: message_content,
78
85
  scheduled_events: scheduled_events,
79
86
  }
80
87
  end
@@ -86,7 +93,7 @@ module Discorb
86
93
  if @raw_value.key?(name)
87
94
  @raw_value[name]
88
95
  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)
96
+ raise ArgumentError, "true/false expected" unless args.is_a?(TrueClass) || args.is_a?(FalseClass)
90
97
 
91
98
  @raw_value[name[0..-2].to_sym] = args
92
99
  else
@@ -129,9 +136,7 @@ module Discorb
129
136
 
130
137
  # Create new intent object with default values.
131
138
  # This will return intents without members and presence.
132
- def default
133
- from_value(@intent_bits.values.reduce(:+) - @intent_bits[:members] - @intent_bits[:presences])
134
- end
139
+ alias default new
135
140
 
136
141
  # Create new intent object with all intents.
137
142
  def all
@@ -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